pax_global_header00006660000000000000000000000064134714713720014523gustar00rootroot0000000000000052 comment=4d76ceb4aa7aaa1fd595368089e99575d708f719 ocfs2-tools-ocfs2-tools-1.8.6/000077500000000000000000000000001347147137200160615ustar00rootroot00000000000000ocfs2-tools-ocfs2-tools-1.8.6/.gitignore000066400000000000000000000002461347147137200200530ustar00rootroot00000000000000cscope.* *.a *.o stamp-md5 configure Config.make config.* autom4te.cache aclocal.m4 ocfs2-tools-*.src.rpm ocfs2-tools-*.tar.gz build-stamp configure-stamp *.pc *.sw? ocfs2-tools-ocfs2-tools-1.8.6/COPYING000066400000000000000000000431121347147137200171150ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 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. , 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. ocfs2-tools-ocfs2-tools-1.8.6/CREDITS000066400000000000000000000016571347147137200171120ustar00rootroot00000000000000fsck.ocfs2: Written by Zach Brown. fsck.ocfs2's behaviour is derived from e2fsck by Theodore Ts'o, Andreas Dilger, Stephen Tweedie, and others. libocfs2: Written by Joel Becker, Zach Brown, and Sunil Mushran Huge portions of libocfs2's API and behavior were derived from libext2fs by Theodore Ts'o, Andreas Dilger, Stephen Tweedie, and others. mkfs.ocfs2: Written by Kurt Hackel. Modified by Manish Singh, Joel Becker, and Mark Fasheh. debugfs.ocfs2: Written by Sunil Mushran. Modified by Mark Fasheh. mounted.ocfs2: Written by Sunil Mushran. tunefs.ocfs2 Written by Sunil Mushran. ocfs2cdsl: Written by Manish Singh. ocfs2console: Written by Manish Singh. o2info: Written by Tristan Ye. defrag.ocfs2: Written by Larry Chen. ocfs2console/blkid: From e2fsprogs 1.37, by Theodore Ts'o and Andreas Dilger. ocfs2console/ocfs2interface/ipwidget.py: From anaconda 10.1.1.13, by Jonathan Blandford and Michael Fulbright ocfs2-tools-ocfs2-tools-1.8.6/Config.make.in000066400000000000000000000036251347147137200205400ustar00rootroot00000000000000PACKAGE = @PACKAGE@ VERSION = @VERSION@ DIST_VERSION = @DIST_VERSION@ MAJOR_VERSION = @MAJOR_VERSION@ MINOR_VERSION = @MINOR_VERSION@ MICRO_VERSION = @MICRO_VERSION@ EXTRA_VERSION = @EXTRA_VERSION@ SHELL = @SHELL@ prefix = @prefix@ exec_prefix = @exec_prefix@ bindir = @bindir@ sbindir = @sbindir@ includedir = @includedir@ libdir = @libdir@ datadir = @datadir@ sysconfdir = @sysconfdir@ mandir = @mandir@ root_prefix = @root_prefix@ root_bindir = @root_bindir@ root_sbindir = @root_sbindir@ root_sysconfdir = @root_sysconfdir@ pyexecdir = @pyexecdir@ top_builddir = . INSTALL = @INSTALL@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_LIBRARY = @INSTALL_PROGRAM@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_HEADER = @INSTALL_DATA@ LN_S = @LN_S@ CC = @CC@ CPP = @CPP@ AR = @AR@ RANLIB = @RANLIB@ WARNINGS = -Wall -Wstrict-prototypes -Wmissing-prototypes \ -Wmissing-declarations CFLAGS = @CFLAGS@ CFLAGS += $(WARNINGS) CPPFLAGS = @CPPFLAGS@ LDFLAGS = @LDFLAGS@ VENDOR = @VENDOR@ COM_ERR_CFLAGS = @COM_ERR_CFLAGS@ COM_ERR_LIBS = @COM_ERR_LIBS@ UUID_LIBS = @UUID_LIBS@ AIO_LIBS = @AIO_LIBS@ READLINE_LIBS = @READLINE_LIBS@ NCURSES_LIBS = @NCURSES_LIBS@ GLIB_CFLAGS = @GLIB_CFLAGS@ GLIB_LIBS = @GLIB_LIBS@ PYTHON = @PYTHON@ PYTHON_INCLUDES = @PYTHON_INCLUDES@ BLKID_CFLAGS = @BLKID_CFLAGS@ BLKID_LIBS = @BLKID_LIBS@ HAVE_BLKID = @HAVE_BLKID@ LIBDLM_FOUND = @LIBDLM_FOUND@ BUILD_OCFS2CONSOLE = @BUILD_OCFS2CONSOLE@ BUILD_DEBUGOCFS2 = @BUILD_DEBUGOCFS2@ HAVE_COROSYNC = @HAVE_COROSYNC@ BUILD_OCFS2_CONTROLD = @BUILD_OCFS2_CONTROLD@ BUILD_PCMK_SUPPORT = @BUILD_PCMK_SUPPORT@ BUILD_CMAN_SUPPORT = @BUILD_CMAN_SUPPORT@ BUILD_CMAP_SUPPORT = @BUILD_CMAP_SUPPORT@ BUILD_FSDLM_SUPPORT = @BUILD_FSDLM_SUPPORT@ CPG_LDFLAGS = @CPG_LDFLAGS@ AIS_LDFLAGS = @AIS_LDFLAGS@ DL_LIBS = @DL_LIBS@ OCFS2_DEBUG = @OCFS2_DEBUG@ OCFS2_DEBUG_EXE = @OCFS2_DEBUG_EXE@ OCFS2_DYNAMIC_FSCK = @OCFS2_DYNAMIC_FSCK@ OCFS2_DYNAMIC_CTL = @OCFS2_DYNAMIC_CTL@ ocfs2-tools-ocfs2-tools-1.8.6/MAINTAINERS000066400000000000000000000007011347147137200175540ustar00rootroot00000000000000fsck.ocfs2: Zach Brown Joel Becker libocfs2: Joel Becker Zach Brown ocfs2tool: Manish Singh ocfs2cdsl: Manish Singh debugfs.ocfs2: Sunil Mushran mounted.ocfs2: Sunil Mushran tunefs.ocfs2: Sunil Mushran ocfs2-tools-ocfs2-tools-1.8.6/Makefile000066400000000000000000000057251347147137200175320ustar00rootroot00000000000000TOPDIR = . include $(TOPDIR)/Preamble.make RPM_TOPDIR = $(CURDIR) RPMBUILD = $(shell /usr/bin/which rpmbuild 2>/dev/null || /usr/bin/which rpm 2>/dev/null || echo /bin/false) SUSEBUILD = $(shell if test -r /etc/SuSE-release; then echo yes; else echo no; fi) PYVERSION = $(shell echo $(pyexecdir) | sed -e 's/.*python\([0-9]\.[0-9]\).*/\1/') ifeq ($(SUSEBUILD),yes) PYGTK_NAME = python-gtk CHKCONFIG_DEP = aaa_base COMPILE_PY = 0 else PYGTK_NAME = pygtk2 CHKCONFIG_DEP = chkconfig COMPILE_PY = 1 endif SUBDIRS1 = include SUBDIRS2 = libtools-internal libo2dlm libo2cb SUBDIRS3 = libocfs2 SUBDIRS4 = fsck.ocfs2 mkfs.ocfs2 mounted.ocfs2 tunefs.ocfs2 debugfs.ocfs2 o2cb_ctl ocfs2_hb_ctl mount.ocfs2 ocfs2_controld o2image o2info o2monitor extras fswreck patches defragfs.ocfs2 ifdef BUILD_OCFS2CONSOLE SUBDIRS4 += ocfs2console endif SUBDIRS4 += vendor $(SUBDIRS2): $(SUBDIRS1) $(SUBDIRS3): $(SUBDIRS2) $(SUBDIRS4): $(SUBDIRS3) SUBDIRS = $(SUBDIRS1) $(SUBDIRS2) $(SUBDIRS3) $(SUBDIRS4) PKGCONFIG_SOURCES = \ o2cb.pc.in \ o2dlm.pc.in \ ocfs2.pc.in PKGCONFIG_FILES = $(patsubst %.pc.in,%.pc,$(PKGCONFIG_SOURCES)) DEBIAN_FILES = \ debian/README.Debian \ debian/changelog \ debian/compat \ debian/control \ debian/copyright \ debian/ocfs2-tools.config \ debian/ocfs2-tools.docs \ debian/ocfs2-tools.install \ debian/ocfs2-tools.manpages \ debian/ocfs2-tools.postinst \ debian/ocfs2-tools.postrm \ debian/ocfs2-tools.templates \ debian/ocfs2console.install \ debian/ocfs2console.manpages \ debian/ocfs2-tools-static-dev.install \ debian/rules DIST_FILES = \ COPYING \ CREDITS \ MAINTAINERS \ README \ README.O2CB \ Config.make.in \ Preamble.make \ Postamble.make \ aclocal.m4 \ blkid.m4 \ glib-2.0.m4 \ mbvendor.m4 \ python.m4 \ pythondev.m4 \ runlog.m4 \ config.guess \ config.sub \ configure \ configure.in \ install-sh \ mkinstalldirs \ rpmarch.guess \ svnrev.guess \ Vendor.make \ vendor.guess \ Makepdfs \ documentation/samples/cluster.conf \ documentation/users_guide.txt \ $(PKGCONFIG_SOURCES) \ $(DEBIAN_FILES) DIST_RULES = dist-subdircreate .PHONY: dist dist-subdircreate dist-bye dist-fresh distclean dist-subdircreate: $(TOPDIR)/mkinstalldirs $(DIST_DIR)/documentation/samples $(TOPDIR)/mkinstalldirs $(DIST_DIR)/debian dist-bye: -rm -rf $(DIST_TOPDIR) dist-fresh: dist-bye $(TOPDIR)/mkinstalldirs $(DIST_TOPDIR) dist: dist-fresh dist-all GZIP=$(GZIP_OPTS) tar chozf $(DIST_TOPDIR).tar.gz $(DIST_TOPDIR) $(MAKE) dist-bye distclean: clean rm -f Config.make config.status config.cache config.log $(PKGCONFIG_FILES) INSTALL_RULES = install-pkgconfig install-pkgconfig: $(PKGCONFIG_FILES) $(SHELL) $(TOPDIR)/mkinstalldirs $(DESTDIR)$(libdir)/pkgconfig for p in $(PKGCONFIG_FILES); do \ $(INSTALL_DATA) $$p $(DESTDIR)$(libdir)/pkgconfig/$$p; \ done include Vendor.make def: @echo $(TOOLSARCH) include $(TOPDIR)/Postamble.make ocfs2-tools-ocfs2-tools-1.8.6/Makepdfs000066400000000000000000000066471347147137200175530ustar00rootroot00000000000000# Converts the manpages to pdf and then merges them to one ocfs2-manpages.pdf OCFS2_7 = libocfs2/ocfs2.7 O2CB_7 = libo2cb/o2cb.7 O2CB_8 = o2cb_ctl/o2cb.8 CLUSCONF_5 = o2cb_ctl/ocfs2.cluster.conf.5 SYSCONFIG_5 = vendor/common/o2cb.sysconfig.5 MKFS_8 = mkfs.ocfs2/mkfs.ocfs2.8 MOUNT_8 = mount.ocfs2/mount.ocfs2.8 MOUNTED_8 = mounted.ocfs2/mounted.ocfs2.8 TUNEFS_8 = tunefs.ocfs2/tunefs.ocfs2.8 O2CLUSTER_8 = tunefs.ocfs2/o2cluster.8 O2INFO_1 = o2info/o2info.1 DEBUGFS_8 = debugfs.ocfs2/debugfs.ocfs2.8 O2IMAGE_8 = o2image/o2image.8 O2HBMON_8 = o2monitor/o2hbmonitor.8 HBCTL_8 = ocfs2_hb_ctl/ocfs2_hb_ctl.8 FSCK_8 = fsck.ocfs2/fsck.ocfs2.8 FSCHECKS_8 = fsck.ocfs2/fsck.ocfs2.checks.8 OCFS2_7_PDF = $(addsuffix .pdf, $(OCFS2_7)) O2CB_7_PDF = $(addsuffix .pdf, $(O2CB_7)) O2CB_8_PDF = $(addsuffix .pdf, $(O2CB_8)) CLUSCONF_5_PDF = $(addsuffix .pdf, $(CLUSCONF_5)) SYSCONFIG_5_PDF = $(addsuffix .pdf, $(SYSCONFIG_5)) MKFS_8_PDF = $(addsuffix .pdf, $(MKFS_8)) MOUNT_8_PDF = $(addsuffix .pdf, $(MOUNT_8)) MOUNTED_8_PDF = $(addsuffix .pdf, $(MOUNTED_8)) TUNEFS_8_PDF = $(addsuffix .pdf, $(TUNEFS_8)) O2CLUSTER_8_PDF = $(addsuffix .pdf, $(O2CLUSTER_8)) O2INFO_1_PDF = $(addsuffix .pdf, $(O2INFO_1)) DEBUGFS_8_PDF = $(addsuffix .pdf, $(DEBUGFS_8)) O2IMAGE_8_PDF = $(addsuffix .pdf, $(O2IMAGE_8)) O2HBMON_8_PDF = $(addsuffix .pdf, $(O2HBMON_8)) HBCTL_8_PDF = $(addsuffix .pdf, $(HBCTL_8)) FSCK_8_PDF = $(addsuffix .pdf, $(FSCK_8)) FSCHECKS_8_PDF = $(addsuffix .pdf, $(FSCHECKS_8)) ALL_PDFS = $(OCFS2_7_PDF) $(O2CB_7_PDF) $(O2CB_8_PDF) $(CLUSCONF_5_PDF) \ $(SYSCONFIG_5_PDF) $(MKFS_8_PDF) $(MOUNT_8_PDF) $(MOUNTED_8_PDF) \ $(TUNEFS_8_PDF) $(O2CLUSTER_8_PDF) $(O2INFO_8_PDF) $(DEBUGFS_8_PDF) \ $(O2IMAGE_8_PDF) $(O2HBMON_8_PDF) $(HBCTL_8_PDF) $(FSCK_8_PDF) \ $(FSCHECKS_8_PDF) $(OCFS2_7_PDF): $(OCFS2_7) groff -man -t -T ps -P-pletter $< > $<.ps ps2pdf $<.ps $@ $(O2CB_7_PDF): $(O2CB_7) groff -man -t -T ps -P-pletter $< > $<.ps ps2pdf $<.ps $@ $(O2CB_8_PDF): $(O2CB_8) groff -man -t -T ps -P-pletter $< > $<.ps ps2pdf $<.ps $@ $(CLUSCONF_5_PDF): $(CLUSCONF_5) groff -man -t -T ps -P-pletter $< > $<.ps ps2pdf $<.ps $@ $(SYSCONFIG_5_PDF): $(SYSCONFIG_5) groff -man -t -T ps -P-pletter $< > $<.ps ps2pdf $<.ps $@ $(MKFS_8_PDF): $(MKFS_8) groff -man -t -T ps -P-pletter $< > $<.ps ps2pdf $<.ps $@ $(MOUNT_8_PDF): $(MOUNT_8) groff -man -t -T ps -P-pletter $< > $<.ps ps2pdf $<.ps $@ $(MOUNTED_8_PDF): $(MOUNTED_8) groff -man -t -T ps -P-pletter $< > $<.ps ps2pdf $<.ps $@ $(TUNEFS_8_PDF): $(TUNEFS_8) groff -man -t -T ps -P-pletter $< > $<.ps ps2pdf $<.ps $@ $(O2CLUSTER_8_PDF): $(O2CLUSTER_8) groff -man -t -T ps -P-pletter $< > $<.ps ps2pdf $<.ps $@ $(O2INFO_1_PDF): $(O2INFO_1) groff -man -t -T ps -P-pletter $< > $<.ps ps2pdf $<.ps $@ $(DEBUGFS_8_PDF): $(DEBUGFS_8) groff -man -t -T ps -P-pletter $< > $<.ps ps2pdf $<.ps $@ $(O2IMAGE_8_PDF): $(O2IMAGE_8) groff -man -t -T ps -P-pletter $< > $<.ps ps2pdf $<.ps $@ $(O2HBMON_8_PDF): $(O2HBMON_8) groff -man -t -T ps -P-pletter $< > $<.ps ps2pdf $<.ps $@ $(HBCTL_8_PDF): $(HBCTL_8) groff -man -t -T ps -P-pletter $< > $<.ps ps2pdf $<.ps $@ $(FSCK_8_PDF): $(FSCK_8) groff -man -t -T ps -P-pletter $< > $<.ps ps2pdf $<.ps $@ $(FSCHECKS_8_PDF): $(FSCHECKS_8) groff -man -t -T ps -P-pletter $< > $<.ps ps2pdf $<.ps $@ ocfs2-manpages.pdf: $(ALL_PDFS) pdftk $(ALL_PDFS) cat output $@ all: ocfs2-manpages.pdf clean: @rm -f ocfs2-manpages.pdf $(ALL_PDFS) $(subst .pdf,.ps,$(ALL_PDFS)) 2>/dev/null ocfs2-tools-ocfs2-tools-1.8.6/Postamble.make000066400000000000000000000116431347147137200206530ustar00rootroot00000000000000ifdef VERSION_SRC ifndef VERSION_FILES $(error Need VERSION_FILES defined for version objects) endif ifndef VERSION_PREFIX $(error Need VERSION_PREFIX defined for version objects) endif VERSION_OBJ = $(subst .c,.o,$(VERSION_SRC)) VERSION_NUM = $(VERSION_PREFIX)_BUILD_VERSION VERSION_DATE = $(VERSION_PREFIX)_BUILD_DATE VERSION_MD5 = $(VERSION_PREFIX)_BUILD_MD5 VERDEFS = -D$(VERSION_NUM)=\""$(VERSION)"\" \ -D$(VERSION_DATE)=\""$(shell LANG=C date)"\" \ -D$(VERSION_MD5)=\""$(shell cat stamp-md5)"\" VERMAGIC = $(if $(filter $(VERSION_OBJ),$@),$(VERDEFS)) ifneq ($(MAKECMDGOALS),install) VERSTAMP = stamp endif stamp: ; stamp-md5: $(VERSION_FILES) @cat $(VERSION_FILES) Makefile | md5sum | sed -e 's/ .*//' > stamp-md5 $(VERSION_OBJ): stamp-md5 $(VERSTAMP) endif LOCAL_CFLAGS = $($(subst /,_,$(basename $@))_CFLAGS) LOCAL_CPPFLAGS = $($(subst /,_,$(basename $@))_CPPFLAGS) %.o: %.c $(CC) $(CFLAGS) $(LOCAL_CFLAGS) $(CPPFLAGS) $(LOCAL_CPPFLAGS) $(INCLUDES) $(DEFINES) $(VERMAGIC) $(CDEPFLAGS) -o $@ -c $< %.p: %.c $(CC) $(CFLAGS) $(LOCAL_CFLAGS) $(CPPFLAGS) $(LOCAL_CPPFLAGS) $(INCLUDES) $(DEFINES) $(VERMAGIC) -E -o $@ -c $< %.s: %.c $(CC) $(CFLAGS) $(LOCAL_CFLAGS) $(CPPFLAGS) $(LOCAL_CPPFLAGS) $(INCLUDES) $(DEFINES) $(VERMAGIC) -S -o $@ -c $< .PHONY: subdirs $(SUBDIRS) subdirs: $(SUBDIRS) $(SUBDIRS): $(MAKE) -C $@ .PHONY: all-rules all-rules: subdirs $(UNINST_LIBRARIES) $(LIBRARIES) $(BIN_PROGRAMS) $(SBIN_PROGRAMS) $(UNINST_PROGRAMS) $(MODULES) $(MANS) $(ALL_RULES) $(SBIN_EXTRA) $(BIN_EXTRA) INSTALL_SUBDIRS = $(addsuffix -install,$(SUBDIRS)) .PHONY: install-rules install-subdirs $(INSTALL_RULES) install-libraries install-headers install-bin-programs install-bin-extra install-sbin-programs install-sbin-extra install-subdirs: $(INSTALL_SUBDIRS) $(INSTALL_SUBDIRS): $(MAKE) -C $(subst -install,,$@) install install-libraries: $(LIBRARIES) ifdef LIBRARIES $(SHELL) $(TOPDIR)/mkinstalldirs $(DESTDIR)$(libdir) for lib in $(LIBRARIES); do \ $(INSTALL_LIBRARY) $$lib $(DESTDIR)$(libdir)/$$lib; \ done endif ifeq ($(filter /%,$(HEADERS_SUBDIR)),) Hsubdir = /$(HEADERS_SUBDIR) else Hsubdir = $(HEADERS_SUBDIR) endif ifeq ($(filter include/%,$(HEADERS)),) Hinstall = else Hinstall = include/ endif install-headers: $(HEADERS) ifdef HEADERS $(SHELL) $(TOPDIR)/mkinstalldirs $(DESTDIR)$(includedir)$(Hsubdir) for hdr in $(patsubst include/%,%,$(HEADERS)); do \ $(INSTALL_HEADER) $(Hinstall)$$hdr $(DESTDIR)$(includedir)$(Hsubdir)/$$hdr; \ done endif install-bin-programs: $(BIN_PROGRAMS) ifdef BIN_PROGRAMS $(SHELL) $(TOPDIR)/mkinstalldirs $(DESTDIR)$(bindir) for prog in $(BIN_PROGRAMS); do \ $(INSTALL_PROGRAM) $$prog $(DESTDIR)$(bindir)/$$prog; \ done endif install-bin-extra: $(BIN_EXTRA) ifdef BIN_EXTRA $(SHELL) $(TOPDIR)/mkinstalldirs $(DESTDIR)$(bindir) for prog in $(BIN_EXTRA); do \ $(INSTALL_PROGRAM) $$prog $(DESTDIR)$(bindir)/$$prog; \ done endif install-sbin-programs: $(SBIN_PROGRAMS) ifdef SBIN_PROGRAMS $(SHELL) $(TOPDIR)/mkinstalldirs $(DESTDIR)$(sbindir) for prog in $(SBIN_PROGRAMS); do \ $(INSTALL_PROGRAM) $$prog $(DESTDIR)$(sbindir)/$$prog; \ done endif install-sbin-extra: $(SBIN_EXTRA) ifdef SBIN_EXTRA $(SHELL) $(TOPDIR)/mkinstalldirs $(DESTDIR)$(sbindir) for prog in $(SBIN_EXTRA); do \ $(INSTALL_PROGRAM) $$prog $(DESTDIR)$(sbindir)/$$prog; \ done endif install-mans: $(MANS) ifdef MANS $(SHELL) $(TOPDIR)/mkinstalldirs $(DESTDIR)$(mandir) for man in $(MANS); do \ dir=`echo $$man | sed -e 's/^.*\\./man/'`; \ $(SHELL) $(TOPDIR)/mkinstalldirs $(DESTDIR)$(mandir)/$$dir; \ $(INSTALL_DATA) $$man $(DESTDIR)$(mandir)/$$dir/$$man; \ done endif install-rules: install-subdirs $(INSTALL_RULES) install-libraries install-headers install-bin-programs install-bin-extra install-sbin-programs install-sbin-extra install-mans CLEAN_SUBDIRS = $(addsuffix -clean,$(SUBDIRS)) .PHONY: clean clean-subdirs $(CLEAN_RULES) $(CLEAN_SUBDIRS) clean-subdirs: $(CLEAN_SUBDIRS) $(CLEAN_SUBDIRS): $(MAKE) -C $(subst -clean,,$@) clean clean: clean-subdirs $(CLEAN_RULES) rm -f *.o *.p .*.d core $(BIN_PROGRAMS) $(SBIN_PROGRAMS) $(LIBRARIES) $(UNINST_PROGRAMS) $(UNINST_LIBRARIES) $(SBIN_EXTRA) $(BIN_EXTRA) stamp-md5 DIST_SUBDIRS = $(addsuffix -dist,$(SUBDIRS)) .PHONY: dist-all dist-mkdir dist-copy dist-subdirs $(DIST_RULES) $(DIST_SUBDIRS) dist-subdirs: $(DIST_SUBDIRS) $(DIST_SUBDIRS): $(MAKE) -C $(subst -dist,,$@) dist-all \ DIST_CURDIR=$(DIST_CURDIR)/$(subst -dist,,$@) dist-mkdir: $(SHELL) $(TOPDIR)/mkinstalldirs $(DIST_DIR) DIST_ALL_FILES = Makefile $(MANS) $(VERSION_FILES) $(DIST_FILES) dist-copy: dist-mkdir $(DIST_ALL_FILES) $(DIST_RULES) @for file in $(DIST_ALL_FILES); do \ echo " cp -p $$file $(DIST_DIR)/$$file"; \ cp -p $$file $(DIST_DIR)/$$file; \ done dist-all: dist-copy dist-subdirs LOCAL_DFILES := $(wildcard .*.d) ifneq ($(LOCAL_DFILES),) .PHONY: $(LOCAL_DFILES) -include $(LOCAL_DFILES) endif ifeq (Cscope.make,$(wildcard Cscope.make)) include Cscope.make endif ocfs2-tools-ocfs2-tools-1.8.6/Preamble.make000066400000000000000000000015131347147137200204470ustar00rootroot00000000000000ifeq ($(TOPDIR)/Config.make,$(wildcard $(TOPDIR)/Config.make)) include $(TOPDIR)/Config.make else .PHONY: dummy-notconfigured dummy-notconfigured: @echo "Please run the configure script first" endif LIBRARIES = UNINST_LIBRARIES = BIN_PROGRAMS = SBIN_PROGRAMS = UNINST_PROGRAMS = MODULES = HEADERS = MANS = HEADERS_SUBDIR = INSTALL_RULES = CLEAN_FILES = CLEAN_RULES = DIST_FILES = DIST_RULES = INCLUDES = DEFINES = CFLAGS += -pipe -D_DEFAULT_SOURCE=1 # protect with configure? CDEPFLAGS = -MD -MP -MF $(@D)/.$(basename $(@F)).d LINK = $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^ VERSION_FILES = VERSION_PREFIX = VERSION_SRC = DIST_TOPDIR = $(TOPDIR)/$(PACKAGE)-$(DIST_VERSION) DIST_CURDIR = . DIST_DIR = $(DIST_TOPDIR)/$(DIST_CURDIR) GZIP_OPTS = --best .PHONY: all strip install all: all-rules strip: strip-rules install: install-rules ocfs2-tools-ocfs2-tools-1.8.6/README000066400000000000000000000000521347147137200167360ustar00rootroot00000000000000These are tools for the OCFS2 filesystem. ocfs2-tools-ocfs2-tools-1.8.6/README.O2CB000066400000000000000000000070561347147137200174350ustar00rootroot00000000000000 O2CB is a simple set of clustering services required to get OCFS2 going. A more complete cluster infrastructure may replace it later. [Describing Your Cluster Configuration] It is recommended that you use ocfs2console to generate your cluster configuration. The cluster confguration is stored in /etc/ocfs2/cluster.conf. This is a hardcoded name right now (boo!), so you have to put it there. The file is in stanza format, with one stanza describing the generic cluster attributes and one stanza for each node. The stanza for the generic cluster attributes looks like so: cluster: name = ocfs2 node_count = 2 Both attributes are required. The cluster name must start with a letter and be made of the usual [-a-ZA-Z0-9_] characters. The node_count attribute must match the number of node stanzas. Each node is described by a stanza: node: name = ca-test17 cluster = ocfs2 number = 0 ip_address = 139.185.118.117 ip_port = 7777 Each attribute is required. The name has the same character restrictions that the cluster name does. The cluster attribute _must_ match the name of the cluster, or this node stanza will be ignored. [Starting the OCFS2 Clustering Services] O2CB is comprised of a simple node manager, heartbeat, TCP protocol, and DLM. The basic steps in startup are as follows: 1) Load the nodemanager, heartbeat, and tcp modules 2) Mount the nodemanager and heartbeat pseudo filesystems. 3) Load the cluster information via o2cb_ctl. This adds all the known nodes to the nodemanager's psuedo filesystem. 4) Load the DLM module. 5) Load the OCFS2 module. 6) Mount an OCFS2 filesystem. The /etc/init.d/o2cb program is responsible for handling steps 1-3 during the boot of a machine. If you are running from the source tree, the script is located at vendor/common/o2cb.init. Also, the o2cb_ctl program needs to be in your path. It lives at o2cb_ctl/o2cb_ctl in the source tree. To enable steps 1 and 2 on boot: # /etc/init.d/o2cb enable Writing O2CB configuration: OK or: # /etc/init.d/o2cb configure Configuring the O2CB driver. This will configure the on-boot properties of the O2CB driver. The following questions will determine whether the driver is loaded on boot. The current values will be shown in brackets ('[]'). Hitting without typing an answer will keep that current value. Ctrl-C will abort. Load O2CB driver on boot (y/n) [n]: y Cluster to start on boot (Enter "none" to clear) []: Writing O2CB configuration: OK If you want step 3 to occur on boot, you must name the cluster to start: # /etc/init.d/o2cb configure Configuring the O2CB driver. This will configure the on-boot properties of the O2CB driver. The following questions will determine whether the driver is loaded on boot. The current values will be shown in brackets ('[]'). Hitting without typing an answer will keep that current value. Ctrl-C will abort. Load O2CB driver on boot (y/n) [n]: y Cluster to start on boot (Enter "none" to clear) []: ocfs2 Writing O2CB configuration: OK You can now start the cluster by hand as well, with: # /etc/init.d/o2cb start If you don't want to configure O2CB to start on boot, or if you don't want to configure a specific cluster to start by default, you can run the steps manually. To run steps 1 and 2: # /etc/init.d/o2cb load To start a cluster (step 3): # /etc/init.d/o2cb online ocfs2 To stop a cluster and unload the modules, you simply use: # /etc/init.d/o2cb stop Unloading the modules doesn't work right now. ocfs2-tools-ocfs2-tools-1.8.6/Vendor.make000066400000000000000000000003441347147137200201560ustar00rootroot00000000000000 # # This Makefile snippet is to be included in Makebo Makefiles that # use the mbvendor infrastructure # PKG_VERSION = $(shell $(TOPDIR)/svnrev.guess $(PACKAGE)) -include $(TOPDIR)/vendor/$(shell ./vendor.guess)/Vendor.make ocfs2-tools-ocfs2-tools-1.8.6/aclocal.m4000066400000000000000000000002531347147137200177210ustar00rootroot00000000000000m4_include([pkg.m4]) m4_include([glib-2.0.m4]) m4_include([runlog.m4]) m4_include([python.m4]) m4_include([pythondev.m4]) m4_include([blkid.m4]) m4_include([mbvendor.m4]) ocfs2-tools-ocfs2-tools-1.8.6/autogen.sh000077500000000000000000000001301347147137200200540ustar00rootroot00000000000000#!/bin/sh set -e PROJECT=ocfs2-tools rm -rf autom4te.cache autoconf ./configure "$@" ocfs2-tools-ocfs2-tools-1.8.6/blkid.m4000066400000000000000000000046631347147137200174210ustar00rootroot00000000000000dnl Support for libblkid included in our tree for ocfs2console AC_DEFUN([OCFS2_BLKID], [ HAVE_BLKID= PKG_CHECK_MODULES(BLKID, blkid >= 1.36, HAVE_BLKID=yes, [AC_MSG_WARN([blkid >= 1.36 not found, using internal version])]) AC_SUBST(HAVE_BLKID) if test "x$HAVE_BLKID" != "xyes"; then AC_CHECK_SIZEOF(short) AC_CHECK_SIZEOF(int) AC_CHECK_SIZEOF(long) AC_CHECK_SIZEOF(long long) AC_CONFIG_COMMANDS([ocfs2console/blkid/blkid_types.h], [ outfile=ocfs2console/blkid/blkid_types.h-tmp cat > $outfile <<_______EOF /* * If linux/types.h is already been included, assume it has defined * everything we need. (cross fingers) Other header files may have * also defined the types that we need. */ #if (!defined(_LINUX_TYPES_H) && !defined(_BLKID_TYPES_H) && \\ !defined(_EXT2_TYPES_H)) #define _BLKID_TYPES_H typedef unsigned char __u8; typedef signed char __s8; #if ($ocfs2_SIZEOF_INT == 8) typedef int __s64; typedef unsigned int __u64; #else #if ($ocfs2_SIZEOF_LONG == 8) typedef long __s64; typedef unsigned long __u64; #else #if ($ocfs2_SIZEOF_LONG_LONG == 8) #if defined(__GNUC__) typedef __signed__ long long __s64; #else typedef signed long long __s64; #endif /* __GNUC__ */ typedef unsigned long long __u64; #endif /* SIZEOF_LONG_LONG == 8 */ #endif /* SIZEOF_LONG == 8 */ #endif /* SIZEOF_INT == 8 */ #if ($ocfs2_SIZEOF_INT == 2) typedef int __s16; typedef unsigned int __u16; #else #if ($ocfs2_SIZEOF_SHORT == 2) typedef short __s16; typedef unsigned short __u16; #else ?==error: undefined 16 bit type #endif /* SIZEOF_SHORT == 2 */ #endif /* SIZEOF_INT == 2 */ #if ($ocfs2_SIZEOF_INT == 4) typedef int __s32; typedef unsigned int __u32; #else #if ($ocfs2_SIZEOF_LONG == 4) typedef long __s32; typedef unsigned long __u32; #else #if ($ocfs2_SIZEOF_SHORT == 4) typedef short __s32; typedef unsigned short __u32; #else ?== error: undefined 32 bit type #endif /* SIZEOF_SHORT == 4 */ #endif /* SIZEOF_LONG == 4 */ #endif /* SIZEOF_INT == 4 */ #endif /* _*_TYPES_H */ _______EOF if cmp -s $outfile ocfs2console/blkid/blkid_types.h; then AC_MSG_NOTICE([ocfs2console/blkid/blkid_types.h is unchanged]) rm -f $outfile else mv $outfile ocfs2console/blkid/blkid_types.h fi ],[ ocfs2_SIZEOF_SHORT=$ac_cv_sizeof_short ocfs2_SIZEOF_INT=$ac_cv_sizeof_int ocfs2_SIZEOF_LONG=$ac_cv_sizeof_long ocfs2_SIZEOF_LONG_LONG=$ac_cv_sizeof_long_long ]) fi ]) ocfs2-tools-ocfs2-tools-1.8.6/config.guess000077500000000000000000001247021347147137200204070ustar00rootroot00000000000000#! /bin/sh # Attempt to guess a canonical system name. # Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, # 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc. timestamp='2004-11-12' # This file 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. # Originally written by Per Bothner . # Please send patches to . Submit a context # diff and a properly formatted ChangeLog entry. # # This script attempts to guess a canonical system name similar to # config.sub. If it succeeds, it prints the system name on stdout, and # exits with 0. Otherwise, it exits with 1. # # The plan is that this can be called by configure scripts if you # don't specify an explicit build system type. me=`echo "$0" | sed -e 's,.*/,,'` usage="\ Usage: $0 [OPTION] Output the configuration name of the system \`$me' is run on. Operation modes: -h, --help print this help, then exit -t, --time-stamp print date of last modification, then exit -v, --version print version number, then exit Report bugs and patches to ." version="\ GNU config.guess ($timestamp) Originally written by Per Bothner. Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." help=" Try \`$me --help' for more information." # Parse command line while test $# -gt 0 ; do case $1 in --time-stamp | --time* | -t ) echo "$timestamp" ; exit 0 ;; --version | -v ) echo "$version" ; exit 0 ;; --help | --h* | -h ) echo "$usage"; exit 0 ;; -- ) # Stop option processing shift; break ;; - ) # Use stdin as input. break ;; -* ) echo "$me: invalid option $1$help" >&2 exit 1 ;; * ) break ;; esac done if test $# != 0; then echo "$me: too many arguments$help" >&2 exit 1 fi trap 'exit 1' 1 2 15 # CC_FOR_BUILD -- compiler used by this script. Note that the use of a # compiler to aid in system detection is discouraged as it requires # temporary files to be created and, as you can see below, it is a # headache to deal with in a portable fashion. # Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still # use `HOST_CC' if defined, but it is deprecated. # Portable tmp directory creation inspired by the Autoconf team. set_cc_for_build=' trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ; trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ; : ${TMPDIR=/tmp} ; { tmp=`(umask 077 && mktemp -d -q "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } || { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } || { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } || { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ; dummy=$tmp/dummy ; tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ; case $CC_FOR_BUILD,$HOST_CC,$CC in ,,) echo "int x;" > $dummy.c ; for c in cc gcc c89 c99 ; do if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then CC_FOR_BUILD="$c"; break ; fi ; done ; if test x"$CC_FOR_BUILD" = x ; then CC_FOR_BUILD=no_compiler_found ; fi ;; ,,*) CC_FOR_BUILD=$CC ;; ,*,*) CC_FOR_BUILD=$HOST_CC ;; esac ;' # This is needed to find uname on a Pyramid OSx when run in the BSD universe. # (ghazi@noc.rutgers.edu 1994-08-24) if (test -f /.attbin/uname) >/dev/null 2>&1 ; then PATH=$PATH:/.attbin ; export PATH fi UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown # Note: order is significant - the case branches are not exclusive. case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in *:NetBSD:*:*) # NetBSD (nbsd) targets should (where applicable) match one or # more of the tupples: *-*-netbsdelf*, *-*-netbsdaout*, # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently # switched to ELF, *-*-netbsd* would select the old # object file format. This provides both forward # compatibility and a consistent mechanism for selecting the # object file format. # # Note: NetBSD doesn't particularly care about the vendor # portion of the name. We always set it to "unknown". sysctl="sysctl -n hw.machine_arch" UNAME_MACHINE_ARCH=`(/sbin/$sysctl 2>/dev/null || \ /usr/sbin/$sysctl 2>/dev/null || echo unknown)` case "${UNAME_MACHINE_ARCH}" in armeb) machine=armeb-unknown ;; arm*) machine=arm-unknown ;; sh3el) machine=shl-unknown ;; sh3eb) machine=sh-unknown ;; *) machine=${UNAME_MACHINE_ARCH}-unknown ;; esac # The Operating System including object format, if it has switched # to ELF recently, or will in the future. case "${UNAME_MACHINE_ARCH}" in arm*|i386|m68k|ns32k|sh3*|sparc|vax) eval $set_cc_for_build if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ | grep __ELF__ >/dev/null then # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout). # Return netbsd for either. FIX? os=netbsd else os=netbsdelf fi ;; *) os=netbsd ;; esac # The OS release # Debian GNU/NetBSD machines have a different userland, and # thus, need a distinct triplet. However, they do not need # kernel version information, so it can be replaced with a # suitable tag, in the style of linux-gnu. case "${UNAME_VERSION}" in Debian*) release='-gnu' ;; *) release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'` ;; esac # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM: # contains redundant information, the shorter form: # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used. echo "${machine}-${os}${release}" exit 0 ;; amd64:OpenBSD:*:*) echo x86_64-unknown-openbsd${UNAME_RELEASE} exit 0 ;; amiga:OpenBSD:*:*) echo m68k-unknown-openbsd${UNAME_RELEASE} exit 0 ;; cats:OpenBSD:*:*) echo arm-unknown-openbsd${UNAME_RELEASE} exit 0 ;; hp300:OpenBSD:*:*) echo m68k-unknown-openbsd${UNAME_RELEASE} exit 0 ;; luna88k:OpenBSD:*:*) echo m88k-unknown-openbsd${UNAME_RELEASE} exit 0 ;; mac68k:OpenBSD:*:*) echo m68k-unknown-openbsd${UNAME_RELEASE} exit 0 ;; macppc:OpenBSD:*:*) echo powerpc-unknown-openbsd${UNAME_RELEASE} exit 0 ;; mvme68k:OpenBSD:*:*) echo m68k-unknown-openbsd${UNAME_RELEASE} exit 0 ;; mvme88k:OpenBSD:*:*) echo m88k-unknown-openbsd${UNAME_RELEASE} exit 0 ;; mvmeppc:OpenBSD:*:*) echo powerpc-unknown-openbsd${UNAME_RELEASE} exit 0 ;; sgi:OpenBSD:*:*) echo mips64-unknown-openbsd${UNAME_RELEASE} exit 0 ;; sun3:OpenBSD:*:*) echo m68k-unknown-openbsd${UNAME_RELEASE} exit 0 ;; *:OpenBSD:*:*) echo ${UNAME_MACHINE}-unknown-openbsd${UNAME_RELEASE} exit 0 ;; *:ekkoBSD:*:*) echo ${UNAME_MACHINE}-unknown-ekkobsd${UNAME_RELEASE} exit 0 ;; macppc:MirBSD:*:*) echo powerppc-unknown-mirbsd${UNAME_RELEASE} exit 0 ;; *:MirBSD:*:*) echo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE} exit 0 ;; alpha:OSF1:*:*) case $UNAME_RELEASE in *4.0) UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'` ;; *5.*) UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'` ;; esac # According to Compaq, /usr/sbin/psrinfo has been available on # OSF/1 and Tru64 systems produced since 1995. I hope that # covers most systems running today. This code pipes the CPU # types through head -n 1, so we only detect the type of CPU 0. ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1` case "$ALPHA_CPU_TYPE" in "EV4 (21064)") UNAME_MACHINE="alpha" ;; "EV4.5 (21064)") UNAME_MACHINE="alpha" ;; "LCA4 (21066/21068)") UNAME_MACHINE="alpha" ;; "EV5 (21164)") UNAME_MACHINE="alphaev5" ;; "EV5.6 (21164A)") UNAME_MACHINE="alphaev56" ;; "EV5.6 (21164PC)") UNAME_MACHINE="alphapca56" ;; "EV5.7 (21164PC)") UNAME_MACHINE="alphapca57" ;; "EV6 (21264)") UNAME_MACHINE="alphaev6" ;; "EV6.7 (21264A)") UNAME_MACHINE="alphaev67" ;; "EV6.8CB (21264C)") UNAME_MACHINE="alphaev68" ;; "EV6.8AL (21264B)") UNAME_MACHINE="alphaev68" ;; "EV6.8CX (21264D)") UNAME_MACHINE="alphaev68" ;; "EV6.9A (21264/EV69A)") UNAME_MACHINE="alphaev69" ;; "EV7 (21364)") UNAME_MACHINE="alphaev7" ;; "EV7.9 (21364A)") UNAME_MACHINE="alphaev79" ;; esac # A Pn.n version is a patched version. # A Vn.n version is a released version. # A Tn.n version is a released field test version. # A Xn.n version is an unreleased experimental baselevel. # 1.2 uses "1.2" for uname -r. echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` exit 0 ;; Alpha\ *:Windows_NT*:*) # How do we know it's Interix rather than the generic POSIX subsystem? # Should we change UNAME_MACHINE based on the output of uname instead # of the specific Alpha model? echo alpha-pc-interix exit 0 ;; 21064:Windows_NT:50:3) echo alpha-dec-winnt3.5 exit 0 ;; Amiga*:UNIX_System_V:4.0:*) echo m68k-unknown-sysv4 exit 0;; *:[Aa]miga[Oo][Ss]:*:*) echo ${UNAME_MACHINE}-unknown-amigaos exit 0 ;; *:[Mm]orph[Oo][Ss]:*:*) echo ${UNAME_MACHINE}-unknown-morphos exit 0 ;; *:OS/390:*:*) echo i370-ibm-openedition exit 0 ;; *:z/VM:*:*) echo s390-ibm-zvmoe exit 0 ;; *:OS400:*:*) echo powerpc-ibm-os400 exit 0 ;; arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*) echo arm-acorn-riscix${UNAME_RELEASE} exit 0;; SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*) echo hppa1.1-hitachi-hiuxmpp exit 0;; Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*) # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE. if test "`(/bin/universe) 2>/dev/null`" = att ; then echo pyramid-pyramid-sysv3 else echo pyramid-pyramid-bsd fi exit 0 ;; NILE*:*:*:dcosx) echo pyramid-pyramid-svr4 exit 0 ;; DRS?6000:unix:4.0:6*) echo sparc-icl-nx6 exit 0 ;; DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*) case `/usr/bin/uname -p` in sparc) echo sparc-icl-nx7 && exit 0 ;; esac ;; sun4H:SunOS:5.*:*) echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit 0 ;; sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit 0 ;; i86pc:SunOS:5.*:*) echo i386-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit 0 ;; sun4*:SunOS:6*:*) # According to config.sub, this is the proper way to canonicalize # SunOS6. Hard to guess exactly what SunOS6 will be like, but # it's likely to be more like Solaris than SunOS4. echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit 0 ;; sun4*:SunOS:*:*) case "`/usr/bin/arch -k`" in Series*|S4*) UNAME_RELEASE=`uname -v` ;; esac # Japanese Language versions have a version number like `4.1.3-JL'. echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'` exit 0 ;; sun3*:SunOS:*:*) echo m68k-sun-sunos${UNAME_RELEASE} exit 0 ;; sun*:*:4.2BSD:*) UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null` test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3 case "`/bin/arch`" in sun3) echo m68k-sun-sunos${UNAME_RELEASE} ;; sun4) echo sparc-sun-sunos${UNAME_RELEASE} ;; esac exit 0 ;; aushp:SunOS:*:*) echo sparc-auspex-sunos${UNAME_RELEASE} exit 0 ;; # The situation for MiNT is a little confusing. The machine name # can be virtually everything (everything which is not # "atarist" or "atariste" at least should have a processor # > m68000). The system name ranges from "MiNT" over "FreeMiNT" # to the lowercase version "mint" (or "freemint"). Finally # the system name "TOS" denotes a system which is actually not # MiNT. But MiNT is downward compatible to TOS, so this should # be no problem. atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*) echo m68k-atari-mint${UNAME_RELEASE} exit 0 ;; atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*) echo m68k-atari-mint${UNAME_RELEASE} exit 0 ;; *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*) echo m68k-atari-mint${UNAME_RELEASE} exit 0 ;; milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*) echo m68k-milan-mint${UNAME_RELEASE} exit 0 ;; hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*) echo m68k-hades-mint${UNAME_RELEASE} exit 0 ;; *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*) echo m68k-unknown-mint${UNAME_RELEASE} exit 0 ;; m68k:machten:*:*) echo m68k-apple-machten${UNAME_RELEASE} exit 0 ;; powerpc:machten:*:*) echo powerpc-apple-machten${UNAME_RELEASE} exit 0 ;; RISC*:Mach:*:*) echo mips-dec-mach_bsd4.3 exit 0 ;; RISC*:ULTRIX:*:*) echo mips-dec-ultrix${UNAME_RELEASE} exit 0 ;; VAX*:ULTRIX*:*:*) echo vax-dec-ultrix${UNAME_RELEASE} exit 0 ;; 2020:CLIX:*:* | 2430:CLIX:*:*) echo clipper-intergraph-clix${UNAME_RELEASE} exit 0 ;; mips:*:*:UMIPS | mips:*:*:RISCos) eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #ifdef __cplusplus #include /* for printf() prototype */ int main (int argc, char *argv[]) { #else int main (argc, argv) int argc; char *argv[]; { #endif #if defined (host_mips) && defined (MIPSEB) #if defined (SYSTYPE_SYSV) printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0); #endif #if defined (SYSTYPE_SVR4) printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0); #endif #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD) printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0); #endif #endif exit (-1); } EOF $CC_FOR_BUILD -o $dummy $dummy.c \ && $dummy `echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` \ && exit 0 echo mips-mips-riscos${UNAME_RELEASE} exit 0 ;; Motorola:PowerMAX_OS:*:*) echo powerpc-motorola-powermax exit 0 ;; Motorola:*:4.3:PL8-*) echo powerpc-harris-powermax exit 0 ;; Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*) echo powerpc-harris-powermax exit 0 ;; Night_Hawk:Power_UNIX:*:*) echo powerpc-harris-powerunix exit 0 ;; m88k:CX/UX:7*:*) echo m88k-harris-cxux7 exit 0 ;; m88k:*:4*:R4*) echo m88k-motorola-sysv4 exit 0 ;; m88k:*:3*:R3*) echo m88k-motorola-sysv3 exit 0 ;; AViiON:dgux:*:*) # DG/UX returns AViiON for all architectures UNAME_PROCESSOR=`/usr/bin/uname -p` if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ] then if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \ [ ${TARGET_BINARY_INTERFACE}x = x ] then echo m88k-dg-dgux${UNAME_RELEASE} else echo m88k-dg-dguxbcs${UNAME_RELEASE} fi else echo i586-dg-dgux${UNAME_RELEASE} fi exit 0 ;; M88*:DolphinOS:*:*) # DolphinOS (SVR3) echo m88k-dolphin-sysv3 exit 0 ;; M88*:*:R3*:*) # Delta 88k system running SVR3 echo m88k-motorola-sysv3 exit 0 ;; XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3) echo m88k-tektronix-sysv3 exit 0 ;; Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD) echo m68k-tektronix-bsd exit 0 ;; *:IRIX*:*:*) echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'` exit 0 ;; ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX. echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id exit 0 ;; # Note that: echo "'`uname -s`'" gives 'AIX ' i*86:AIX:*:*) echo i386-ibm-aix exit 0 ;; ia64:AIX:*:*) if [ -x /usr/bin/oslevel ] ; then IBM_REV=`/usr/bin/oslevel` else IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} fi echo ${UNAME_MACHINE}-ibm-aix${IBM_REV} exit 0 ;; *:AIX:2:3) if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #include main() { if (!__power_pc()) exit(1); puts("powerpc-ibm-aix3.2.5"); exit(0); } EOF $CC_FOR_BUILD -o $dummy $dummy.c && $dummy && exit 0 echo rs6000-ibm-aix3.2.5 elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then echo rs6000-ibm-aix3.2.4 else echo rs6000-ibm-aix3.2 fi exit 0 ;; *:AIX:*:[45]) IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'` if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then IBM_ARCH=rs6000 else IBM_ARCH=powerpc fi if [ -x /usr/bin/oslevel ] ; then IBM_REV=`/usr/bin/oslevel` else IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} fi echo ${IBM_ARCH}-ibm-aix${IBM_REV} exit 0 ;; *:AIX:*:*) echo rs6000-ibm-aix exit 0 ;; ibmrt:4.4BSD:*|romp-ibm:BSD:*) echo romp-ibm-bsd4.4 exit 0 ;; ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to exit 0 ;; # report: romp-ibm BSD 4.3 *:BOSX:*:*) echo rs6000-bull-bosx exit 0 ;; DPX/2?00:B.O.S.:*:*) echo m68k-bull-sysv3 exit 0 ;; 9000/[34]??:4.3bsd:1.*:*) echo m68k-hp-bsd exit 0 ;; hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*) echo m68k-hp-bsd4.4 exit 0 ;; 9000/[34678]??:HP-UX:*:*) HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` case "${UNAME_MACHINE}" in 9000/31? ) HP_ARCH=m68000 ;; 9000/[34]?? ) HP_ARCH=m68k ;; 9000/[678][0-9][0-9]) if [ -x /usr/bin/getconf ]; then sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null` sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null` case "${sc_cpu_version}" in 523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0 528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1 532) # CPU_PA_RISC2_0 case "${sc_kernel_bits}" in 32) HP_ARCH="hppa2.0n" ;; 64) HP_ARCH="hppa2.0w" ;; '') HP_ARCH="hppa2.0" ;; # HP-UX 10.20 esac ;; esac fi if [ "${HP_ARCH}" = "" ]; then eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #define _HPUX_SOURCE #include #include int main () { #if defined(_SC_KERNEL_BITS) long bits = sysconf(_SC_KERNEL_BITS); #endif long cpu = sysconf (_SC_CPU_VERSION); switch (cpu) { case CPU_PA_RISC1_0: puts ("hppa1.0"); break; case CPU_PA_RISC1_1: puts ("hppa1.1"); break; case CPU_PA_RISC2_0: #if defined(_SC_KERNEL_BITS) switch (bits) { case 64: puts ("hppa2.0w"); break; case 32: puts ("hppa2.0n"); break; default: puts ("hppa2.0"); break; } break; #else /* !defined(_SC_KERNEL_BITS) */ puts ("hppa2.0"); break; #endif default: puts ("hppa1.0"); break; } exit (0); } EOF (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy` test -z "$HP_ARCH" && HP_ARCH=hppa fi ;; esac if [ ${HP_ARCH} = "hppa2.0w" ] then # avoid double evaluation of $set_cc_for_build test -n "$CC_FOR_BUILD" || eval $set_cc_for_build if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E -) | grep __LP64__ >/dev/null then HP_ARCH="hppa2.0w" else HP_ARCH="hppa64" fi fi echo ${HP_ARCH}-hp-hpux${HPUX_REV} exit 0 ;; ia64:HP-UX:*:*) HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` echo ia64-hp-hpux${HPUX_REV} exit 0 ;; 3050*:HI-UX:*:*) eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #include int main () { long cpu = sysconf (_SC_CPU_VERSION); /* The order matters, because CPU_IS_HP_MC68K erroneously returns true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct results, however. */ if (CPU_IS_PA_RISC (cpu)) { switch (cpu) { case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break; case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break; case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break; default: puts ("hppa-hitachi-hiuxwe2"); break; } } else if (CPU_IS_HP_MC68K (cpu)) puts ("m68k-hitachi-hiuxwe2"); else puts ("unknown-hitachi-hiuxwe2"); exit (0); } EOF $CC_FOR_BUILD -o $dummy $dummy.c && $dummy && exit 0 echo unknown-hitachi-hiuxwe2 exit 0 ;; 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* ) echo hppa1.1-hp-bsd exit 0 ;; 9000/8??:4.3bsd:*:*) echo hppa1.0-hp-bsd exit 0 ;; *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*) echo hppa1.0-hp-mpeix exit 0 ;; hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* ) echo hppa1.1-hp-osf exit 0 ;; hp8??:OSF1:*:*) echo hppa1.0-hp-osf exit 0 ;; i*86:OSF1:*:*) if [ -x /usr/sbin/sysversion ] ; then echo ${UNAME_MACHINE}-unknown-osf1mk else echo ${UNAME_MACHINE}-unknown-osf1 fi exit 0 ;; parisc*:Lites*:*:*) echo hppa1.1-hp-lites exit 0 ;; C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*) echo c1-convex-bsd exit 0 ;; C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*) if getsysinfo -f scalar_acc then echo c32-convex-bsd else echo c2-convex-bsd fi exit 0 ;; C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*) echo c34-convex-bsd exit 0 ;; C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*) echo c38-convex-bsd exit 0 ;; C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*) echo c4-convex-bsd exit 0 ;; CRAY*Y-MP:*:*:*) echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit 0 ;; CRAY*[A-Z]90:*:*:*) echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \ | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \ -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \ -e 's/\.[^.]*$/.X/' exit 0 ;; CRAY*TS:*:*:*) echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit 0 ;; CRAY*T3E:*:*:*) echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit 0 ;; CRAY*SV1:*:*:*) echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit 0 ;; *:UNICOS/mp:*:*) echo craynv-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit 0 ;; F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*) FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'` echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" exit 0 ;; 5000:UNIX_System_V:4.*:*) FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` FUJITSU_REL=`echo ${UNAME_RELEASE} | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/ /_/'` echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" exit 0 ;; i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*) echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE} exit 0 ;; sparc*:BSD/OS:*:*) echo sparc-unknown-bsdi${UNAME_RELEASE} exit 0 ;; *:BSD/OS:*:*) echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE} exit 0 ;; *:FreeBSD:*:*) echo ${UNAME_MACHINE}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` exit 0 ;; i*:CYGWIN*:*) echo ${UNAME_MACHINE}-pc-cygwin exit 0 ;; i*:MINGW*:*) echo ${UNAME_MACHINE}-pc-mingw32 exit 0 ;; i*:PW*:*) echo ${UNAME_MACHINE}-pc-pw32 exit 0 ;; x86:Interix*:[34]*) echo i586-pc-interix${UNAME_RELEASE}|sed -e 's/\..*//' exit 0 ;; [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*) echo i${UNAME_MACHINE}-pc-mks exit 0 ;; i*:Windows_NT*:* | Pentium*:Windows_NT*:*) # How do we know it's Interix rather than the generic POSIX subsystem? # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we # UNAME_MACHINE based on the output of uname instead of i386? echo i586-pc-interix exit 0 ;; i*:UWIN*:*) echo ${UNAME_MACHINE}-pc-uwin exit 0 ;; p*:CYGWIN*:*) echo powerpcle-unknown-cygwin exit 0 ;; prep*:SunOS:5.*:*) echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit 0 ;; *:GNU:*:*) # the GNU system echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'` exit 0 ;; *:GNU/*:*:*) # other systems with GNU libc and userland echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-gnu exit 0 ;; i*86:Minix:*:*) echo ${UNAME_MACHINE}-pc-minix exit 0 ;; arm*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-gnu exit 0 ;; cris:Linux:*:*) echo cris-axis-linux-gnu exit 0 ;; crisv32:Linux:*:*) echo crisv32-axis-linux-gnu exit 0 ;; frv:Linux:*:*) echo frv-unknown-linux-gnu exit 0 ;; ia64:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-gnu exit 0 ;; m32r*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-gnu exit 0 ;; m68*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-gnu exit 0 ;; mips:Linux:*:*) eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #undef CPU #undef mips #undef mipsel #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) CPU=mipsel #else #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) CPU=mips #else CPU= #endif #endif EOF eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^CPU=` test x"${CPU}" != x && echo "${CPU}-unknown-linux-gnu" && exit 0 ;; mips64:Linux:*:*) eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #undef CPU #undef mips64 #undef mips64el #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) CPU=mips64el #else #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) CPU=mips64 #else CPU= #endif #endif EOF eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^CPU=` test x"${CPU}" != x && echo "${CPU}-unknown-linux-gnu" && exit 0 ;; ppc:Linux:*:*) echo powerpc-unknown-linux-gnu exit 0 ;; ppc64:Linux:*:*) echo powerpc64-unknown-linux-gnu exit 0 ;; alpha:Linux:*:*) case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in EV5) UNAME_MACHINE=alphaev5 ;; EV56) UNAME_MACHINE=alphaev56 ;; PCA56) UNAME_MACHINE=alphapca56 ;; PCA57) UNAME_MACHINE=alphapca56 ;; EV6) UNAME_MACHINE=alphaev6 ;; EV67) UNAME_MACHINE=alphaev67 ;; EV68*) UNAME_MACHINE=alphaev68 ;; esac objdump --private-headers /bin/sh | grep ld.so.1 >/dev/null if test "$?" = 0 ; then LIBC="libc1" ; else LIBC="" ; fi echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC} exit 0 ;; parisc:Linux:*:* | hppa:Linux:*:*) # Look for CPU level case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in PA7*) echo hppa1.1-unknown-linux-gnu ;; PA8*) echo hppa2.0-unknown-linux-gnu ;; *) echo hppa-unknown-linux-gnu ;; esac exit 0 ;; parisc64:Linux:*:* | hppa64:Linux:*:*) echo hppa64-unknown-linux-gnu exit 0 ;; s390:Linux:*:* | s390x:Linux:*:*) echo ${UNAME_MACHINE}-ibm-linux exit 0 ;; sh64*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-gnu exit 0 ;; sh*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-gnu exit 0 ;; sparc:Linux:*:* | sparc64:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-gnu exit 0 ;; x86_64:Linux:*:*) echo x86_64-unknown-linux-gnu exit 0 ;; i*86:Linux:*:*) # The BFD linker knows what the default object file format is, so # first see if it will tell us. cd to the root directory to prevent # problems with other programs or directories called `ld' in the path. # Set LC_ALL=C to ensure ld outputs messages in English. ld_supported_targets=`cd /; LC_ALL=C ld --help 2>&1 \ | sed -ne '/supported targets:/!d s/[ ][ ]*/ /g s/.*supported targets: *// s/ .*// p'` case "$ld_supported_targets" in elf32-i386) TENTATIVE="${UNAME_MACHINE}-pc-linux-gnu" ;; a.out-i386-linux) echo "${UNAME_MACHINE}-pc-linux-gnuaout" exit 0 ;; coff-i386) echo "${UNAME_MACHINE}-pc-linux-gnucoff" exit 0 ;; "") # Either a pre-BFD a.out linker (linux-gnuoldld) or # one that does not give us useful --help. echo "${UNAME_MACHINE}-pc-linux-gnuoldld" exit 0 ;; esac # Determine whether the default compiler is a.out or elf eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #include #ifdef __ELF__ # ifdef __GLIBC__ # if __GLIBC__ >= 2 LIBC=gnu # else LIBC=gnulibc1 # endif # else LIBC=gnulibc1 # endif #else #ifdef __INTEL_COMPILER LIBC=gnu #else LIBC=gnuaout #endif #endif #ifdef __dietlibc__ LIBC=dietlibc #endif EOF eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^LIBC=` test x"${LIBC}" != x && echo "${UNAME_MACHINE}-pc-linux-${LIBC}" && exit 0 test x"${TENTATIVE}" != x && echo "${TENTATIVE}" && exit 0 ;; i*86:DYNIX/ptx:4*:*) # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. # earlier versions are messed up and put the nodename in both # sysname and nodename. echo i386-sequent-sysv4 exit 0 ;; i*86:UNIX_SV:4.2MP:2.*) # Unixware is an offshoot of SVR4, but it has its own version # number series starting with 2... # I am not positive that other SVR4 systems won't match this, # I just have to hope. -- rms. # Use sysv4.2uw... so that sysv4* matches it. echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION} exit 0 ;; i*86:OS/2:*:*) # If we were able to find `uname', then EMX Unix compatibility # is probably installed. echo ${UNAME_MACHINE}-pc-os2-emx exit 0 ;; i*86:XTS-300:*:STOP) echo ${UNAME_MACHINE}-unknown-stop exit 0 ;; i*86:atheos:*:*) echo ${UNAME_MACHINE}-unknown-atheos exit 0 ;; i*86:syllable:*:*) echo ${UNAME_MACHINE}-pc-syllable exit 0 ;; i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.0*:*) echo i386-unknown-lynxos${UNAME_RELEASE} exit 0 ;; i*86:*DOS:*:*) echo ${UNAME_MACHINE}-pc-msdosdjgpp exit 0 ;; i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*) UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'` if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL} else echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL} fi exit 0 ;; i*86:*:5:[78]*) case `/bin/uname -X | grep "^Machine"` in *486*) UNAME_MACHINE=i486 ;; *Pentium) UNAME_MACHINE=i586 ;; *Pent*|*Celeron) UNAME_MACHINE=i686 ;; esac echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION} exit 0 ;; i*86:*:3.2:*) if test -f /usr/options/cb.name; then UNAME_REL=`sed -n 's/.*Version //p' /dev/null >/dev/null ; then UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')` (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486 (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \ && UNAME_MACHINE=i586 (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \ && UNAME_MACHINE=i686 (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \ && UNAME_MACHINE=i686 echo ${UNAME_MACHINE}-pc-sco$UNAME_REL else echo ${UNAME_MACHINE}-pc-sysv32 fi exit 0 ;; pc:*:*:*) # Left here for compatibility: # uname -m prints for DJGPP always 'pc', but it prints nothing about # the processor, so we play safe by assuming i386. echo i386-pc-msdosdjgpp exit 0 ;; Intel:Mach:3*:*) echo i386-pc-mach3 exit 0 ;; paragon:*:*:*) echo i860-intel-osf1 exit 0 ;; i860:*:4.*:*) # i860-SVR4 if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4 else # Add other i860-SVR4 vendors below as they are discovered. echo i860-unknown-sysv${UNAME_RELEASE} # Unknown i860-SVR4 fi exit 0 ;; mini*:CTIX:SYS*5:*) # "miniframe" echo m68010-convergent-sysv exit 0 ;; mc68k:UNIX:SYSTEM5:3.51m) echo m68k-convergent-sysv exit 0 ;; M680?0:D-NIX:5.3:*) echo m68k-diab-dnix exit 0 ;; M68*:*:R3V[5678]*:*) test -r /sysV68 && echo 'm68k-motorola-sysv' && exit 0 ;; 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0) OS_REL='' test -r /etc/.relid \ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && echo i486-ncr-sysv4.3${OS_REL} && exit 0 /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ && echo i586-ncr-sysv4.3${OS_REL} && exit 0 ;; 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*) /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && echo i486-ncr-sysv4 && exit 0 ;; m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*) echo m68k-unknown-lynxos${UNAME_RELEASE} exit 0 ;; mc68030:UNIX_System_V:4.*:*) echo m68k-atari-sysv4 exit 0 ;; TSUNAMI:LynxOS:2.*:*) echo sparc-unknown-lynxos${UNAME_RELEASE} exit 0 ;; rs6000:LynxOS:2.*:*) echo rs6000-unknown-lynxos${UNAME_RELEASE} exit 0 ;; PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.0*:*) echo powerpc-unknown-lynxos${UNAME_RELEASE} exit 0 ;; SM[BE]S:UNIX_SV:*:*) echo mips-dde-sysv${UNAME_RELEASE} exit 0 ;; RM*:ReliantUNIX-*:*:*) echo mips-sni-sysv4 exit 0 ;; RM*:SINIX-*:*:*) echo mips-sni-sysv4 exit 0 ;; *:SINIX-*:*:*) if uname -p 2>/dev/null >/dev/null ; then UNAME_MACHINE=`(uname -p) 2>/dev/null` echo ${UNAME_MACHINE}-sni-sysv4 else echo ns32k-sni-sysv fi exit 0 ;; PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort # says echo i586-unisys-sysv4 exit 0 ;; *:UNIX_System_V:4*:FTX*) # From Gerald Hewes . # How about differentiating between stratus architectures? -djm echo hppa1.1-stratus-sysv4 exit 0 ;; *:*:*:FTX*) # From seanf@swdc.stratus.com. echo i860-stratus-sysv4 exit 0 ;; *:VOS:*:*) # From Paul.Green@stratus.com. echo hppa1.1-stratus-vos exit 0 ;; mc68*:A/UX:*:*) echo m68k-apple-aux${UNAME_RELEASE} exit 0 ;; news*:NEWS-OS:6*:*) echo mips-sony-newsos6 exit 0 ;; R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*) if [ -d /usr/nec ]; then echo mips-nec-sysv${UNAME_RELEASE} else echo mips-unknown-sysv${UNAME_RELEASE} fi exit 0 ;; BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only. echo powerpc-be-beos exit 0 ;; BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only. echo powerpc-apple-beos exit 0 ;; BePC:BeOS:*:*) # BeOS running on Intel PC compatible. echo i586-pc-beos exit 0 ;; SX-4:SUPER-UX:*:*) echo sx4-nec-superux${UNAME_RELEASE} exit 0 ;; SX-5:SUPER-UX:*:*) echo sx5-nec-superux${UNAME_RELEASE} exit 0 ;; SX-6:SUPER-UX:*:*) echo sx6-nec-superux${UNAME_RELEASE} exit 0 ;; Power*:Rhapsody:*:*) echo powerpc-apple-rhapsody${UNAME_RELEASE} exit 0 ;; *:Rhapsody:*:*) echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE} exit 0 ;; *:Darwin:*:*) UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown case $UNAME_PROCESSOR in *86) UNAME_PROCESSOR=i686 ;; unknown) UNAME_PROCESSOR=powerpc ;; esac echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE} exit 0 ;; *:procnto*:*:* | *:QNX:[0123456789]*:*) UNAME_PROCESSOR=`uname -p` if test "$UNAME_PROCESSOR" = "x86"; then UNAME_PROCESSOR=i386 UNAME_MACHINE=pc fi echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE} exit 0 ;; *:QNX:*:4*) echo i386-pc-qnx exit 0 ;; NSR-?:NONSTOP_KERNEL:*:*) echo nsr-tandem-nsk${UNAME_RELEASE} exit 0 ;; *:NonStop-UX:*:*) echo mips-compaq-nonstopux exit 0 ;; BS2000:POSIX*:*:*) echo bs2000-siemens-sysv exit 0 ;; DS/*:UNIX_System_V:*:*) echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE} exit 0 ;; *:Plan9:*:*) # "uname -m" is not consistent, so use $cputype instead. 386 # is converted to i386 for consistency with other x86 # operating systems. if test "$cputype" = "386"; then UNAME_MACHINE=i386 else UNAME_MACHINE="$cputype" fi echo ${UNAME_MACHINE}-unknown-plan9 exit 0 ;; *:TOPS-10:*:*) echo pdp10-unknown-tops10 exit 0 ;; *:TENEX:*:*) echo pdp10-unknown-tenex exit 0 ;; KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*) echo pdp10-dec-tops20 exit 0 ;; XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*) echo pdp10-xkl-tops20 exit 0 ;; *:TOPS-20:*:*) echo pdp10-unknown-tops20 exit 0 ;; *:ITS:*:*) echo pdp10-unknown-its exit 0 ;; SEI:*:*:SEIUX) echo mips-sei-seiux${UNAME_RELEASE} exit 0 ;; *:DragonFly:*:*) echo ${UNAME_MACHINE}-unknown-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` exit 0 ;; *:*VMS:*:*) UNAME_MACHINE=`(uname -p) 2>/dev/null` case "${UNAME_MACHINE}" in A*) echo alpha-dec-vms && exit 0 ;; I*) echo ia64-dec-vms && exit 0 ;; V*) echo vax-dec-vms && exit 0 ;; esac ;; *:XENIX:*:SysV) echo i386-pc-xenix exit 0 ;; esac #echo '(No uname command or uname output not recognized.)' 1>&2 #echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 1>&2 eval $set_cc_for_build cat >$dummy.c < # include #endif main () { #if defined (sony) #if defined (MIPSEB) /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed, I don't know.... */ printf ("mips-sony-bsd\n"); exit (0); #else #include printf ("m68k-sony-newsos%s\n", #ifdef NEWSOS4 "4" #else "" #endif ); exit (0); #endif #endif #if defined (__arm) && defined (__acorn) && defined (__unix) printf ("arm-acorn-riscix"); exit (0); #endif #if defined (hp300) && !defined (hpux) printf ("m68k-hp-bsd\n"); exit (0); #endif #if defined (NeXT) #if !defined (__ARCHITECTURE__) #define __ARCHITECTURE__ "m68k" #endif int version; version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`; if (version < 4) printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version); else printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version); exit (0); #endif #if defined (MULTIMAX) || defined (n16) #if defined (UMAXV) printf ("ns32k-encore-sysv\n"); exit (0); #else #if defined (CMU) printf ("ns32k-encore-mach\n"); exit (0); #else printf ("ns32k-encore-bsd\n"); exit (0); #endif #endif #endif #if defined (__386BSD__) printf ("i386-pc-bsd\n"); exit (0); #endif #if defined (sequent) #if defined (i386) printf ("i386-sequent-dynix\n"); exit (0); #endif #if defined (ns32000) printf ("ns32k-sequent-dynix\n"); exit (0); #endif #endif #if defined (_SEQUENT_) struct utsname un; uname(&un); if (strncmp(un.version, "V2", 2) == 0) { printf ("i386-sequent-ptx2\n"); exit (0); } if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */ printf ("i386-sequent-ptx1\n"); exit (0); } printf ("i386-sequent-ptx\n"); exit (0); #endif #if defined (vax) # if !defined (ultrix) # include # if defined (BSD) # if BSD == 43 printf ("vax-dec-bsd4.3\n"); exit (0); # else # if BSD == 199006 printf ("vax-dec-bsd4.3reno\n"); exit (0); # else printf ("vax-dec-bsd\n"); exit (0); # endif # endif # else printf ("vax-dec-bsd\n"); exit (0); # endif # else printf ("vax-dec-ultrix\n"); exit (0); # endif #endif #if defined (alliant) && defined (i860) printf ("i860-alliant-bsd\n"); exit (0); #endif exit (1); } EOF $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null && $dummy && exit 0 # Apollos put the system type in the environment. test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit 0; } # Convex versions that predate uname can use getsysinfo(1) if [ -x /usr/convex/getsysinfo ] then case `getsysinfo -f cpu_type` in c1*) echo c1-convex-bsd exit 0 ;; c2*) if getsysinfo -f scalar_acc then echo c32-convex-bsd else echo c2-convex-bsd fi exit 0 ;; c34*) echo c34-convex-bsd exit 0 ;; c38*) echo c38-convex-bsd exit 0 ;; c4*) echo c4-convex-bsd exit 0 ;; esac fi cat >&2 < in order to provide the needed information to handle your system. config.guess timestamp = $timestamp uname -m = `(uname -m) 2>/dev/null || echo unknown` uname -r = `(uname -r) 2>/dev/null || echo unknown` uname -s = `(uname -s) 2>/dev/null || echo unknown` uname -v = `(uname -v) 2>/dev/null || echo unknown` /usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null` /bin/uname -X = `(/bin/uname -X) 2>/dev/null` hostinfo = `(hostinfo) 2>/dev/null` /bin/universe = `(/bin/universe) 2>/dev/null` /usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null` /bin/arch = `(/bin/arch) 2>/dev/null` /usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null` /usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null` UNAME_MACHINE = ${UNAME_MACHINE} UNAME_RELEASE = ${UNAME_RELEASE} UNAME_SYSTEM = ${UNAME_SYSTEM} UNAME_VERSION = ${UNAME_VERSION} EOF exit 1 # Local variables: # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "timestamp='" # time-stamp-format: "%:y-%02m-%02d" # time-stamp-end: "'" # End: ocfs2-tools-ocfs2-tools-1.8.6/config.sub000077500000000000000000000753531347147137200200610ustar00rootroot00000000000000#! /bin/sh # Configuration validation subroutine script. # Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, # 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc. timestamp='2004-11-30' # This file is (in principle) common to ALL GNU software. # The presence of a machine in this file suggests that SOME GNU software # can handle that machine. It does not imply ALL GNU software can. # # This file 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., 51 Franklin St, Fifth Floor, # Boston, MA 02110-1301, USA. # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. # Please send patches to . Submit a context # diff and a properly formatted ChangeLog entry. # # Configuration subroutine to validate and canonicalize a configuration type. # Supply the specified configuration type as an argument. # If it is invalid, we print an error message on stderr and exit with code 1. # Otherwise, we print the canonical config type on stdout and succeed. # This file is supposed to be the same for all GNU packages # and recognize all the CPU types, system types and aliases # that are meaningful with *any* GNU software. # Each package is responsible for reporting which valid configurations # it does not support. The user should be able to distinguish # a failure to support a valid configuration from a meaningless # configuration. # The goal of this file is to map all the various variations of a given # machine specification into a single specification in the form: # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM # or in some cases, the newer four-part form: # CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM # It is wrong to echo any other type of specification. me=`echo "$0" | sed -e 's,.*/,,'` usage="\ Usage: $0 [OPTION] CPU-MFR-OPSYS $0 [OPTION] ALIAS Canonicalize a configuration name. Operation modes: -h, --help print this help, then exit -t, --time-stamp print date of last modification, then exit -v, --version print version number, then exit Report bugs and patches to ." version="\ GNU config.sub ($timestamp) Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." help=" Try \`$me --help' for more information." # Parse command line while test $# -gt 0 ; do case $1 in --time-stamp | --time* | -t ) echo "$timestamp" ; exit 0 ;; --version | -v ) echo "$version" ; exit 0 ;; --help | --h* | -h ) echo "$usage"; exit 0 ;; -- ) # Stop option processing shift; break ;; - ) # Use stdin as input. break ;; -* ) echo "$me: invalid option $1$help" exit 1 ;; *local*) # First pass through any local machine types. echo $1 exit 0;; * ) break ;; esac done case $# in 0) echo "$me: missing argument$help" >&2 exit 1;; 1) ;; *) echo "$me: too many arguments$help" >&2 exit 1;; esac # Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any). # Here we must recognize all the valid KERNEL-OS combinations. maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'` case $maybe_os in nto-qnx* | linux-gnu* | linux-dietlibc | linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | \ kfreebsd*-gnu* | knetbsd*-gnu* | netbsd*-gnu* | storm-chaos* | os2-emx* | rtmk-nova*) os=-$maybe_os basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'` ;; *) basic_machine=`echo $1 | sed 's/-[^-]*$//'` if [ $basic_machine != $1 ] then os=`echo $1 | sed 's/.*-/-/'` else os=; fi ;; esac ### Let's recognize common machines as not being operating systems so ### that things like config.sub decstation-3100 work. We also ### recognize some manufacturers as not being operating systems, so we ### can provide default operating systems below. case $os in -sun*os*) # Prevent following clause from handling this invalid input. ;; -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \ -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \ -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \ -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\ -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \ -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \ -apple | -axis | -knuth | -cray) os= basic_machine=$1 ;; -sim | -cisco | -oki | -wec | -winbond) os= basic_machine=$1 ;; -scout) ;; -wrs) os=-vxworks basic_machine=$1 ;; -chorusos*) os=-chorusos basic_machine=$1 ;; -chorusrdb) os=-chorusrdb basic_machine=$1 ;; -hiux*) os=-hiuxwe2 ;; -sco5) os=-sco3.2v5 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -sco4) os=-sco3.2v4 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -sco3.2.[4-9]*) os=`echo $os | sed -e 's/sco3.2./sco3.2v/'` basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -sco3.2v[4-9]*) # Don't forget version if it is 3.2v4 or newer. basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -sco*) os=-sco3.2v2 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -udk*) basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -isc) os=-isc2.2 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -clix*) basic_machine=clipper-intergraph ;; -isc*) basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -lynx*) os=-lynxos ;; -ptx*) basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'` ;; -windowsnt*) os=`echo $os | sed -e 's/windowsnt/winnt/'` ;; -psos*) os=-psos ;; -mint | -mint[0-9]*) basic_machine=m68k-atari os=-mint ;; esac # Decode aliases for certain CPU-COMPANY combinations. case $basic_machine in # Recognize the basic CPU types without company name. # Some are omitted here because they have special meanings below. 1750a | 580 \ | a29k \ | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \ | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \ | am33_2.0 \ | arc | arm | arm[bl]e | arme[lb] | armv[2345] | armv[345][lb] | avr \ | c4x | clipper \ | d10v | d30v | dlx | dsp16xx \ | fr30 | frv \ | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \ | i370 | i860 | i960 | ia64 \ | ip2k | iq2000 \ | m32r | m32rle | m68000 | m68k | m88k | mcore \ | mips | mipsbe | mipseb | mipsel | mipsle \ | mips16 \ | mips64 | mips64el \ | mips64vr | mips64vrel \ | mips64orion | mips64orionel \ | mips64vr4100 | mips64vr4100el \ | mips64vr4300 | mips64vr4300el \ | mips64vr5000 | mips64vr5000el \ | mipsisa32 | mipsisa32el \ | mipsisa32r2 | mipsisa32r2el \ | mipsisa64 | mipsisa64el \ | mipsisa64r2 | mipsisa64r2el \ | mipsisa64sb1 | mipsisa64sb1el \ | mipsisa64sr71k | mipsisa64sr71kel \ | mipstx39 | mipstx39el \ | mn10200 | mn10300 \ | msp430 \ | ns16k | ns32k \ | openrisc | or32 \ | pdp10 | pdp11 | pj | pjl \ | powerpc | powerpc64 | powerpc64le | powerpcle | ppcbe \ | pyramid \ | sh | sh[1234] | sh[23]e | sh[34]eb | shbe | shle | sh[1234]le | sh3ele \ | sh64 | sh64le \ | sparc | sparc64 | sparc86x | sparclet | sparclite | sparcv8 | sparcv9 | sparcv9b \ | strongarm \ | tahoe | thumb | tic4x | tic80 | tron \ | v850 | v850e \ | we32k \ | x86 | xscale | xscalee[bl] | xstormy16 | xtensa \ | z8k) basic_machine=$basic_machine-unknown ;; m6811 | m68hc11 | m6812 | m68hc12) # Motorola 68HC11/12. basic_machine=$basic_machine-unknown os=-none ;; m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k) ;; # We use `pc' rather than `unknown' # because (1) that's what they normally are, and # (2) the word "unknown" tends to confuse beginning users. i*86 | x86_64) basic_machine=$basic_machine-pc ;; # Object if more than one company name word. *-*-*) echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 exit 1 ;; # Recognize the basic CPU types with company name. 580-* \ | a29k-* \ | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \ | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \ | alphapca5[67]-* | alpha64pca5[67]-* | arc-* \ | arm-* | armbe-* | armle-* | armeb-* | armv*-* \ | avr-* \ | bs2000-* \ | c[123]* | c30-* | [cjt]90-* | c4x-* | c54x-* | c55x-* | c6x-* \ | clipper-* | craynv-* | cydra-* \ | d10v-* | d30v-* | dlx-* \ | elxsi-* \ | f30[01]-* | f700-* | fr30-* | frv-* | fx80-* \ | h8300-* | h8500-* \ | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \ | i*86-* | i860-* | i960-* | ia64-* \ | ip2k-* | iq2000-* \ | m32r-* | m32rle-* \ | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \ | m88110-* | m88k-* | mcore-* \ | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \ | mips16-* \ | mips64-* | mips64el-* \ | mips64vr-* | mips64vrel-* \ | mips64orion-* | mips64orionel-* \ | mips64vr4100-* | mips64vr4100el-* \ | mips64vr4300-* | mips64vr4300el-* \ | mips64vr5000-* | mips64vr5000el-* \ | mipsisa32-* | mipsisa32el-* \ | mipsisa32r2-* | mipsisa32r2el-* \ | mipsisa64-* | mipsisa64el-* \ | mipsisa64r2-* | mipsisa64r2el-* \ | mipsisa64sb1-* | mipsisa64sb1el-* \ | mipsisa64sr71k-* | mipsisa64sr71kel-* \ | mipstx39-* | mipstx39el-* \ | mmix-* \ | msp430-* \ | none-* | np1-* | ns16k-* | ns32k-* \ | orion-* \ | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \ | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* | ppcbe-* \ | pyramid-* \ | romp-* | rs6000-* \ | sh-* | sh[1234]-* | sh[23]e-* | sh[34]eb-* | shbe-* \ | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \ | sparc-* | sparc64-* | sparc86x-* | sparclet-* | sparclite-* \ | sparcv8-* | sparcv9-* | sparcv9b-* | strongarm-* | sv1-* | sx?-* \ | tahoe-* | thumb-* \ | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \ | tron-* \ | v850-* | v850e-* | vax-* \ | we32k-* \ | x86-* | x86_64-* | xps100-* | xscale-* | xscalee[bl]-* \ | xstormy16-* | xtensa-* \ | ymp-* \ | z8k-*) ;; # Recognize the various machine names and aliases which stand # for a CPU type and a company and sometimes even an OS. 386bsd) basic_machine=i386-unknown os=-bsd ;; 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc) basic_machine=m68000-att ;; 3b*) basic_machine=we32k-att ;; a29khif) basic_machine=a29k-amd os=-udi ;; abacus) basic_machine=abacus-unknown ;; adobe68k) basic_machine=m68010-adobe os=-scout ;; alliant | fx80) basic_machine=fx80-alliant ;; altos | altos3068) basic_machine=m68k-altos ;; am29k) basic_machine=a29k-none os=-bsd ;; amd64) basic_machine=x86_64-pc ;; amd64-*) basic_machine=x86_64-`echo $basic_machine | sed 's/^[^-]*-//'` ;; amdahl) basic_machine=580-amdahl os=-sysv ;; amiga | amiga-*) basic_machine=m68k-unknown ;; amigaos | amigados) basic_machine=m68k-unknown os=-amigaos ;; amigaunix | amix) basic_machine=m68k-unknown os=-sysv4 ;; apollo68) basic_machine=m68k-apollo os=-sysv ;; apollo68bsd) basic_machine=m68k-apollo os=-bsd ;; aux) basic_machine=m68k-apple os=-aux ;; balance) basic_machine=ns32k-sequent os=-dynix ;; c90) basic_machine=c90-cray os=-unicos ;; convex-c1) basic_machine=c1-convex os=-bsd ;; convex-c2) basic_machine=c2-convex os=-bsd ;; convex-c32) basic_machine=c32-convex os=-bsd ;; convex-c34) basic_machine=c34-convex os=-bsd ;; convex-c38) basic_machine=c38-convex os=-bsd ;; cray | j90) basic_machine=j90-cray os=-unicos ;; craynv) basic_machine=craynv-cray os=-unicosmp ;; cr16c) basic_machine=cr16c-unknown os=-elf ;; crds | unos) basic_machine=m68k-crds ;; crisv32 | crisv32-* | etraxfs*) basic_machine=crisv32-axis ;; cris | cris-* | etrax*) basic_machine=cris-axis ;; crx) basic_machine=crx-unknown os=-elf ;; da30 | da30-*) basic_machine=m68k-da30 ;; decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn) basic_machine=mips-dec ;; decsystem10* | dec10*) basic_machine=pdp10-dec os=-tops10 ;; decsystem20* | dec20*) basic_machine=pdp10-dec os=-tops20 ;; delta | 3300 | motorola-3300 | motorola-delta \ | 3300-motorola | delta-motorola) basic_machine=m68k-motorola ;; delta88) basic_machine=m88k-motorola os=-sysv3 ;; djgpp) basic_machine=i586-pc os=-msdosdjgpp ;; dpx20 | dpx20-*) basic_machine=rs6000-bull os=-bosx ;; dpx2* | dpx2*-bull) basic_machine=m68k-bull os=-sysv3 ;; ebmon29k) basic_machine=a29k-amd os=-ebmon ;; elxsi) basic_machine=elxsi-elxsi os=-bsd ;; encore | umax | mmax) basic_machine=ns32k-encore ;; es1800 | OSE68k | ose68k | ose | OSE) basic_machine=m68k-ericsson os=-ose ;; fx2800) basic_machine=i860-alliant ;; genix) basic_machine=ns32k-ns ;; gmicro) basic_machine=tron-gmicro os=-sysv ;; go32) basic_machine=i386-pc os=-go32 ;; h3050r* | hiux*) basic_machine=hppa1.1-hitachi os=-hiuxwe2 ;; h8300hms) basic_machine=h8300-hitachi os=-hms ;; h8300xray) basic_machine=h8300-hitachi os=-xray ;; h8500hms) basic_machine=h8500-hitachi os=-hms ;; harris) basic_machine=m88k-harris os=-sysv3 ;; hp300-*) basic_machine=m68k-hp ;; hp300bsd) basic_machine=m68k-hp os=-bsd ;; hp300hpux) basic_machine=m68k-hp os=-hpux ;; hp3k9[0-9][0-9] | hp9[0-9][0-9]) basic_machine=hppa1.0-hp ;; hp9k2[0-9][0-9] | hp9k31[0-9]) basic_machine=m68000-hp ;; hp9k3[2-9][0-9]) basic_machine=m68k-hp ;; hp9k6[0-9][0-9] | hp6[0-9][0-9]) basic_machine=hppa1.0-hp ;; hp9k7[0-79][0-9] | hp7[0-79][0-9]) basic_machine=hppa1.1-hp ;; hp9k78[0-9] | hp78[0-9]) # FIXME: really hppa2.0-hp basic_machine=hppa1.1-hp ;; hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893) # FIXME: really hppa2.0-hp basic_machine=hppa1.1-hp ;; hp9k8[0-9][13679] | hp8[0-9][13679]) basic_machine=hppa1.1-hp ;; hp9k8[0-9][0-9] | hp8[0-9][0-9]) basic_machine=hppa1.0-hp ;; hppa-next) os=-nextstep3 ;; hppaosf) basic_machine=hppa1.1-hp os=-osf ;; hppro) basic_machine=hppa1.1-hp os=-proelf ;; i370-ibm* | ibm*) basic_machine=i370-ibm ;; # I'm not sure what "Sysv32" means. Should this be sysv3.2? i*86v32) basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` os=-sysv32 ;; i*86v4*) basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` os=-sysv4 ;; i*86v) basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` os=-sysv ;; i*86sol2) basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` os=-solaris2 ;; i386mach) basic_machine=i386-mach os=-mach ;; i386-vsta | vsta) basic_machine=i386-unknown os=-vsta ;; iris | iris4d) basic_machine=mips-sgi case $os in -irix*) ;; *) os=-irix4 ;; esac ;; isi68 | isi) basic_machine=m68k-isi os=-sysv ;; m88k-omron*) basic_machine=m88k-omron ;; magnum | m3230) basic_machine=mips-mips os=-sysv ;; merlin) basic_machine=ns32k-utek os=-sysv ;; mingw32) basic_machine=i386-pc os=-mingw32 ;; miniframe) basic_machine=m68000-convergent ;; *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*) basic_machine=m68k-atari os=-mint ;; mips3*-*) basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'` ;; mips3*) basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown ;; monitor) basic_machine=m68k-rom68k os=-coff ;; morphos) basic_machine=powerpc-unknown os=-morphos ;; msdos) basic_machine=i386-pc os=-msdos ;; mvs) basic_machine=i370-ibm os=-mvs ;; ncr3000) basic_machine=i486-ncr os=-sysv4 ;; netbsd386) basic_machine=i386-unknown os=-netbsd ;; netwinder) basic_machine=armv4l-rebel os=-linux ;; news | news700 | news800 | news900) basic_machine=m68k-sony os=-newsos ;; news1000) basic_machine=m68030-sony os=-newsos ;; news-3600 | risc-news) basic_machine=mips-sony os=-newsos ;; necv70) basic_machine=v70-nec os=-sysv ;; next | m*-next ) basic_machine=m68k-next case $os in -nextstep* ) ;; -ns2*) os=-nextstep2 ;; *) os=-nextstep3 ;; esac ;; nh3000) basic_machine=m68k-harris os=-cxux ;; nh[45]000) basic_machine=m88k-harris os=-cxux ;; nindy960) basic_machine=i960-intel os=-nindy ;; mon960) basic_machine=i960-intel os=-mon960 ;; nonstopux) basic_machine=mips-compaq os=-nonstopux ;; np1) basic_machine=np1-gould ;; nsr-tandem) basic_machine=nsr-tandem ;; op50n-* | op60c-*) basic_machine=hppa1.1-oki os=-proelf ;; or32 | or32-*) basic_machine=or32-unknown os=-coff ;; os400) basic_machine=powerpc-ibm os=-os400 ;; OSE68000 | ose68000) basic_machine=m68000-ericsson os=-ose ;; os68k) basic_machine=m68k-none os=-os68k ;; pa-hitachi) basic_machine=hppa1.1-hitachi os=-hiuxwe2 ;; paragon) basic_machine=i860-intel os=-osf ;; pbd) basic_machine=sparc-tti ;; pbb) basic_machine=m68k-tti ;; pc532 | pc532-*) basic_machine=ns32k-pc532 ;; pentium | p5 | k5 | k6 | nexgen | viac3) basic_machine=i586-pc ;; pentiumpro | p6 | 6x86 | athlon | athlon_*) basic_machine=i686-pc ;; pentiumii | pentium2 | pentiumiii | pentium3) basic_machine=i686-pc ;; pentium4) basic_machine=i786-pc ;; pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*) basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'` ;; pentiumpro-* | p6-* | 6x86-* | athlon-*) basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` ;; pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*) basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` ;; pentium4-*) basic_machine=i786-`echo $basic_machine | sed 's/^[^-]*-//'` ;; pn) basic_machine=pn-gould ;; power) basic_machine=power-ibm ;; ppc) basic_machine=powerpc-unknown ;; ppc-*) basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'` ;; ppcle | powerpclittle | ppc-le | powerpc-little) basic_machine=powerpcle-unknown ;; ppcle-* | powerpclittle-*) basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'` ;; ppc64) basic_machine=powerpc64-unknown ;; ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'` ;; ppc64le | powerpc64little | ppc64-le | powerpc64-little) basic_machine=powerpc64le-unknown ;; ppc64le-* | powerpc64little-*) basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'` ;; ps2) basic_machine=i386-ibm ;; pw32) basic_machine=i586-unknown os=-pw32 ;; rom68k) basic_machine=m68k-rom68k os=-coff ;; rm[46]00) basic_machine=mips-siemens ;; rtpc | rtpc-*) basic_machine=romp-ibm ;; s390 | s390-*) basic_machine=s390-ibm ;; s390x | s390x-*) basic_machine=s390x-ibm ;; sa29200) basic_machine=a29k-amd os=-udi ;; sb1) basic_machine=mipsisa64sb1-unknown ;; sb1el) basic_machine=mipsisa64sb1el-unknown ;; sei) basic_machine=mips-sei os=-seiux ;; sequent) basic_machine=i386-sequent ;; sh) basic_machine=sh-hitachi os=-hms ;; sh64) basic_machine=sh64-unknown ;; sparclite-wrs | simso-wrs) basic_machine=sparclite-wrs os=-vxworks ;; sps7) basic_machine=m68k-bull os=-sysv2 ;; spur) basic_machine=spur-unknown ;; st2000) basic_machine=m68k-tandem ;; stratus) basic_machine=i860-stratus os=-sysv4 ;; sun2) basic_machine=m68000-sun ;; sun2os3) basic_machine=m68000-sun os=-sunos3 ;; sun2os4) basic_machine=m68000-sun os=-sunos4 ;; sun3os3) basic_machine=m68k-sun os=-sunos3 ;; sun3os4) basic_machine=m68k-sun os=-sunos4 ;; sun4os3) basic_machine=sparc-sun os=-sunos3 ;; sun4os4) basic_machine=sparc-sun os=-sunos4 ;; sun4sol2) basic_machine=sparc-sun os=-solaris2 ;; sun3 | sun3-*) basic_machine=m68k-sun ;; sun4) basic_machine=sparc-sun ;; sun386 | sun386i | roadrunner) basic_machine=i386-sun ;; sv1) basic_machine=sv1-cray os=-unicos ;; symmetry) basic_machine=i386-sequent os=-dynix ;; t3e) basic_machine=alphaev5-cray os=-unicos ;; t90) basic_machine=t90-cray os=-unicos ;; tic54x | c54x*) basic_machine=tic54x-unknown os=-coff ;; tic55x | c55x*) basic_machine=tic55x-unknown os=-coff ;; tic6x | c6x*) basic_machine=tic6x-unknown os=-coff ;; tx39) basic_machine=mipstx39-unknown ;; tx39el) basic_machine=mipstx39el-unknown ;; toad1) basic_machine=pdp10-xkl os=-tops20 ;; tower | tower-32) basic_machine=m68k-ncr ;; tpf) basic_machine=s390x-ibm os=-tpf ;; udi29k) basic_machine=a29k-amd os=-udi ;; ultra3) basic_machine=a29k-nyu os=-sym1 ;; v810 | necv810) basic_machine=v810-nec os=-none ;; vaxv) basic_machine=vax-dec os=-sysv ;; vms) basic_machine=vax-dec os=-vms ;; vpp*|vx|vx-*) basic_machine=f301-fujitsu ;; vxworks960) basic_machine=i960-wrs os=-vxworks ;; vxworks68) basic_machine=m68k-wrs os=-vxworks ;; vxworks29k) basic_machine=a29k-wrs os=-vxworks ;; w65*) basic_machine=w65-wdc os=-none ;; w89k-*) basic_machine=hppa1.1-winbond os=-proelf ;; xbox) basic_machine=i686-pc os=-mingw32 ;; xps | xps100) basic_machine=xps100-honeywell ;; ymp) basic_machine=ymp-cray os=-unicos ;; z8k-*-coff) basic_machine=z8k-unknown os=-sim ;; none) basic_machine=none-none os=-none ;; # Here we handle the default manufacturer of certain CPU types. It is in # some cases the only manufacturer, in others, it is the most popular. w89k) basic_machine=hppa1.1-winbond ;; op50n) basic_machine=hppa1.1-oki ;; op60c) basic_machine=hppa1.1-oki ;; romp) basic_machine=romp-ibm ;; mmix) basic_machine=mmix-knuth ;; rs6000) basic_machine=rs6000-ibm ;; vax) basic_machine=vax-dec ;; pdp10) # there are many clones, so DEC is not a safe bet basic_machine=pdp10-unknown ;; pdp11) basic_machine=pdp11-dec ;; we32k) basic_machine=we32k-att ;; sh3 | sh4 | sh[34]eb | sh[1234]le | sh[23]ele) basic_machine=sh-unknown ;; sh64) basic_machine=sh64-unknown ;; sparc | sparcv8 | sparcv9 | sparcv9b) basic_machine=sparc-sun ;; cydra) basic_machine=cydra-cydrome ;; orion) basic_machine=orion-highlevel ;; orion105) basic_machine=clipper-highlevel ;; mac | mpw | mac-mpw) basic_machine=m68k-apple ;; pmac | pmac-mpw) basic_machine=powerpc-apple ;; *-unknown) # Make sure to match an already-canonicalized machine name. ;; *) echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 exit 1 ;; esac # Here we canonicalize certain aliases for manufacturers. case $basic_machine in *-digital*) basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'` ;; *-commodore*) basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'` ;; *) ;; esac # Decode manufacturer-specific aliases for certain operating systems. if [ x"$os" != x"" ] then case $os in # First match some system type aliases # that might get confused with valid system types. # -solaris* is a basic system type, with this one exception. -solaris1 | -solaris1.*) os=`echo $os | sed -e 's|solaris1|sunos4|'` ;; -solaris) os=-solaris2 ;; -svr4*) os=-sysv4 ;; -unixware*) os=-sysv4.2uw ;; -gnu/linux*) os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'` ;; # First accept the basic system types. # The portable systems comes first. # Each alternative MUST END IN A *, to match a version number. # -sysv* is not here because it comes later, after sysvr4. -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \ | -*vms* | -sco* | -esix* | -isc* | -aix* | -sunos | -sunos[34]*\ | -hpux* | -unos* | -osf* | -luna* | -dgux* | -solaris* | -sym* \ | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \ | -aos* \ | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \ | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \ | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* | -openbsd* \ | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \ | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \ | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \ | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \ | -chorusos* | -chorusrdb* \ | -cygwin* | -pe* | -psos* | -moss* | -proelf* | -rtems* \ | -mingw32* | -linux-gnu* | -linux-uclibc* | -uxpv* | -beos* | -mpeix* | -udk* \ | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \ | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \ | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \ | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \ | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \ | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly*) # Remember, each alternative MUST END IN *, to match a version number. ;; -qnx*) case $basic_machine in x86-* | i*86-*) ;; *) os=-nto$os ;; esac ;; -nto-qnx*) ;; -nto*) os=`echo $os | sed -e 's|nto|nto-qnx|'` ;; -sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \ | -windows* | -osx | -abug | -netware* | -os9* | -beos* \ | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*) ;; -mac*) os=`echo $os | sed -e 's|mac|macos|'` ;; -linux-dietlibc) os=-linux-dietlibc ;; -linux*) os=`echo $os | sed -e 's|linux|linux-gnu|'` ;; -sunos5*) os=`echo $os | sed -e 's|sunos5|solaris2|'` ;; -sunos6*) os=`echo $os | sed -e 's|sunos6|solaris3|'` ;; -opened*) os=-openedition ;; -os400*) os=-os400 ;; -wince*) os=-wince ;; -osfrose*) os=-osfrose ;; -osf*) os=-osf ;; -utek*) os=-bsd ;; -dynix*) os=-bsd ;; -acis*) os=-aos ;; -atheos*) os=-atheos ;; -syllable*) os=-syllable ;; -386bsd) os=-bsd ;; -ctix* | -uts*) os=-sysv ;; -nova*) os=-rtmk-nova ;; -ns2 ) os=-nextstep2 ;; -nsk*) os=-nsk ;; # Preserve the version number of sinix5. -sinix5.*) os=`echo $os | sed -e 's|sinix|sysv|'` ;; -sinix*) os=-sysv4 ;; -tpf*) os=-tpf ;; -triton*) os=-sysv3 ;; -oss*) os=-sysv3 ;; -svr4) os=-sysv4 ;; -svr3) os=-sysv3 ;; -sysvr4) os=-sysv4 ;; # This must come after -sysvr4. -sysv*) ;; -ose*) os=-ose ;; -es1800*) os=-ose ;; -xenix) os=-xenix ;; -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) os=-mint ;; -aros*) os=-aros ;; -kaos*) os=-kaos ;; -zvmoe) os=-zvmoe ;; -none) ;; *) # Get rid of the `-' at the beginning of $os. os=`echo $os | sed 's/[^-]*-//'` echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2 exit 1 ;; esac else # Here we handle the default operating systems that come with various machines. # The value should be what the vendor currently ships out the door with their # machine or put another way, the most popular os provided with the machine. # Note that if you're going to try to match "-MANUFACTURER" here (say, # "-sun"), then you have to tell the case statement up towards the top # that MANUFACTURER isn't an operating system. Otherwise, code above # will signal an error saying that MANUFACTURER isn't an operating # system, and we'll never get to this point. case $basic_machine in *-acorn) os=-riscix1.2 ;; arm*-rebel) os=-linux ;; arm*-semi) os=-aout ;; c4x-* | tic4x-*) os=-coff ;; # This must come before the *-dec entry. pdp10-*) os=-tops20 ;; pdp11-*) os=-none ;; *-dec | vax-*) os=-ultrix4.2 ;; m68*-apollo) os=-domain ;; i386-sun) os=-sunos4.0.2 ;; m68000-sun) os=-sunos3 # This also exists in the configure program, but was not the # default. # os=-sunos4 ;; m68*-cisco) os=-aout ;; mips*-cisco) os=-elf ;; mips*-*) os=-elf ;; or32-*) os=-coff ;; *-tti) # must be before sparc entry or we get the wrong os. os=-sysv3 ;; sparc-* | *-sun) os=-sunos4.1.1 ;; *-be) os=-beos ;; *-ibm) os=-aix ;; *-knuth) os=-mmixware ;; *-wec) os=-proelf ;; *-winbond) os=-proelf ;; *-oki) os=-proelf ;; *-hp) os=-hpux ;; *-hitachi) os=-hiux ;; i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent) os=-sysv ;; *-cbm) os=-amigaos ;; *-dg) os=-dgux ;; *-dolphin) os=-sysv3 ;; m68k-ccur) os=-rtu ;; m88k-omron*) os=-luna ;; *-next ) os=-nextstep ;; *-sequent) os=-ptx ;; *-crds) os=-unos ;; *-ns) os=-genix ;; i370-*) os=-mvs ;; *-next) os=-nextstep3 ;; *-gould) os=-sysv ;; *-highlevel) os=-bsd ;; *-encore) os=-bsd ;; *-sgi) os=-irix ;; *-siemens) os=-sysv4 ;; *-masscomp) os=-rtu ;; f30[01]-fujitsu | f700-fujitsu) os=-uxpv ;; *-rom68k) os=-coff ;; *-*bug) os=-coff ;; *-apple) os=-macos ;; *-atari*) os=-mint ;; *) os=-none ;; esac fi # Here we handle the case where we know the os, and the CPU type, but not the # manufacturer. We pick the logical manufacturer. vendor=unknown case $basic_machine in *-unknown) case $os in -riscix*) vendor=acorn ;; -sunos*) vendor=sun ;; -aix*) vendor=ibm ;; -beos*) vendor=be ;; -hpux*) vendor=hp ;; -mpeix*) vendor=hp ;; -hiux*) vendor=hitachi ;; -unos*) vendor=crds ;; -dgux*) vendor=dg ;; -luna*) vendor=omron ;; -genix*) vendor=ns ;; -mvs* | -opened*) vendor=ibm ;; -os400*) vendor=ibm ;; -ptx*) vendor=sequent ;; -tpf*) vendor=ibm ;; -vxsim* | -vxworks* | -windiss*) vendor=wrs ;; -aux*) vendor=apple ;; -hms*) vendor=hitachi ;; -mpw* | -macos*) vendor=apple ;; -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) vendor=atari ;; -vos*) vendor=stratus ;; esac basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"` ;; esac echo $basic_machine$os exit 0 # Local variables: # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "timestamp='" # time-stamp-format: "%:y-%02m-%02d" # time-stamp-end: "'" # End: ocfs2-tools-ocfs2-tools-1.8.6/configure.in000066400000000000000000000316121347147137200203750ustar00rootroot00000000000000AC_PREREQ(2.54) AC_INIT(libocfs2/bitmap.c) PACKAGE=ocfs2-tools AC_SUBST(PACKAGE) # Adjust these for the software version. MAJOR_VERSION=1 MINOR_VERSION=8 MICRO_VERSION=6 EXTRA_VERSION= DIST_VERSION=$MAJOR_VERSION.$MINOR_VERSION.$MICRO_VERSION if test -z "$EXTRA_VERSION"; then VERSION=$DIST_VERSION else VERSION=$DIST_VERSION-$EXTRA_VERSION fi AC_SUBST(MAJOR_VERSION) AC_SUBST(MINOR_VERSION) AC_SUBST(MICRO_VERSION) AC_SUBST(EXTRA_VERSION) AC_SUBST(DIST_VERSION) AC_SUBST(VERSION) AC_CANONICAL_HOST case "$host" in *-*-linux*) ;; *) AC_MSG_ERROR([This filesystem will only work on Linux]) ;; esac # # If CFLAGS is non-empty, leave the debugging and optimization symobls # to the caller. Otherwise, set them according to --enable-debug. # OCFS2_DEBUG= AC_MSG_CHECKING(for debugging) if test "x$CFLAGS" = "x"; then AC_ARG_ENABLE(debug, [ --enable-debug=[yes/no] Turn on debugging [default=no]],,enable_debug=no) if test "x$enable_debug" = "xyes"; then OCFS2_DEBUG=yes CFLAGS="-g" else CFLAGS="-O2" fi AC_MSG_RESULT($enable_debug) else AC_MSG_RESULT([skipped, CFLAGS is set]) fi AC_SUBST(OCFS2_DEBUG) AC_PROG_CC AC_PROG_CPP # # If OCFS2_DEBUG was set, we know CFLAGS must be "-g". If we're using # GNU C, get extra debugging symbols. # if test "x$GCC" = "xyes" -a "x$OCFS2_DEBUG" = "xyes"; then CFLAGS="-ggdb" fi AC_PROG_INSTALL AC_PROG_LN_S AC_PROG_RANLIB AC_PATH_PROG(AR, ar) AC_HEADER_STDC AC_C_CONST MB_VENDOR(VENDOR) if test "x$GCC" != "xyes"; then AC_MSG_ERROR(GCC is required) fi AC_ARG_WITH([root-prefix], [ --with-root-prefix=PREFIX override prefix variable for files to be placed in the root], root_prefix=$withval, root_prefix="") root_bindir='${root_prefix}/bin' root_sbindir='${root_prefix}/sbin' root_sysconfdir='${root_prefix}/etc' AC_SUBST(root_prefix) AC_SUBST(root_bindir) AC_SUBST(root_sbindir) AC_SUBST(root_sysconfdir) COM_ERR_LIBS= PKG_CHECK_MODULES(COM_ERR, com_err,, [ AC_CHECK_LIB(com_err, com_err, COM_ERR_LIBS=-lcom_err) if test "x$COM_ERR_LIBS" = "x"; then AC_MSG_ERROR([Unable to find com_err library]) fi AC_CHECK_HEADER(et/com_err.h, :, AC_MSG_ERROR([Unable to find com_err headers])) AC_SUBST(COM_ERR_LIBS) ]) UUID_LIBS= AC_CHECK_LIB(uuid, uuid_unparse, UUID_LIBS=-luuid) if test "x$UUID_LIBS" = "x"; then AC_MSG_ERROR([Unable to find uuid library]) fi AC_CHECK_HEADER(uuid/uuid.h, :, AC_MSG_ERROR([Unable to find uuid headers])) AC_SUBST(UUID_LIBS) AIO_LIBS= AC_CHECK_LIB(aio, io_setup, AIO_LIBS=-laio) if test "x$AIO_LIBS" = "x"; then AC_MSG_ERROR([Unable to find libaio library]) fi AC_CHECK_HEADER(libaio.h, :, AC_MSG_ERROR([Unable to find /usr/include/libaio.h])) AC_SUBST(AIO_LIBS) NCURSES_LIBS= AC_CHECK_LIB(ncurses, tgetstr, NCURSES_LIBS=-lncurses) if test "x$NCURSES_LIBS" = "x"; then AC_MSG_ERROR([Unable to find ncurses library]) fi AC_SUBST(NCURSES_LIBS) saved_LDFLAGS="$LDFLAGS" LDFLAGS="$LDFLAGS -lncurses" READLINE_LIBS= AC_CHECK_LIB(readline, readline, READLINE_LIBS=-lreadline) if test "x$READLINE_LIBS" = "x"; then AC_MSG_ERROR([Unable to find readline library]) fi AC_CHECK_HEADER(readline/readline.h, :, AC_MSG_ERROR([Unable to find readline headers])) AC_SUBST(READLINE_LIBS) LDFLAGS="$saved_LDFLAGS" AC_MSG_CHECKING(for debug executables) AC_ARG_ENABLE(debugexe, [ --enable-debugexe=[yes/no] Enable debug executables for library source files [default=no]],,enable_debugexe=no) OCFS2_DEBUG_EXE= if test "x$enable_debugexe" = "xyes"; then OCFS2_DEBUG_EXE=yes fi AC_SUBST(OCFS2_DEBUG_EXE) AC_MSG_RESULT($enable_debugexe) GLIB_REQUIRED_VERSION=2.2.3 AM_PATH_GLIB_2_0($GLIB_REQUIRED_VERSION, have_glib=yes, AC_MSG_ERROR([GLib $GLIB_REQUIRED_VERSION or better is required])) # # Can we build statically? This is important # dyn_save_CFLAGS="$CFLAGS" dyn_save_LIBS="$LIBS" CFLAGS="$CFLAGS $GLIB_CFLAGS" LIBS="-static $LIBS $GLIB_LIBS $COM_ERR_LIBS" AC_MSG_CHECKING(whether glib and com_err can be linked statically) AC_TRY_LINK([ #include #include #include ], [ char *e = error_message(-1); return ((glib_major_version) || (glib_minor_version) || (glib_micro_version)); ], [ static_lib_link=yes ], [ static_lib_link=no ]) AC_MSG_RESULT($static_lib_link) CFLAGS="$dyn_save_CFLAGS" LIBS="$dyn_save_LIBS" OCFS2_DYNAMIC_FSCK=yes AC_MSG_CHECKING(whether to build fsck dynamically) AC_ARG_ENABLE([dynamic-fsck], [ --enable-dynamic-fsck=[yes/no] Build fsck dynamically [default=yes]],,enable_dynamic_fsck=yes) AC_MSG_RESULT($enable_dynamic_fsck) if test "x$enable_dynamic_fsck" = "xno" -a "x$static_lib_link" = "xno"; then AC_MSG_ERROR([Unable to statically link fsck.ocfs2]) fi if test "x$enable_dynamic_fsck" = "xno"; then OCFS2_DYNAMIC_FSCK= fi AC_SUBST(OCFS2_DYNAMIC_FSCK) OCFS2_DYNAMIC_CTL=yes AC_MSG_CHECKING(whether to build cluster control tools dynamically) AC_ARG_ENABLE([dynamic-ctl], [ --enable-dynamic-ctl=[yes/no] Build cluster control tools dynamically [default=yes]],,enable_dynamic_ctl=yes) AC_MSG_RESULT($enable_dynamic_ctl) if test "x$enable_dynamic_ctl" = "xno" -a "x$static_lib_link" = "xno"; then AC_MSG_ERROR([Unable to statically link cluster control tools]) fi if test "x$enable_dynamic_ctl" = "xno"; then OCFS2_DYNAMIC_CTL= fi AC_SUBST(OCFS2_DYNAMIC_CTL) BUILD_DEBUGOCFS2= ocfs_tools_save_LIBS="$LIBS" LIBS="$LIBS -lncurses" AC_CHECK_LIB(readline, readline, [AC_CHECK_HEADER(readline/readline.h, BUILD_DEBUGOCFS2=yes, [AC_MSG_WARN([readline not found, debugfs.ocfs2 will not be built])])], [AC_MSG_WARN([readline not found, debugfs.ocfs2 will not be built])]) LIBS="$ocfs_tools_save_LIBS" AC_SUBST(BUILD_DEBUGOCFS2) pcmk_found= AC_CHECK_LIB(crmcluster, crm_get_peer, [AC_CHECK_HEADER(pacemaker/crm_config.h, pcmk_found=yes, [AC_MSG_WARN([Pacemaker headers not found, pacemaker support will not be built])])], [AC_MSG_WARN([libcrmcluster not found, pacemaker support will not be built])]) # We use cman_replyto_shutdown to insure a new enough libcman cman_found= AC_CHECK_LIB(cman, cman_replyto_shutdown, [AC_CHECK_HEADER(libcman.h, cman_found=yes, [AC_MSG_WARN([libcman.h not found, cman support will not be built])])], [AC_MSG_WARN([libcman not found, cman support will not be built])]) cpg_found= HAVE_COROSYNC= AC_CHECK_HEADER(corosync/cpg.h, [ cpg_found=yes HAVE_COROSYNC=yes ], [AC_CHECK_HEADER(openais/cpg.h, cpg_found=yes, [AC_MSG_WARN([openais/cpg.h not found, ocfs2_controld will not be built])])]) AC_SUBST(HAVE_COROSYNC) # # We can't use AC_CHECK_LIB on corosync or openais libraries, because they # are in ${libdir}/corosync or ${libdir}/openais, and we can't expand it in # configure.in. What we'll try instead is to search AIS_TRY_PATH for # -L/ -l. # NOTE that the initial colon ':' is *intentional*. We want the empty # string first in case the user passed LDFLAGS. # AIS_TRY_PATH=":/usr/lib64:/usr/lib:/usr/local/lib64:/usr/local/lib" CPG_LDFLAGS= if test "x$cpg_found" = "xyes"; then cpg_found= if test "x$HAVE_COROSYNC" = "xyes"; then cpg_package=corosync else cpg_package=openais fi TRY_PATH="$AIS_TRY_PATH" AC_MSG_CHECKING([for cpg_initialize in -lcpg]) while test "x$TRY_PATH" != "x"; do TRY="`echo $TRY_PATH | cut -f1 -d:`" NEW_TRY_PATH="`echo $TRY_PATH | cut -f2- -d:`" if test "x$NEW_TRY_PATH" != "x$TRY_PATH"; then TRY_PATH="$NEW_TRY_PATH" else TRY_PATH="" fi if test "x$TRY" != "x"; then TRY="-L${TRY}/${cpg_package}" fi # TRY="$TRY" saved_LDFLAGS="$LDFLAGS" LDFLAGS="$LDFLAGS $TRY -lcpg" AC_LINK_IFELSE([AC_LANG_CALL([], [cpg_initialize])], cpg_found=yes) LDFLAGS="$saved_LDFLAGS" if test "x$cpg_found" = "xyes"; then CPG_LDFLAGS="$TRY" break fi done fi if test "x$cpg_found" = "xyes"; then AC_MSG_RESULT(yes) else AC_MSG_RESULT(no) AC_MSG_WARN([libcpg not found, ocfs2_controld will not be built]) fi AC_SUBST(CPG_LDFLAGS) ckpt_found= AC_CHECK_HEADER(openais/saCkpt.h, ckpt_found=yes, [AC_MSG_WARN([openais/saCkpt.h not found, ocfs2_controld will not be built])], [#include ]) AIS_LDFLAGS= if test "x$ckpt_found" = "xyes"; then ckpt_found= TRY_PATH="$AIS_TRY_PATH" AC_MSG_CHECKING([for saCkptInitialize in -lSaCkpt]) while test "x$TRY_PATH" != "x"; do TRY="`echo $TRY_PATH | cut -f1 -d:`" NEW_TRY_PATH="`echo $TRY_PATH | cut -f2- -d:`" if test "x$NEW_TRY_PATH" != "x$TRY_PATH"; then TRY_PATH="$NEW_TRY_PATH" else TRY_PATH="" fi if test "x$TRY" != "x"; then TRY="-L${TRY}/openais" fi # TRY="$TRY" saved_LDFLAGS="$LDFLAGS" LDFLAGS="$LDFLAGS $TRY -lSaCkpt" AC_LINK_IFELSE([AC_LANG_CALL([], [saCkptInitialize])], ckpt_found=yes) LDFLAGS="$saved_LDFLAGS" if test "x$ckpt_found" = "xyes"; then AIS_LDFLAGS="$TRY" break fi done fi if test "x$ckpt_found" = "xyes"; then AC_MSG_RESULT(yes) else AC_MSG_RESULT(no) AC_MSG_WARN([libSaCkpt not found, ocfs2_controld will not be built]) fi AC_SUBST(AIS_LDFLAGS) libdlmcontrol_found= AC_CHECK_LIB(dlmcontrol, dlmc_fs_connect, [AC_CHECK_HEADER(libdlmcontrol.h, libdlmcontrol_found=yes, [AC_MSG_WARN([libdlmcontrol.h not found, ocfs2_controld will not be built])], [ #include #include #include ])], [AC_MSG_WARN([libdlmcontrol not found, ocfs2_controld will not be built])]) LIBDLM_FOUND= AC_CHECK_HEADER(libdlm.h, LIBDLM_FOUND=yes, [AC_MSG_WARN([libdlm.h not found, fsdlm support will not be built])]) AC_SUBST(LIBDLM_FOUND) fsdlm_found= if test "x$LIBDLM_FOUND" = "xyes"; then AC_CHECK_LIB(dlm_lt, dlm_create_lockspace, fsdlm_found=yes) if test "xfsdlm_found" = "xyes"; then AC_MSG_WARN([libdlm_lt not found, fsdlm support will not be built]) fi fi BUILD_FSDLM_SUPPORT= DL_LIBS= if test "x$fsdlm_found" = "xyes"; then BUILD_FSDLM_SUPPORT=yes DL_LIBS=-ldl fi AC_SUBST(BUILD_FSDLM_SUPPORT) AC_SUBST(DL_LIBS) BUILD_OCFS2_CONTROLD= if test "x$cpg_found" = "xyes" -a "x$ckpt_found" = "xyes" -a "x$libdlmcontrol_found" = "xyes"; then if test "x$fsdlm_found" = "xyes"; then BUILD_OCFS2_CONTROLD=yes else AC_MSG_WARN([fsdlm support is not available, ocfs2_controld will not be built]) fi fi AC_SUBST(BUILD_OCFS2_CONTROLD) LIBCMAP_FOUND= AC_CHECK_HEADER(corosync/cmap.h, LIBCMAP_FOUND=yes, [AC_MSG_WARN([corosync/cmap.h not found, cmap support will not be built. Older api will be used])]) AC_SUBST(LIBCMAP_FOUND) fsdlm_found= if test "x$LIBCMAP_FOUND" = "xyes"; then AC_CHECK_LIB(cmap, cmap_initialize, cmap_found=yes) if ! test "x$cmap_found" = "xyes"; then AC_MSG_WARN([libcmap not found, cmap support will not be built. Older api will be used]) fi fi BUILD_CMAP_SUPPORT= if test "x$cmap_found" = "xyes"; then BUILD_CMAP_SUPPORT=yes fi AC_SUBST(BUILD_CMAP_SUPPORT) BUILD_PCMK_SUPPORT= if test "x$pcmk_found" = "xyes" -a "x$BUILD_OCFS2_CONTROLD" = "xyes"; then BUILD_PCMK_SUPPORT=yes fi AC_SUBST(BUILD_PCMK_SUPPORT) BUILD_CMAN_SUPPORT= if test "x$cman_found" = "xyes" -a "x$BUILD_OCFS2_CONTROLD" = "xyes"; then BUILD_CMAN_SUPPORT=yes fi AC_SUBST(BUILD_CMAN_SUPPORT) HAVE_PYTHON=no AM_PATH_PYTHON(2.3, have_python=yes, have_python=no) if test "x$have_python" = "xyes"; then AM_CHECK_PYTHON_HEADERS(have_pythondev=yes, have_pythondev=no) if test "x$have_pythondev" = "xyes"; then if $PYTHON -c 'import gobject' >/dev/null 2>&1; then HAVE_PYTHON=yes OCFS2_BLKID else AC_MSG_WARN([could not find PyGTK, ocfs2console will not be built]) fi else AC_MSG_WARN([could not find Python headers, ocfs2console will not be built]) fi else AC_MSG_WARN([could not find Python 2.3 or higher, ocfs2console will not be built]) fi BUILD_OCFS2CONSOLE= AC_MSG_CHECKING(whether to build ocfs2console) AC_ARG_ENABLE(ocfs2console, [ --enable-ocfs2console=[yes/no] Build GUI frontend [default=no]],,enable_ocfs2console=no) AC_MSG_RESULT($enable_ocfs2console) if test "x$enable_ocfs2console" = "xyes"; then if test "x$HAVE_PYTHON" = "xno"; then AC_MSG_ERROR([Unable to build ocfs2console]) fi BUILD_OCFS2CONSOLE=yes fi AC_SUBST(BUILD_OCFS2CONSOLE) if test "x$BUILD_OCFS2CONSOLE" = "xyes"; then AC_CONFIG_FILES([ ocfs2console/ocfs2interface/confdefs.py ocfs2console/ocfs2console.8 ]) fi AC_CONFIG_FILES([ Config.make o2cb.pc o2dlm.pc ocfs2.pc debugfs.ocfs2/debugfs.ocfs2.8 mkfs.ocfs2/mkfs.ocfs2.8 mounted.ocfs2/mounted.ocfs2.8 fsck.ocfs2/fsck.ocfs2.8 fsck.ocfs2/fsck.ocfs2.checks.8 mount.ocfs2/mount.ocfs2.8 o2cb_ctl/o2cb_ctl.8 o2cb_ctl/o2cb.8 ocfs2_hb_ctl/ocfs2_hb_ctl.8 tunefs.ocfs2/tunefs.ocfs2.8 tunefs.ocfs2/o2cluster.8 o2image/o2image.8 o2info/o2info.1 libo2cb/o2cb.7 o2monitor/o2hbmonitor.8 o2cb_ctl/ocfs2.cluster.conf.5 vendor/common/o2cb.sysconfig.5 libocfs2/ocfs2.7 vendor/common/ocfs2-tools.spec-generic defragfs.ocfs2/defragfs.ocfs2.8 ]) AC_OUTPUT ocfs2-tools-ocfs2-tools-1.8.6/debian/000077500000000000000000000000001347147137200173035ustar00rootroot00000000000000ocfs2-tools-ocfs2-tools-1.8.6/debian/.gitignore000066400000000000000000000002601347147137200212710ustar00rootroot00000000000000*.sw? files *.debhelper tmp dsh *.substvars ocfs2-tools.o2cb.default ocfs2-tools.o2cb.init ocfs2-tools.ocfs2.init ocfs2-tools ocfs2-tools-static-dev stamp-patched ocfs2console ocfs2-tools-ocfs2-tools-1.8.6/debian/NEWS000066400000000000000000000006721347147137200200070ustar00rootroot00000000000000ocfs2-tools (1.2.1-1) unstable; urgency=low * The init script needs modules now in order to function properly. Upstream strongly discourages the changes I did in 1.1.5 in order to work for the monolithic kernel case. The sctp module is no longer necessary. Please read /usr/share/doc/ocfs2-tools/README.Debian for the complete requirements. -- David Martínez Moreno Sun, 28 May 2006 13:18:50 +0200 ocfs2-tools-ocfs2-tools-1.8.6/debian/README.Debian000066400000000000000000000016041347147137200213450ustar00rootroot00000000000000Instructions for OCFS2 filesystems in Debian ============================================ The init script for Debian expects to have most of the kernel code it uses available in modular form. In previous releases sctp module was necessary to work around a kernel starvation that led to crashes. With 1.2.1 this seems to be no longer necessary. Here follows the name and location of the modules: Module name Location -------------------------------- configfs File systems -> Pseudo filesystems -> Userspace-driven configuration filesystem ocfs2_nodemanager File systems -> OCFS2 file system support ocfs2_dlm File systems -> OCFS2 file system support ocfs2_dlmfs File systems -> OCFS2 file system support ocfs2-tools-ocfs2-tools-1.8.6/debian/changelog000066400000000000000000000214341347147137200211610ustar00rootroot00000000000000ocfs2-tools (1.3.9-1) UNRELEASED; urgency=low * Update default timeouts in template and translations files. * debian/rules: fix clean target to not purge upstream patches/ directory. * debian/control: rework Build-Depends - sort them: debian build specific bits first, upstream after in configure check order. - add pkg-config. - add libselinux1-dev and libsepol1-dev. * add package ocfs2-tools-static-dev to include all development headers and static libraries required to build ocfs2-test (and possibly other tools that already in development stage. * stop shipping local copy of mount.ocfs2.8 -- Fabio M. Di Nitto Mon, 19 Nov 2007 06:53:59 +0100 ocfs2-tools (1.2.4-1) unstable; urgency=low * The "Lazo rosa" release. * New upstream release. This version was released on Apr 6th, 2007. Main changes are: - Allow network timeout configuration. - Bug fixes. - offsetof(3) issues have been fixed, so patches/102_stddef.h is no longer relevant. * Added debconf support for O2CB_IDLE_TIMEOUT_MS, O2CB_KEEPALIVE_DELAY_MS and O2CB_RECONNECT_DELAY_MS in postinst, config and templates mainly from Ubuntu. * po/es.po: While at it, updated Spanish translation. * po/vi.po: Updated Vietnamese translation, thanks to Clytie Siddall (closes: #438451). * po/ru.po: Updated Russian translation, thanks to Yuri Kozlov (closes: #438633). * po/fr.po: Updated French translation. Thanks to Jean-Baka Domelevo-Entfellner (closes: #440579). * po/cs.po: Updated Czech translation. Thanks to Miroslav Kure (closes: #442903). * Added /etc/init.d/ocfs2 in order to mount and unmount OCFS2 volumes, that I simply missed on past releases. This allows to unmount OCFS2 during a reboot and thus avoid a panic (closes: #416550). * debian/rules: Removed old code for quilt patching now that we include the Makefile from that package. * This version does not fail to build twice in a row (strange...). Closes: #442692. -- David Martínez Moreno Tue, 18 Sep 2007 02:02:31 +0200 ocfs2-tools (1.2.3-1) unstable; urgency=low * New upstream release. This version was released on Feb 2nd, 2007. Main changes are: - Backup superblock support added. - Local mount support added (with kernel 2.6.20+). - Heartbeat thread's IO priority can now be set (with kernel 2.6.20+). - Bugs fixed in fsck, debugfs and console . * Removed parts of debian/NEWS no longer relevant. * Renamed debian/README to debian/README.Debian in order to be included by dh_installdocs. * Added short description to o2cb init script in order to clean a lintian warning. -- David Martínez Moreno Thu, 16 Aug 2007 02:41:22 +0200 ocfs2-tools (1.2.2-1) unstable; urgency=low * New upstream release. This version was released on Oct 20th, 2006. Main changes are: - tunefs.ocfs2: Volumes can be resized offline. - debugfs.ocfs2: Commands bmap, findpath, ncheck and decode added. - Fixed bug in o2cb_ctl when adding nodes (closes: #405353). - Fixed lots of bugs. * Removed ocfs2console/blkid/blkid_types.h, that slipped in 1.2.1-1.2. * Acknowledged NMUs (closes: #380879, #389916). * Added Dutch translation (thanks, cobaco). Closes: #436863. * Merged 01-manpages-typos and some parts of 02-init-typos patches upstream. * debian/rules: Changed dh_install to --list-missing in order to ignore temporally the now installed headers. * debian/control: Replaced Source-Version with binary:Version. -- David Martínez Moreno Wed, 15 Aug 2007 02:46:28 +0200 ocfs2-tools (1.2.1-1.4) unstable; urgency=low * Non-maintainer upload. * add patches/102_stddef.h to fix FTBFS (missing #include to know about offsetof). closes: #428949. -- Pierre Habouzit Wed, 18 Jul 2007 17:37:23 +0200 ocfs2-tools (1.2.1-1.3) unstable; urgency=low * Non-maintainer upload to fix longstanding l10n issues * Fix the spelling of "milliseconds" in debconf templates * Unmark a string for translation. Closes: #372520 * Debconf templates translations: - Czech updated. Closes: #369582 - French updated. Closes: #374335 - Vietnamese added. Sent during the call for updates of the NMU campaign. - Swedish added. Sent during the call for updates of the NMU campaign. - German added. Sent during the call for updates of the NMU campaign. - Russian added. Sent during the call for updates of the NMU campaign. - Brazilian Portuguese added. Sent during the call for updates of the NMU campaign. - Fix the encoding of the Spanish translation..:-) -- Christian Perrier Mon, 18 Dec 2006 19:49:10 +0100 ocfs2-tools (1.2.1-1.2) unstable; urgency=medium * Non-maintainer upload. * Move dh_installdeb to after dh_pysupport, to make sure update-python-modules is indeed called in postinst. (Closes: #388529) -- Steinar H. Gunderson Thu, 28 Sep 2006 13:19:03 +0200 ocfs2-tools (1.2.1-1.1) unstable; urgency=low * Non-maintainer upload. * Update package to the last python policy (Closes: #380879). -- Pierre Habouzit Thu, 31 Aug 2006 13:45:20 +0200 ocfs2-tools (1.2.1-1) unstable; urgency=low * New upstream release (closes: #362204, #364549). This release also fixes the double-free error that new glibc triggered, so closes: #358784 as well. * Added Czech debconf translation. Thanks, Miroslav Kure. Closes: #353372. * Added French debconf translation. Thanks, Julien Rosal. Closes: #354274. * Rewrote debconf templates, rendering above changes partly useless. Thanks to Thomas Huriaux for the time and the patch. Closes: #352505. * Updated Spanish translation. * debian/control: - Added libdevmapper-dev to Build-Depends, and bumped debhelper dependency to >= 5. - Bumped Standards-Version to 3.7.2.0. * Added ocfs2_faq.txt to docs. * debian/compat: Bumped debhelper compatibility to 5. * Added README.Debian. * Removed changes in init script, now it is near to the original one. Many thanks to Joel Becker from Oracle for taking the time to explain it thoroughly. The modular case works again, I have embraced the faith. :-) Closes: #363121. -- David Martínez Moreno Tue, 30 May 2006 00:38:27 +0200 ocfs2-tools (1.1.5-2) unstable; urgency=low * Removed by upstream request: - usr/sbin/find_hardlinks - usr/sbin/find_dup_extents - usr/sbin/find_inode_paths - usr/sbin/set_random_bits - usr/sbin/decode_lockres - usr/sbin/encode_lockres - usr/sbin/mark_journal_dirty These utilities are for OCFS2 development only, and may cause severe data loss, apart from little use for normal OCFS2 user. * debian/control: Added quilt to Build-Depends, and rewrite part of the build system in order to support patch and unpatch targets. * Created patches: - debian/patches/01-manpages-typos -- David Martínez Moreno Fri, 10 Feb 2006 02:53:40 +0100 ocfs2-tools (1.1.5-1) unstable; urgency=low * New upstream release (closes: #329699). * debian/control: - Bumped Standards-Version to 3.6.2.2. - Added python-gtk2 to ocfs2console Depends. - Added po-debconf to Build-Depends. * debian/copyright: Updated date and FSF's address. * fsck.ocfs2/fsck.ocfs2.8.in: Killed two birds in a row: fixed tytso's surname and removed another lintian error. * Added to ocfs2-tools.install: - usr/sbin/find_hardlinks - usr/sbin/find_dup_extents - usr/sbin/find_inode_paths - usr/sbin/set_random_bits - usr/sbin/decode_lockres - usr/sbin/encode_lockres - usr/sbin/mark_journal_dirty * debian/rules: - Update dh_installinit call to not restart the cluster on updates and to start ocfs2 very early in the boot process. - Added --fail-missing to dh_install for tracking down new binaries in future versions. - Added code to remove stuff from the diff.gz. * Added debconf infrastructure for avoiding running "/etc/init.d/o2cb configure". The skeleton was stolen from an Ubuntu grave. While at it, added Spanish translation of debconf templates. -- David Martínez Moreno Thu, 26 Jan 2006 02:10:48 +0100 ocfs2-tools (0.99.12-1) unstable; urgency=low * First public release. * Copied shamelessly a lot of the Ubuntu diff to the Debian package (thanks to Fabionne). * Changed /bin/sh to /bin/bash in the init script to avoid evident bashisms. * Stripped down debian/ directory from the original tarball. * Fixed some typos in the init.d script and in the default configuration. Also added to the init script some Oracle string in order to ease identification in the boot process. -- David Martínez Moreno Mon, 18 Jul 2005 19:01:39 +0200 ocfs2-tools-ocfs2-tools-1.8.6/debian/compat000066400000000000000000000000021347147137200205010ustar00rootroot000000000000005 ocfs2-tools-ocfs2-tools-1.8.6/debian/control000066400000000000000000000045431347147137200207140ustar00rootroot00000000000000Source: ocfs2-tools Section: admin Priority: optional Maintainer: David Martínez Moreno Standards-Version: 3.7.2.0 Build-Depends: debhelper (>= 5), po-debconf, python-support (>= 0.4), quilt, pkg-config, comerr-dev, uuid-dev, libncurses5-dev, libreadline5-dev, libglib2.0-dev (>= 2.2.3), libblkid-dev (>= 1.36), libdevmapper-dev, libselinux1-dev, libsepol1-dev, python-dev, python-gtk2 Package: ocfs2-tools Architecture: any Depends: ${shlibs:Depends}, ${misc:Depends} Section: admin Suggests: ocfs2console Description: tools for managing OCFS2 cluster filesystems OCFS2 is a general purpose cluster filesystem. Unlike the initial release of OCFS, which supported only Oracle database workloads, OCFS2 provides full support as a general purpose filesystem. OCFS2 is a complete rewrite of the previous version, designed to work as a seamless addition to the Linux kernel. . This package installs the tools to manage the OCFS2 filesystem, including mkfs, tunefs, fsck, debugfs, and the utilities to control the O2CB clustering stack. Package: ocfs2-tools-static-dev Architecture: any Depends: ${shlibs:Depends}, ocfs2-tools (= ${Source-Version}) Section: devel Description: tools for managing OCFS2 cluster filesystems - development files OCFS2 is a general purpose cluster filesystem. Unlike the initial release of OCFS, which supported only Oracle database workloads, OCFS2 provides full support as a general purpose filesystem. OCFS2 is a complete rewrite of the previous version, designed to work as a seamless addition to the Linux kernel. . This package installs the development headers and static libraries. Package: ocfs2console Architecture: any Depends: ${shlibs:Depends}, ${python:Depends}, ocfs2-tools (= ${binary:Version}), python-gtk2 Section: admin Description: tools for managing OCFS2 cluster filesystems (graphical interface) OCFS2 is a general purpose cluster filesystem. Unlike the initial release of OCFS, which supported only Oracle database workloads, OCFS2 provides full support as a general purpose filesystem. OCFS2 is a complete rewrite of the previous version, designed to work as a seamless addition to the Linux kernel. . A GUI frontend for managing OCFS2 volumes on the system. One can mount and unmount volumes, format, view overview information and individual files, and view and modify the current cluster configuration. ocfs2-tools-ocfs2-tools-1.8.6/debian/copyright000066400000000000000000000020561347147137200212410ustar00rootroot00000000000000This package was debianized by David Martínez Moreno on Tue, 5 Jul 2005 17:02:00 +0200. It was downloaded from http://oss.oracle.com/projects/ocfs2-tools/files/source/ Copyright: (c) 2004-2007 Oracle Corporation, All Rights Reserved. License: 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. On Debian systems, the complete text of the GNU General Public License can be found in /usr/share/common-licenses/GPL file. ocfs2-tools-ocfs2-tools-1.8.6/debian/ocfs2-tools-static-dev.install000066400000000000000000000000561347147137200251070ustar00rootroot00000000000000usr/include/* usr/lib/*.a usr/lib/pkgconfig/* ocfs2-tools-ocfs2-tools-1.8.6/debian/ocfs2-tools.config000066400000000000000000000026421347147137200226500ustar00rootroot00000000000000#!/bin/sh set -e ## Source debconf library. . /usr/share/debconf/confmodule ## be sure to read the values from the config file. if [ -e /etc/default/o2cb ]; then . /etc/default/o2cb if [ -z "$O2CB_HEARTBEAT_THRESHOLD" ];then O2CB_HEARTBEAT_THRESHOLD=7 fi if [ -z "$O2CB_IDLE_TIMEOUT_MS" ];then O2CB_IDLE_TIMEOUT_MS=10000 fi if [ -z "$O2CB_KEEPALIVE_DELAY_MS" ];then O2CB_KEEPALIVE_DELAY_MS=5000 fi if [ -z "$O2CB_RECONNECT_DELAY_MS" ];then O2CB_RECONNECT_DELAY_MS=2000 fi db_set ocfs2-tools/init "$O2CB_ENABLED" db_set ocfs2-tools/clustername "$O2CB_BOOTCLUSTER" db_set ocfs2-tools/heartbeat_threshold "$O2CB_HEARTBEAT_THRESHOLD" db_set ocfs2-tools/idle_timeout "$O2CB_IDLE_TIMEOUT_MS" db_set ocfs2-tools/keepalive_delay "$O2CB_KEEPALIVE_DELAY_MS" db_set ocfs2-tools/reconnect_delay "$O2CB_RECONNECT_DELAY_MS" fi if [ "$1" = "configure" ] || [ "$1" = "reconfigure" ]; then db_input medium ocfs2-tools/init || true db_go || true db_get ocfs2-tools/init if [ "$RET" = "true" ]; then db_input medium ocfs2-tools/clustername || true db_go || true db_input medium ocfs2-tools/heartbeat_threshold || true db_go || true db_input low ocfs2-tools/idle_timeout || true db_go || true db_input low ocfs2-tools/keepalive_delay || true db_go || true db_input low ocfs2-tools/reconnect_delay || true db_go || true fi fi #DEBHELPER# ocfs2-tools-ocfs2-tools-1.8.6/debian/ocfs2-tools.docs000066400000000000000000000000241347147137200223230ustar00rootroot00000000000000CREDITS MAINTAINERS ocfs2-tools-ocfs2-tools-1.8.6/debian/ocfs2-tools.install000066400000000000000000000010231347147137200230410ustar00rootroot00000000000000sbin/debugfs.ocfs2 sbin/fsck.ocfs2 sbin/mkfs.ocfs2 sbin/mount.ocfs2 sbin/mounted.ocfs2 sbin/o2cb_ctl sbin/ocfs2_hb_ctl sbin/tunefs.ocfs2 usr/bin/o2info usr/sbin/o2hbmonitor usr/share/man/man8/debugfs.ocfs2.8 usr/share/man/man8/fsck.ocfs2.8 usr/share/man/man8/fsck.ocfs2.checks.8 usr/share/man/man8/mkfs.ocfs2.8 usr/share/man/man8/mount.ocfs2.8 usr/share/man/man8/mounted.ocfs2.8 usr/share/man/man7/o2cb.7 usr/share/man/man8/o2cb_ctl.8 usr/share/man/man8/ocfs2_hb_ctl.8 usr/share/man/man8/tunefs.ocfs2.8 usr/share/man/man1/o2info.1 ocfs2-tools-ocfs2-tools-1.8.6/debian/ocfs2-tools.manpages000066400000000000000000000012171347147137200231730ustar00rootroot00000000000000debian/tmp/usr/share/man/man8/debugfs.ocfs2.8 debian/tmp/usr/share/man/man8/fsck.ocfs2.8 debian/tmp/usr/share/man/man8/fsck.ocfs2.checks.8 debian/tmp/usr/share/man/man8/mkfs.ocfs2.8 debian/tmp/usr/share/man/man8/tunefs.ocfs2.8 debian/tmp/usr/share/man/man8/mounted.ocfs2.8 debian/tmp/usr/share/man/man8/o2cb_ctl.8 debian/tmp/usr/share/man/man8/ocfs2_hb_ctl.8 debian/tmp/usr/share/man/man8/o2image.8 debian/tmp/usr/share/man/man7/o2cb.7 debian/tmp/usr/share/man/man1/o2info.1 debian/tmp/usr/share/man/man8/o2hbmonitor.8 debian/tmp/usr/share/man/man5/ocfs2.cluster.conf.5 debian/tmp/usr/share/man/man5/o2cb.sysconfig.5 debian/tmp/usr/share/man/man7/ocfs2.7 ocfs2-tools-ocfs2-tools-1.8.6/debian/ocfs2-tools.postinst000066400000000000000000000027331347147137200232670ustar00rootroot00000000000000#!/bin/sh set -e ## Source debconf library. . /usr/share/debconf/confmodule if [ "$1" = "configure" ] || [ "$1" = "reconfigure" ]; then db_get ocfs2-tools/init O2CB_ENABLED="$RET" db_get ocfs2-tools/clustername O2CB_BOOTCLUSTER="$RET" db_get ocfs2-tools/heartbeat_threshold O2CB_HEARTBEAT_THRESHOLD="$RET" db_get ocfs2-tools/idle_timeout O2CB_IDLE_TIMEOUT_MS="$RET" db_get ocfs2-tools/keepalive_delay O2CB_KEEPALIVE_DELAY_MS="$RET" db_get ocfs2-tools/reconnect_delay O2CB_RECONNECT_DELAY_MS="$RET" cat > /etc/default/o2cb <, 2006, 2007. # msgid "" msgstr "" "Project-Id-Version: ocfs2-tools 1.2.4-1\n" "Report-Msgid-Bugs-To: Source: ocfs2-tools@packages.debian.org\n" "POT-Creation-Date: 2007-11-19 06:56+0100\n" "PO-Revision-Date: 2007-09-17 20:48+0200\n" "Last-Translator: Miroslav Kure \n" "Language-Team: Czech \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" #. Type: boolean #. Description #: ../ocfs2-tools.templates:1001 msgid "Would you like to start an OCFS2 cluster (O2CB) at boot time?" msgstr "Chcete spustit cluster OCFS2 (O2CB) během zavádění systému?" #. Type: string #. Description #: ../ocfs2-tools.templates:2001 msgid "Name of the cluster to start at boot time:" msgstr "Jméno clusteru, který se má spustit při zavádění systému:" #. Type: string #. Description #: ../ocfs2-tools.templates:3001 msgid "O2CB heartbeat threshold:" msgstr "Práh kontroly O2CB:" #. Type: string #. Description #: ../ocfs2-tools.templates:3001 msgid "" "The O2CB heartbeat threshold sets up the maximum time in seconds that a node " "awaits for an I/O operation. After it, the node \"fences\" itself, and you " "will probably see a crash." msgstr "" "Práh kontroly O2CB nastaví maximální čas v sekundách, po který uzel očekává " "V/V operaci. Po této době se uzel sám \"izoluje\" a pravděpodobně se dočkáte " "pádu." #. Type: string #. Description #: ../ocfs2-tools.templates:3001 msgid "It is calculated as the result of: (threshold - 1) x 2." msgstr "Hodnota se počítá jako: (práh - 1) x 2." #. Type: string #. Description #: ../ocfs2-tools.templates:3001 msgid "Its default value is 31 (60 seconds)." msgstr "Výchozí hodnota je 31 (60 sekund)." #. Type: string #. Description #: ../ocfs2-tools.templates:3001 msgid "" "Raise it if you have slow disks and/or crashes with kernel messages like:" msgstr "" "Práh je dobré zvýšit v případě pomalých disků a/nebo pokud v syslogu vídáte " "zprávy jako:" #. Type: string #. Description #: ../ocfs2-tools.templates:4001 msgid "O2CB idle timeout:" msgstr "Časový limit nečinnosti O2CB:" #. Type: string #. Description #: ../ocfs2-tools.templates:4001 msgid "" "The O2CB idle timeout (expressed in milliseconds) is the time before a " "network connection is considered dead." msgstr "" "Časový limit nečinnosti O2CB je čas (vyjádřený v milisekundách), po kterém " "je síťové spojení považováno za mrtvé." #. Type: string #. Description #: ../ocfs2-tools.templates:4001 msgid "" "Its default value is 30000 (30 seconds) and the minimum recommended value is " "5000 (5 seconds)." msgstr "" "Výchozí hodnota je 30000 (30 sekund), minimální doporučená hodnota pak 5000 " "(5 sekund)." #. Type: string #. Description #: ../ocfs2-tools.templates:5001 msgid "O2CB keepalive delay:" msgstr "Interval udržování spojení O2CB:" #. Type: string #. Description #: ../ocfs2-tools.templates:5001 msgid "" "The O2CB keepalive delay (expressed in milliseconds) is the maximum time " "before a keepalive package is sent." msgstr "" "Interval udržování spojení O2CB je maximální čas (vyjádřený v " "milisekundách), po kterém je odeslán udržovací paket." #. Type: string #. Description #: ../ocfs2-tools.templates:5001 msgid "" "Its default value is 2000 (2 seconds) and the minimum recommended value is " "1000 (1 second)." msgstr "" "Výchozí hodnota je 2000 (2 sekund), minimální doporučená hodnota pak 1000 (1 " "sekunda)." #. Type: string #. Description #: ../ocfs2-tools.templates:6001 msgid "O2CB reconnect delay:" msgstr "Pauza před novým připojením O2CB" #. Type: string #. Description #: ../ocfs2-tools.templates:6001 msgid "" "The O2CB reconnect delay (expressed in milliseconds) is the minimum time " "between connection attempts." msgstr "" "Pauza před novým připojením O2CB je minimální čas (vyjádřený v " "milisekundách) mezi pokusy o nové připojení." #. Type: string #. Description #: ../ocfs2-tools.templates:6001 msgid "Its default and recommended minimum value is 2000 (2 seconds)." msgstr "Výchozí a zároveň doporučená minimální hodnota je 2000 (2 sekundy)." ocfs2-tools-ocfs2-tools-1.8.6/debian/po/de.po000066400000000000000000000105671347147137200206620ustar00rootroot00000000000000# translation of po-debconf template to German # # Translators, if you are not familiar with the PO format, gettext # documentation is worth reading, especially sections dedicated to # this format, e.g. by running: # info -n '(gettext)PO Files' # info -n '(gettext)Header Entry' # Some information specific to po-debconf are available at # /usr/share/doc/po-debconf/README-trans # or http://www.debian.org/intl/l10n/po-debconf/README-trans# # Developers do not need to manually edit POT or PO files. # # Matthias Julius , 2006. msgid "" msgstr "" "Project-Id-Version: ocfs2-tools 1.2.1-1.2\n" "Report-Msgid-Bugs-To: Source: ocfs2-tools@packages.debian.org\n" "POT-Creation-Date: 2007-11-19 06:56+0100\n" "PO-Revision-Date: 2006-12-14 21:25-0500\n" "Last-Translator: Matthias Julius \n" "Language-Team: German \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Generator: KBabel 1.11.4\n" #. Type: boolean #. Description #: ../ocfs2-tools.templates:1001 msgid "Would you like to start an OCFS2 cluster (O2CB) at boot time?" msgstr "" "Möchten Sie, dass ein OCFS2-Cluster (O2CB) beim Systemstart gestartet wird?" #. Type: string #. Description #: ../ocfs2-tools.templates:2001 msgid "Name of the cluster to start at boot time:" msgstr "Name des Clusters, der beim Systemstart gestartet werden soll:" #. Type: string #. Description #: ../ocfs2-tools.templates:3001 msgid "O2CB heartbeat threshold:" msgstr "Grenzwert des O2BC-Heartbeats:" #. Type: string #. Description #: ../ocfs2-tools.templates:3001 msgid "" "The O2CB heartbeat threshold sets up the maximum time in seconds that a node " "awaits for an I/O operation. After it, the node \"fences\" itself, and you " "will probably see a crash." msgstr "" "Der Grenzwert des O2CB-Heartbeats bestimmt die maximale Zeit in Sekunden, " "die ein Knoten für eine E/A-Operation abwartet. Danach »grenzt« sich der " "Knoten aus, und Sie werden wahrscheinlich einen Absturz beobachten." #. Type: string #. Description #: ../ocfs2-tools.templates:3001 msgid "It is calculated as the result of: (threshold - 1) x 2." msgstr "Er ist berechnet als das Ergebnis von: (Grenzwert - 1) x 2." #. Type: string #. Description #: ../ocfs2-tools.templates:3001 msgid "Its default value is 31 (60 seconds)." msgstr "Der voreingestellte Wert ist 31 (60 Sekunden)." #. Type: string #. Description #: ../ocfs2-tools.templates:3001 msgid "" "Raise it if you have slow disks and/or crashes with kernel messages like:" msgstr "" "Erhöhen Sie ihn, falls Sie langsame Festplatten und/oder Abstürze haben mit " "Kernel-Meldungen wie:" #. Type: string #. Description #: ../ocfs2-tools.templates:4001 msgid "O2CB idle timeout:" msgstr "" #. Type: string #. Description #: ../ocfs2-tools.templates:4001 msgid "" "The O2CB idle timeout (expressed in milliseconds) is the time before a " "network connection is considered dead." msgstr "" #. Type: string #. Description #: ../ocfs2-tools.templates:4001 #, fuzzy #| msgid "Its default value is 7 (12 seconds)." msgid "" "Its default value is 30000 (30 seconds) and the minimum recommended value is " "5000 (5 seconds)." msgstr "Der voreingestellte Wert ist 7 (12 Sekunden)." #. Type: string #. Description #: ../ocfs2-tools.templates:5001 msgid "O2CB keepalive delay:" msgstr "" #. Type: string #. Description #: ../ocfs2-tools.templates:5001 msgid "" "The O2CB keepalive delay (expressed in milliseconds) is the maximum time " "before a keepalive package is sent." msgstr "" #. Type: string #. Description #: ../ocfs2-tools.templates:5001 #, fuzzy #| msgid "Its default value is 7 (12 seconds)." msgid "" "Its default value is 2000 (2 seconds) and the minimum recommended value is " "1000 (1 second)." msgstr "Der voreingestellte Wert ist 7 (12 Sekunden)." #. Type: string #. Description #: ../ocfs2-tools.templates:6001 msgid "O2CB reconnect delay:" msgstr "" #. Type: string #. Description #: ../ocfs2-tools.templates:6001 msgid "" "The O2CB reconnect delay (expressed in milliseconds) is the minimum time " "between connection attempts." msgstr "" #. Type: string #. Description #: ../ocfs2-tools.templates:6001 #, fuzzy #| msgid "Its default value is 7 (12 seconds)." msgid "Its default and recommended minimum value is 2000 (2 seconds)." msgstr "Der voreingestellte Wert ist 7 (12 Sekunden)." ocfs2-tools-ocfs2-tools-1.8.6/debian/po/es.po000066400000000000000000000123361347147137200206750ustar00rootroot00000000000000# # Translators, if you are not familiar with the PO format, gettext # documentation is worth reading, especially sections dedicated to # this format, e.g. by running: # info -n '(gettext)PO Files' # info -n '(gettext)Header Entry' # # Some information specific to po-debconf are available at # /usr/share/doc/po-debconf/README-trans # or http://www.debian.org/intl/l10n/po-debconf/README-trans # # Developers do not need to manually edit POT or PO files. # # Copyright David Martnez Moreno, 2006, 2007. # msgid "" msgstr "" "Project-Id-Version: ocfs2-tools 1.2.4\n" "Report-Msgid-Bugs-To: Source: ocfs2-tools@packages.debian.org\n" "POT-Creation-Date: 2007-11-19 06:56+0100\n" "PO-Revision-Date: 2007-08-17 03:22+0100\n" "Last-Translator: David Martnez Moreno \n" "Language-Team: Spanish \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=iso-8859-15\n" "Content-Transfer-Encoding: 8bit\n" #. Type: boolean #. Description #: ../ocfs2-tools.templates:1001 msgid "Would you like to start an OCFS2 cluster (O2CB) at boot time?" msgstr "Desea iniciar un clster de OCFS2 (O2CB) en el arranque del sistema? " #. Type: string #. Description #: ../ocfs2-tools.templates:2001 msgid "Name of the cluster to start at boot time:" msgstr "Nombre del clster que desea que se inicie en el arranque:" #. Type: string #. Description #: ../ocfs2-tools.templates:3001 msgid "O2CB heartbeat threshold:" msgstr "Intervalo de latidos de O2CB:" #. Type: string #. Description #: ../ocfs2-tools.templates:3001 msgid "" "The O2CB heartbeat threshold sets up the maximum time in seconds that a node " "awaits for an I/O operation. After it, the node \"fences\" itself, and you " "will probably see a crash." msgstr "" "El intervalo de latidos de O2CB configura el tiempo mximo en segundos que " "un nodo espera en una operacin de E/S. Despus, el nodo se aislar, y " "probablemente ver un fallo grave." #. Type: string #. Description #: ../ocfs2-tools.templates:3001 msgid "It is calculated as the result of: (threshold - 1) x 2." msgstr "Es el resultado de la operacin: (intervalo -1) x 2." #. Type: string #. Description #: ../ocfs2-tools.templates:3001 msgid "Its default value is 31 (60 seconds)." msgstr "Su valor predeterminado es 31 (es decir, 60 segundos)." #. Type: string #. Description #: ../ocfs2-tools.templates:3001 msgid "" "Raise it if you have slow disks and/or crashes with kernel messages like:" msgstr "" "Aumntelo si tiene discos lentos y/o cuelgues con mensajes del ncleo del " "tipo:" #. Type: string #. Description #: ../ocfs2-tools.templates:4001 msgid "O2CB idle timeout:" msgstr "Tiempo de expiracin de inactividad de OC2B:" #. Type: string #. Description #: ../ocfs2-tools.templates:4001 msgid "" "The O2CB idle timeout (expressed in milliseconds) is the time before a " "network connection is considered dead." msgstr "" "El tiempo de expiracin de inactividad de OC2B es el intervalo de tiempo " "necesario para que una conexin de red se considere inactiva." #. Type: string #. Description #: ../ocfs2-tools.templates:4001 msgid "" "Its default value is 30000 (30 seconds) and the minimum recommended value is " "5000 (5 seconds)." msgstr "" "Su valor predeterminado es 30000 (30 segundos) y el mnimo valor que se " "recomienda es 5000 (5 segundos)." #. Type: string #. Description #: ../ocfs2-tools.templates:5001 msgid "O2CB keepalive delay:" msgstr "Retraso en notificacin de funcionamiento de O2CB:" #. Type: string #. Description #: ../ocfs2-tools.templates:5001 msgid "" "The O2CB keepalive delay (expressed in milliseconds) is the maximum time " "before a keepalive package is sent." msgstr "" "El retraso en la notificacin de funcionamiento de O2CB (expresado en " "milisegundos) es el intervalo mximo de tiempo antes de enviar un paquete de " "notificacin (keepalive)." #. Type: string #. Description #: ../ocfs2-tools.templates:5001 msgid "" "Its default value is 2000 (2 seconds) and the minimum recommended value is " "1000 (1 second)." msgstr "" "Su valor predeterminado es 2000 (2 segundos) y el mnimo valor que se " "recomienda es 1000 (1 segundo)." #. Type: string #. Description #: ../ocfs2-tools.templates:6001 msgid "O2CB reconnect delay:" msgstr "Intervalo de reconexin OC2B:" #. Type: string #. Description #: ../ocfs2-tools.templates:6001 msgid "" "The O2CB reconnect delay (expressed in milliseconds) is the minimum time " "between connection attempts." msgstr "" "El intervalo de reconexin OC2B (expresado en milisegundos) es el tiempo " "mnimo entre intentos de conexin." #. Type: string #. Description #: ../ocfs2-tools.templates:6001 msgid "Its default and recommended minimum value is 2000 (2 seconds)." msgstr "Su valor predeterminado y recomendado es 2000 (2 segundos)." #~ msgid "" #~ "o2hb_write_timeout: 164 ERROR: heartbeat write timeout to device XXXX " #~ "after NNNN miliseconds" #~ msgstr "" #~ "o2hb_write_timeout: 164 ERROR: heartbeat write timeout to device XXXX " #~ "after NNNN miliseconds" #~ msgid "" #~ "Please enter the desired O2CB heartbeat threshold. Its default value is 7 " #~ "(12 seconds)." #~ msgstr "" #~ "Por favor, introduzca el intervalo de latidos para O2CB. El valor por " #~ "omisin es 7 (es decir, 12 segundos)." ocfs2-tools-ocfs2-tools-1.8.6/debian/po/fr.po000066400000000000000000000112541347147137200206730ustar00rootroot00000000000000# translation of fr.po to French # Julien Rosal , 2006. # Jean-Baka Domelevo-Entfellner , 2006. # msgid "" msgstr "" "Project-Id-Version: ocfs2-tools 1.2.1-1\n" "Report-Msgid-Bugs-To: Source: ocfs2-tools@packages.debian.org\n" "POT-Creation-Date: 2007-11-19 06:56+0100\n" "PO-Revision-Date: 2007-08-22 14:54+0100\n" "Last-Translator: Jean-Baka Domelevo-Entfellner \n" "Language-Team: French \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=iso-8859-15\n" "Content-Transfer-Encoding: 8bit\n" #. Type: boolean #. Description #: ../ocfs2-tools.templates:1001 msgid "Would you like to start an OCFS2 cluster (O2CB) at boot time?" msgstr "Voulez-vous lancer un cluster OCFS2 (OC2B) au dmarrage?" #. Type: string #. Description #: ../ocfs2-tools.templates:2001 msgid "Name of the cluster to start at boot time:" msgstr "Nom du cluster lancer au dmarrage:" #. Type: string #. Description #: ../ocfs2-tools.templates:3001 msgid "O2CB heartbeat threshold:" msgstr "Seuil de battement O2CB:" #. Type: string #. Description #: ../ocfs2-tools.templates:3001 msgid "" "The O2CB heartbeat threshold sets up the maximum time in seconds that a node " "awaits for an I/O operation. After it, the node \"fences\" itself, and you " "will probably see a crash." msgstr "" "Le seuil de battement O2CB dtermine le temps maximal en secondes qu'un nud " "peut attendre lors d'une opration d'E/S. Pass ce dlai, le nud " "implose et le systme a de fortes chances de planter." #. Type: string #. Description #: ../ocfs2-tools.templates:3001 msgid "It is calculated as the result of: (threshold - 1) x 2." msgstr "" "La valeur relle est le rsultat de l'opration suivante: (seuil-1)x2." #. Type: string #. Description #: ../ocfs2-tools.templates:3001 msgid "Its default value is 31 (60 seconds)." msgstr "" "La valeur par dfaut est de 31, ce qui correspond un seuil de 60 secondes." #. Type: string #. Description #: ../ocfs2-tools.templates:3001 msgid "" "Raise it if you have slow disks and/or crashes with kernel messages like:" msgstr "" "Augmentez cette valeur si vous avez des disques lents et/ou des plantages " "signals par des messages du noyau tels que:" #. Type: string #. Description #: ../ocfs2-tools.templates:4001 msgid "O2CB idle timeout:" msgstr "Dlai d'attente sur inactivit O2CB :" #. Type: string #. Description #: ../ocfs2-tools.templates:4001 msgid "" "The O2CB idle timeout (expressed in milliseconds) is the time before a " "network connection is considered dead." msgstr "" "Le dlai d'attente sur inactivit O2CB (exprim en millisecondes) est le " "dlai l'expiration duquel une connection rseau est considre inactive." #. Type: string #. Description #: ../ocfs2-tools.templates:4001 msgid "" "Its default value is 30000 (30 seconds) and the minimum recommended value is " "5000 (5 seconds)." msgstr "" "Sa valeur par dfaut est de 30000 (soit 30 secondes), le minimum recommand " "tant de 5000 (soit 5 secondes)." #. Type: string #. Description #: ../ocfs2-tools.templates:5001 msgid "O2CB keepalive delay:" msgstr "Intervalle de test de lien O2CB :" #. Type: string #. Description #: ../ocfs2-tools.templates:5001 msgid "" "The O2CB keepalive delay (expressed in milliseconds) is the maximum time " "before a keepalive package is sent." msgstr "" "L'intervalle de test de lien O2CB (exprim en millisecondes) est la dure " "maximum au bout de laquelle un paquet dit \"keepalive\" est envoy pour " "tester l'activit d'un lien." #. Type: string #. Description #: ../ocfs2-tools.templates:5001 msgid "" "Its default value is 2000 (2 seconds) and the minimum recommended value is " "1000 (1 second)." msgstr "" "Sa valeur par dfaut est de 2000 (soit 2 secondes), le minimum recommand " "tant de 1000 (soit 1 seconde)." #. Type: string #. Description #: ../ocfs2-tools.templates:6001 msgid "O2CB reconnect delay:" msgstr "Dlai de reconnexion O2CB :" #. Type: string #. Description #: ../ocfs2-tools.templates:6001 msgid "" "The O2CB reconnect delay (expressed in milliseconds) is the minimum time " "between connection attempts." msgstr "" "Le dlai de reconnexion O2CB (exprim en millisecondes) est l'intervalle de " "temps minimal entre deux tentatives de connexion." #. Type: string #. Description #: ../ocfs2-tools.templates:6001 msgid "Its default and recommended minimum value is 2000 (2 seconds)." msgstr "La valeur par dfaut, recommande, est de 2000 (2 secondes)." #~ msgid "" #~ "o2hb_write_timeout: 164 ERROR: heartbeat write timeout to device XXXX " #~ "after NNNN miliseconds" #~ msgstr "" #~ "o2hb_write_timeout: 164 ERROR: heartbeat write timeout to device XXXX " #~ "after NNNN miliseconds" ocfs2-tools-ocfs2-tools-1.8.6/debian/po/nl.po000066400000000000000000000103171347147137200206740ustar00rootroot00000000000000# # Translators, if you are not familiar with the PO format, gettext # documentation is worth reading, especially sections dedicated to # this format, e.g. by running: # info -n '(gettext)PO Files' # info -n '(gettext)Header Entry' # # Some information specific to po-debconf are available at # /usr/share/doc/po-debconf/README-trans # or http://www.debian.org/intl/l10n/po-debconf/README-trans # # Developers do not need to manually edit POT or PO files. # msgid "" msgstr "" "Project-Id-Version: ocfs2-tools\n" "Report-Msgid-Bugs-To: Source: ocfs2-tools@packages.debian.org\n" "POT-Creation-Date: 2007-11-19 06:56+0100\n" "PO-Revision-Date: 2007-05-18 18:33+0100\n" "Last-Translator: Bart Cornelis \n" "Language-Team: debian-l10n-dutch \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Poedit-Language: Dutch\n" #. Type: boolean #. Description #: ../ocfs2-tools.templates:1001 msgid "Would you like to start an OCFS2 cluster (O2CB) at boot time?" msgstr "" "Wilt u een OCFS2-cluster (O2CB) starten tijdens het opstarten van de machine?" #. Type: string #. Description #: ../ocfs2-tools.templates:2001 msgid "Name of the cluster to start at boot time:" msgstr "Naam van de bij het opstarten van de machine te starten cluster:" #. Type: string #. Description #: ../ocfs2-tools.templates:3001 msgid "O2CB heartbeat threshold:" msgstr "O2CB-hartslagdrempel:" #. Type: string #. Description #: ../ocfs2-tools.templates:3001 msgid "" "The O2CB heartbeat threshold sets up the maximum time in seconds that a node " "awaits for an I/O operation. After it, the node \"fences\" itself, and you " "will probably see a crash." msgstr "" "De O2CB-hartslagdrempel zet de maximum tijd (in seconden) voor een knooppunt " "om te wachten op een I/O-operatie. Daarna zal het knooppunt zichzelf " "'insluiten', wat waarschijnlijk leidt tot een crash." #. Type: string #. Description #: ../ocfs2-tools.templates:3001 msgid "It is calculated as the result of: (threshold - 1) x 2." msgstr "Dit is het resultaat van: (drempel - 1) x 2." #. Type: string #. Description #: ../ocfs2-tools.templates:3001 msgid "Its default value is 31 (60 seconds)." msgstr "De standaardwaarde is 31 (60 seconden)." #. Type: string #. Description #: ../ocfs2-tools.templates:3001 msgid "" "Raise it if you have slow disks and/or crashes with kernel messages like:" msgstr "" "U dient dit te verhogen als u trage schijven heeft of crashes met kernel-" "berichten als volgt:" #. Type: string #. Description #: ../ocfs2-tools.templates:4001 msgid "O2CB idle timeout:" msgstr "" #. Type: string #. Description #: ../ocfs2-tools.templates:4001 msgid "" "The O2CB idle timeout (expressed in milliseconds) is the time before a " "network connection is considered dead." msgstr "" #. Type: string #. Description #: ../ocfs2-tools.templates:4001 #, fuzzy #| msgid "Its default value is 7 (12 seconds)." msgid "" "Its default value is 30000 (30 seconds) and the minimum recommended value is " "5000 (5 seconds)." msgstr "De standaardwaarde is 7 (12 seconden)." #. Type: string #. Description #: ../ocfs2-tools.templates:5001 msgid "O2CB keepalive delay:" msgstr "" #. Type: string #. Description #: ../ocfs2-tools.templates:5001 msgid "" "The O2CB keepalive delay (expressed in milliseconds) is the maximum time " "before a keepalive package is sent." msgstr "" #. Type: string #. Description #: ../ocfs2-tools.templates:5001 #, fuzzy #| msgid "Its default value is 7 (12 seconds)." msgid "" "Its default value is 2000 (2 seconds) and the minimum recommended value is " "1000 (1 second)." msgstr "De standaardwaarde is 7 (12 seconden)." #. Type: string #. Description #: ../ocfs2-tools.templates:6001 msgid "O2CB reconnect delay:" msgstr "" #. Type: string #. Description #: ../ocfs2-tools.templates:6001 msgid "" "The O2CB reconnect delay (expressed in milliseconds) is the minimum time " "between connection attempts." msgstr "" #. Type: string #. Description #: ../ocfs2-tools.templates:6001 #, fuzzy #| msgid "Its default value is 7 (12 seconds)." msgid "Its default and recommended minimum value is 2000 (2 seconds)." msgstr "De standaardwaarde is 7 (12 seconden)." ocfs2-tools-ocfs2-tools-1.8.6/debian/po/pt_BR.po000066400000000000000000000103461347147137200212730ustar00rootroot00000000000000# # Translators, if you are not familiar with the PO format, gettext # documentation is worth reading, especially sections dedicated to # this format, e.g. by running: # info -n '(gettext)PO Files' # info -n '(gettext)Header Entry' # # Some information specific to po-debconf are available at # /usr/share/doc/po-debconf/README-trans # or http://www.debian.org/intl/l10n/po-debconf/README-trans # # Developers do not need to manually edit POT or PO files. # msgid "" msgstr "" "Project-Id-Version: ocfs2-tools\n" "Report-Msgid-Bugs-To: Source: ocfs2-tools@packages.debian.org\n" "POT-Creation-Date: 2007-11-19 06:56+0100\n" "PO-Revision-Date: 2006-12-17 12:43-0200\n" "Last-Translator: André Luís Lopes \n" "Language-Team: Debian-BR Project \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" #. Type: boolean #. Description #: ../ocfs2-tools.templates:1001 msgid "Would you like to start an OCFS2 cluster (O2CB) at boot time?" msgstr "" "Você gostaria de iniciar um cluster OCFS2 (O2CB) junto a inicialização ?" #. Type: string #. Description #: ../ocfs2-tools.templates:2001 msgid "Name of the cluster to start at boot time:" msgstr "Nome do cluster a ser iniciado junto a inicialização:" #. Type: string #. Description #: ../ocfs2-tools.templates:3001 msgid "O2CB heartbeat threshold:" msgstr "Threshold do heartbeat O2CB:" #. Type: string #. Description #: ../ocfs2-tools.templates:3001 msgid "" "The O2CB heartbeat threshold sets up the maximum time in seconds that a node " "awaits for an I/O operation. After it, the node \"fences\" itself, and you " "will probably see a crash." msgstr "" "O threshold do heartbeat O2CB define o tempo máximo em segundos que um nó " "aguarda por uma operação de I/O. Após esse tempo, o nó executa um \"fence\" " "em si própŕio e, neste caso, você provavelmente verá um travamento." #. Type: string #. Description #: ../ocfs2-tools.templates:3001 msgid "It is calculated as the result of: (threshold - 1) x 2." msgstr "É calculado como o resultado de: (threshold -1) x 2." #. Type: string #. Description #: ../ocfs2-tools.templates:3001 msgid "Its default value is 31 (60 seconds)." msgstr "Seu valor padrão é 31 (60 segundos)." #. Type: string #. Description #: ../ocfs2-tools.templates:3001 msgid "" "Raise it if you have slow disks and/or crashes with kernel messages like:" msgstr "" "Aumente esse valor caso você possua discos lentos e/ou esteja tendo " "mensagens do kernel com as seguintes:" #. Type: string #. Description #: ../ocfs2-tools.templates:4001 msgid "O2CB idle timeout:" msgstr "" #. Type: string #. Description #: ../ocfs2-tools.templates:4001 msgid "" "The O2CB idle timeout (expressed in milliseconds) is the time before a " "network connection is considered dead." msgstr "" #. Type: string #. Description #: ../ocfs2-tools.templates:4001 #, fuzzy #| msgid "Its default value is 7 (12 seconds)." msgid "" "Its default value is 30000 (30 seconds) and the minimum recommended value is " "5000 (5 seconds)." msgstr "Seu valor padrão é 7 (12 segundos)." #. Type: string #. Description #: ../ocfs2-tools.templates:5001 msgid "O2CB keepalive delay:" msgstr "" #. Type: string #. Description #: ../ocfs2-tools.templates:5001 msgid "" "The O2CB keepalive delay (expressed in milliseconds) is the maximum time " "before a keepalive package is sent." msgstr "" #. Type: string #. Description #: ../ocfs2-tools.templates:5001 #, fuzzy #| msgid "Its default value is 7 (12 seconds)." msgid "" "Its default value is 2000 (2 seconds) and the minimum recommended value is " "1000 (1 second)." msgstr "Seu valor padrão é 7 (12 segundos)." #. Type: string #. Description #: ../ocfs2-tools.templates:6001 msgid "O2CB reconnect delay:" msgstr "" #. Type: string #. Description #: ../ocfs2-tools.templates:6001 msgid "" "The O2CB reconnect delay (expressed in milliseconds) is the minimum time " "between connection attempts." msgstr "" #. Type: string #. Description #: ../ocfs2-tools.templates:6001 #, fuzzy #| msgid "Its default value is 7 (12 seconds)." msgid "Its default and recommended minimum value is 2000 (2 seconds)." msgstr "Seu valor padrão é 7 (12 segundos)." ocfs2-tools-ocfs2-tools-1.8.6/debian/po/ru.po000066400000000000000000000137301347147137200207130ustar00rootroot00000000000000# translation of ru.po to Russian # # Translators, if you are not familiar with the PO format, gettext # documentation is worth reading, especially sections dedicated to # this format, e.g. by running: # info -n '(gettext)PO Files' # info -n '(gettext)Header Entry' # Some information specific to po-debconf are available at # /usr/share/doc/po-debconf/README-trans # or http://www.debian.org/intl/l10n/po-debconf/README-trans# # Developers do not need to manually edit POT or PO files. # # Yuri Kozlov , 2006, 2007. msgid "" msgstr "" "Project-Id-Version: 1.2.1-1.2\n" "Report-Msgid-Bugs-To: Source: ocfs2-tools@packages.debian.org\n" "POT-Creation-Date: 2007-11-19 06:56+0100\n" "PO-Revision-Date: 2007-08-18 17:54+0400\n" "Last-Translator: Yuri Kozlov \n" "Language-Team: Russian \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Generator: KBabel 1.11.4\n" "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%" "10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" #. Type: boolean #. Description #: ../ocfs2-tools.templates:1001 msgid "Would you like to start an OCFS2 cluster (O2CB) at boot time?" msgstr "Запускать кластер OCFS2 (O2CB) во время загрузки компьютера?" #. Type: string #. Description #: ../ocfs2-tools.templates:2001 msgid "Name of the cluster to start at boot time:" msgstr "Имя кластера, запускаемого во время загрузки компьютера:" #. Type: string #. Description #: ../ocfs2-tools.templates:3001 msgid "O2CB heartbeat threshold:" msgstr "Пороговое значение \"сердцебиения\" O2CB:" #. Type: string #. Description #: ../ocfs2-tools.templates:3001 msgid "" "The O2CB heartbeat threshold sets up the maximum time in seconds that a node " "awaits for an I/O operation. After it, the node \"fences\" itself, and you " "will probably see a crash." msgstr "" "Пороговое значение \"сердцебиения\" (heartbeat threshold) O2CB задаёт " "максимальный промежуток в секундах, который узел должен ждать операцию ввода/" "вывода (I/O). Не дождавшись, узел \"защищает (fences)\" себя, и, вероятно, " "вы получите аварийное завершение программы." #. Type: string #. Description #: ../ocfs2-tools.templates:3001 msgid "It is calculated as the result of: (threshold - 1) x 2." msgstr "Оно вычисляется как: (порог - 1) x 2." #. Type: string #. Description #: ../ocfs2-tools.templates:3001 msgid "Its default value is 31 (60 seconds)." msgstr "Значение по умолчанию равно 31 (60 секунд)." #. Type: string #. Description #: ../ocfs2-tools.templates:3001 msgid "" "Raise it if you have slow disks and/or crashes with kernel messages like:" msgstr "" "Увеличивайте его, если у вас медленные диски и/или происходит аварийное " "завершение работы с сообщением ядра типа:" #. Type: string #. Description #: ../ocfs2-tools.templates:4001 msgid "O2CB idle timeout:" msgstr "Время простоя O2CB:" #. Type: string #. Description #: ../ocfs2-tools.templates:4001 msgid "" "The O2CB idle timeout (expressed in milliseconds) is the time before a " "network connection is considered dead." msgstr "" "Время простоя (idle timeout) O2CB -- это временной промежуток, который " "должен пройти перед тем как сетевое подключение будет рассматриваться " "нерабочим (выражается в секундах)." #. Type: string #. Description #: ../ocfs2-tools.templates:4001 msgid "" "Its default value is 30000 (30 seconds) and the minimum recommended value is " "5000 (5 seconds)." msgstr "" "Значение по умолчанию равно 30000 (30 секунд), а минимальное рекомендуемое " "значение равно 5000 (5 секунд)." #. Type: string #. Description #: ../ocfs2-tools.templates:5001 msgid "O2CB keepalive delay:" msgstr "Задержка O2CB keepalive:" #. Type: string #. Description #: ../ocfs2-tools.templates:5001 msgid "" "The O2CB keepalive delay (expressed in milliseconds) is the maximum time " "before a keepalive package is sent." msgstr "" "Задержка O2CB keepalive (выражается в секундах) -- это максимальный " "промежуток времени перед отправкой пакета keepalive." #. Type: string #. Description #: ../ocfs2-tools.templates:5001 msgid "" "Its default value is 2000 (2 seconds) and the minimum recommended value is " "1000 (1 second)." msgstr "" "Значение по умолчанию равно 2000 (2 секунд), а минимальное рекомендуемое " "значение равно 1000 (1 секунда)." #. Type: string #. Description #: ../ocfs2-tools.templates:6001 msgid "O2CB reconnect delay:" msgstr "Задержка переподключения O2CB:" #. Type: string #. Description #: ../ocfs2-tools.templates:6001 msgid "" "The O2CB reconnect delay (expressed in milliseconds) is the minimum time " "between connection attempts." msgstr "" "Задержка переподключения O2CB (выражается в секундах) -- это минимальный " "промежуток времени между попытками подключения." #. Type: string #. Description #: ../ocfs2-tools.templates:6001 msgid "Its default and recommended minimum value is 2000 (2 seconds)." msgstr "" "Значение по умолчанию равно и рекомендуемое значение равно 2000 (2 секунды)." ocfs2-tools-ocfs2-tools-1.8.6/debian/po/sv.po000066400000000000000000000102051347147137200207070ustar00rootroot00000000000000# # Translators, if you are not familiar with the PO format, gettext # documentation is worth reading, especially sections dedicated to # this format, e.g. by running: # info -n '(gettext)PO Files' # info -n '(gettext)Header Entry' # # Some information specific to po-debconf are available at # /usr/share/doc/po-debconf/README-trans # or http://www.debian.org/intl/l10n/po-debconf/README-trans # # Developers do not need to manually edit POT or PO files. # msgid "" msgstr "" "Project-Id-Version: ocfs2-tools\n" "Report-Msgid-Bugs-To: Source: ocfs2-tools@packages.debian.org\n" "POT-Creation-Date: 2007-11-19 06:56+0100\n" "PO-Revision-Date: 2006-12-12 08:55+0100\n" "Last-Translator: Daniel Nylander \n" "Language-Team: Swedish \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=iso-8859-1\n" "Content-Transfer-Encoding: 8bit\n" #. Type: boolean #. Description #: ../ocfs2-tools.templates:1001 msgid "Would you like to start an OCFS2 cluster (O2CB) at boot time?" msgstr "Vill du starta ett OCFS2-kluster (O2CB) vid systemets uppstart?" #. Type: string #. Description #: ../ocfs2-tools.templates:2001 msgid "Name of the cluster to start at boot time:" msgstr "Namn p klustret att starta vid systemuppstart:" #. Type: string #. Description #: ../ocfs2-tools.templates:3001 msgid "O2CB heartbeat threshold:" msgstr "Trskelvrde fr O2CB-hjrtslag:" #. Type: string #. Description #: ../ocfs2-tools.templates:3001 msgid "" "The O2CB heartbeat threshold sets up the maximum time in seconds that a node " "awaits for an I/O operation. After it, the node \"fences\" itself, and you " "will probably see a crash." msgstr "" "Trskelvrdet fr O2CB-hjrtslag stller in maximal tid (i sekunder) som en " "nod vntar p en in/ut-operation. Efter det kommer noden att \"fences\" och " "du kommer antagligen att f se en krasch." #. Type: string #. Description #: ../ocfs2-tools.templates:3001 msgid "It is calculated as the result of: (threshold - 1) x 2." msgstr "Det berknas som resultatet av: (trskelvrde - 1) x 2." #. Type: string #. Description #: ../ocfs2-tools.templates:3001 msgid "Its default value is 31 (60 seconds)." msgstr "Dess standardvrde r 31 (60 sekunder)." #. Type: string #. Description #: ../ocfs2-tools.templates:3001 msgid "" "Raise it if you have slow disks and/or crashes with kernel messages like:" msgstr "" "ka vrdet om du har lngsamma diskar och/eller kraschar med krnmeddelanden " "som:" #. Type: string #. Description #: ../ocfs2-tools.templates:4001 msgid "O2CB idle timeout:" msgstr "" #. Type: string #. Description #: ../ocfs2-tools.templates:4001 msgid "" "The O2CB idle timeout (expressed in milliseconds) is the time before a " "network connection is considered dead." msgstr "" #. Type: string #. Description #: ../ocfs2-tools.templates:4001 #, fuzzy #| msgid "Its default value is 7 (12 seconds)." msgid "" "Its default value is 30000 (30 seconds) and the minimum recommended value is " "5000 (5 seconds)." msgstr "Dess standardvrde r 7 (12 sekunder)." #. Type: string #. Description #: ../ocfs2-tools.templates:5001 msgid "O2CB keepalive delay:" msgstr "" #. Type: string #. Description #: ../ocfs2-tools.templates:5001 msgid "" "The O2CB keepalive delay (expressed in milliseconds) is the maximum time " "before a keepalive package is sent." msgstr "" #. Type: string #. Description #: ../ocfs2-tools.templates:5001 #, fuzzy #| msgid "Its default value is 7 (12 seconds)." msgid "" "Its default value is 2000 (2 seconds) and the minimum recommended value is " "1000 (1 second)." msgstr "Dess standardvrde r 7 (12 sekunder)." #. Type: string #. Description #: ../ocfs2-tools.templates:6001 msgid "O2CB reconnect delay:" msgstr "" #. Type: string #. Description #: ../ocfs2-tools.templates:6001 msgid "" "The O2CB reconnect delay (expressed in milliseconds) is the minimum time " "between connection attempts." msgstr "" #. Type: string #. Description #: ../ocfs2-tools.templates:6001 #, fuzzy #| msgid "Its default value is 7 (12 seconds)." msgid "Its default and recommended minimum value is 2000 (2 seconds)." msgstr "Dess standardvrde r 7 (12 sekunder)." ocfs2-tools-ocfs2-tools-1.8.6/debian/po/templates.pot000066400000000000000000000060211347147137200224420ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: Source: ocfs2-tools@packages.debian.org\n" "POT-Creation-Date: 2007-11-19 06:56+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=CHARSET\n" "Content-Transfer-Encoding: 8bit\n" #. Type: boolean #. Description #: ../ocfs2-tools.templates:1001 msgid "Would you like to start an OCFS2 cluster (O2CB) at boot time?" msgstr "" #. Type: string #. Description #: ../ocfs2-tools.templates:2001 msgid "Name of the cluster to start at boot time:" msgstr "" #. Type: string #. Description #: ../ocfs2-tools.templates:3001 msgid "O2CB heartbeat threshold:" msgstr "" #. Type: string #. Description #: ../ocfs2-tools.templates:3001 msgid "" "The O2CB heartbeat threshold sets up the maximum time in seconds that a node " "awaits for an I/O operation. After it, the node \"fences\" itself, and you " "will probably see a crash." msgstr "" #. Type: string #. Description #: ../ocfs2-tools.templates:3001 msgid "It is calculated as the result of: (threshold - 1) x 2." msgstr "" #. Type: string #. Description #: ../ocfs2-tools.templates:3001 msgid "Its default value is 31 (60 seconds)." msgstr "" #. Type: string #. Description #: ../ocfs2-tools.templates:3001 msgid "" "Raise it if you have slow disks and/or crashes with kernel messages like:" msgstr "" #. Type: string #. Description #: ../ocfs2-tools.templates:4001 msgid "O2CB idle timeout:" msgstr "" #. Type: string #. Description #: ../ocfs2-tools.templates:4001 msgid "" "The O2CB idle timeout (expressed in milliseconds) is the time before a " "network connection is considered dead." msgstr "" #. Type: string #. Description #: ../ocfs2-tools.templates:4001 msgid "" "Its default value is 30000 (30 seconds) and the minimum recommended value is " "5000 (5 seconds)." msgstr "" #. Type: string #. Description #: ../ocfs2-tools.templates:5001 msgid "O2CB keepalive delay:" msgstr "" #. Type: string #. Description #: ../ocfs2-tools.templates:5001 msgid "" "The O2CB keepalive delay (expressed in milliseconds) is the maximum time " "before a keepalive package is sent." msgstr "" #. Type: string #. Description #: ../ocfs2-tools.templates:5001 msgid "" "Its default value is 2000 (2 seconds) and the minimum recommended value is " "1000 (1 second)." msgstr "" #. Type: string #. Description #: ../ocfs2-tools.templates:6001 msgid "O2CB reconnect delay:" msgstr "" #. Type: string #. Description #: ../ocfs2-tools.templates:6001 msgid "" "The O2CB reconnect delay (expressed in milliseconds) is the minimum time " "between connection attempts." msgstr "" #. Type: string #. Description #: ../ocfs2-tools.templates:6001 msgid "Its default and recommended minimum value is 2000 (2 seconds)." msgstr "" ocfs2-tools-ocfs2-tools-1.8.6/debian/po/vi.po000066400000000000000000000111731347147137200207020ustar00rootroot00000000000000# Vietnamese translation for OCFS2 Tools. # Copyright © 2007 Free Software Foundation, Inc. # Clytie Siddall , 2006-2007. # msgid "" msgstr "" "Project-Id-Version: ocfs2-tools 1.2.4-1\n" "Report-Msgid-Bugs-To: Source: ocfs2-tools@packages.debian.org\n" "POT-Creation-Date: 2007-11-19 06:56+0100\n" "PO-Revision-Date: 2006-12-12 18:44+1030\n" "Last-Translator: Clytie Siddall \n" "Language-Team: Vietnamese \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" "X-Generator: LocFactoryEditor 1.7b1\n" #. Type: boolean #. Description #: ../ocfs2-tools.templates:1001 msgid "Would you like to start an OCFS2 cluster (O2CB) at boot time?" msgstr "Lúc khởi động máy, bạn có muốn khởi chạy một chùm OCFS2 (O2CB) không?" #. Type: string #. Description #: ../ocfs2-tools.templates:2001 msgid "Name of the cluster to start at boot time:" msgstr "Tên của chùm cần khởi chạy vào lúc khởi động:" #. Type: string #. Description #: ../ocfs2-tools.templates:3001 msgid "O2CB heartbeat threshold:" msgstr "Ngưỡng nhịp tim O2CB:" #. Type: string #. Description #: ../ocfs2-tools.templates:3001 msgid "" "The O2CB heartbeat threshold sets up the maximum time in seconds that a node " "awaits for an I/O operation. After it, the node \"fences\" itself, and you " "will probably see a crash." msgstr "" "Ngưỡng nhịp tim (heartbeat threshold) kiểu O2CB thì đặt thời gian tối đa " "theo giây mỗi nút đợi một thao tác nhập vào/xuất ra (V/R). Sau đó, nút tự « " "rào lại », và bạn rất có thể sẽ thấy phần mềm sụp đổ." #. Type: string #. Description #: ../ocfs2-tools.templates:3001 msgid "It is calculated as the result of: (threshold - 1) x 2." msgstr "Giá trị này được tính là kết quả của công thức: (ngưỡng - 1) × 2" #. Type: string #. Description #: ../ocfs2-tools.templates:3001 msgid "Its default value is 31 (60 seconds)." msgstr "Giá trị mặc định là 31 (60 giây)." #. Type: string #. Description #: ../ocfs2-tools.templates:3001 msgid "" "Raise it if you have slow disks and/or crashes with kernel messages like:" msgstr "" "Hãy tăng giá trị này nếu bạn có đĩa chạy chậm hay/và phần mềm sụp đổ với " "thông điệp hạt nhân như :" #. Type: string #. Description #: ../ocfs2-tools.templates:4001 msgid "O2CB idle timeout:" msgstr "Thời hạn nghỉ O2CB:" #. Type: string #. Description #: ../ocfs2-tools.templates:4001 msgid "" "The O2CB idle timeout (expressed in milliseconds) is the time before a " "network connection is considered dead." msgstr "" "Thời hạn nghỉ O2CB (xác định theo mili-giây) là khoảng thời gian sau đó thấy " "kết nối mạng đã chết." #. Type: string #. Description #: ../ocfs2-tools.templates:4001 msgid "" "Its default value is 30000 (30 seconds) and the minimum recommended value is " "5000 (5 seconds)." msgstr "" "Giá trị mặc định là 30000 (30 giây), còn giá trị tối thiểu khuyến khích là " "5000 (5 giây)." #. Type: string #. Description #: ../ocfs2-tools.templates:5001 msgid "O2CB keepalive delay:" msgstr "Khoảng đợi cứ hoạt động O2CB:" #. Type: string #. Description #: ../ocfs2-tools.templates:5001 msgid "" "The O2CB keepalive delay (expressed in milliseconds) is the maximum time " "before a keepalive package is sent." msgstr "" "Khoảng đợi cứ hoạt động O2CB (xác định theo mili-giây) là khoảng thời gian " "tối đa sau đó gửi gói tin giữ cho kết nối hoạt động." #. Type: string #. Description #: ../ocfs2-tools.templates:5001 msgid "" "Its default value is 2000 (2 seconds) and the minimum recommended value is " "1000 (1 second)." msgstr "" "Giá trị mặc định là 2000 (2 giây), còn giá trị tối thiểu khuyến khích là " "1000 (1 giây)." #. Type: string #. Description #: ../ocfs2-tools.templates:6001 msgid "O2CB reconnect delay:" msgstr "Khoảng đợi tái kết nối O2CB:" #. Type: string #. Description #: ../ocfs2-tools.templates:6001 msgid "" "The O2CB reconnect delay (expressed in milliseconds) is the minimum time " "between connection attempts." msgstr "" "Khoảng đợi tái kết nối O2CB (xác định theo mili-giây) là khoảng thời gian " "tối thiểu giữa hai lần thử kết nối." #. Type: string #. Description #: ../ocfs2-tools.templates:6001 msgid "Its default and recommended minimum value is 2000 (2 seconds)." msgstr "Giá trị mặc định và khuyến khích là 2000 (2 giây)." ocfs2-tools-ocfs2-tools-1.8.6/debian/rules000077500000000000000000000030521347147137200203630ustar00rootroot00000000000000#!/usr/bin/make -f # Uncomment this to turn on verbose mode. # export DH_VERBOSE=1 include /usr/share/quilt/quilt.make configure: patch configure-stamp configure-stamp: dh_testdir ./configure --disable-debug \ --enable-dynamic-ctl \ --enable-dynamic-fsck \ --prefix=/usr \ --exec-prefix=/usr \ --mandir=/usr/share/man touch $@ build: configure build-stamp build-stamp: dh_testdir $(MAKE) touch $@ install: build dh_testdir dh_testroot dh_clean -k $(MAKE) install DESTDIR=`pwd`/debian/tmp/ # Install manual stuff cp -f vendor/common/o2cb.init debian/ocfs2-tools.o2cb.init cp -f vendor/common/o2cb.sysconfig debian/ocfs2-tools.o2cb.default cp -f vendor/common/ocfs2.init debian/ocfs2-tools.ocfs2.init dh_install --sourcedir=debian/tmp --list-missing dh_installinit --name=o2cb --no-restart-on-upgrade -- start 60 S . start 6 0 6 . dh_installinit --name=ocfs2 --no-restart-on-upgrade -- start 65 S . start 2 0 6 . dh_installdocs dh_installexamples documentation/samples/cluster.conf dh_installchangelogs binary-arch: install dh_testdir dh_testroot dh_strip dh_compress dh_installdebconf dh_fixperms dh_makeshlibs dh_shlibdeps dh_pysupport dh_installdeb dh_gencontrol dh_md5sums dh_builddeb binary-indep: # do nothing binary: binary-arch binary-indep clean: unpatch dh_testdir dh_testroot dh_clean rm -rf debian/patches/log/ .pc rm -f *-stamp debian/ocfs2-tools.o2cb.* debian/ocfs2-tools.ocfs2.* rm -f vendor/common/ocfs2-tools.spec rm -f ocfs2console/ocfs2interface/*so $(MAKE) distclean debconf-updatepo ocfs2-tools-ocfs2-tools-1.8.6/debugfs.ocfs2/000077500000000000000000000000001347147137200205135ustar00rootroot00000000000000ocfs2-tools-ocfs2-tools-1.8.6/debugfs.ocfs2/.gitignore000066400000000000000000000001121347147137200224750ustar00rootroot00000000000000cscope.out debugfs.ocfs2 cscope.files *.sw? *.d debugfs.ocfs2.8 stamp-md5 ocfs2-tools-ocfs2-tools-1.8.6/debugfs.ocfs2/Cscope.make000066400000000000000000000004521347147137200225670ustar00rootroot00000000000000.PHONY: cscope cscope: rm -f cscope.* echo "-k" >> cscope.files echo "-I inc" >> cscope.files find . -name '*.c' -print >>cscope.files find . -name '*.h' -print >>cscope.files find ../libocfs2/ -name '*.h' -print >>cscope.files find ../libocfs2/ -name '*.c' -print >>cscope.files cscope -b ocfs2-tools-ocfs2-tools-1.8.6/debugfs.ocfs2/Makefile000066400000000000000000000024671347147137200221640ustar00rootroot00000000000000TOPDIR = .. include $(TOPDIR)/Preamble.make sbindir = $(root_sbindir) SBIN_PROGRAMS = debugfs.ocfs2 DEFINES = -DG_DISABLE_DEPRECATED -DLINUX -DDEBUG DEFINES += -DVERSION=\"$(VERSION)\" INCLUDES = -I$(TOPDIR)/include -Iinclude INCLUDES += $(GLIB_CFLAGS) CFILES = main.c commands.c dump.c utils.c journal.c find_block_inode.c \ find_inode_paths.c dump_fs_locks.c dump_dlm_locks.c stat_sysdir.c \ dump_net_stats.c HFILES = \ include/main.h \ include/commands.h \ include/dump.h \ include/utils.h \ include/journal.h \ include/find_block_inode.h \ include/find_inode_paths.h \ include/ocfs2_internals.h \ include/dump_fs_locks.h \ include/dump_dlm_locks.h \ include/stat_sysdir.h \ include/dump_net_stats.h OBJS = $(subst .c,.o,$(CFILES)) LIBOCFS2_LIBS = -L$(TOPDIR)/libocfs2 -locfs2 LIBO2CB_LIBS = -L$(TOPDIR)/libo2cb -lo2cb ifneq ($(BUILD_FSDLM_SUPPORT),) LIBO2CB_LIBS += -ldlm_lt endif ifneq ($(BUILD_CMAP_SUPPORT),) LIBO2CB_LIBS += -lcmap endif MANS = debugfs.ocfs2.8 DIST_FILES = $(CFILES) $(HFILES) README debugfs.ocfs2.8.in DIST_RULES = dist-subdircreate dist-subdircreate: $(TOPDIR)/mkinstalldirs $(DIST_DIR)/include debugfs.ocfs2: $(OBJS) $(LINK) $(GLIB_LIBS) $(LIBOCFS2_LIBS) $(LIBO2CB_LIBS) $(COM_ERR_LIBS) $(READLINE_LIBS) $(NCURSES_LIBS) $(AIO_LIBS) include $(TOPDIR)/Postamble.make ocfs2-tools-ocfs2-tools-1.8.6/debugfs.ocfs2/README000066400000000000000000000036761347147137200214070ustar00rootroot00000000000000debugfs.ocfs2 is a debugging tool for ocfs2 useful for printing on-disk structures in a readable style. It performs the same task as debugocfs does for ocfs1 with one big difference, viz., user interface. debugfs.ocfs2 uses the debugfs (ext2) style. Currently, apart from being read-only, it does not provide any directory traversal functionality. What that means is that the user is expected to provide the inode block number to access any metadata on volume. "open " opens the device. "stats" shows the super block information. "ls " lists the directory at the block number. "nodes" prints the list of nodes configured on the device. "cat " prints the contents of the file at that block number. "dump " copies the contents of the file into outfile. "logdump " prints the contents of the journal file. One can either use the interactive mode, i.e., issue commands at a prompt, or the echo mode. (echo "stats" | debugfs.ocfs2 /dev/sdb1). The echo mode allows users to redirect the output to a file (very useful when reading the journal file). The current list of commands are as follows: curdev Show current device open Open a device close Close a device show_super_stats, stats [-h] Show superblock show_inode_info, stat Show inode pwd Print working directory ls List directory cat [outfile] Prints or concatenates file to stdout/outfile dump Dumps file to outfile nodes List of nodes publish Publish blocks vote Vote blocks logdump Prints journal file help, ? This information quit, q Exit the program =============================================================================== ocfs2-tools-ocfs2-tools-1.8.6/debugfs.ocfs2/commands.c000066400000000000000000001321641347147137200224670ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * commands.c * * handles debugfs commands * * Copyright (C) 2004, 2011 Oracle. All rights reserved. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * */ #include "main.h" #include "ocfs2/image.h" #include "ocfs2/byteorder.h" #define SYSTEM_FILE_NAME_MAX 40 #define MAX_BLOCKS 50 struct dbgfs_gbls gbls; typedef void (*cmdfunc)(char **args); struct command { char *cmd_name; cmdfunc cmd_func; char *cmd_usage; char *cmd_desc; }; static struct command *find_command(char *cmd); static void do_bmap(char **args); static void do_cat(char **args); static void do_cd(char **args); static void do_chroot(char **args); static void do_close(char **args); static void do_controld(char **args); static void do_curdev(char **args); static void do_decode_lockres(char **args); static void do_dirblocks(char **args); static void do_dlm_locks(char **args); static void do_dump(char **args); static void do_dx_dump(char **args); static void do_dx_leaf(char **args); static void do_dx_root(char **args); static void do_dx_space(char **args); static void do_encode_lockres(char **args); static void do_extent(char **args); static void do_frag(char **args); static void do_fs_locks(char **args); static void do_group(char **args); static void do_grpextents(char **args); static void do_hb(char **args); static void do_help(char **args); static void do_icheck(char **args); static void do_lcd(char **args); static void do_locate(char **args); static void do_logdump(char **args); static void do_ls(char **args); static void do_net_stats(char **args); static void do_open(char **args); static void do_quit(char **args); static void do_rdump(char **args); static void do_refcount(char **args); static void do_slotmap(char **args); static void do_stat(char **args); static void do_stat_sysdir(char **args); static void do_stats(char **args); static void do_xattr(char **args); static struct command commands[] = { { "bmap", do_bmap, "bmap ", "Show the corresponding physical block# for the inode", }, { "cat", do_cat, "cat ", "Show file on stdout", }, { "cd", do_cd, "cd ", "Change directory", }, { "chroot", do_chroot, "chroot ", "Change root", }, { "close", do_close, "close", "Close a device", }, { "controld", do_controld, "controld dump", "Obtain information from ocfs2_controld", }, { "curdev", do_curdev, "curdev", "Show current device", }, { "decode", do_decode_lockres, "decode ...", "Decode block#(s) from the lockname(s)", }, { "dirblocks", do_dirblocks, "dirblocks ", "Dump directory blocks", }, { "dlm_locks", do_dlm_locks, "dlm_locks [-f ] [-l] lockname", "Show live dlm locking state", }, { "dump", do_dump, "dump [-p] ", "Dumps file to outfile on a mounted fs", }, { "dx_dump", do_dx_dump, "dx_dump ", "Show directory index information", }, { "dx_leaf", do_dx_leaf, "dx_leaf ", "Show directory index leaf block only", }, { "dx_root", do_dx_root, "dx_root ", "Show directory index root block only", }, { "dx_space", do_dx_space, "dx_space ", "Dump directory free space list", }, { "encode", do_encode_lockres, "encode ", "Show lock name", }, { "extent", do_extent, "extent ", "Show extent block", }, { "findpath", do_locate, "findpath ", "List one pathname of the inode/lockname", }, { "frag", do_frag, "frag ", "Show inode extents / clusters ratio", }, { "fs_locks", do_fs_locks, "fs_locks [-f ] [-l] [-B]", "Show live fs locking state", }, { "group", do_group, "group ", "Show chain group", }, { "grpextents", do_grpextents, "grpextents ", "Show free extents in a chain group", }, { "hb", do_hb, "hb", "Show the heartbeat blocks", }, { "help", do_help, "help, ?", "This information", }, { "?", do_help, NULL, NULL, }, { "icheck", do_icheck, "icheck block# ...", "List inode# that is using the block#", }, { "lcd", do_lcd, "lcd ", "Change directory on a mounted flesystem", }, { "locate", do_locate, "locate ...", "List all pathnames of the inode(s)/lockname(s)", }, { "logdump", do_logdump, "logdump [-T] ", "Show journal file for the node slot", }, { "ls", do_ls, "ls [-l] ", "List directory", }, { "net_stats", do_net_stats, "net_stats [-f ] [interval [count]]", "Show net statistics", }, { "ncheck", do_locate, "ncheck ...", "List all pathnames of the inode(s)/lockname(s)", }, { "open", do_open, "open [-i] [-s backup#]", "Open a device", }, { "quit", do_quit, "quit, q", "Exit the program", }, { "q", do_quit, NULL, NULL, }, { "rdump", do_rdump, "rdump [-v] ", "Recursively dumps from src to a dir on a mounted filesystem", }, { "refcount", do_refcount, "refcount [-e] ", "Dump the refcount tree for the inode or refcount block", }, { "slotmap", do_slotmap, "slotmap", "Show slot map", }, { "stat", do_stat, "stat [-t|-T] ", "Show inode", }, { "stat_sysdir", do_stat_sysdir, "stat_sysdir", "Show all objects in the system directory", }, { "stats", do_stats, "stats [-h]", "Show superblock", }, { "xattr", do_xattr, "xattr [-v] ", "Show extended attributes", }, }; void handle_signal(int sig) { switch (sig) { case SIGTERM: case SIGINT: if (gbls.device) do_close(NULL); exit(1); } return ; } static struct command *find_command(char *cmd) { unsigned int i; for (i = 0; i < sizeof(commands) / sizeof(commands[0]); i++) if (strcmp(cmd, commands[i].cmd_name) == 0) return &commands[i]; return NULL; } void do_command(char *cmd) { char **args; struct command *command; if (*cmd == '\0') return; args = g_strsplit(cmd, " ", -1); /* Move empty strings to the end */ crunch_strsplit(args); /* ignore commented line */ if (!strncmp(args[0], "#", 1)) goto bail; command = find_command(args[0]); fflush(stdout); if (command) { gbls.cmd = command->cmd_name; command->cmd_func(args); } else fprintf(stderr, "%s: command not found\n", args[0]); bail: g_strfreev(args); } static int check_device_open(void) { if (!gbls.fs) { fprintf(stderr, "No device open\n"); return -1; } return 0; } static int process_inode_args(char **args, uint64_t *blkno) { errcode_t ret; char *opts = args[1]; if (check_device_open()) return -1; if (!opts) { fprintf(stderr, "usage: %s \n", args[0]); return -1; } ret = string_to_inode(gbls.fs, gbls.root_blkno, gbls.cwd_blkno, opts, blkno); if (ret) { com_err(args[0], ret, "'%s'", opts); return -1; } if (*blkno >= gbls.max_blocks) { com_err(args[0], OCFS2_ET_BAD_BLKNO, "- %"PRIu64"", *blkno); return -1; } return 0; } static int process_ls_args(char **args, uint64_t *blkno, int *long_opt) { errcode_t ret; char *def = "."; char *opts; int ind = 1; if (check_device_open()) return -1; if (args[ind]) { if (!strcmp(args[1], "-l")) { *long_opt = 1; ++ind; } } if (args[ind]) opts = args[ind]; else opts = def; ret = string_to_inode(gbls.fs, gbls.root_blkno, gbls.cwd_blkno, opts, blkno); if (ret) { com_err(args[0], ret, "'%s'", opts); return -1; } if (*blkno >= gbls.max_blocks) { com_err(args[0], OCFS2_ET_BAD_BLKNO, "- %"PRIu64"", *blkno); return -1; } return 0; } /* * process_inodestr_args() * args: arguments starting from command * count: max space available in blkno * blkno: block nums extracted * RETURN: number of blknos * */ static int process_inodestr_args(char **args, int count, uint64_t *blkno) { uint64_t *p; int i; if (check_device_open()) return -1; if (count < 1) return 0; for (i = 1, p = blkno; i < count + 1; ++i, ++p) { if (!args[i] || inodestr_to_inode(args[i], p)) break; if (*p >= gbls.max_blocks) { com_err(args[0], OCFS2_ET_BAD_BLKNO, "- %"PRIu64"", *p); return -1; } } --i; if (!i) { fprintf(stderr, "usage: %s \n", args[0]); return -1; } return i; } /* open the device, read the block from the device and get the * blocksize from the offset of the ocfs2_super_block. */ static errcode_t get_blocksize(char* dev, uint64_t offset, uint64_t *blocksize, int super_no) { errcode_t ret; uint64_t blkno; uint32_t blocksize_bits; char *buf = NULL; io_channel* channel = NULL; struct ocfs2_dinode *di = NULL; struct ocfs2_image_hdr *hdr; ret = io_open(dev, OCFS2_FLAG_RO, &channel); if (ret) goto bail; /* since ocfs2_super_block inode can be stored in OCFS2_MIN_BLOCKSIZE, * so here we just use the minimum block size and read the information * in the specific offset. */ ret = io_set_blksize(channel, OCFS2_MIN_BLOCKSIZE); if (ret) goto bail; ret = ocfs2_malloc_block(channel, &buf); if (ret) goto bail; if (gbls.imagefile) { ret = io_read_block(channel, 0, 1, buf); if (ret) goto bail; hdr = (struct ocfs2_image_hdr *)buf; ocfs2_image_swap_header(hdr); if (super_no > hdr->hdr_superblkcnt) { ret = OCFS2_ET_IO; goto bail; } offset = hdr->hdr_superblocks[super_no-1] * hdr->hdr_fsblksz; } blkno = offset / OCFS2_MIN_BLOCKSIZE; ret = io_read_block(channel, blkno, 1, buf); if (ret) goto bail; di = (struct ocfs2_dinode *)buf; blocksize_bits = le32_to_cpu(di->id2.i_super.s_blocksize_bits); *blocksize = 1ULL << blocksize_bits; bail: if (buf) ocfs2_free(&buf); if (channel) io_close(channel); return ret; } static int process_open_args(char **args, uint64_t *superblock, uint64_t *blocksize) { errcode_t ret = 0; uint32_t s = 0; char *ptr, *dev; uint64_t byte_off[OCFS2_MAX_BACKUP_SUPERBLOCKS]; uint64_t blksize = 0; int num, argc, c; for (argc = 0; (args[argc]); ++argc); dev = strdup(args[1]); optind = 0; while ((c = getopt(argc, args, "is:")) != EOF) { switch (c) { case 'i': gbls.imagefile = 1; break; case 's': s = strtoul(optarg, &ptr, 0); break; default: ret = 1; goto bail; break; } } if (!s) { ret = 0; goto bail; } num = ocfs2_get_backup_super_offsets(NULL, byte_off, ARRAY_SIZE(byte_off)); if (!num) { ret = -1; goto bail; } if (s < 1 || s > num) { fprintf(stderr, "Backup super block is outside of valid range" "(between 1 and %d)\n", num); ret = -1; goto bail; } ret = get_blocksize(dev, byte_off[s-1], &blksize, s); if (ret) { com_err(args[0],ret, "Can't get the blocksize from the device" " by the num %u\n", s); goto bail; } *blocksize = blksize; *superblock = byte_off[s-1]/blksize; ret = 0; bail: g_free(dev); return ret; } static int get_slotnum(char *str, uint16_t *slotnum) { struct ocfs2_super_block *sb = OCFS2_RAW_SB(gbls.fs->fs_super); char *endptr; if (!str) return -1; *slotnum = strtoul(str, &endptr, 0); if (*endptr) return -1; if (*slotnum >= sb->s_max_slots) return -1; return 0; } static errcode_t find_block_offset(ocfs2_filesys *fs, struct ocfs2_extent_list *el, uint64_t blkoff, FILE *out) { struct ocfs2_extent_block *eb; struct ocfs2_extent_rec *rec; errcode_t ret = 0; char *buf = NULL; int i; uint32_t clstoff, clusters; uint32_t tmp; clstoff = ocfs2_blocks_to_clusters(fs, blkoff); for (i = 0; i < el->l_next_free_rec; ++i) { rec = &(el->l_recs[i]); clusters = ocfs2_rec_clusters(el->l_tree_depth, rec); /* * For a sparse file, we may find an empty record. * Just skip it. */ if (!clusters) continue; if (clstoff >= (rec->e_cpos + clusters)) continue; if (!el->l_tree_depth) { if (clstoff < rec->e_cpos) { dump_logical_blkno(out, 0); } else { tmp = blkoff - ocfs2_clusters_to_blocks(fs, rec->e_cpos); dump_logical_blkno(out, rec->e_blkno + tmp); } goto bail; } ret = ocfs2_malloc_block(gbls.fs->fs_io, &buf); if (ret) { com_err(gbls.cmd, ret, "while allocating a block"); goto bail; } ret = ocfs2_read_extent_block(fs, rec->e_blkno, buf); if (ret) { com_err(gbls.cmd, ret, "while reading extent %"PRIu64, (uint64_t)rec->e_blkno); goto bail; } eb = (struct ocfs2_extent_block *)buf; ret = find_block_offset(fs, &(eb->h_list), blkoff, out); goto bail; } dump_logical_blkno(out, 0); bail: if (buf) ocfs2_free(&buf); return ret; } static void do_open(char **args) { char *dev = args[1]; int flags; errcode_t ret = 0; char sysfile[SYSTEM_FILE_NAME_MAX]; int i; struct ocfs2_super_block *sb; uint64_t superblock = 0, block_size = 0; if (gbls.device) do_close(NULL); if (dev == NULL || process_open_args(args, &superblock, &block_size)) { fprintf(stderr, "usage: %s [-i] [-s num]\n", args[0]); gbls.imagefile = 0; return ; } flags = gbls.allow_write ? OCFS2_FLAG_RW : OCFS2_FLAG_RO; flags |= OCFS2_FLAG_HEARTBEAT_DEV_OK|OCFS2_FLAG_NO_ECC_CHECKS; if (gbls.imagefile) flags |= OCFS2_FLAG_IMAGE_FILE; ret = ocfs2_open(dev, flags, superblock, block_size, &gbls.fs); if (ret) { gbls.fs = NULL; gbls.imagefile = 0; com_err(args[0], ret, "while opening context for device %s", dev); return ; } /* allocate blocksize buffer */ ret = ocfs2_malloc_block(gbls.fs->fs_io, &gbls.blockbuf); if (ret) { com_err(args[0], ret, "while allocating a block"); return ; } sb = OCFS2_RAW_SB(gbls.fs->fs_super); /* set globals */ gbls.device = g_strdup (dev); gbls.max_clusters = gbls.fs->fs_super->i_clusters; gbls.max_blocks = ocfs2_clusters_to_blocks(gbls.fs, gbls.max_clusters); gbls.root_blkno = sb->s_root_blkno; gbls.sysdir_blkno = sb->s_system_dir_blkno; gbls.cwd_blkno = sb->s_root_blkno; if (gbls.cwd) free(gbls.cwd); gbls.cwd = strdup("/"); /* lookup heartbeat file */ snprintf(sysfile, sizeof(sysfile), "%s", ocfs2_system_inodes[HEARTBEAT_SYSTEM_INODE].si_name); ret = ocfs2_lookup(gbls.fs, gbls.sysdir_blkno, sysfile, strlen(sysfile), NULL, &gbls.hb_blkno); if (ret) gbls.hb_blkno = 0; /* lookup slotmap file */ snprintf(sysfile, sizeof(sysfile), "%s", ocfs2_system_inodes[SLOT_MAP_SYSTEM_INODE].si_name); ret = ocfs2_lookup(gbls.fs, gbls.sysdir_blkno, sysfile, strlen(sysfile), NULL, &gbls.slotmap_blkno); if (ret) gbls.slotmap_blkno = 0; /* lookup journal files */ for (i = 0; i < sb->s_max_slots; ++i) { snprintf(sysfile, sizeof(sysfile), ocfs2_system_inodes[JOURNAL_SYSTEM_INODE].si_name, i); ret = ocfs2_lookup(gbls.fs, gbls.sysdir_blkno, sysfile, strlen(sysfile), NULL, &gbls.jrnl_blkno[i]); if (ret) gbls.jrnl_blkno[i] = 0; } return ; } static void do_close(char **args) { errcode_t ret = 0; if (check_device_open()) return ; ret = ocfs2_close(gbls.fs); if (ret) com_err(args[0], ret, "while closing context"); gbls.fs = NULL; gbls.imagefile = 0; if (gbls.blockbuf) ocfs2_free(&gbls.blockbuf); g_free(gbls.device); gbls.device = NULL; return ; } static void do_cd(char **args) { uint64_t blkno; errcode_t ret; if (process_inode_args(args, &blkno)) return ; ret = ocfs2_check_directory(gbls.fs, blkno); if (ret) { com_err(args[0], ret, "while checking directory at " "block %"PRIu64"", blkno); return ; } gbls.cwd_blkno = blkno; return ; } static void do_chroot(char **args) { uint64_t blkno; errcode_t ret; if (process_inode_args(args, &blkno)) return ; ret = ocfs2_check_directory(gbls.fs, blkno); if (ret) { com_err(args[0], ret, "while checking directory at " "blkno %"PRIu64"", blkno); return ; } gbls.root_blkno = blkno; return ; } static void do_ls(char **args) { uint64_t blkno; errcode_t ret = 0; struct list_dir_opts ls_opts = { gbls.fs, NULL, 0, NULL }; if (process_ls_args(args, &blkno, &ls_opts.long_opt)) return ; ret = ocfs2_check_directory(gbls.fs, blkno); if (ret) { com_err(args[0], ret, "while checking directory at " "block %"PRIu64"", blkno); return ; } if (ls_opts.long_opt) { ret = ocfs2_malloc_block(gbls.fs->fs_io, &ls_opts.buf); if (ret) { com_err(args[0], ret, "while allocating a block"); return ; } } ls_opts.out = open_pager(gbls.interactive); ret = ocfs2_dir_iterate(gbls.fs, blkno, 0, NULL, dump_dir_entry, (void *)&ls_opts); if (ret) com_err(args[0], ret, "while iterating directory at " "block %"PRIu64"", blkno); close_pager(ls_opts.out); if (ls_opts.buf) ocfs2_free(&ls_opts.buf); return ; } static void do_help(char **args) { int i, usagelen = 0; int numcmds = sizeof(commands) / sizeof(commands[0]); for (i = 0; i < numcmds; ++i) { if (commands[i].cmd_usage) usagelen = max(usagelen, strlen(commands[i].cmd_usage)); } #define MIN_USAGE_LEN 40 usagelen = max(usagelen, MIN_USAGE_LEN); for (i = 0; i < numcmds; ++i) { if (commands[i].cmd_usage) fprintf(stdout, "%-*s %s\n", usagelen, commands[i].cmd_usage, commands[i].cmd_desc); } } static void do_quit(char **args) { if (gbls.device) do_close(NULL); exit(0); } static void do_lcd(char **args) { char buf[PATH_MAX]; if (check_device_open()) return ; if (!args[1]) { /* show cwd */ if (!getcwd(buf, sizeof(buf))) { com_err(args[0], errno, "while reading current " "working directory"); return ; } fprintf(stdout, "%s\n", buf); return ; } if (chdir(args[1]) == -1) { com_err(args[0], errno, "'%s'", args[1]); return ; } return ; } static void do_controld_dump(char **args) { FILE *out; errcode_t ret; char *debug_buffer; ret = o2cb_control_daemon_debug(&debug_buffer); if (ret) { com_err(args[0], ret, "while obtaining the debug buffer"); return; } out = open_pager(gbls.interactive); fprintf(out, "%s", debug_buffer); close_pager(out); free(debug_buffer); } static void do_controld(char **args) { if (!args[1]) fprintf(stderr, "%s: Operation required\n", args[0]); else if (!strcmp(args[1], "dump")) do_controld_dump(args); else fprintf(stderr, "%s: Invalid operation: \"%s\"\n", args[0], args[1]); } static void do_curdev(char **args) { printf("%s\n", gbls.device ? gbls.device : "No device"); } static void do_stats(char **args) { FILE *out; errcode_t ret; struct ocfs2_dinode *in; struct ocfs2_super_block *sb; int c, argc; int sb_num = 0; int only_super = 0; char *ptr = NULL; char *stats_usage = "usage: stats [-h] [-s backup#]"; char *buf = gbls.blockbuf; if (check_device_open()) goto bail; for (argc = 0; (args[argc]); ++argc); optind = 0; while ((c = getopt(argc, args, "hs:")) != -1) { switch (c) { case 'h': only_super = 1; break; case 's': sb_num = strtoul(optarg, &ptr, 0); if (!ptr || *ptr) { fprintf(stderr, "%s\n", stats_usage); goto bail; } break; default: break; } } if (!sb_num) in = gbls.fs->fs_super; else { ret = ocfs2_read_backup_super(gbls.fs, sb_num - 1, buf); if (ret) { com_err(gbls.cmd, ret, "while reading backup " "superblock"); goto bail; } in = (struct ocfs2_dinode *)buf; } sb = OCFS2_RAW_SB(in); out = open_pager(gbls.interactive); dump_super_block(out, sb); if (!only_super) dump_inode(out, in); close_pager(out); bail: return ; } static void do_stat(char **args) { struct ocfs2_dinode *inode; uint64_t blkno; char *buf = NULL; FILE *out; errcode_t ret = 0; const char *stat_usage = "usage: stat [-t|-T] "; int index = 1, traverse = 1; if (check_device_open()) return; if (!args[index]) { fprintf(stderr, "%s\n", stat_usage); return ; } if (!strncmp(args[index], "-t", 2)) { traverse = 1; index++; } else if (!strncmp(args[index], "-T", 2)) { traverse = 0; index++; } if (!args[index]) { fprintf(stderr, "%s\n", stat_usage); return ; } ret = string_to_inode(gbls.fs, gbls.root_blkno, gbls.cwd_blkno, args[index], &blkno); if (ret) { com_err(args[0], ret, "'%s'", args[index]); return ; } buf = gbls.blockbuf; ret = ocfs2_read_inode(gbls.fs, blkno, buf); if (ret) { com_err(args[0], ret, "while reading inode %"PRIu64"", blkno); return ; } inode = (struct ocfs2_dinode *)buf; out = open_pager(gbls.interactive); dump_inode(out, inode); if (traverse) { if ((inode->i_flags & OCFS2_LOCAL_ALLOC_FL)) dump_local_alloc(out, &(inode->id2.i_lab)); else if ((inode->i_flags & OCFS2_CHAIN_FL)) ret = traverse_chains(gbls.fs, &(inode->id2.i_chain), out); else if (S_ISLNK(inode->i_mode) && !inode->i_clusters) dump_fast_symlink(out, (char *)inode->id2.i_symlink); else if (inode->i_flags & OCFS2_DEALLOC_FL) dump_truncate_log(out, &(inode->id2.i_dealloc)); else if (!(inode->i_dyn_features & OCFS2_INLINE_DATA_FL)) ret = traverse_extents(gbls.fs, &(inode->id2.i_list), out); if (ret) com_err(args[0], ret, "while traversing inode at block " "%"PRIu64, blkno); } close_pager(out); return ; } static void do_hb(char **args) { char *hbbuf = NULL; FILE *out; int len; errcode_t ret; if (check_device_open()) return ; ret = ocfs2_read_whole_file(gbls.fs, gbls.hb_blkno, &hbbuf, &len); if (ret) { com_err(args[0], ret, "while reading heartbeat system file"); goto bail; } out = open_pager(gbls.interactive); dump_hb(out, hbbuf, len); close_pager(out); bail: if (hbbuf) ocfs2_free(&hbbuf); return ; } static void do_dump(char **args) { uint64_t blkno; int preserve = 0; int ind; const char *dump_usage = "usage: dump [-p] "; char *in_fn; char *out_fn; errcode_t ret; int fd; if (check_device_open()) return; ind = 1; if (!args[ind]) { fprintf(stderr, "%s\n", dump_usage); return ; } if (!strncasecmp(args[ind], "-p", 2)) { ++preserve; ++ind; } in_fn = args[ind]; out_fn = args[ind + 1]; if (!in_fn || !out_fn) { fprintf(stderr, "%s\n", dump_usage); return ; } ret = string_to_inode(gbls.fs, gbls.root_blkno, gbls.cwd_blkno, in_fn, &blkno); if (ret) { com_err(args[0], ret, "'%s'", in_fn); return ; } fd = open64(out_fn, O_CREAT | O_WRONLY | O_TRUNC, 0666); if (fd < 0) { com_err(args[0], errno, "'%s'", out_fn); return ; } ret = dump_file(gbls.fs, blkno, fd, out_fn, preserve); if (ret) com_err(args[0], ret, "while dumping file"); return; } static void do_cat(char **args) { uint64_t blkno; errcode_t ret; char *buf; struct ocfs2_dinode *di; if (process_inode_args(args, &blkno)) return ; buf = gbls.blockbuf; ret = ocfs2_read_inode(gbls.fs, blkno, buf); if (ret) { com_err(args[0], ret, "while reading inode %"PRIu64"", blkno); return ; } di = (struct ocfs2_dinode *)buf; if (!S_ISREG(di->i_mode)) { fprintf(stderr, "%s: Not a regular file\n", args[0]); return ; } ret = dump_file(gbls.fs, blkno, fileno(stdout), NULL, 0); if (ret) com_err(args[0], ret, "while reading file for inode %"PRIu64"", blkno); return ; } static void do_logdump(char **args) { errcode_t ret; uint16_t slotnum; uint64_t blkno; FILE *out; int index = 1; const char *logdump_usage = "usage: logdump [-T] "; if (check_device_open()) return ; if (!args[index]) { fprintf(stderr, "%s\n", logdump_usage); return ; } if (!strncmp(args[index], "-T", 2)) { index++; } if (get_slotnum(args[index], &slotnum)) { fprintf(stderr, "%s: Invalid node slot number\n", args[0]); fprintf(stderr, "%s\n", logdump_usage); return ; } blkno = gbls.jrnl_blkno[slotnum]; out = open_pager(gbls.interactive); ret = read_journal(gbls.fs, blkno, out); close_pager(out); if (ret) com_err(gbls.cmd, ret, "while reading journal"); return; } static void do_group(char **args) { struct ocfs2_group_desc *grp; uint64_t blkno; char *buf = NULL; FILE *out; errcode_t ret = 0; int index = 0; if (process_inodestr_args(args, 1, &blkno) != 1) return ; buf = gbls.blockbuf; out = open_pager(gbls.interactive); while (blkno) { ret = ocfs2_read_group_desc(gbls.fs, blkno, buf); if (ret) { com_err(args[0], ret, "while reading block group " "descriptor %"PRIu64"", blkno); close_pager(out); return ; } grp = (struct ocfs2_group_desc *)buf; dump_group_descriptor(out, grp, index); blkno = grp->bg_next_group; index++; } close_pager(out); return ; } static void do_grpextents(char **args) { struct ocfs2_group_desc *grp; uint64_t blkno; char *buf = NULL; FILE *out; errcode_t ret = 0; if (process_inodestr_args(args, 1, &blkno) != 1) return; buf = gbls.blockbuf; out = open_pager(gbls.interactive); while (blkno) { ret = ocfs2_read_group_desc(gbls.fs, blkno, buf); if (ret) { com_err(args[0], ret, "while reading block group " "descriptor %"PRIu64"", blkno); close_pager(out); return; } grp = (struct ocfs2_group_desc *)buf; dump_group_extents(out, grp); blkno = grp->bg_next_group; } close_pager(out); } static int dirblocks_proxy(ocfs2_filesys *fs, uint64_t blkno, uint64_t bcount, uint16_t ext_flags, void *priv_data) { errcode_t ret; struct dirblocks_walk *ctxt = priv_data; ret = ocfs2_read_dir_block(fs, ctxt->di, blkno, ctxt->buf); if (!ret) { fprintf(ctxt->out, "\tDirblock: %"PRIu64"\n", blkno); dump_dir_block(ctxt->out, ctxt->buf); } else com_err(gbls.cmd, ret, "while reading dirblock %"PRIu64" on inode " "%"PRIu64"\n", blkno, (uint64_t)ctxt->di->i_blkno); return 0; } static void do_dirblocks(char **args) { uint64_t ino_blkno; errcode_t ret = 0; struct dirblocks_walk ctxt = { .fs = gbls.fs, }; if (process_inode_args(args, &ino_blkno)) return; ret = ocfs2_check_directory(gbls.fs, ino_blkno); if (ret) { com_err(args[0], ret, "while checking directory at " "block %"PRIu64"", ino_blkno); return; } ret = ocfs2_read_inode(gbls.fs, ino_blkno, gbls.blockbuf); if (ret) { com_err(args[0], ret, "while reading inode %"PRIu64"", ino_blkno); return; } ctxt.di = (struct ocfs2_dinode *)gbls.blockbuf; ret = ocfs2_malloc_block(ctxt.fs->fs_io, &ctxt.buf); if (ret) { com_err(gbls.cmd, ret, "while allocating a block"); return; } ctxt.out = open_pager(gbls.interactive); ret = ocfs2_block_iterate_inode(gbls.fs, ctxt.di, 0, dirblocks_proxy, &ctxt); if (ret) com_err(args[0], ret, "while iterating directory at " "block %"PRIu64"", ino_blkno); close_pager(ctxt.out); ocfs2_free(&ctxt.buf); } static void do_dx_root(char **args) { struct ocfs2_dx_root_block *dx_root; uint64_t blkno; char *buf = NULL; FILE *out; errcode_t ret = 0; if (process_inodestr_args(args, 1, &blkno) != 1) return; buf = gbls.blockbuf; out = open_pager(gbls.interactive); ret = ocfs2_read_dx_root(gbls.fs, blkno, buf); if (ret) { com_err(args[0], ret, "while reading dx dir root " "block %"PRIu64"", blkno); close_pager(out); return; } dx_root = (struct ocfs2_dx_root_block *)buf; dump_dx_root(out, dx_root); if (!(dx_root->dr_flags & OCFS2_DX_FLAG_INLINE)) traverse_extents(gbls.fs, &dx_root->dr_list, out); close_pager(out); return; } static void do_dx_leaf(char **args) { struct ocfs2_dx_leaf *dx_leaf; uint64_t blkno; char *buf = NULL; FILE *out; errcode_t ret = 0; if (process_inodestr_args(args, 1, &blkno) != 1) return; buf = gbls.blockbuf; out = open_pager(gbls.interactive); ret = ocfs2_read_dx_leaf(gbls.fs, blkno, buf); if (ret) { com_err(args[0], ret, "while reading dx dir leaf " "block %"PRIu64"", blkno); close_pager(out); return; } dx_leaf = (struct ocfs2_dx_leaf *)buf; dump_dx_leaf(out, dx_leaf); close_pager(out); return; } static void do_dx_dump(char **args) { struct ocfs2_dinode *inode; uint64_t ino_blkno; char *buf = NULL; FILE *out; errcode_t ret = 0; if (process_inode_args(args, &ino_blkno)) return; out = open_pager(gbls.interactive); buf = gbls.blockbuf; ret = ocfs2_read_inode(gbls.fs, ino_blkno, buf); if (ret) { com_err(args[0], ret, "while reading inode %"PRIu64"", ino_blkno); close_pager(out); return ; } inode = (struct ocfs2_dinode *)buf; dump_dx_entries(out, inode); close_pager(out); return; } static void do_dx_space(char **args) { struct ocfs2_dinode *inode; struct ocfs2_dx_root_block *dx_root; uint64_t ino_blkno, dx_blkno; char *buf = NULL, *dx_root_buf = NULL; FILE *out; errcode_t ret = 0; if (process_inode_args(args, &ino_blkno)) return; out = open_pager(gbls.interactive); buf = gbls.blockbuf; ret = ocfs2_read_inode(gbls.fs, ino_blkno, buf); if (ret) { com_err(args[0], ret, "while reading inode %"PRIu64"", ino_blkno); goto out; } inode = (struct ocfs2_dinode *)buf; if (!(ocfs2_dir_indexed(inode))) { fprintf(out, "Inode %"PRIu64" is not indexed\n", ino_blkno); goto out; } ret = ocfs2_malloc_block(gbls.fs->fs_io, &dx_root_buf); if (ret) { goto out; } dx_blkno = (uint64_t) inode->i_dx_root; ret = ocfs2_read_dx_root(gbls.fs, dx_blkno, dx_root_buf); if (ret) { com_err(args[0], ret, "while reading dx dir root " "block %"PRIu64"", dx_blkno); goto out; } dx_root = (struct ocfs2_dx_root_block *)dx_root_buf; dump_dx_space(out, inode, dx_root); out: close_pager(out); if (dx_root_buf) ocfs2_free(&dx_root_buf); return; } static void do_extent(char **args) { struct ocfs2_extent_block *eb; uint64_t blkno; char *buf = NULL; FILE *out; errcode_t ret = 0; if (process_inodestr_args(args, 1, &blkno) != 1) return ; buf = gbls.blockbuf; ret = ocfs2_read_extent_block(gbls.fs, blkno, buf); if (ret) { com_err(args[0], ret, "while reading extent block %"PRIu64"", blkno); return ; } eb = (struct ocfs2_extent_block *)buf; out = open_pager(gbls.interactive); dump_extent_block(out, eb); dump_extent_list(out, &eb->h_list); close_pager(out); return ; } static void do_slotmap(char **args) { FILE *out; errcode_t ret; int num_slots; struct ocfs2_slot_map_extended *se = NULL; struct ocfs2_slot_map *sm = NULL; if (check_device_open()) return ; num_slots = OCFS2_RAW_SB(gbls.fs->fs_super)->s_max_slots; if (ocfs2_uses_extended_slot_map(OCFS2_RAW_SB(gbls.fs->fs_super))) ret = ocfs2_read_slot_map_extended(gbls.fs, num_slots, &se); else ret = ocfs2_read_slot_map(gbls.fs, num_slots, &sm); if (ret) { com_err(args[0], ret, "while reading slotmap system file"); goto bail; } out = open_pager(gbls.interactive); dump_slots(out, se, sm, num_slots); close_pager(out); bail: if (sm) ocfs2_free(&sm); return ; } static void do_rdump(char **args) { uint64_t blkno; struct stat st; char *p; char *usage = "usage: rdump [-v] "; errcode_t ret; int ind = 1; int verbose = 0; char tmp_str[40]; if (check_device_open()) return ; if (!args[1]) { fprintf(stderr, "%s\n", usage); return ; } if (!strcmp(args[1], "-v")) { ++ind; ++verbose; } if (!args[ind] || !args[ind+1]) { fprintf(stderr, "%s\n", usage); return ; } /* source */ ret = string_to_inode(gbls.fs, gbls.root_blkno, gbls.cwd_blkno, args[ind], &blkno); if (ret) { com_err(args[0], ret, "while translating %s", args[ind]); return ; } /* destination... has to be a dir on a mounted fs */ if (stat(args[ind+1], &st) == -1) { com_err(args[0], errno, "'%s'", args[ind+1]); return ; } if (!S_ISDIR(st.st_mode)) { com_err(args[0], OCFS2_ET_NO_DIRECTORY, "'%s'", args[ind+1]); return ; } p = strrchr(args[ind], '/'); if (p) p++; else p = args[ind]; /* I could traverse the dirs from the root and find the directory */ /* name... but this is debugfs, for crying out loud */ if (!strcmp(p, ".") || !strcmp(p, "..") || !strcmp(p, "/")) { time_t tt; struct tm *tm; time(&tt); tm = localtime(&tt); /* YYYY-MM-DD_HH:MI:SS */ snprintf(tmp_str, sizeof(tmp_str), "%4d-%2d-%2d_%02d:%02d:%02d", 1900 + tm->tm_year, tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); p = tmp_str; } /* drop the trailing '/' in destination */ if (1) { char *q = args[ind+1]; q = q + strlen(args[ind+1]) - 1; if (*q == '/') *q = '\0'; } fprintf(stdout, "Copying to %s/%s\n", args[ind+1], p); ret = rdump_inode(gbls.fs, blkno, p, args[ind+1], verbose); if (ret) com_err(args[0], ret, "while recursively dumping " "inode %"PRIu64, blkno); return ; } /* * do_encode_lockres() * * This function only encodes the Super and the Inode lock. For the * rest, use the --encode parameter directly. */ static void do_encode_lockres(char **args) { struct ocfs2_dinode *inode = (struct ocfs2_dinode *)gbls.blockbuf; uint64_t blkno; uint32_t gen = 0; errcode_t ret = 0; char lock[OCFS2_LOCK_ID_MAX_LEN]; enum ocfs2_lock_type type = OCFS2_LOCK_TYPE_META; if (process_inode_args(args, &blkno)) return; if (blkno == OCFS2_SUPER_BLOCK_BLKNO) type = OCFS2_LOCK_TYPE_SUPER; else { ret = ocfs2_read_inode(gbls.fs, blkno, (char *)inode); if (ret) { com_err(args[0], ret, "while reading inode %"PRIu64"", blkno); return; } if (inode->i_flags & OCFS2_SYSTEM_FL) gen = inode->i_generation; } ret = ocfs2_encode_lockres(type, blkno, gen, 0, lock); if (ret) return; printf("\t"); printf("%s ", lock); printf("\n"); return ; } static void do_decode_lockres(char **args) { uint64_t blkno[MAX_BLOCKS]; int count; int i; count = process_inodestr_args(args, MAX_BLOCKS, blkno); if (count < 1) return ; for (i = 0; i < count; ++i) printf("\t%s\t%"PRIu64"\n", args[i + 1], blkno[i]); } static void do_locate(char **args) { uint64_t blkno[MAX_BLOCKS]; int count = 0; int findall = 1; count = process_inodestr_args(args, MAX_BLOCKS, blkno); if (count < 1) return ; if (!strncasecmp(args[0], "findpath", 8)) findall = 0; find_inode_paths(gbls.fs, args, findall, count, blkno, stdout); } static void do_dlm_locks(char **args) { FILE *out; int dump_lvbs = 0; int i; struct list_head locklist; char *path = NULL, *uuid_str = NULL; int c, argc; init_stringlist(&locklist); for (argc = 0; (args[argc]); ++argc); optind = 0; while ((c = getopt(argc, args, "lf:")) != -1) { switch (c) { case 'l': dump_lvbs = 1; break; case 'f': path = optarg; break; default: break; } } if ((path == NULL)) { /* Only error for a missing device if we're asked to * read from a live file system. */ if (check_device_open()) return; uuid_str = gbls.fs->uuid_str; } i = optind; if (args[i] && strlen(args[i])) { for ( ; args[i] && strlen(args[i]); ++i) if (add_to_stringlist(args[i], &locklist)) break; } out = open_pager(gbls.interactive); dump_dlm_locks(uuid_str, out, path, dump_lvbs, &locklist); close_pager(out); free_stringlist(&locklist); } static void do_fs_locks(char **args) { FILE *out; int dump_lvbs = 0; int only_busy = 0; int c, argc; struct list_head locklist; char *path = NULL, *uuid_str = NULL; for (argc = 0; (args[argc]); ++argc); optind = 0; while ((c = getopt(argc, args, "lBf:")) != -1) { switch (c) { case 'l': dump_lvbs = 1; break; case 'B': only_busy = 1; break; case 'f': path = optarg; break; default: break; } } if ((path == NULL)) { /* Only error for a missing device if we're asked to * read from a live file system. */ if (check_device_open()) return; uuid_str = gbls.fs->uuid_str; } init_stringlist(&locklist); if (optind < argc) { for ( ; args[optind] && strlen(args[optind]); ++optind) if (add_to_stringlist(args[optind], &locklist)) break; } out = open_pager(gbls.interactive); dump_fs_locks(uuid_str, out, path, dump_lvbs, only_busy, &locklist); close_pager(out); free_stringlist(&locklist); } static void do_bmap(char **args) { struct ocfs2_dinode *inode; const char *bmap_usage = "usage: bmap "; uint64_t blkno; uint64_t blkoff; char *endptr; char *buf = NULL; FILE *out; errcode_t ret = 1; if (check_device_open()) return; if (!args[1]) { fprintf(stderr, "usage: %s\n", bmap_usage); return; } if (!args[1] || !args[2]) { fprintf(stderr, "usage: %s\n", bmap_usage); return; } ret = string_to_inode(gbls.fs, gbls.root_blkno, gbls.cwd_blkno, args[1], &blkno); if (ret) { com_err(args[0], ret, "'%s'", args[1]); return; } if (blkno >= gbls.max_blocks) { com_err(args[0], OCFS2_ET_BAD_BLKNO, "- %"PRIu64"", blkno); return; } blkoff = strtoull(args[2], &endptr, 0); if (*endptr) { com_err(args[0], OCFS2_ET_BAD_BLKNO, "- %"PRIu64"", blkoff); return; } buf = gbls.blockbuf; ret = ocfs2_read_inode(gbls.fs, blkno, buf); if (ret) { com_err(args[0], ret, "while reading inode %"PRIu64, blkno); return; } inode = (struct ocfs2_dinode *)buf; out = open_pager(gbls.interactive); if (inode->i_flags & (OCFS2_LOCAL_ALLOC_FL | OCFS2_CHAIN_FL | OCFS2_DEALLOC_FL)) { dump_logical_blkno(out, 0); goto bail; } if (S_ISLNK(inode->i_mode) && !inode->i_clusters) { dump_logical_blkno(out, 0); goto bail; } if (blkoff > (inode->i_size >> OCFS2_RAW_SB(gbls.fs->fs_super)->s_blocksize_bits)) { dump_logical_blkno(out, 0); goto bail; } find_block_offset(gbls.fs, &(inode->id2.i_list), blkoff, out); bail: close_pager(out); return; } static void do_icheck(char **args) { const char *testb_usage = "usage: icheck block# ..."; char *endptr; uint64_t blkno[MAX_BLOCKS]; int i; FILE *out; if (check_device_open()) return; if (!args[1]) { fprintf(stderr, "%s\n", testb_usage); return; } for (i = 0; i < MAX_BLOCKS && args[i + 1]; ++i) { blkno[i] = strtoull(args[i + 1], &endptr, 0); if (*endptr) { com_err(args[0], OCFS2_ET_BAD_BLKNO, "- %s", args[i + 1]); return; } if (blkno[i] >= gbls.max_blocks) { com_err(args[0], OCFS2_ET_BAD_BLKNO, "- %"PRIu64"", blkno[i]); return; } } out = open_pager(gbls.interactive); find_block_inode(gbls.fs, blkno, i, out); close_pager(out); return; } static void do_xattr(char **args) { struct ocfs2_dinode *inode; char *buf = NULL; FILE *out; char *usage = "usage: xattr [-v] "; uint64_t blkno, xattrs_bucket = 0; uint32_t xattrs_ibody = 0, xattrs_block = 0; int ind = 1, verbose = 0; errcode_t ret = 0; if (check_device_open()) return; if (!args[1]) { fprintf(stderr, "%s\n", usage); return; } if (!strcmp(args[1], "-v")) { ++ind; ++verbose; } if (!args[ind]) { fprintf(stderr, "%s\n", usage); return; } ret = string_to_inode(gbls.fs, gbls.root_blkno, gbls.cwd_blkno, args[ind], &blkno); if (ret) { com_err(args[0], ret, "while translating %s", args[ind]); return; } buf = gbls.blockbuf; ret = ocfs2_read_inode(gbls.fs, blkno, buf); if (ret) { com_err(args[0], ret, "while reading inode %"PRIu64"", blkno); return; } inode = (struct ocfs2_dinode *)buf; if (!(inode->i_dyn_features & OCFS2_HAS_XATTR_FL)) return; out = open_pager(gbls.interactive); xattrs_ibody = dump_xattr_ibody(out, gbls.fs, inode, verbose); if (inode->i_xattr_loc) ret = dump_xattr_block(out, gbls.fs, inode, &xattrs_block, &xattrs_bucket, verbose); if (ret) com_err(args[0], ret, "while traversing inode at block " "%"PRIu64, blkno); else fprintf(out, "\n\tExtended attributes in total: %"PRIu64"\n", xattrs_ibody + xattrs_block + xattrs_bucket); close_pager(out); return ; } static errcode_t calc_num_extents(ocfs2_filesys *fs, struct ocfs2_extent_list *el, uint32_t *ne) { struct ocfs2_extent_block *eb; struct ocfs2_extent_rec *rec; errcode_t ret = 0; char *buf = NULL; int i; uint32_t clusters; uint32_t extents = 0; *ne = 0; for (i = 0; i < el->l_next_free_rec; ++i) { rec = &(el->l_recs[i]); clusters = ocfs2_rec_clusters(el->l_tree_depth, rec); /* * In a unsuccessful insertion, we may shift a tree * add a new branch for it and do no insertion. So we * may meet a extent block which have * clusters == 0, this should only be happen * in the last extent rec. */ if (!clusters && i == el->l_next_free_rec - 1) break; extents = 1; if (el->l_tree_depth) { ret = ocfs2_malloc_block(gbls.fs->fs_io, &buf); if (ret) goto bail; ret = ocfs2_read_extent_block(fs, rec->e_blkno, buf); if (ret) goto bail; eb = (struct ocfs2_extent_block *)buf; ret = calc_num_extents(fs, &(eb->h_list), &extents); if (ret) goto bail; } *ne = *ne + extents; } bail: if (buf) ocfs2_free(&buf); return ret; } static void do_frag(char **args) { struct ocfs2_dinode *inode; uint64_t blkno; char *buf = NULL; FILE *out; errcode_t ret = 0; uint32_t clusters; uint32_t extents = 0; if (process_inode_args(args, &blkno)) return; buf = gbls.blockbuf; ret = ocfs2_read_inode(gbls.fs, blkno, buf); if (ret) { com_err(args[0], ret, "while reading inode %"PRIu64"", blkno); return ; } inode = (struct ocfs2_dinode *)buf; out = open_pager(gbls.interactive); clusters = inode->i_clusters; if (!(inode->i_dyn_features & OCFS2_INLINE_DATA_FL)) ret = calc_num_extents(gbls.fs, &(inode->id2.i_list), &extents); if (ret) com_err(args[0], ret, "while traversing inode at block " "%"PRIu64, blkno); else dump_frag(out, inode->i_blkno, clusters, extents); close_pager(out); return ; } static void walk_refcount_block(FILE *out, struct ocfs2_refcount_block *rb, int extent_tree) { errcode_t ret = 0; uint32_t phys_cpos = UINT32_MAX; uint32_t e_cpos = 0, num_clusters = 0; uint64_t p_blkno = 0; char *buf = NULL; struct ocfs2_refcount_block *leaf_rb; if (!(rb->rf_flags & OCFS2_REFCOUNT_TREE_FL)) { dump_refcount_records(out, rb); return; } if (extent_tree) { fprintf(out, "\tExtent tree in refcount block %"PRIu64"\n" "\tDepth: %d Records: %d\n", (uint64_t)rb->rf_blkno, rb->rf_list.l_tree_depth, rb->rf_list.l_next_free_rec); ret = traverse_extents(gbls.fs, &rb->rf_list, out); if (ret) com_err("refcount", ret, "while traversing the extent tree " "of refcount block %"PRIu64, (uint64_t)rb->rf_blkno); } ret = ocfs2_malloc_block(gbls.fs->fs_io, &buf); if (ret) { com_err("refcount", ret, "while allocating a buffer"); return; } while (phys_cpos > 0) { ret = ocfs2_refcount_tree_get_rec(gbls.fs, rb, phys_cpos, &p_blkno, &e_cpos, &num_clusters); if (ret) { com_err("refcount", ret, "while looking up next refcount leaf in " "recount block %"PRIu64"\n", (uint64_t)rb->rf_blkno); break; } ret = ocfs2_read_refcount_block(gbls.fs, p_blkno, buf); if (ret) { com_err("refcount", ret, "while reading refcount block" " %"PRIu64, p_blkno); break; } leaf_rb = (struct ocfs2_refcount_block *)buf; dump_refcount_block(out, leaf_rb); walk_refcount_block(out, leaf_rb, extent_tree); if (e_cpos == 0) break; phys_cpos = e_cpos - 1; } ocfs2_free(&buf); } /* do_refcount() can take an inode or a refcount block address. */ static void do_refcount(char **args) { struct ocfs2_dinode *di; struct ocfs2_refcount_block *rb; uint64_t blkno; char *buf = NULL; FILE *out; errcode_t ret = 0; int extent_tree = 0; char *inode_args[3] = { args[0], args[1], NULL, }; if (args[1] && !strcmp(args[1], "-e")) { extent_tree = 1; inode_args[1] = args[2]; } if (!inode_args[1]) { fprintf(stderr, "usage: %s [-e] \n", args[0]); return; } if (process_inode_args(inode_args, &blkno)) return ; buf = gbls.blockbuf; ret = ocfs2_read_inode(gbls.fs, blkno, buf); if (!ret) { di = (struct ocfs2_dinode *)buf; if (!(di->i_dyn_features & OCFS2_HAS_REFCOUNT_FL)) { fprintf(stderr, "%s: Inode %"PRIu64" does not have a " "refcount tree\n", args[0], blkno); return; } blkno = di->i_refcount_loc; } else if ((ret != OCFS2_ET_IO) && (ret != OCFS2_ET_BAD_INODE_MAGIC)) { /* * If the user passed a refcount block address, * read_inode() will return ET_IO or ET_BAD_INODE_MAGIC. * For those cases we proceed treating blkno as a * refcount block. All other errors are real errors. */ com_err(args[0], ret, "while reading inode %"PRIu64"", blkno); return; } ret = ocfs2_read_refcount_block(gbls.fs, blkno, buf); if (ret) { com_err(args[0], ret, "while reading refcount block %"PRIu64, blkno); return; } out = open_pager(gbls.interactive); rb = (struct ocfs2_refcount_block *)buf; dump_refcount_block(out, rb); walk_refcount_block(out, rb, extent_tree); close_pager(out); } static void do_net_stats(char **args) { int interval = 0, count = 0; char *net_stats_usage = "usage: net_stats [-f ] [interval [count]]"; char *endptr; char *path = NULL; int c, argc; for (argc = 0; (args[argc]); ++argc); optind = 0; while ((c = getopt(argc, args, "f:")) != -1) { switch (c) { case 'f': path = optarg; break; default: break; } } if (args[optind]) { interval = strtoul(args[optind], &endptr, 0); if (!*endptr && args[++optind]) count = strtoul(args[optind], &endptr, 0); if (*endptr) { fprintf(stderr, "%s\n", net_stats_usage); return ; } } /* Ignore device is user specifies the stats file */ if (path == NULL && check_device_open()) return; dump_net_stats(stdout, path, interval, count); } static void do_stat_sysdir(char **args) { FILE *out; if (check_device_open()) goto bail; out = open_pager(gbls.interactive); show_stat_sysdir(gbls.fs, out); close_pager(out); bail: return ; } ocfs2-tools-ocfs2-tools-1.8.6/debugfs.ocfs2/debugfs.ocfs2.8.in000066400000000000000000000223341347147137200236470ustar00rootroot00000000000000.TH "debugfs.ocfs2" "8" "January 2012" "Version @VERSION@" "OCFS2 Manual Pages" .SH "NAME" debugfs.ocfs2 \- OCFS2 file system debugger. .SH "SYNOPSIS" \fBdebugfs.ocfs2\fR [\fB\-f\fR \fIcmdfile\fR] [\fB\-R\fR \fIcommand\fR] [\fB\-s\fR \fIbackup\fR] [\fB\-nwV?\fR] [\fIdevice\fR] .TP \fBdebugfs.ocfs2\fR \fB\-l\fR [\fItracebit\fR ... [\fBallow\fR|\fBoff\fR|\fBdeny\fR]] ... .TP \fBdebugfs.ocfs2\fR \fB\-d, \-\-decode\fR \fIlockname\fR .TP \fBdebugfs.ocfs2\fR \fB\-e, \-\-encode\fB \fIlock_type block_num [generation | parent]\fR .SH "DESCRIPTION" .PP The \fBdebugfs.ocfs2\fR program is an interactive file system debugger useful in displaying on-disk \fBOCFS2\fR filesystem structures on the specified \fIdevice\fR. .SH "OPTIONS" .TP \fB\-d, \-\-decode\fR \fIlockname\fR Display the information encoded in the \fIlockname\fR. .TP \fB\-e, \-\-encode\fR \fIlock_type block_num [generation | parent]\fR Display the lockname obtained by encoding the arguments provided. .TP \fB\-f, \-\-file\fR \fIcmdfile\fR Executes the debugfs commands in \fIcmdfile\fR. .TP \fB\-i, \-\-image\fR Specifies device is an o2image file created by \fIo2image\fR tool. .TP \fB\-l\fR [\fItracebit\fR ... [\fBallow\fR|\fBoff\fR|\fBdeny\fR]] ... Control \fBOCFS2\fR filesystem tracing by enabling and disabling trace bits. Do \fIdebugfs.ocfs2 -l\fR to get the list of all trace bits. .TP \fB\-n, \-\-noprompt\fR Hide prompt. .TP \fB\-R, \-\-request\fR \fIcommand\fR Executes a single debugfs command. .TP \fB\-s, \-\-superblock\fR \fIbackup\-number\fR \fImkfs.ocfs2\fR makes up to 6 backup copies of the superblock at offsets 1G, 4G, 16G, 64G, 256G and 1T depending on the size of the volume. Use this option to specify the backup, 1 thru 6, to use to open the volume. .TP \fB\-w, \-\-write\fR Opens the filesystem in \fIRW\fR mode. By default the filesystem is opened in \fIRO\fR mode. .TP \fB\-V, \-\-version\fR Display version and exit. .TP \fB\-?, \-\-help\fR Displays help and exit. .SH "SPECIFYING FILES" Many \fBdebugfs.ocfs2\fR commands take a \fIfilespec\fR as an argument to specify an inode (as opposed to a pathname) in the filesystem which is currently opened by \fBdebugfs.ocfs2\fR. The \fIfilespec\fR argument may be specified in two forms. The first form is an inode number or lockname surrounded by angle brackets, e.g., <32>. The second form is a pathname; if the pathname is prefixed by a forward slash ('/'), then it is interpreted relative to the root of the filesystem which is currently opened by \fBdebugfs.ocfs2\fR. If not, the path is interpreted relative to the current working directory as maintained by \fBdebugfs.ocfs2\fR, which can be modified using the command \fIcd\fR. If the pathname is prefixed by a double forward slash ('//'), then it is interpreted relative to the root of the system directory of the filesystem opened by \fBdebugfs.ocfs2\fR. .SH "LOCKNAMES" Locknames are specially formatted strings used by the file system to uniquely identify objects in the filesystem. Most locknames used by \fIOCFS2\fR are generated using the inode number and its generation number and can be decoded using the \fIdecode\fR command or used directly in place of an inode number in commands requiring a \fIfilespec\fR. Like inode numbers, locknames need to be enclosed in angle brackets, e.g., . Use the \fIencode\fR command to generate all possible locknames for an object. .SH "COMMANDS" This is a list of the commands which debugfs.ocfs2 supports. .TP \fIbmap filespec logical_block\fR Display the physical block number corresponding to the logical block number \fIlogical_block\fR in the inode \fIfilespec\fR. .TP \fIcat filespec\fR Dump the contents of inode \fIfilespec\fR to stdout. .TP \fIcd filespec\fR Change the current working directory to \fIfilespec\fR. .TP \fIchroot filespec\fR Change the root directory to be the directory \fIfilespec\fR. .TP \fIclose\fR Close the currently opened filesystem. .TP \fIcontrold dump\fR Display information obtained from ocfs2_controld. .TP \fIcurdev\fR Show the currently open device. .TP \fIdecode \fR Display the inode number encoded in the \fIlockname\fR. .TP \fIdirblocks \fR Display the directory blocks associated with the given \fIfilespec\fR. .TP \fIdlm_locks [\-f ] [\-l] []...\fR Display the status of all lock resources in the \fIo2dlm\fR domain that the file system is a member of. This command expects the debugfs filesystem to be mounted as \fImount -t debugfs debugfs /sys/kernel/debug\fR. Use \fIlockname(s)\fR to limit the output to the given lock resources, \fI-l\fR to include contents of the lock value block and \fI-f \fR to specify a saved copy of /sys/kernel/debug/o2dlm//locking_state. .TP \fIdump [\-p] filespec outfile\fR Dump the contents of the inode \fIfilespec\fR to the output file \fIoutfile\fR. If the \fI-p\fR is given, set the owner, group, timestamps and permissions information on \fIoutfile\fR to match those of \fIfilespec\fR. .TP \fIdx_dump filespec\fR Display the indexed directory information for the given directory. .TP \fIdx_leaf \fR Display the contents of the given indexed directory leaf block. .TP \fIdx_root \fR Display the contents of the given indexed directory root block. .TP \fIdx_space filespec\fR Display the directory free space list. .TP \fIencode filespec\fR Display the lockname for the \fIfilespec\fR. .TP \fIextent \fR Display the contents of the extent structure at \fIblock#\fR. .TP \fIfindpath [|]\fR Display the pathname for the inode specified by \fIlockname\fR or \fIinode#\fR. This command does not display all the hard-linked paths for the inode. .TP \fIfrag filespec\fR Display the inode's number of extents to clusters ratio. .TP \fIfs_locks [-f ] [-l] [-B] []...\fR Display the status of all locks known by the file system. This command expects the debugfs filesystem to be mounted as \fImount -t debugfs debugfs /sys/kernel/debug\fR. Use \fIlockname(s)\fR to limit the output to the given lock resources, \fI-B\fR to limit the output to only the busy locks, \fI-l\fR to include contents of the lock value block and \fI-f \fR to specify a saved copy of /sys/kernel/debug/ocfs2//locking_state. .TP \fIgroup \fR Display the contents of the group descriptor at \fIblock#\fR. .TP \fIgrpextents \fR Display free extents in the chain group. .TP \fIhb\fR Display the contents of the heartbeat system file. .TP \fIhelp, ?\fR Print the list of commands understood by \fBdebugfs.ocfs2\fR. .TP \fIicheck block# ...\fR Display the inodes that use the one or more blocks specified on the command line. If the inode is a regular file, also display the corresponding logical block offset. .TP \fIlcd directory\fR Change the current working directory of the \fBdebugfs.ocfs2\fR process to the \fIdirectory\fR on the native filesystem. .TP \fIlocate [|] ...\fR Display all pathnames for the inode(s) specified by \fIlockname\fRs or \fIinode#\fRs. .TP \fIlogdump [-T] slot#\fR Display the contents of the journal for slot \fIslot#\fR. Use \fI-T\fR to limit the output to just the summary of the inodes in the journal. .TP \fIls [\-l] filespec\fR Print the listing of the files in the directory \fIfilespec\fR. The \fI\-l\fR flag will list files in the long format. .TP \fInet_stats [-f ] [interval [count]]\fR Display the net statistics. This command expects the debugfs filesystem to be mounted at \fI/sys/kernel/debug\fR. The \fIinterval\fR is in seconds. Use the \fI-f\fR parameter to specify a saved copy of /sys/kernel/debug/o2net/stats. .TP \fIncheck [|] ...\fR See \fIlocate\fR. .TP \fIopen device\fR Open the filesystem on \fIdevice\fR. .TP \fIquit, q\fR Quit \fBdebugfs.ocfs2\fR. .TP \fIrdump [\-v] filespec outdir\fR Recursively dump directory \fIfilespec\fR and all its contents (including regular files, symbolic links and other directories) into the \fIoutdir\fR which should be an existing directory on the native filesystem. .TP \fIrefcount [\-e] filespec\fR Display the refcount block, and optionally its tree, of the specified inode. .TP \fIslotmap\fR Display the contents of the \fIslotmap\fR system file. .TP \fIstat [\-t|\-T] filespec\fR Display the contents of the inode structure for the \fIfilespec\fR. The \fI-t\fR ("traverse") option selects traversal of the inode's metadata. The extent tree, chain list, or other extra metadata will be dumped. This is the default. The \fI-T\fR option turns off traversal to reduce the I/O required when basic inode information is needed. .TP \fIstat_sysdir\fR Display the contents of all objects in the system directory. .TP \fIstats [\-h] [\-s backup\-number]\fR Display the contents of the superblock. Use \fI\-s\fR to display a specific backup superblock. Use \fI\-h\fR to hide the inode. .TP \fIxattr [-v] \fR Display extended attributes associated with the given \fIfilespec\fR. .SH "ACKNOWLEDGEMENT" This tool has been modelled after \fBdebugfs\fR, a debugging tool for ext2. .SH "SEE ALSO" .BR fsck.ocfs2(8) .BR fsck.ocfs2.checks(8) .BR mkfs.ocfs2(8) .BR mount.ocfs2(8) .BR mounted.ocfs2(8) .BR o2cluster(8) .BR o2image(8) .BR o2info(1) .BR ocfs2(7) .BR tunefs.ocfs2(8) .SH "AUTHOR" Oracle Corporation .SH "COPYRIGHT" Copyright \(co 2004, 2012 Oracle. All rights reserved. ocfs2-tools-ocfs2-tools-1.8.6/debugfs.ocfs2/dump.c000066400000000000000000001054661347147137200216400ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * dump.c * * dumps ocfs2 structures * * Copyright (C) 2004, 2011 Oracle. All rights reserved. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. */ #include #include "main.h" #include "ocfs2/byteorder.h" extern struct dbgfs_gbls gbls; /* * dump_super_block() * */ void dump_super_block(FILE *out, struct ocfs2_super_block *sb) { int i; char *str; char buf[PATH_MAX]; time_t lastcheck; fprintf(out, "\tRevision: %u.%u\n", sb->s_major_rev_level, sb->s_minor_rev_level); fprintf(out, "\tMount Count: %u Max Mount Count: %u\n", sb->s_mnt_count, sb->s_max_mnt_count); fprintf(out, "\tState: %u Errors: %u\n", sb->s_state, sb->s_errors); lastcheck = (time_t)sb->s_lastcheck; str = ctime(&lastcheck); fprintf(out, "\tCheck Interval: %u Last Check: %s", sb->s_checkinterval, str); fprintf(out, "\tCreator OS: %u\n", sb->s_creator_os); get_compat_flag(sb, buf, sizeof(buf)); fprintf(out, "\tFeature Compat: %u %s\n", sb->s_feature_compat, buf); get_incompat_flag(sb, buf, sizeof(buf)); fprintf(out, "\tFeature Incompat: %u %s\n", sb->s_feature_incompat, buf); get_tunefs_flag(sb, buf, sizeof(buf)); fprintf(out, "\tTunefs Incomplete: %u %s\n", sb->s_tunefs_flag, buf); get_rocompat_flag(sb, buf, sizeof(buf)); fprintf(out, "\tFeature RO compat: %u %s\n", sb->s_feature_ro_compat, buf); fprintf(out, "\tRoot Blknum: %"PRIu64" System Dir Blknum: %"PRIu64"\n", (uint64_t)sb->s_root_blkno, (uint64_t)sb->s_system_dir_blkno); fprintf(out, "\tFirst Cluster Group Blknum: %"PRIu64"\n", (uint64_t)sb->s_first_cluster_group); fprintf(out, "\tBlock Size Bits: %u Cluster Size Bits: %u\n", sb->s_blocksize_bits, sb->s_clustersize_bits); fprintf(out, "\tMax Node Slots: %u\n", sb->s_max_slots); fprintf(out, "\tExtended Attributes Inline Size: %u\n", sb->s_xattr_inline_size); fprintf(out, "\tLabel: %.*s\n", OCFS2_MAX_VOL_LABEL_LEN, sb->s_label); fprintf(out, "\tUUID: "); for (i = 0; i < 16; i++) fprintf(out, "%02X", sb->s_uuid[i]); fprintf(out, "\n"); fprintf(out, "\tHash: %u (0x%x)\n", sb->s_uuid_hash, sb->s_uuid_hash); fprintf(out, "\tDX Seeds: %u %u %u (0x%08x 0x%08x 0x%08x)\n", sb->s_dx_seed[0], sb->s_dx_seed[1], sb->s_dx_seed[2], sb->s_dx_seed[0], sb->s_dx_seed[1], sb->s_dx_seed[2]); if (ocfs2_clusterinfo_valid(sb)) { strncpy(buf, (char *)sb->s_cluster_info.ci_stack, OCFS2_STACK_LABEL_LEN); buf[OCFS2_STACK_LABEL_LEN] = '\0'; fprintf(out, "\tCluster stack: %s\n", buf); strncpy(buf, (char *)sb->s_cluster_info.ci_cluster, OCFS2_CLUSTER_NAME_LEN); buf[OCFS2_CLUSTER_NAME_LEN] = '\0'; fprintf(out, "\tCluster name: %s\n", buf); } else fprintf(out, "\tCluster stack: classic o2cb\n"); get_cluster_info_flag(sb, buf, sizeof(buf)); fprintf(out, "\tCluster flags: %u %s\n", sb->s_cluster_info.ci_stackflags, buf); return ; } /* * dump_local_alloc() * */ void dump_local_alloc(FILE *out, struct ocfs2_local_alloc *loc) { fprintf(out, "\tLocal Bitmap Offset: %u Size: %u\n", loc->la_bm_off, loc->la_size); return ; } /* * dump_truncate_log() * */ void dump_truncate_log(FILE *out, struct ocfs2_truncate_log *tl) { int i; fprintf(out, "\tTotal Records: %u Used: %u\n", tl->tl_count, tl->tl_used); fprintf(out, "\t## %-10s %-10s\n", "Start Cluster", "Num Clusters"); for(i = 0; i < tl->tl_used; i++) fprintf(out, "\t%-2d %-10u %-10u\n", i, tl->tl_recs[i].t_start, tl->tl_recs[i].t_clusters); return ; } /* * dump_fast_symlink() * */ void dump_fast_symlink(FILE *out, char *link) { fprintf(out, "\tFast Symlink Destination: %s\n", link); return ; } /* * dump_block_check() * * As the checksum is computed in the disk format, we have to swap_from_cpu * before computing it. */ void dump_block_check(FILE *out, struct ocfs2_block_check *bc, void *block) { struct ocfs2_block_check tmp = *bc; int crc_fail; fprintf(out, "\tCRC32: %.8"PRIx32" ECC: %.4"PRIx16"\n", le32_to_cpu(tmp.bc_crc32e), le16_to_cpu(tmp.bc_ecc)); /* Cannot validate unknown blocks */ if (ocfs2_detect_block(block) == OCFS2_BLOCK_UNKNOWN) { fprintf(out, "\t**UNKNOWN BLOCK** (Cannot validate checksum)\n"); return; } ocfs2_swap_block_from_cpu(gbls.fs, block); ocfs2_compute_meta_ecc(gbls.fs, block, bc); ocfs2_swap_block_to_cpu(gbls.fs, block); crc_fail = memcmp(bc, &tmp, sizeof(*bc)); if (crc_fail) fprintf(out, "\t**FAILED CHECKSUM** Computed CRC32: %.8" PRIx32" ECC: %.4"PRIx16"\n", le32_to_cpu(bc->bc_crc32e), le16_to_cpu(bc->bc_ecc)); /* Leave the block as we found it. */ *bc = tmp; } /* * dump_inode() * */ void dump_inode(FILE *out, struct ocfs2_dinode *in) { struct passwd *pw; struct group *gr; char *str; uint16_t mode; GString *flags = NULL; GString *dyn_features = NULL; char tmp_str[30]; struct timespec ts; char timebuf[50]; time_t tm; if (S_ISREG(in->i_mode)) str = "Regular"; else if (S_ISDIR(in->i_mode)) str = "Directory"; else if (S_ISCHR(in->i_mode)) str = "Char Device"; else if (S_ISBLK(in->i_mode)) str = "Block Device"; else if (S_ISFIFO(in->i_mode)) str = "FIFO"; else if (S_ISLNK(in->i_mode)) str = "Symbolic Link"; else if (S_ISSOCK(in->i_mode)) str = "Socket"; else str = "Unknown"; mode = in->i_mode & (S_IRWXU | S_IRWXG | S_IRWXO); flags = g_string_new(NULL); if (in->i_flags & OCFS2_VALID_FL) g_string_append(flags, "Valid "); if (in->i_flags & OCFS2_UNUSED2_FL) g_string_append(flags, "Unused2 "); if (in->i_flags & OCFS2_ORPHANED_FL) g_string_append(flags, "Orphan "); if (in->i_flags & OCFS2_UNUSED3_FL) g_string_append(flags, "Unused3 "); if (in->i_flags & OCFS2_SYSTEM_FL) g_string_append(flags, "System "); if (in->i_flags & OCFS2_SUPER_BLOCK_FL) g_string_append(flags, "Superblock "); if (in->i_flags & OCFS2_LOCAL_ALLOC_FL) g_string_append(flags, "Localalloc "); if (in->i_flags & OCFS2_BITMAP_FL) g_string_append(flags, "Allocbitmap "); if (in->i_flags & OCFS2_JOURNAL_FL) g_string_append(flags, "Journal "); if (in->i_flags & OCFS2_HEARTBEAT_FL) g_string_append(flags, "Heartbeat "); if (in->i_flags & OCFS2_CHAIN_FL) g_string_append(flags, "Chain "); if (in->i_flags & OCFS2_DEALLOC_FL) g_string_append(flags, "Dealloc "); dyn_features = g_string_new(NULL); if (in->i_dyn_features & OCFS2_INLINE_DATA_FL) g_string_append(dyn_features, "InlineData "); if (in->i_dyn_features & OCFS2_HAS_XATTR_FL) g_string_append(dyn_features, "HasXattr "); if (in->i_dyn_features & OCFS2_INLINE_XATTR_FL) g_string_append(dyn_features, "InlineXattr "); if (in->i_dyn_features & OCFS2_INDEXED_DIR_FL) g_string_append(dyn_features, "IndexedDir "); if (in->i_dyn_features & OCFS2_HAS_REFCOUNT_FL) g_string_append(dyn_features, "Refcounted "); fprintf(out, "\tInode: %"PRIu64" Mode: 0%0o Generation: %u (0x%x)\n", (uint64_t)in->i_blkno, mode, in->i_generation, in->i_generation); fprintf(out, "\tFS Generation: %u (0x%x)\n", in->i_fs_generation, in->i_fs_generation); dump_block_check(out, &in->i_check, in); fprintf(out, "\tType: %s Attr: 0x%x Flags: %s\n", str, in->i_attr, flags->str); fprintf(out, "\tDynamic Features: (0x%x) %s\n", in->i_dyn_features, dyn_features->str); if (in->i_dyn_features & OCFS2_HAS_XATTR_FL) fprintf(out, "\tExtended Attributes Block: %"PRIu64 " Extended Attributes Inline Size: %u\n", (uint64_t)in->i_xattr_loc, in->i_xattr_inline_size); pw = getpwuid(in->i_uid); gr = getgrgid(in->i_gid); fprintf(out, "\tUser: %d (%s) Group: %d (%s) Size: %"PRIu64"\n", in->i_uid, (pw ? pw->pw_name : "unknown"), in->i_gid, (gr ? gr->gr_name : "unknown"), (uint64_t)in->i_size); fprintf(out, "\tLinks: %u Clusters: %u\n", in->i_links_count, in->i_clusters); #define SHOW_TIME(str, sec, nsec) \ do { \ ts.tv_sec = (sec); ts.tv_nsec = (nsec); \ ctime_nano(&ts, timebuf, sizeof(timebuf)); \ fprintf(out, "\t%s: 0x%"PRIx64" 0x%x -- %s", (str), (uint64_t)(sec), \ (nsec), timebuf); \ } while (0) SHOW_TIME("ctime", in->i_ctime, in->i_ctime_nsec); SHOW_TIME("atime", in->i_atime, in->i_atime_nsec); SHOW_TIME("mtime", in->i_mtime, in->i_mtime_nsec); #undef SHOW_TIME tm = (time_t)in->i_dtime; fprintf(out, "\tdtime: 0x%"PRIx64" -- %s", (uint64_t)tm, ctime(&tm)); fprintf(out, "\tRefcount Block: %"PRIu64"\n", (uint64_t)in->i_refcount_loc); fprintf(out, "\tLast Extblk: %"PRIu64" Orphan Slot: %d\n", (uint64_t)in->i_last_eb_blk, in->i_orphaned_slot); if (in->i_suballoc_slot == (uint16_t)OCFS2_INVALID_SLOT) strcpy(tmp_str, "Global"); else sprintf(tmp_str, "%d", in->i_suballoc_slot); fprintf(out, "\tSub Alloc Slot: %s Sub Alloc Bit: %u", tmp_str, in->i_suballoc_bit); if (in->i_suballoc_loc) fprintf(out, " Sub Alloc Group %"PRIu64"\n", (uint64_t)in->i_suballoc_loc); else fprintf(out, "\n"); if (in->i_flags & OCFS2_BITMAP_FL) fprintf(out, "\tBitmap Total: %u Used: %u Free: %u\n", in->id1.bitmap1.i_total, in->id1.bitmap1.i_used, (in->id1.bitmap1.i_total - in->id1.bitmap1.i_used)); if (in->i_flags & OCFS2_JOURNAL_FL) { fprintf(out, "\tJournal Flags: "); if (in->id1.journal1.ij_flags & OCFS2_JOURNAL_DIRTY_FL) fprintf(out, "Dirty "); fprintf(out, "\n"); fprintf(out, "\tRecovery Generation: %u\n", in->id1.journal1.ij_recovery_generation); } if (in->i_dyn_features & OCFS2_INLINE_DATA_FL) { fprintf(out, "\tInline Data Max: %u\n", in->id2.i_data.id_count); } else if (in->i_dyn_features & OCFS2_INDEXED_DIR_FL) { fprintf(out, "\tIndexed Tree Root: %"PRIu64"\n", (uint64_t)in->i_dx_root); } if (flags) g_string_free(flags, 1); if (dyn_features) g_string_free(dyn_features, 1); return ; } /* * dump_chain_list() * */ void dump_chain_list(FILE *out, struct ocfs2_chain_list *cl) { struct ocfs2_chain_rec *rec; int i; fprintf(out, "\tClusters per Group: %u Bits per Cluster: %u\n", cl->cl_cpg, cl->cl_bpc); fprintf(out, "\tCount: %u Next Free Rec: %u\n", cl->cl_count, cl->cl_next_free_rec); if (!cl->cl_next_free_rec) goto bail; fprintf(out, "\t## %-10s %-10s %-10s %s\n", "Total", "Used", "Free", "Block#"); for (i = 0; i < cl->cl_next_free_rec; ++i) { rec = &(cl->cl_recs[i]); fprintf(out, "\t%-2d %-10u %-10u %-10u %"PRIu64"\n", i, rec->c_total, (rec->c_total - rec->c_free), rec->c_free, (uint64_t)rec->c_blkno); } bail: return ; } void dump_extent_list(FILE *out, struct ocfs2_extent_list *ext) { struct ocfs2_extent_rec *rec; int i; uint32_t clusters; char flags[PATH_MAX]; fprintf(out, "\tTree Depth: %u Count: %u Next Free Rec: %u\n", ext->l_tree_depth, ext->l_count, ext->l_next_free_rec); if (!ext->l_next_free_rec) goto bail; if (ext->l_tree_depth) fprintf(out, "\t## %-11s %-12s %-s\n", "Offset", "Clusters", "Block#"); else fprintf(out, "\t## %-11s %-12s %-13s %s\n", "Offset", "Clusters", "Block#", "Flags"); for (i = 0; i < ext->l_next_free_rec; ++i) { rec = &(ext->l_recs[i]); clusters = ocfs2_rec_clusters(ext->l_tree_depth, rec); if (ext->l_tree_depth) fprintf(out, "\t%-2d %-11u %-12u %"PRIu64"\n", i, rec->e_cpos, clusters, (uint64_t)rec->e_blkno); else { flags[0] = '\0'; if (ocfs2_snprint_extent_flags(flags, PATH_MAX, rec->e_flags)) flags[0] = '\0'; fprintf(out, "\t%-2d %-11u %-12u " "%-13"PRIu64" 0x%x %s\n", i, rec->e_cpos, clusters, (uint64_t)rec->e_blkno, rec->e_flags, flags); } } bail: return ; } /* * dump_extent_block() * */ void dump_extent_block(FILE *out, struct ocfs2_extent_block *blk) { fprintf(out, "\tSubAlloc Bit: %u SubAlloc Slot: %u", blk->h_suballoc_bit, blk->h_suballoc_slot); if (blk->h_suballoc_loc) fprintf(out, " SubAlloc Group: %"PRIu64"\n", (uint64_t)blk->h_suballoc_loc); else fprintf(out, "\n"); fprintf(out, "\tBlknum: %"PRIu64" Next Leaf: %"PRIu64"\n", (uint64_t)blk->h_blkno, (uint64_t)blk->h_next_leaf_blk); dump_block_check(out, &blk->h_check, blk); return ; } /* * dump_group_descriptor() * */ void dump_group_descriptor(FILE *out, struct ocfs2_group_desc *grp, int index) { int max_contig_free_bits = 0; if (!index) { fprintf(out, "\tGroup Chain: %u Parent Inode: %"PRIu64" " "Generation: %u\n", grp->bg_chain, (uint64_t)grp->bg_parent_dinode, grp->bg_generation); dump_block_check(out, &grp->bg_check, grp); fprintf(out, "\t## %-15s %-6s %-6s %-6s %-6s %-6s\n", "Block#", "Total", "Used", "Free", "Contig", "Size"); } find_max_contig_free_bits(grp, &max_contig_free_bits); fprintf(out, "\t%-2d %-15"PRIu64" %-6u %-6u %-6u %-6u %-6u\n", index, (uint64_t)grp->bg_blkno, grp->bg_bits, (grp->bg_bits - grp->bg_free_bits_count), grp->bg_free_bits_count, max_contig_free_bits, grp->bg_size); if (ocfs2_gd_is_discontig(grp)) dump_extent_list(out, &grp->bg_list); return ; } void dump_group_extents(FILE *out, struct ocfs2_group_desc *grp) { fprintf(out, "\tGroup# %"PRIu64" Total: %u Used: %u Free: %u\n", (uint64_t)grp->bg_blkno, grp->bg_bits, (grp->bg_bits - grp->bg_free_bits_count), grp->bg_free_bits_count); print_contig_bits(out, grp); } /* * dump_dir_entry() * */ int dump_dir_entry(struct ocfs2_dir_entry *rec, uint64_t blocknr, int offset, int blocksize, char *buf, void *priv_data) { struct list_dir_opts *ls = (struct list_dir_opts *)priv_data; char tmp = rec->name[rec->name_len]; struct ocfs2_dinode *di; char perms[20]; char timestr[40]; errcode_t ret; rec->name[rec->name_len] = '\0'; if (!ls->long_opt) { fprintf(ls->out, "\t%-15"PRIu64" %-4u %-4u %-2u %s\n", (uint64_t)rec->inode, rec->rec_len, rec->name_len, rec->file_type, rec->name); } else { memset(ls->buf, 0, ls->fs->fs_blocksize); ret = ocfs2_read_inode(ls->fs, rec->inode, ls->buf); if (ret) return ret; di = (struct ocfs2_dinode *)ls->buf; inode_perms_to_str(di->i_mode, perms, sizeof(perms)); inode_time_to_str(di->i_mtime, timestr, sizeof(timestr)); fprintf(ls->out, "\t%-15"PRIu64" %10s %3u %5u %5u %15"PRIu64" %s %s\n", (uint64_t)rec->inode, perms, di->i_links_count, di->i_uid, di->i_gid, (uint64_t)di->i_size, timestr, rec->name); } rec->name[rec->name_len] = tmp; return 0; } /* * dump_dir_trailer() */ static void dump_dir_trailer(FILE *out, struct ocfs2_dir_block_trailer *trailer) { fprintf(out, "\tTrailer Block: %"PRIu64" Inode: %"PRIu64" Len: %u\n", (uint64_t)trailer->db_blkno, (uint64_t)trailer->db_parent_dinode, trailer->db_compat_rec_len); fprintf(out, "\tLargest Hole: %u Next Block: %"PRIu64"\n", trailer->db_free_rec_len, (uint64_t)trailer->db_free_next); dump_block_check(out, &trailer->db_check, trailer); } /* * dump_dir_block() * */ void dump_dir_block(FILE *out, char *buf) { struct ocfs2_dir_entry *dirent; int offset = 0; int end = ocfs2_dir_trailer_blk_off(gbls.fs); struct ocfs2_dir_block_trailer *trailer = ocfs2_dir_trailer_from_block(gbls.fs, buf); struct list_dir_opts ls_opts = { .fs = gbls.fs, .out = out, }; if (!strncmp((char *)trailer->db_signature, OCFS2_DIR_TRAILER_SIGNATURE, sizeof(trailer->db_signature))) dump_dir_trailer(out, trailer); else end = gbls.fs->fs_blocksize; fprintf(out, "\tEntries:\n"); while (offset < end) { dirent = (struct ocfs2_dir_entry *) (buf + offset); if (((offset + dirent->rec_len) > end) || (dirent->rec_len < 8) || ((dirent->rec_len % 4) != 0) || (((dirent->name_len & 0xFF)+8) > dirent->rec_len)) { /* Corrupted */ return; } dump_dir_entry(dirent, 0, offset, gbls.fs->fs_blocksize, NULL, &ls_opts); offset += dirent->rec_len; } } static void dump_dx_entry(FILE *out, int i, struct ocfs2_dx_entry *dx_entry) { fprintf(out, "\t %-2d (0x%08x 0x%08x) %-13"PRIu64"\n", i, dx_entry->dx_major_hash, dx_entry->dx_minor_hash, (uint64_t)dx_entry->dx_dirent_blk); } static void dump_dx_entry_list(FILE *out, struct ocfs2_dx_entry_list *dl_list, int traverse) { int i; fprintf(out, "\tCount: %u Num Used: %u\n", dl_list->de_count, dl_list->de_num_used); if (traverse) { fprintf(out, "\t## %-11s %-13s\n", "Hash (Major Minor)", "Dir Block#"); for (i = 0; i < dl_list->de_num_used; i++) dump_dx_entry(out, i, &dl_list->de_entries[i]); } } void dump_dx_root(FILE *out, struct ocfs2_dx_root_block *dr) { char tmp_str[30]; GString *flags = NULL; flags = g_string_new(NULL); if (dr->dr_flags & OCFS2_DX_FLAG_INLINE) g_string_append(flags, "Inline "); fprintf(out, "\tDir Index Root: %"PRIu64" FS Generation: %u (0x%x)\n", (uint64_t)dr->dr_blkno, dr->dr_fs_generation, dr->dr_fs_generation); fprintf(out, "\tClusters: %u Last Extblk: %"PRIu64" " "Dir Inode: %"PRIu64"\n", dr->dr_clusters, (uint64_t)dr->dr_last_eb_blk, (uint64_t)dr->dr_dir_blkno); if (dr->dr_suballoc_slot == (uint16_t)OCFS2_INVALID_SLOT) strcpy(tmp_str, "Invalid Slot"); else sprintf(tmp_str, "%d", dr->dr_suballoc_slot); fprintf(out, "\tSub Alloc Slot: %s Sub Alloc Bit: %u", tmp_str, dr->dr_suballoc_bit); if (dr->dr_suballoc_loc) fprintf(out, " SubAlloc Group: %"PRIu64"\n", (uint64_t)dr->dr_suballoc_loc); else fprintf(out, "\n"); fprintf(out, "\tFlags: (0x%x) %s\n", dr->dr_flags, flags->str); fprintf(out, "\tTotal Entry Count: %d\n", dr->dr_num_entries); dump_block_check(out, &dr->dr_check, dr); if (dr->dr_flags & OCFS2_DX_FLAG_INLINE) dump_dx_entry_list(out, &dr->dr_entries, 0); if (flags) g_string_free(flags, 1); } void dump_dx_leaf(FILE *out, struct ocfs2_dx_leaf *dx_leaf) { fprintf(out, "\tDir Index Leaf: %"PRIu64" FS Generation: %u (0x%x)\n", (uint64_t)dx_leaf->dl_blkno, dx_leaf->dl_fs_generation, dx_leaf->dl_fs_generation); dump_block_check(out, &dx_leaf->dl_check, dx_leaf); dump_dx_entry_list(out, &dx_leaf->dl_list, 1); } static int entries_iter(ocfs2_filesys *fs, struct ocfs2_dx_entry_list *entry_list, struct ocfs2_dx_root_block *dx_root, struct ocfs2_dx_leaf *dx_leaf, void *priv_data) { FILE *out = priv_data; if (dx_leaf) { dump_dx_leaf(out, dx_leaf); return 0; } /* Inline entries. Dump the list directly. */ dump_dx_entry_list(out, entry_list, 1); return 0; } void dump_dx_entries(FILE *out, struct ocfs2_dinode *inode) { struct ocfs2_dx_root_block *dx_root; uint64_t dx_blkno; char *buf = NULL; errcode_t ret = 0; if (ocfs2_malloc_block(gbls.fs->fs_io, &buf)) return; if (!(ocfs2_dir_indexed(inode))) return; dx_blkno = (uint64_t) inode->i_dx_root; ret = ocfs2_read_dx_root(gbls.fs, dx_blkno, buf); if (ret) return; dx_root = (struct ocfs2_dx_root_block *)buf; dump_dx_root(out, dx_root); ocfs2_dx_entries_iterate(gbls.fs, inode, 0, entries_iter, out); return; } static int dx_space_iter(ocfs2_filesys *fs, uint64_t blkno, struct ocfs2_dir_block_trailer *trailer, char *dirblock, void *priv_data) { FILE *out = priv_data; dump_dir_trailer(out, trailer); return 0; } void dump_dx_space(FILE *out, struct ocfs2_dinode *inode, struct ocfs2_dx_root_block *dx_root) { ocfs2_dx_frees_iterate(gbls.fs, inode, dx_root, 0, dx_space_iter, out); } /* * dump_jbd_header() * */ void dump_jbd_header(FILE *out, journal_header_t *header) { GString *jstr = NULL; jstr = g_string_new(NULL); get_journal_block_type(ntohl(header->h_blocktype), jstr); fprintf(out, "\tSeq: %u Type: %d (%s)\n", ntohl(header->h_sequence), ntohl(header->h_blocktype), jstr->str); if (jstr) g_string_free(jstr, 1); return; } /* * dump_jbd_superblock() * */ void dump_jbd_superblock(FILE *out, journal_superblock_t *jsb) { char buf[PATH_MAX]; uint32_t compat = ntohl(jsb->s_feature_compat); uint32_t incompat = ntohl(jsb->s_feature_incompat); uint32_t rocompat = ntohl(jsb->s_feature_ro_compat); int i; fprintf(out, "\tBlock 0: Journal Superblock\n"); dump_jbd_header(out, &(jsb->s_header)); fprintf(out, "\tBlocksize: %u Total Blocks: %u First Block: %u\n", ntohl(jsb->s_blocksize), ntohl(jsb->s_maxlen), ntohl(jsb->s_first)); fprintf(out, "\tFirst Commit ID: %u Start Log Blknum: %u\n", ntohl(jsb->s_sequence), ntohl(jsb->s_start)); fprintf(out, "\tError: %d\n", ntohl(jsb->s_errno)); get_journal_compat_flag(compat, buf, sizeof(buf)); fprintf(out, "\tFeature Compat: %u %s\n", compat, buf); get_journal_incompat_flag(incompat, buf, sizeof(buf)); fprintf(out, "\tFeature Incompat: %u %s\n", incompat, buf); get_journal_rocompat_flag(rocompat, buf, sizeof(buf)); fprintf(out, "\tFeature RO compat: %u %s\n", rocompat, buf); fprintf(out, "\tJournal UUID: "); for(i = 0; i < 16; i++) fprintf(out, "%02X", jsb->s_uuid[i]); fprintf(out, "\n"); fprintf(out, "\tFS Share Cnt: %u Dynamic Superblk Blknum: %u\n", ntohl(jsb->s_nr_users), ntohl(jsb->s_dynsuper)); fprintf(out, "\tPer Txn Block Limit Journal: %u Data: %u\n", ntohl(jsb->s_max_transaction), ntohl(jsb->s_max_trans_data)); fprintf(out, "\n"); return; } /* * dump_jbd_block() * */ void dump_jbd_block(FILE *out, journal_superblock_t *jsb, journal_header_t *header, uint64_t blknum) { int i; int j; int count = 0; GString *tagflg = NULL; /* for descriptors */ journal_block_tag_t *tag; journal_revoke_header_t *revoke; char *blk = (char *) header; uint32_t *blocknr; char *uuid; struct ocfs2_super_block *sb = OCFS2_RAW_SB(gbls.fs->fs_super); int tag_bytes = ocfs2_journal_tag_bytes(jsb); tagflg = g_string_new(NULL); fprintf(out, "\tBlock %"PRIu64": ", blknum); switch (ntohl(header->h_blocktype)) { case JBD2_DESCRIPTOR_BLOCK: fprintf(out, "Journal Descriptor\n"); dump_jbd_header(out, header); fprintf(out, "\t%3s %-15s %-s\n", "No.", "Blocknum", "Flags"); for (i = sizeof(journal_header_t); i < (1 << sb->s_blocksize_bits); i+=tag_bytes) { tag = (journal_block_tag_t *) &blk[i]; get_tag_flag(ntohl(tag->t_flags), tagflg); fprintf(out, "\t%2d. %-15"PRIu64" %-s\n", count, ocfs2_journal_tag_block(tag, tag_bytes), tagflg->str); g_string_truncate(tagflg, 0); if (tag->t_flags & htonl(JBD2_FLAG_LAST_TAG)) break; /* skip the uuid. */ if (!(tag->t_flags & htonl(JBD2_FLAG_SAME_UUID))) { uuid = &blk[i + tag_bytes]; fprintf(out, "\tUUID: "); for(j = 0; j < 16; j++) fprintf(out, "%02X",uuid[j]); fprintf(out, "\n"); i += 16; } count++; } break; case JBD2_COMMIT_BLOCK: fprintf(out, "Journal Commit Block\n"); dump_jbd_header(out, header); break; case JBD2_REVOKE_BLOCK: /*TODO*/ fprintf(out, "Journal Revoke Block\n"); dump_jbd_header(out, header); revoke = (journal_revoke_header_t *) blk; fprintf(out, "\tr_count:\t\t%d\n", ntohl(revoke->r_count)); count = (ntohl(revoke->r_count) - sizeof(journal_revoke_header_t)) / sizeof(uint32_t); blocknr = (uint32_t *) &blk[sizeof(journal_revoke_header_t)]; for(i = 0; i < count; i++) fprintf(out, "\trevoke[%d]:\t\t%u\n", i, ntohl(blocknr[i])); break; default: fprintf(out, "Unknown Block Type\n"); break; } fprintf(out, "\n"); if (tagflg) g_string_free(tagflg, 1); return; } /* * dump_jbd_metadata() * */ void dump_jbd_metadata(FILE *out, enum ocfs2_block_type type, char *buf, uint64_t blknum) { struct ocfs2_dir_block_trailer *trailer; struct ocfs2_xattr_block *xb; struct ocfs2_xattr_header *xh; struct ocfs2_refcount_block *rb; struct ocfs2_dx_root_block *dx_root; struct ocfs2_dx_leaf *dx_leaf; struct ocfs2_dinode *di; struct ocfs2_extent_block *eb; fprintf(out, "\tBlock %"PRIu64": ", blknum); switch (type) { case OCFS2_BLOCK_INODE: case OCFS2_BLOCK_SUPERBLOCK: fprintf(out, "Inode\n"); di = (struct ocfs2_dinode *)buf; ocfs2_swap_inode_to_cpu(gbls.fs, di); dump_inode(out, di); if (di->i_flags & OCFS2_LOCAL_ALLOC_FL) dump_local_alloc(out, &(di->id2.i_lab)); else if (di->i_flags & OCFS2_CHAIN_FL) dump_chain_list(out, &(di->id2.i_chain)); else if (S_ISLNK(di->i_mode) && !di->i_clusters) dump_fast_symlink(out, (char *)di->id2.i_symlink); else if (di->i_flags & OCFS2_DEALLOC_FL) dump_truncate_log(out, &(di->id2.i_dealloc)); else if (!(di->i_dyn_features & OCFS2_INLINE_DATA_FL)) dump_extent_list(out, &(di->id2.i_list)); fprintf(out, "\n"); break; case OCFS2_BLOCK_EXTENT_BLOCK: fprintf(out, "Extent\n"); eb = (struct ocfs2_extent_block *)buf; ocfs2_swap_extent_block_to_cpu(gbls.fs, eb); dump_extent_block(out, eb); dump_extent_list(out, &(eb->h_list)); fprintf(out, "\n"); break; case OCFS2_BLOCK_GROUP_DESCRIPTOR: fprintf(out, "Group\n"); ocfs2_swap_group_desc_to_cpu(gbls.fs, (struct ocfs2_group_desc *)buf); dump_group_descriptor(out, (struct ocfs2_group_desc *)buf, 0); fprintf(out, "\n"); break; case OCFS2_BLOCK_DIR_BLOCK: fprintf(out, "Dirblock\n"); /* * We know there's a trailer, because that's how it * was detected */ ocfs2_swap_dir_entries_to_cpu(buf, ocfs2_dir_trailer_blk_off(gbls.fs)); trailer = ocfs2_dir_trailer_from_block(gbls.fs, buf); ocfs2_swap_dir_trailer(trailer); dump_dir_block(out, buf); fprintf(out, "\n"); break; case OCFS2_BLOCK_XATTR: fprintf(out, "Xattr\n"); xb = (struct ocfs2_xattr_block *)buf; ocfs2_swap_xattr_block_to_cpu(gbls.fs, xb); if (!(xb->xb_flags & OCFS2_XATTR_INDEXED)) { xh = &xb->xb_attrs.xb_header; dump_xattr(out, xh); } fprintf(out, "\n"); break; case OCFS2_BLOCK_REFCOUNT: fprintf(out, "Refcount\n"); rb = (struct ocfs2_refcount_block *)buf; ocfs2_swap_refcount_block_to_cpu(gbls.fs, rb); dump_refcount_block(out, rb); fprintf(out, "\n"); break; case OCFS2_BLOCK_DXROOT: fprintf(out, "DxRoot\n"); dx_root = (struct ocfs2_dx_root_block *)buf; ocfs2_swap_dx_root_to_cpu(gbls.fs, dx_root); dump_dx_root(out, dx_root); fprintf(out, "\n"); break; case OCFS2_BLOCK_DXLEAF: fprintf(out, "DxLeaf\n"); dx_leaf = (struct ocfs2_dx_leaf *)buf; ocfs2_swap_dx_leaf_to_cpu(dx_leaf); dump_dx_leaf(out, dx_leaf); fprintf(out, "\n"); break; default: fprintf(out, "TODO\n\n"); break; } return ; } /* * dump_jbd_unknown() * */ void dump_jbd_unknown(FILE *out, uint64_t start, uint64_t end) { if (start == end - 1) fprintf(out, "\tBlock %"PRIu64": ", start); else fprintf(out, "\tBlock %"PRIu64" to %"PRIu64": ", start, (end - 1)); fprintf(out, "Unknown -- Probably Data\n\n"); return ; } /* * dump_slots() * */ void dump_slots(FILE *out, struct ocfs2_slot_map_extended *se, struct ocfs2_slot_map *sm, int num_slots) { int i; unsigned int node_num; fprintf(out, "\t%5s %5s\n", "Slot#", "Node#"); for (i = 0; i < num_slots; ++i) { if (se) { if (!se->se_slots[i].es_valid) continue; node_num = se->se_slots[i].es_node_num; } else { if (sm->sm_slots[i] == (uint16_t)OCFS2_INVALID_SLOT) continue; node_num = sm->sm_slots[i]; } fprintf(out, "\t%5d %5u\n", i, node_num); } } void dump_hb(FILE *out, char *buf, uint32_t len) { uint32_t i; struct o2hb_disk_heartbeat_block *hb; fprintf(out, "\t%4s: %4s %16s %16s %8s %6s\n", "node", "node", "seq", "generation", "checksum", "deadms"); for (i = 0; i < 255 && ((i + 1) * 512 < len); ++i) { hb = (struct o2hb_disk_heartbeat_block *)(buf + (i * 512)); ocfs2_swap_disk_heartbeat_block(hb); if (hb->hb_seq) fprintf(out, "\t%4u: %4u %016"PRIx64" %016"PRIx64" " "%08"PRIx32" %6u\n", i, hb->hb_node, (uint64_t)hb->hb_seq, (uint64_t)hb->hb_generation, hb->hb_cksum, hb->hb_dead_ms); } return ; } void dump_inode_path(FILE *out, uint64_t blkno, char *path) { fprintf(out, "\t%"PRIu64"\t%s\n", blkno, path); } /* * dump_logical_blkno() * */ void dump_logical_blkno(FILE *out, uint64_t blkno) { fprintf(out, "\t%"PRIu64"\n", blkno); } /* * dump_icheck() * */ void dump_icheck(FILE *out, int hdr, uint64_t blkno, uint64_t inode, int validoffset, uint64_t offset, int status) { char inostr[30] = " "; char offstr[30] = " "; if (hdr) fprintf(out, "\t%-15s %-15s %-15s\n", "Block#", "Inode", "Block Offset"); switch (status) { case 1: snprintf(inostr, sizeof(inostr), "%-15"PRIu64, inode); if (validoffset) sprintf(offstr, "%-15"PRIu64, offset); break; case 2: snprintf(inostr, sizeof(offstr), "Unused"); break; default: snprintf(inostr, sizeof(offstr), "Unknown"); break; } fprintf(out, "\t%-15"PRIu64" %-15s %-15s\n", blkno, inostr, offstr); } void dump_xattr(FILE *out, struct ocfs2_xattr_header *xh) { int i; fprintf(out, "\t### %-4s %-6s %-13s %s\n", "Type", "Inline", "Name Length", "Value Length"); for (i = 0 ; i < xh->xh_count; i++) { struct ocfs2_xattr_entry *xe = &xh->xh_entries[i]; fprintf(out, "\t#%-2d %-4u %-6u %-13u %-13"PRIu64"\n", i, ocfs2_xattr_get_type(xe), ocfs2_xattr_is_local(xe) ? 1 : 0, xe->xe_name_len, (uint64_t)xe->xe_value_size); if (!ocfs2_xattr_is_local(xe)) { struct ocfs2_xattr_value_root *xv = (struct ocfs2_xattr_value_root *) ((void *)xh + xe->xe_name_offset + OCFS2_XATTR_SIZE(xe->xe_name_len)); struct ocfs2_extent_list *el = &xv->xr_list; dump_extent_list(out, el); } } return; } static errcode_t dump_xattr_buckets(FILE *out, ocfs2_filesys *fs, uint64_t blkno, uint32_t clusters, uint64_t *xattrs_bucket, int verbose) { int i; errcode_t ret = 0; char *bucket = NULL; struct ocfs2_xattr_header *xh; int blk_per_bucket = ocfs2_blocks_per_xattr_bucket(fs); uint32_t bpc = ocfs2_xattr_buckets_per_cluster(fs); uint32_t num_buckets = clusters * bpc; ret = ocfs2_malloc_blocks(fs->fs_io, blk_per_bucket, &bucket); if (ret) goto out; fprintf(out, "\tExtended Attributes extent record start at #%"PRIu64 " Has clusters: %u", blkno, clusters); for (i = 0; i < num_buckets; i++, blkno += blk_per_bucket) { ret = ocfs2_read_xattr_bucket(fs, blkno, bucket); if (ret) goto out; xh = (struct ocfs2_xattr_header *)bucket; /* * The real bucket num in this series of blocks is stored * in the 1st bucket. */ if (i == 0) { num_buckets = xh->xh_num_buckets; fprintf(out, " Has buckets: %d\n", num_buckets); } fprintf(out, "\t\tExtended Attributes in bucket #%d: %u\n", i, xh->xh_count); if (verbose) dump_xattr(out, xh); *xattrs_bucket += xh->xh_count; } out: if (bucket) ocfs2_free(&bucket); return ret; } static errcode_t dump_xattr_index_block(FILE *out, ocfs2_filesys *fs, struct ocfs2_dinode *di, struct ocfs2_xattr_block *xb, uint64_t *xattrs_bucket, int verbose) { struct ocfs2_extent_list *el = &xb->xb_attrs.xb_root.xt_list; errcode_t ret = 0; uint32_t name_hash = UINT_MAX, e_cpos = 0, num_clusters = 0; uint64_t p_blkno = 0; if (!el->l_next_free_rec) return 0; fprintf(out, "\tExtended Attributes extent tree in index block #%" PRIu64" Depth: %d Records: %d\n", (uint64_t)di->i_xattr_loc, el->l_tree_depth, el->l_next_free_rec); if (verbose) dump_extent_list(out, el); while (name_hash > 0) { ret = ocfs2_xattr_get_rec(fs, xb, name_hash, &p_blkno, &e_cpos, &num_clusters); if (ret) goto out; ret = dump_xattr_buckets(out, fs, p_blkno, num_clusters, xattrs_bucket, verbose); if (ret) goto out; if (e_cpos == 0) break; name_hash = e_cpos - 1; } out: return ret; } /* * dump_xattr_block() * */ errcode_t dump_xattr_block(FILE *out, ocfs2_filesys *fs, struct ocfs2_dinode *in, uint32_t *xattrs_block, uint64_t *xattrs_bucket, int verbose) { errcode_t ret; char *blk = NULL; struct ocfs2_xattr_block *xb = NULL; ret = ocfs2_malloc_block(fs->fs_io, &blk); if (ret) goto out; ret = ocfs2_read_xattr_block(fs, in->i_xattr_loc, blk); if (ret) goto out; xb = (struct ocfs2_xattr_block *)blk; if (!(xb->xb_flags & OCFS2_XATTR_INDEXED)) { struct ocfs2_xattr_header *xh = &xb->xb_attrs.xb_header; *xattrs_block = xh->xh_count; fprintf(out, "\tExtended Attributes in block #%"PRIu64": %u\n", (uint64_t)in->i_xattr_loc, *xattrs_block); if (verbose) dump_xattr(out, xh); } else { ret = dump_xattr_index_block(out, fs, in, xb, xattrs_bucket, verbose); } out: if (blk) ocfs2_free(&blk); return ret; } /* * dump_xattr_ibody() * */ uint32_t dump_xattr_ibody(FILE *out, ocfs2_filesys *fs, struct ocfs2_dinode *in, int verbose) { if (in->i_dyn_features & OCFS2_INLINE_XATTR_FL) { struct ocfs2_xattr_header *xh = (struct ocfs2_xattr_header *) ((void *)in + fs->fs_blocksize - in->i_xattr_inline_size); fprintf(out, "\tExtended Attributes inline: %u\n", xh->xh_count); if (verbose) dump_xattr(out, xh); return xh->xh_count; } else { fprintf(out, "\tExtended Attributes inline: 0\n"); return 0; } } void dump_frag(FILE *out, uint64_t ino, uint32_t clusters, uint32_t extents) { float frag_level = 0; int clusters_per_mb = ocfs2_clusters_in_bytes(gbls.fs, OCFS2_MAX_CLUSTERSIZE); if (clusters > 1 && extents) { float e = extents, c = clusters; frag_level = 100 * (e / c); } fprintf(out, "Inode: %"PRIu64"\t%% fragmented: %.2f\tclusters:" " %u\textents: %u\tscore: %.0f\n", ino, frag_level, clusters, extents, frag_level * clusters_per_mb); } void dump_refcount_block(FILE *out, struct ocfs2_refcount_block *rb) { char flags[PATH_MAX]; fprintf(out, "\tSubAlloc Bit: %u SubAlloc Slot: %u", rb->rf_suballoc_bit, rb->rf_suballoc_slot); if (rb->rf_suballoc_loc) fprintf(out, " SubAlloc Group: %"PRIu64"\n", (uint64_t)rb->rf_suballoc_loc); else fprintf(out, "\n"); fprintf(out, "\tFS Generation: %u (0x%x)\n", rb->rf_fs_generation, rb->rf_fs_generation); fprintf(out, "\tBlknum: %"PRIu64" Parent: %"PRIu64"\n", (uint64_t)rb->rf_blkno, (uint64_t)rb->rf_parent); fprintf(out, "\tCpos: %"PRIu64" Last Leaf block: %"PRIu64"\n", (uint64_t)rb->rf_cpos, (uint64_t)rb->rf_last_eb_blk); fprintf(out, "\tReftree Count: %u Ref clusters: %u\n", rb->rf_count, rb->rf_clusters); flags[0] = '\0'; if (ocfs2_snprint_refcount_flags(flags, PATH_MAX, rb->rf_flags)) flags[0] = '\0'; fprintf(out, "\tFlags: 0x%x %s\n", rb->rf_flags, flags); dump_block_check(out, &rb->rf_check, rb); return; } void dump_refcount_records(FILE *out, struct ocfs2_refcount_block *rb) { int i; struct ocfs2_refcount_list *rl = &rb->rf_records; fprintf(out, "\tRefcount records: %u Used: %u\n", rl->rl_count, rl->rl_used); fprintf(out, "\t### %-20s %-12s %-12s\n", "Physical cpos", "Clusters", "Reference count"); for (i = 0; i < rl->rl_used; i++) { fprintf(out, "\t%-3d %-20"PRIu64" %-12"PRIu32" %"PRIu32"\n", i, (uint64_t)rl->rl_recs[i].r_cpos, rl->rl_recs[i].r_clusters, rl->rl_recs[i].r_refcount); } } ocfs2-tools-ocfs2-tools-1.8.6/debugfs.ocfs2/dump_dlm_locks.c000066400000000000000000000257351347147137200236670ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * dump_dlm_locks.c * * Interface with the kernel and dump current dlm locking state * * Copyright (C) 2008 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License, version 2, as published by the Free Software Foundation. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * */ #define _XOPEN_SOURCE 600 /* Triggers XOPEN2K in features.h */ #define _LARGEFILE64_SOURCE #include "main.h" #include "ocfs2/byteorder.h" #include "ocfs2_internals.h" static void dump_raw_lvb(const char *lvb, FILE *out) { int i, j; fprintf(out, "Raw LVB:\t"); for (i = 0, j = 0; i < DLM_LVB_LEN; i++, j += 2) { fprintf(out, "%c%c ", lvb[j], lvb[j+1]); if (!((i+1) % 16) && i != (DLM_LVB_LEN-1)) fprintf(out, "\n\t\t"); } fprintf(out, "\n"); } static void get_lock_level(int level, char *str, int len) { switch (level) { case 0: strncpy(str, "NL", len); break; case 3: strncpy(str, "PR", len); break; case 5: strncpy(str, "EX", len); break; default: snprintf(str, len, "%d", level); break; } } static void dump_lock(struct lock *lock, char *queue, FILE *out) { GString *action = NULL; char *ast, *bast, level[5], conv[5]; action = g_string_new(NULL); get_lock_level(lock->type, level, sizeof(level)); get_lock_level(lock->convert_type, conv, sizeof(conv)); ast = (lock->ast_list) ? "Yes" : "No"; bast = (lock->bast_list) ? "Yes" : "No"; if (lock->ast_pending) g_string_append(action, "Ast "); if (lock->bast_pending) g_string_append(action, "Bast "); if (lock->convert_pending) g_string_append(action, "Convert "); if (lock->lock_pending) g_string_append(action, "Lock "); if (lock->cancel_pending) g_string_append(action, "Cancel "); if (lock->unlock_pending) g_string_append(action, "Unlock "); if (!action->len) g_string_append(action, "None"); fprintf(out, " %-10s %-4d %-5s %-4s %-15s %-4d %-3s %-4s %s\n", queue, lock->node, level, conv, lock->cookie, lock->refs, ast, bast, action->str); g_string_free(action, 1); } static void get_lockres_state(__u16 state, GString *str) { if (state & DLM_LOCK_RES_UNINITED) g_string_append(str, "Uninitialized "); if (state & DLM_LOCK_RES_RECOVERING) g_string_append(str, "Recovering "); if (state & DLM_LOCK_RES_READY) g_string_append(str, "Ready "); if (state & DLM_LOCK_RES_DIRTY) g_string_append(str, "Dirty "); if (state & DLM_LOCK_RES_IN_PROGRESS) g_string_append(str, "InProgress "); if (state & DLM_LOCK_RES_MIGRATING) g_string_append(str, "Migrating "); if (state & DLM_LOCK_RES_DROPPING_REF) g_string_append(str, "DroppingRef "); if (state & DLM_LOCK_RES_BLOCK_DIRTY) g_string_append(str, "BlockDirty "); if (state & DLM_LOCK_RES_SETREF_INPROG) g_string_append(str, "SetRefInProg "); if (!str->len) g_string_append(str, ""); } static void dump_lockres(char *name, struct lockres *res, FILE *out) { struct lock *lock; struct list_head *iter; GString *lists = NULL; GString *state = NULL; int numlocks = 0; state = g_string_new(NULL); lists = g_string_new(NULL); if (res->purge) g_string_append(lists, "Purge "); if (res->dirty) g_string_append(lists, "Dirty "); if (res->recovering) g_string_append(lists, "Recovering "); if (!lists->len) g_string_append(lists, "None"); get_lockres_state(res->state, state); if (!list_empty(&res->granted)) list_for_each(iter, &res->granted) ++numlocks; if (!list_empty(&res->converting)) list_for_each(iter, &res->converting) ++numlocks; if (!list_empty(&res->blocked)) list_for_each(iter, &res->blocked) ++numlocks; /* Lockres: xx Owner: xx State: 0x1 xxx */ fprintf(out, "Lockres: %-32s Owner: %-3d State: 0x%X %s\n", name, res->owner, res->state, state->str); /* Last Used: x ASTs Reserved: x Inflight: x Migration Pending: x */ fprintf(out, "Last Used: %-5d ASTs Reserved: %-3d Inflight: %-3d " "Migration Pending: %s\n", res->last_used, res->asts_reserved, res->inflight_locks, (res->migration_pending ? "Yes" : "No")); /* Refs: xx Locks: xx On Lists: xx */ fprintf(out, "Refs: %-3d Locks: %-3d On Lists: %s\n", res->refs, numlocks, lists->str); /* Reference Map: xx, xx, xx, xx, xx */ fprintf(out, "Reference Map: "); if (res->refmap) fprintf(out, "%s", res->refmap); fprintf(out, "\n"); /* Raw LVB: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 */ if (res->lvb) dump_raw_lvb(res->lvb, out); /* Lock-Queue Node Level Conv Cookie Refs AST BAST Pendi... */ fprintf(out, " %-10s %-4s %-5s %-4s %-15s %-4s %-3s %-4s %s\n", "Lock-Queue", "Node", "Level", "Conv", "Cookie", "Refs", "AST", "BAST", "Pending-Action"); /* Granted Queue: */ if (!list_empty(&res->granted)) { list_for_each(iter, &res->granted) { lock = list_entry(iter, struct lock, list); dump_lock(lock, "Granted", out); } } /* Converting Queue: */ if (!list_empty(&res->converting)) { list_for_each(iter, &res->converting) { lock = list_entry(iter, struct lock, list); dump_lock(lock, "Converting", out); } } /* Blocked Queue: */ if (!list_empty(&res->blocked)) { list_for_each(iter, &res->blocked) { lock = list_entry(iter, struct lock, list); dump_lock(lock, "Blocked", out); } } fprintf(out, "\n"); g_string_free(state, 1); g_string_free(lists, 1); } static int read_lvbx(char *line, struct lockres *res) { res->lvb = strdup(line); return !!res->lvb; } static int read_rmap(char *line, struct lockres *res) { int i; res->refmap = strdup(line); if (!res->refmap) return 0; i = strlen(res->refmap); if (i) res->refmap[i - 1] = '\0'; return 1; } #define CURRENT_LOCK_PROTO 1 static int read_lock(char *line, struct lockres *res) { char data[512]; int version; struct lock *lock = NULL; __u8 queue; int ret; __u32 ck1, ck2; lock = calloc(1, sizeof(struct lock)); if (!lock) goto bail; INIT_LIST_HEAD(&lock->list); /* read version */ ret = sscanf(line, "%d,%s\n", &version, data); if (ret != 2) goto bail; if (version > CURRENT_LOCK_PROTO) { fprintf(stdout, "Lock string proto %u found, but %u is the " "highest I understand.\n", version, CURRENT_LOCK_PROTO); goto bail; } /* Version 1 */ if (version == 1) { ret = sscanf(data, "%hhu,%hhd,%hhd,%hhu,%u:%u,%hhu,%hhu,%hhu," "%hhu,%hhu,%hhu,%hhu,%hhu,%u", &queue, &lock->type, &lock->convert_type, &lock->node, &ck1, &ck2, &lock->ast_list, &lock->bast_list, &lock->ast_pending, &lock->bast_pending, &lock->convert_pending, &lock->lock_pending, &lock->cancel_pending, &lock->unlock_pending, &lock->refs); if (ret != 15) goto bail; snprintf(lock->cookie, sizeof(lock->cookie) - 1, "%u:%u", ck1, ck2); } switch (queue) { case GRANTED: list_add_tail(&lock->list, &res->granted); break; case CONVERTING: list_add_tail(&lock->list, &res->converting); break; case BLOCKED: list_add_tail(&lock->list, &res->blocked); break; default: free(lock); return 0; } return 1; bail: if (lock) free(lock); return 0; } #define CURRENT_LRES_PROTO 1 static int read_lres(char *line, struct lockres *res) { char data[512]; int version; int ret; /* read version */ ret = sscanf(line, "%d,%s\n", &version, data); if (ret != 2) return 0; if (version > CURRENT_LRES_PROTO) { fprintf(stdout, "Lockres string proto %u found, but %u is the " "highest I understand.\n", version, CURRENT_LRES_PROTO); return 0; } /* Version 1 */ if (version == 1) { ret = sscanf(data, "%hhu,%hu,%u,%hhu,%hhu,%hhu,%u,%hhu,%u,%u", &res->owner, &res->state, &res->last_used, &res->purge, &res->dirty, &res->recovering, &res->inflight_locks, &res->migration_pending, &res->asts_reserved, &res->refs); if (ret != 10) return 0; } return 1; } static void init_lockres(struct lockres *res) { memset(res, 0, sizeof(struct lockres)); INIT_LIST_HEAD(&res->granted); INIT_LIST_HEAD(&res->converting); INIT_LIST_HEAD(&res->blocked); } static void clean_lockres(struct lockres *res) { struct list_head *iter, *iter2; struct lock *lock; if (res->lvb) free(res->lvb); if (res->refmap) free(res->refmap); if (!list_empty(&res->granted)) { list_for_each_safe(iter, iter2, &res->granted) { lock = list_entry(iter, struct lock, list); list_del(iter); free(lock); } } if (!list_empty(&res->converting)) { list_for_each_safe(iter, iter2, &res->converting) { lock = list_entry(iter, struct lock, list); list_del(iter); free(lock); } } if (!list_empty(&res->blocked)) { list_for_each_safe(iter, iter2, &res->blocked) { lock = list_entry(iter, struct lock, list); list_del(iter); free(lock); } } init_lockres(res); } static void read_lockres(FILE *file, struct lockres *res, int lvb) { char line[512]; while (fgets(line, sizeof(line), file)) { if (line[0] == '\n') break; if (!strncmp(line, "LRES:", 5)) read_lres(line + 5, res); else if (!strncmp(line, "RMAP:", 5)) read_rmap(line + 5, res); else if (!strncmp(line, "LOCK:", 5)) read_lock(line + 5, res); else if (!strncmp(line, "LVBX:", 5)) { if (lvb) read_lvbx(line + 5, res); } } } static int get_next_dlm_lockname(FILE *file, char *name, int len) { char line[512]; while (fgets(line, sizeof(line), file)) { if (strncmp(line, "NAME:", 5)) continue; sscanf(line + 5, "%s\n", name); return 1; } return 0; } void dump_dlm_locks(char *uuid, FILE *out, char *path, int dump_lvbs, struct list_head *locklist) { errcode_t ret; char debugfs_path[PATH_MAX]; FILE *file; char name[OCFS2_LOCK_ID_MAX_LEN]; struct lockres res; int show_all_locks = 0; if (path == NULL) { ret = get_debugfs_path(debugfs_path, sizeof(debugfs_path)); if (ret) { fprintf(stderr, "Could not locate debugfs file system. " "Perhaps it is not mounted?\n"); return; } ret = open_debugfs_file(debugfs_path, "o2dlm", uuid, "locking_state", &file); if (ret) { fprintf(stderr, "Could not open debug state for \"%s\".\n" "Perhaps that OCFS2 file system is not mounted?\n", uuid); return; } } else { file = fopen(path, "r"); if (!file) { fprintf(stderr, "Could not open file at \"%s\"\n", path); return; } } show_all_locks = list_empty(locklist); init_lockres(&res); while (get_next_dlm_lockname(file, name, sizeof(name))) { if (show_all_locks || del_from_stringlist(name, locklist)) { read_lockres(file, &res, dump_lvbs); dump_lockres(name, &res, out); clean_lockres(&res); } if (!show_all_locks && list_empty(locklist)) break; } fclose(file); } ocfs2-tools-ocfs2-tools-1.8.6/debugfs.ocfs2/dump_fs_locks.c000066400000000000000000000243601347147137200235140ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * dump_fs_locks.c * * Interface with the kernel and dump current fs locking state * * Copyright (C) 2005, 2008 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License, version 2, as published by the Free Software Foundation. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * */ #define _XOPEN_SOURCE 600 /* Triggers XOPEN2K in features.h */ #define _LARGEFILE64_SOURCE #include #include #include #include #include #include #include #include #include "main.h" #include "ocfs2/byteorder.h" #include "ocfs2_internals.h" static char *level_str(int level) { char *s; switch (level) { case LKM_IVMODE: s = "Invalid"; break; case LKM_NLMODE: s = "No Lock"; break; case LKM_CRMODE: s = "Concurrent Read"; break; case LKM_CWMODE: s = "Concurrent Write"; break; case LKM_PRMODE: s = "Protected Read"; break; case LKM_PWMODE: s = "Protected Write"; break; case LKM_EXMODE: s = "Exclusive"; break; default: s = "Unknown"; } return s; } static void print_flags(unsigned long flags, FILE *out) { if (flags & OCFS2_LOCK_INITIALIZED ) fprintf(out, " Initialized"); if (flags & OCFS2_LOCK_ATTACHED) fprintf(out, " Attached"); if (flags & OCFS2_LOCK_BUSY) fprintf(out, " Busy"); if (flags & OCFS2_LOCK_BLOCKED) fprintf(out, " Blocked"); if (flags & OCFS2_LOCK_LOCAL) fprintf(out, " Local"); if (flags & OCFS2_LOCK_NEEDS_REFRESH) fprintf(out, " Needs Refresh"); if (flags & OCFS2_LOCK_REFRESHING) fprintf(out, " Refreshing"); if (flags & OCFS2_LOCK_FREEING) fprintf(out, " Freeing"); if (flags & OCFS2_LOCK_QUEUED) fprintf(out, " Queued"); } static char *action_str(unsigned int action) { char *s; switch (action) { case OCFS2_AST_INVALID: s = "None"; break; case OCFS2_AST_ATTACH: s = "Attach"; break; case OCFS2_AST_CONVERT: s = "Convert"; break; case OCFS2_AST_DOWNCONVERT: s = "Downconvert"; break; default: s = "Unknown"; } return s; } static char *unlock_action_str(unsigned int unlock_action) { char *s; switch (unlock_action) { case OCFS2_UNLOCK_INVALID: s = "None"; break; case OCFS2_UNLOCK_CANCEL_CONVERT: s = "Cancel Convert"; break; case OCFS2_UNLOCK_DROP_LOCK: s = "Drop Lock"; break; default: s = "Unknown"; } return s; } static void dump_raw_lvb(const char *lvb, FILE *out) { int i; fprintf(out, "Raw LVB:\t"); for(i = 0; i < DLM_LVB_LEN; i++) { fprintf(out, "%02hhx ", lvb[i]); if (!((i+1) % 16) && i != (DLM_LVB_LEN-1)) fprintf(out, "\n\t\t"); } fprintf(out, "\n"); } static void dump_meta_lvb_v1(struct ocfs2_meta_lvb_v1 *lvb, FILE *out) { fprintf(out, "Decoded LVB:\t"); fprintf(out, "Version: %u " "Clusters: %u " "Size: %"PRIu64"\n", be32_to_cpu(lvb->lvb_version), be32_to_cpu(lvb->lvb_iclusters), be64_to_cpu(lvb->lvb_isize)); fprintf(out, "\t\tMode: 0%o " "UID: %u " "GID: %u " "Nlink: %u\n", be16_to_cpu(lvb->lvb_imode), be32_to_cpu(lvb->lvb_iuid), be32_to_cpu(lvb->lvb_igid), be16_to_cpu(lvb->lvb_inlink)); fprintf(out, "\t\tAtime_packed: 0x%"PRIx64"\n" "\t\tCtime_packed: 0x%"PRIx64"\n" "\t\tMtime_packed: 0x%"PRIx64"\n", be64_to_cpu(lvb->lvb_iatime_packed), be64_to_cpu(lvb->lvb_ictime_packed), be64_to_cpu(lvb->lvb_imtime_packed)); } static void dump_meta_lvb_v2(struct ocfs2_meta_lvb_v2 *lvb, FILE *out) { fprintf(out, "Decoded LVB:\t"); fprintf(out, "Version: %u " "Clusters: %u " "Size: %"PRIu64"\n", be32_to_cpu(lvb->lvb_version), be32_to_cpu(lvb->lvb_iclusters), be64_to_cpu(lvb->lvb_isize)); fprintf(out, "\t\tMode: 0%o " "UID: %u " "GID: %u " "Nlink: %u\n", be16_to_cpu(lvb->lvb_imode), be32_to_cpu(lvb->lvb_iuid), be32_to_cpu(lvb->lvb_igid), be16_to_cpu(lvb->lvb_inlink)); fprintf(out, "\t\tAtime_packed: 0x%"PRIx64"\n" "\t\tCtime_packed: 0x%"PRIx64"\n" "\t\tMtime_packed: 0x%"PRIx64"\n", be64_to_cpu(lvb->lvb_iatime_packed), be64_to_cpu(lvb->lvb_ictime_packed), be64_to_cpu(lvb->lvb_imtime_packed)); } static void dump_meta_lvb(const char *raw_lvb, FILE *out) { struct ocfs2_meta_lvb_v1 *lvb1 = (struct ocfs2_meta_lvb_v1 *) raw_lvb; struct ocfs2_meta_lvb_v2 *lvb2 = (struct ocfs2_meta_lvb_v2 *) raw_lvb; if (!lvb1->lvb_old_seq && be32_to_cpu(lvb1->lvb_version) == 1) dump_meta_lvb_v1(lvb1, out); else if (be32_to_cpu(lvb2->lvb_version) == 2) dump_meta_lvb_v2(lvb2, out); } /* 0 = eof, > 0 = success, < 0 = error */ static int dump_version_two_and_three(FILE *file, FILE *out, int v3) { unsigned long long num_prmode, num_exmode; unsigned int num_prmode_failed, num_exmode_failed; unsigned long long total_prmode, total_exmode; unsigned long long avg_prmode = 0, avg_exmode = 0; unsigned int max_prmode, max_exmode, num_refresh; int ret; #define NSEC_PER_USEC 1000 ret = fscanf(file, "%llu\t" "%llu\t" "%u\t" "%u\t" "%llu\t" "%llu\t" "%u\t" "%u\t" "%u", &num_prmode, &num_exmode, &num_prmode_failed, &num_exmode_failed, &total_prmode, &total_exmode, &max_prmode, &max_exmode, &num_refresh); if (ret != 9) { ret = -EINVAL; goto out; } if (!v3) { max_prmode /= NSEC_PER_USEC; max_exmode /= NSEC_PER_USEC; } if (num_prmode) avg_prmode = total_prmode/num_prmode; if (num_exmode) avg_exmode = total_exmode/num_exmode; fprintf(out, "PR > Gets: %llu Fails: %u Waits Total: %lluus " "Max: %uus Avg: %lluns\n", num_prmode, num_prmode_failed, total_prmode/NSEC_PER_USEC, max_prmode, avg_prmode); fprintf(out, "EX > Gets: %llu Fails: %u Waits Total: %lluus " "Max: %uus Avg: %lluns\n", num_exmode, num_exmode_failed, total_exmode/NSEC_PER_USEC, max_exmode, avg_exmode); fprintf(out, "Disk Refreshes: %u\n", num_refresh); ret = 1; out: return ret; } /* 0 = eof, > 0 = success, < 0 = error */ static int dump_version_one(FILE *file, FILE *out, int lvbs, int only_busy, struct list_head *locklist, int *skipped, unsigned int version) { char id[OCFS2_LOCK_ID_MAX_LEN + 1]; char lvb[DLM_LVB_LEN]; int ret, i, level, requested, blocking; unsigned long flags; unsigned int action, unlock_action, ro, ex, dummy; const char *format; *skipped = 1; ret = fscanf(file, "%s\t" "%d\t" "0x%lx\t" "0x%x\t" "0x%x\t" "%u\t" "%u\t" "%d\t" "%d\t", id, &level, &flags, &action, &unlock_action, &ro, &ex, &requested, &blocking); if (ret != 9) { ret = -EINVAL; goto out; } format = "0x%x\t"; for (i = 0; i < DLM_LVB_LEN; i++) { /* This is the known last part of the record. If we * include the field delimiting '\t' then fscanf will * also catch the record delimiting '\n' character, * which we want to save for the caller to find. */ if ((i == (DLM_LVB_LEN - 1)) && (version < 2)) format = "0x%x"; ret = fscanf(file, format, &dummy); if (ret != 1) { ret = -EINVAL; goto out; } lvb[i] = (char) dummy; } if (!list_empty(locklist)) { if (!del_from_stringlist(id, locklist)) { ret = 1; goto out; } } if (only_busy) { if (!(flags & OCFS2_LOCK_BUSY)) { ret = 1; goto out; } } fprintf(out, "Lockres: %s Mode: %s\nFlags:", id, level_str(level)); print_flags(flags, out); fprintf(out, "\nRO Holders: %u EX Holders: %u\n", ro, ex); fprintf(out, "Pending Action: %s Pending Unlock Action: %s\n", action_str(action), unlock_action_str(unlock_action)); fprintf(out, "Requested Mode: %s Blocking Mode: %s\n", level_str(requested), level_str(blocking)); if (lvbs) { dump_raw_lvb(lvb, out); if (id[0] == 'M') dump_meta_lvb(lvb, out); } *skipped = 0; ret = 1; out: return ret; } static int end_line(FILE *f) { int ret; do { ret = fgetc(f); if (ret == EOF) return 1; } while (ret != '\n'); return 0; } #define CURRENT_PROTO 3 /* returns 0 on error or end of file */ static int dump_one_lockres(FILE *file, FILE *out, int lvbs, int only_busy, struct list_head *locklist) { unsigned int version; int ret, v3; int skipped = 0; ret = fscanf(file, "%x\t", &version); if (ret != 1) return 0; if (version > CURRENT_PROTO) { fprintf(stdout, "Debug string proto %u found, but %u is the " "highest I understand.\n", version, CURRENT_PROTO); return 0; } ret = dump_version_one(file, out, lvbs, only_busy, locklist, &skipped, version); if (ret <= 0) return 0; if (!skipped) { if (version > 1) { v3 = !!(version == 3); ret = dump_version_two_and_three(file, out, v3); if (ret <= 0) return 0; } fprintf(out, "\n"); } /* Read to the end of the record here. Any new fields tagged * onto the current format will be silently ignored. */ ret = !end_line(file); return ret; } void dump_fs_locks(char *uuid_str, FILE *out, char *path, int dump_lvbs, int only_busy, struct list_head *locklist) { errcode_t ret; char debugfs_path[PATH_MAX]; FILE *file; int show_select; if (path == NULL) { ret = get_debugfs_path(debugfs_path, sizeof(debugfs_path)); if (ret) { fprintf(stderr, "Could not locate debugfs file system. " "Perhaps it is not mounted?\n"); return; } ret = open_debugfs_file(debugfs_path, "ocfs2", uuid_str, "locking_state", &file); if (ret) { fprintf(stderr, "Could not open debug state for " "\"%s\".\nPerhaps that OCFS2 file system is " "not mounted?\n", uuid_str); return; } } else { file = fopen(path, "r"); if (!file) { fprintf(stderr, "Could not open file at \"%s\"\n", path); return; } } show_select = !list_empty(locklist); while (dump_one_lockres(file, out, dump_lvbs, only_busy, locklist)) { if (show_select && list_empty(locklist)) break; } fclose(file); } ocfs2-tools-ocfs2-tools-1.8.6/debugfs.ocfs2/dump_net_stats.c000066400000000000000000000136461347147137200237220ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * dump_net_stats.c * * Interface with the kernel and dump current o2net locking state * * Copyright (C) 2011 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License, version 2, as published by the Free Software Foundation. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * */ #define _XOPEN_SOURCE 600 /* Triggers XOPEN2K in features.h */ #define _LARGEFILE64_SOURCE #include "main.h" #include "ocfs2/byteorder.h" #include "ocfs2_internals.h" static char *cmd = "net_stats"; static void show_net_stats(FILE *out, struct net_stats *prev, struct net_stats *curr, int num_entries, int interval, unsigned long proto) { int i; struct net_stats *c, *p; double total_send_time; double send_count, aqry_time, send_time, wait_time; double recv_count, proc_time; fprintf(out, "%-5s %-s %-s %-s %-s %-s %-s\n", " ", "-------", "msg / sec", "-------", "---------------------------", "usecs / msg", "---------------------------"); fprintf(out, "%-5s %-12s %-12s %-12s %-12s %-13s %-12s %-12s\n", "Node#", "send q", "recv q", "(acquiry", "xmit", "wait )", "send", "process"); for (i = 0; i < num_entries; ++i) { c = &(curr[i]); p = &(prev[i]); if (!c->ns_valid) continue; c->ns_send_count *= 10; c->ns_send_count /= 10; send_count = c->ns_send_count; aqry_time = c->ns_aqry_time; send_time = c->ns_send_time; wait_time = c->ns_wait_time; recv_count = c->ns_recv_count; proc_time = c->ns_proc_time; if (p->ns_valid) { send_count -= p->ns_send_count; if (send_count) { aqry_time -= p->ns_aqry_time; send_time -= p->ns_send_time; wait_time -= p->ns_wait_time; } recv_count -= p->ns_recv_count; if (recv_count) proc_time -= p->ns_proc_time; } /* Times converted from nsecs to usecs */ if (send_count) { aqry_time /= (send_count * 1000); send_time /= (send_count * 1000); wait_time /= (send_count * 1000); } else { aqry_time = 0; send_time = 0; wait_time = 0; } if (recv_count) proc_time /= (recv_count * 1000); else proc_time = 0; if (p->ns_valid && interval) { if (send_count) send_count /= interval; if (recv_count) recv_count /= interval; } total_send_time = aqry_time + send_time + wait_time; fprintf(out, "%-5d %-12lu %-12lu %-12.3f %-12.3f " "%-12.3f %-12.3f %-12.3f\n", i, (unsigned long)send_count, (unsigned long)recv_count, aqry_time, send_time, wait_time, total_send_time, proc_time); } fprintf(out, "\n\n"); } #define CURRENT_O2NET_STATS_PROTO 1 #define MAX_O2NET_STATS_STR_LEN 1024 static int read_net_stats(const char *debugfs_path, char *path, struct net_stats *stats, int num_entries, unsigned long *proto) { FILE *file = NULL; char rec1[MAX_O2NET_STATS_STR_LEN], rec2[MAX_O2NET_STATS_STR_LEN]; unsigned long node_num; errcode_t ret; memset(stats, 0, sizeof(struct net_stats) * num_entries); if (!path) { ret = open_debugfs_file(debugfs_path, "o2net", NULL, "stats", &file); if (ret) { com_err(cmd, ret, "; could not open %s/o2net/stats", debugfs_path); goto bail; } } else { file = fopen(path, "r"); if (!file) { ret = errno; com_err(cmd, ret, "\"%s\"", path); goto bail; } } while (fgets(rec1, sizeof(rec1), file)) { /* read protocol version */ ret = sscanf(rec1, "%lu,%s\n", proto, rec2); if (ret != 2) { ret = OCFS2_ET_INTERNAL_FAILURE; com_err(cmd, ret, "Error reading protocol version\n"); goto bail; } if (*proto > CURRENT_O2NET_STATS_PROTO) { ret = OCFS2_ET_INTERNAL_FAILURE; com_err(cmd, ret, "o2net stats proto %lu found, but %u " "is the highest I understand.\n", *proto, CURRENT_O2NET_STATS_PROTO); goto bail; } /* Protocol version 1 - begin */ ret = sscanf(rec2, "%lu,%s", &node_num, rec1); if (ret != 2) { ret = OCFS2_ET_INTERNAL_FAILURE; com_err(cmd, ret, "Error reading node#\n"); goto bail; } if (node_num > num_entries - 1) { ret = OCFS2_ET_INTERNAL_FAILURE; com_err(cmd, ret, "Invalid node# %lu\n", node_num); goto bail; } ret = sscanf(rec1, "%lu,%lld,%lld,%lld,%lu,%lld", &stats[node_num].ns_send_count, &stats[node_num].ns_aqry_time, &stats[node_num].ns_send_time, &stats[node_num].ns_wait_time, &stats[node_num].ns_recv_count, &stats[node_num].ns_proc_time); if (ret != 6) { ret = OCFS2_ET_INTERNAL_FAILURE; com_err(cmd, ret, "Error reading o2net stats\n"); goto bail; } stats[node_num].ns_valid = 1; /* Protocol version 1 - end */ } ret = 0; bail: if (file) fclose(file); return ret; } void dump_net_stats(FILE *out, char *path, int interval, int count) { errcode_t ret; char debugfs_path[PATH_MAX]; struct net_stats buf1[O2NM_MAX_NODES], buf2[O2NM_MAX_NODES]; struct net_stats *curr = buf1, *prev = buf2; unsigned long proto; ret = get_debugfs_path(debugfs_path, sizeof(debugfs_path)); if (ret) { com_err(cmd, ret, "Could not locate debugfs file system. " "Perhaps it is not mounted?\n"); return; } count = (count == 0) ? -1 : count; memset(prev, 0 , sizeof(buf1)); do { ret = read_net_stats(debugfs_path, path, curr, O2NM_MAX_NODES, &proto); if (ret) break; show_net_stats(out, prev, curr, O2NM_MAX_NODES, interval, proto); if (!interval) break; if (count > 0 && !--count) break; dbfs_swap(prev, curr); sleep(interval); } while(1); } ocfs2-tools-ocfs2-tools-1.8.6/debugfs.ocfs2/find_block_inode.c000066400000000000000000000224561347147137200241400ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * find_block_inode.c * * Take a block number and returns the owning inode number. * * Copyright (C) 2006 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License, version 2, as published by the Free Software Foundation. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * */ #include "main.h" extern struct dbgfs_gbls gbls; struct block_array { uint64_t blkno; uint32_t inode; /* Backing inode# */ uint64_t offset; int data; /* offset is valid if this is set */ #define STATUS_UNKNOWN 0 #define STATUS_USED 1 #define STATUS_FREE 2 int status; }; static errcode_t lookup_regular(ocfs2_filesys *fs, uint64_t inode, struct ocfs2_extent_list *el, struct block_array *ba, int count, int *found) { struct ocfs2_extent_block *eb; struct ocfs2_extent_rec *rec; errcode_t ret = 0; char *buf = NULL; int i; int j; uint64_t numblks; uint32_t clusters; if (*found >= count) return 0; ret = ocfs2_malloc_block(gbls.fs->fs_io, &buf); if (ret) { com_err(gbls.cmd, ret, "while allocating a block"); goto bail; } eb = (struct ocfs2_extent_block *)buf; for (i = 0; i < el->l_next_free_rec; ++i) { rec = &(el->l_recs[i]); clusters = ocfs2_rec_clusters(el->l_tree_depth, rec); /* * For a sparse file, we may find an empty record. * Just skip it. */ if (!clusters) continue; if (el->l_tree_depth) { ret = ocfs2_read_extent_block(fs, rec->e_blkno, buf); if (ret) { com_err(gbls.cmd, ret, "while reading extent " "block %"PRIu64, (uint64_t)rec->e_blkno); goto bail; } for (j = 0; j < count; ++j) { if (ba[j].status != STATUS_UNKNOWN) continue; if (ba[j].blkno == rec->e_blkno) { ba[j].status = STATUS_USED; ba[j].inode = inode; (*found)++; } } lookup_regular(fs, inode, &(eb->h_list), ba, count, found); continue; } for (j = 0; j < count; ++j) { if (ba[j].status != STATUS_UNKNOWN) continue; numblks = ocfs2_clusters_to_blocks(fs, clusters); if (ba[j].blkno >= rec->e_blkno && ba[j].blkno < rec->e_blkno + numblks) { ba[j].status = STATUS_USED; ba[j].inode = inode; ba[j].data = 1; ba[j].offset = ocfs2_clusters_to_blocks(fs, rec->e_cpos); ba[j].offset += ba[j].blkno - rec->e_blkno; (*found)++; } } if (*found >= count) return 0; } bail: if (buf) ocfs2_free(&buf); return ret; } struct walk_it { char *buf; struct block_array *ba; uint64_t inode; int count; int found; }; static int walk_chain_func(ocfs2_filesys *fs, uint64_t blkno, int chain, void *priv_data) { struct walk_it *wi = (struct walk_it *)priv_data; struct ocfs2_group_desc *gd; int i; errcode_t ret; ret = ocfs2_read_group_desc(fs, blkno, wi->buf); if (ret) { com_err(gbls.cmd, ret, "while reading group %"PRIu64, blkno); return ret; } gd = (struct ocfs2_group_desc *)wi->buf; for (i = 0; i < wi->count; ++i) { if (wi->ba[i].status != STATUS_UNKNOWN) continue; if (wi->ba[i].blkno == gd->bg_blkno) { wi->ba[i].status = STATUS_USED; wi->ba[i].inode = wi->inode; wi->found++; } if (wi->found >= wi->count) break; } return 0; } static errcode_t lookup_chain(ocfs2_filesys *fs, struct ocfs2_dinode *di, struct block_array *ba, int count, int *found) { struct walk_it wi; errcode_t ret = 0; memset(&wi, 0, sizeof(wi)); wi.ba = ba; wi.inode = di->i_blkno; wi.count = count; wi.found = *found; ret = ocfs2_malloc_block(fs->fs_io, &wi.buf); if (ret) { com_err(gbls.cmd, ret, "while allocating a block"); goto bail; } ret = ocfs2_chain_iterate(fs, di->i_blkno, walk_chain_func, &wi); if (ret) { com_err(gbls.cmd, ret, "while walking extents"); goto bail; } *found = wi.found; bail: if (wi.buf) ocfs2_free(&wi.buf); return ret; } static errcode_t lookup_global_bitmap(ocfs2_filesys *fs, uint64_t *blkno) { char sysfile[50]; errcode_t ret = 0; snprintf(sysfile, sizeof(sysfile), "%s", ocfs2_system_inodes[GLOBAL_BITMAP_SYSTEM_INODE].si_name); ret = ocfs2_lookup(fs, fs->fs_sysdir_blkno, sysfile, strlen(sysfile), NULL, blkno); if (ret) com_err(gbls.cmd, ret, "while looking up global bitmap"); return ret; } static errcode_t scan_bitmap(ocfs2_filesys *fs, uint64_t bm_blkno, struct block_array *ba, int count, int *found) { ocfs2_cached_inode *ci = NULL; uint32_t num_cluster; errcode_t ret = 0; int set; int i; ret = ocfs2_read_cached_inode(fs, bm_blkno, &ci); if (ret) { com_err(gbls.cmd, ret, "while reading inode %"PRIu64, bm_blkno); goto bail; } ret = ocfs2_load_chain_allocator(fs, ci); if (ret) { com_err(gbls.cmd, ret, "while loading chain allocator"); goto bail; } for (i = 0; i < count; ++i) { if (ba[i].status != STATUS_UNKNOWN) continue; num_cluster = ocfs2_blocks_to_clusters(fs, ba[i].blkno); ret = ocfs2_bitmap_test(ci->ci_chains, (uint64_t)num_cluster, &set); if (ret) { com_err(gbls.cmd, ret, "while looking up global bitmap"); goto bail; } if (!set) { ba[i].status = STATUS_FREE; (*found)++; } } bail: if (ci) ocfs2_free_cached_inode(fs, ci); return ret; } static void check_computed_blocks(ocfs2_filesys *fs, uint64_t gb_blkno, struct block_array *ba, int count, int *found) { uint64_t blks_in_superzone; uint32_t cpg; uint32_t bpg; uint64_t blkoff; uint32_t blks_in_cluster = ocfs2_clusters_to_blocks(fs, 1); int i; /* Blocks in the superblock zone */ blks_in_superzone = ocfs2_clusters_to_blocks(fs, 1); if (blks_in_superzone < OCFS2_SUPER_BLOCK_BLKNO) blks_in_superzone = OCFS2_SUPER_BLOCK_BLKNO; for (i = 0; i < count; ++i) { if (ba[i].blkno <= blks_in_superzone) { ba[i].status = STATUS_USED; ba[i].inode = OCFS2_SUPER_BLOCK_BLKNO; (*found)++; } if (ba[i].blkno >= fs->fs_first_cg_blkno && ba[i].blkno < (fs->fs_first_cg_blkno + blks_in_cluster)) { ba[i].status = STATUS_USED; ba[i].inode = gb_blkno; (*found)++; } } if (*found >= count) return; cpg = ocfs2_group_bitmap_size(fs->fs_blocksize, 0, OCFS2_RAW_SB(fs->fs_super)->s_feature_incompat) * 8; bpg = ocfs2_clusters_to_blocks(fs, cpg); for (i = 0; i < count; ++i) { for (blkoff = bpg; blkoff < fs->fs_blocks; blkoff += bpg) { if (ba[i].blkno >= blkoff && ba[i].blkno < (blkoff + blks_in_cluster)) { ba[i].status = STATUS_USED; ba[i].inode = gb_blkno; (*found)++; } } } return; } errcode_t find_block_inode(ocfs2_filesys *fs, uint64_t *blkno, int count, FILE *out) { errcode_t ret = 0; uint64_t inode_num; struct block_array *ba = NULL; char *buf = NULL; struct ocfs2_dinode *di; ocfs2_inode_scan *scan = NULL; int i; int found = 0; uint64_t gb_blkno; ret = ocfs2_malloc_block(fs->fs_io, &buf); if (ret) { com_err(gbls.cmd, ret, "while allocating a block"); goto out; } di = (struct ocfs2_dinode *)buf; ba = calloc(count, sizeof(struct block_array)); if (!ba) { com_err(gbls.cmd, ret, "while allocating memory"); goto out; } for (i = 0; i < count; ++i) ba[i].blkno = blkno[i]; if (found >= count) goto output; ret = lookup_global_bitmap(fs, &gb_blkno); if (ret) goto out_free; check_computed_blocks(fs, gb_blkno, ba, count, &found); if (found >= count) goto output; ret = scan_bitmap(fs, gb_blkno, ba, count, &found); if (ret) goto out_free; if (found >= count) goto output; ret = ocfs2_open_inode_scan(fs, &scan); if (ret) { com_err(gbls.cmd, ret, "while opening inode scan"); goto out_free; } for (;;) { ret = ocfs2_get_next_inode(scan, &inode_num, (char *)di); if (ret) { com_err(gbls.cmd, ret, "while scanning next inode"); goto out_close_scan; } if (!inode_num) break; if (memcmp(di->i_signature, OCFS2_INODE_SIGNATURE, strlen(OCFS2_INODE_SIGNATURE))) continue; ocfs2_swap_inode_to_cpu(fs, di); if (di->i_fs_generation != fs->fs_super->i_fs_generation) continue; if (!(di->i_flags & OCFS2_VALID_FL)) continue; for (i = 0; i < count; ++i) { if (ba[i].status != STATUS_UNKNOWN) continue; if (ba[i].blkno == di->i_blkno) { ba[i].status = STATUS_USED; ba[i].inode = di->i_blkno; found++; } } if (found >= count) break; if (S_ISLNK(di->i_mode) && !di->i_clusters) continue; if (di->i_flags & (OCFS2_LOCAL_ALLOC_FL | OCFS2_DEALLOC_FL)) continue; if (di->i_blkno == gb_blkno) continue; if (di->i_flags & OCFS2_CHAIN_FL) ret = lookup_chain(fs, di, ba, count, &found); else ret = lookup_regular(fs, di->i_blkno, &(di->id2.i_list), ba, count, &found); if (ret) goto out_close_scan; if (found >= count) break; } output: for (i = 0; i < count; ++i) dump_icheck(out, (i == 0), ba[i].blkno, ba[i].inode, ba[i].data, ba[i].offset, ba[i].status); out_close_scan: if (scan) ocfs2_close_inode_scan(scan); out_free: if (buf) ocfs2_free(&buf); if (ba) free(ba); out: return 0; } ocfs2-tools-ocfs2-tools-1.8.6/debugfs.ocfs2/find_inode_paths.c000066400000000000000000000076671347147137200241740ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * find_inode_paths.c * * Takes an inode block number and find all paths leading to it. * * Copyright (C) 2004, 2011 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License, version 2, as published by the Free Software Foundation. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * * This code is a port of e2fsprogs/lib/ext2fs/dir_iterate.c * Copyright (C) 1993, 1994, 1994, 1995, 1996, 1997 Theodore Ts'o. */ #include "main.h" struct walk_path { char *argv0; FILE *out; ocfs2_filesys *fs; char *path; uint32_t found; uint32_t count; int findall; uint64_t *inode; }; static int walk_tree_func(struct ocfs2_dir_entry *dentry, uint64_t blocknr, int offset, int blocksize, char *buf, void *priv_data) { errcode_t ret; int len; int reti = 0; int i = 0; int print = 0; char *old_path, *path; struct walk_path *wp = priv_data; if (!strncmp(dentry->name, ".", dentry->name_len) || !strncmp(dentry->name, "..", dentry->name_len)) return 0; len = strlen(wp->path); if (len + dentry->name_len > 4095) { com_err(wp->argv0, OCFS2_ET_NO_SPACE, "name is too long in %s\n", wp->path); return OCFS2_DIRENT_ABORT; } ret = ocfs2_malloc0(4096, &path); if (ret) { com_err(wp->argv0, ret, "while allocating path memory in %s\n", wp->path); return OCFS2_DIRENT_ABORT; } memcpy(path, wp->path, len); memcpy(path + len, dentry->name, dentry->name_len); if (dentry->file_type == OCFS2_FT_DIR) path[len + dentry->name_len] = '/'; for (i = 0; i < wp->count; ++i) { if (dentry->inode == wp->inode[i]) { if (!print) dump_inode_path(wp->out, dentry->inode, path); ++wp->found; ++print; } } if (!wp->findall) { if (wp->found >= wp->count) { ocfs2_free(&path); return OCFS2_DIRENT_ABORT; } } if (dentry->file_type == OCFS2_FT_DIR) { old_path = wp->path; wp->path = path; ret = ocfs2_dir_iterate(wp->fs, dentry->inode, 0, NULL, walk_tree_func, wp); if (ret) { com_err(wp->argv0, ret, "while walking %s", wp->path); reti = OCFS2_DIRENT_ABORT; } wp->path = old_path; } ocfs2_free(&path); return reti; } errcode_t find_inode_paths(ocfs2_filesys *fs, char **args, int findall, uint32_t count, uint64_t *blknos, FILE *out) { errcode_t ret = 0; struct walk_path wp; int i; int printroot = 0; int printsysd = 0; wp.argv0 = args[0]; wp.out = out; wp.count = count; wp.inode = blknos; wp.findall = findall; wp.found = 0; wp.fs = fs; /* Compare with root and sysdir */ for (i = 0; i < count; ++i) { if (blknos[i] == fs->fs_root_blkno) { if (!printroot) dump_inode_path(out, blknos[i], "/"); ++wp.found; ++printroot; } if (blknos[i] == fs->fs_sysdir_blkno) { if (!printsysd) dump_inode_path(out, blknos[i], "//"); ++wp.found; ++printsysd; } } if (!findall) { if (wp.found >= wp.count) goto bail; } /* Walk system dir */ wp.path = "//"; ret = ocfs2_dir_iterate(fs, OCFS2_RAW_SB(fs->fs_super)->s_system_dir_blkno, 0, NULL, walk_tree_func, &wp); if (ret) { com_err(args[0], ret, "while walking system dir"); goto bail; } /* Walk root dir */ wp.path = "/"; ret = ocfs2_dir_iterate(fs, OCFS2_RAW_SB(fs->fs_super)->s_root_blkno, 0, NULL, walk_tree_func, &wp); if (ret) { com_err(args[0], ret, "while walking root dir"); goto bail; } if (!wp.found) com_err(args[0], OCFS2_ET_FILE_NOT_FOUND, " "); bail: return ret; } ocfs2-tools-ocfs2-tools-1.8.6/debugfs.ocfs2/include/000077500000000000000000000000001347147137200221365ustar00rootroot00000000000000ocfs2-tools-ocfs2-tools-1.8.6/debugfs.ocfs2/include/commands.h000066400000000000000000000020021347147137200241020ustar00rootroot00000000000000/* * commands.h * * Function prototypes, macros, etc. for related 'C' files * * Copyright (C) 2004 Oracle. All rights reserved. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * * Authors: Sunil Mushran */ #ifndef __COMMANDS_H__ #define __COMMANDS_H__ void do_command (char *cmd); void handle_signal (int sig); #endif /* __COMMANDS_H__ */ ocfs2-tools-ocfs2-tools-1.8.6/debugfs.ocfs2/include/dump.h000066400000000000000000000065731347147137200232670ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * dump.h * * Function prototypes, macros, etc. for related 'C' files * * Copyright (C) 2004,2009 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License version 2 as published by the Free Software Foundation. * * 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. */ #ifndef __DUMP_H__ #define __DUMP_H__ struct list_dir_opts { ocfs2_filesys *fs; FILE *out; int long_opt; char *buf; }; struct dirblocks_walk { ocfs2_filesys *fs; FILE *out; struct ocfs2_dinode *di; char *buf; }; void dump_super_block (FILE *out, struct ocfs2_super_block *sb); void dump_local_alloc (FILE *out, struct ocfs2_local_alloc *loc); void dump_truncate_log (FILE *out, struct ocfs2_truncate_log *tl); void dump_inode (FILE *out, struct ocfs2_dinode *in); void dump_extent_list (FILE *out, struct ocfs2_extent_list *ext); void dump_chain_list (FILE *out, struct ocfs2_chain_list *cl); void dump_extent_block (FILE *out, struct ocfs2_extent_block *blk); void dump_group_descriptor (FILE *out, struct ocfs2_group_desc *grp, int index); void dump_group_extents(FILE *out, struct ocfs2_group_desc *grp); int dump_dir_entry (struct ocfs2_dir_entry *rec, uint64_t blocknr, int offset, int blocksize, char *buf, void *priv_data); void dump_dx_root (FILE *out, struct ocfs2_dx_root_block *dx_root); void dump_dx_leaf (FILE *out, struct ocfs2_dx_leaf *dx_leaf); void dump_dir_block(FILE *out, char *buf); void dump_dx_entries(FILE *out, struct ocfs2_dinode *inode); void dump_dx_space(FILE *out, struct ocfs2_dinode *inode, struct ocfs2_dx_root_block *dx_root); void dump_jbd_header (FILE *out, journal_header_t *header); void dump_jbd_superblock (FILE *out, journal_superblock_t *jsb); void dump_jbd_block (FILE *out, journal_superblock_t *jsb, journal_header_t *header, uint64_t blknum); void dump_jbd_metadata (FILE *out, enum ocfs2_block_type type, char *buf, uint64_t blknum); void dump_jbd_unknown (FILE *out, uint64_t start, uint64_t end); void dump_slots (FILE *out, struct ocfs2_slot_map_extended *se, struct ocfs2_slot_map *sm, int num_slots); void dump_fast_symlink (FILE *out, char *link); void dump_hb (FILE *out, char *buf, uint32_t len); void dump_inode_path (FILE *out, uint64_t blkno, char *path); void dump_logical_blkno(FILE *out, uint64_t blkno); void dump_icheck(FILE *out, int hdr, uint64_t blkno, uint64_t inode, int validoffset, uint64_t offset, int status); void dump_block_check(FILE *out, struct ocfs2_block_check *bc, void *block); uint32_t dump_xattr_ibody(FILE *out, ocfs2_filesys *fs, struct ocfs2_dinode *in, int verbose); void dump_xattr(FILE *out, struct ocfs2_xattr_header *xh); errcode_t dump_xattr_block(FILE *out, ocfs2_filesys *fs, struct ocfs2_dinode *in, uint32_t *xattrs_block, uint64_t *xattrs_bucket, int verbose); void dump_frag(FILE *out, uint64_t ino, uint32_t clusters, uint32_t extents); void dump_refcount_block(FILE *out, struct ocfs2_refcount_block *rb); void dump_refcount_records(FILE *out, struct ocfs2_refcount_block *rb); #endif /* __DUMP_H__ */ ocfs2-tools-ocfs2-tools-1.8.6/debugfs.ocfs2/include/dump_dlm_locks.h000066400000000000000000000042041347147137200253030ustar00rootroot00000000000000/* * dump_dlm_locks.h * * Function prototypes, macros, etc. for related 'C' files * * Copyright (C) 2008 Oracle. All rights reserved. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * */ #ifndef _DUMP_DLM_LOCKS_H_ #define _DUMP_DLM_LOCKS_H_ #define DLM_LOCK_RES_UNINITED 0x00000001 #define DLM_LOCK_RES_RECOVERING 0x00000002 #define DLM_LOCK_RES_READY 0x00000004 #define DLM_LOCK_RES_DIRTY 0x00000008 #define DLM_LOCK_RES_IN_PROGRESS 0x00000010 #define DLM_LOCK_RES_MIGRATING 0x00000020 #define DLM_LOCK_RES_DROPPING_REF 0x00000040 #define DLM_LOCK_RES_BLOCK_DIRTY 0x00001000 #define DLM_LOCK_RES_SETREF_INPROG 0x00002000 #define GRANTED 0 #define CONVERTING 1 #define BLOCKED 2 struct lockres { __u8 owner; __u16 state; __u32 last_used; __u32 inflight_locks; __u32 asts_reserved; __u32 refs; __u8 purge; __u8 dirty; __u8 recovering; __u8 migration_pending; char *refmap; char *lvb; struct list_head granted; struct list_head converting; struct list_head blocked; }; struct lock { __s8 type; __s8 convert_type; __u8 node; __u8 ast_list; __u8 bast_list; __u8 ast_pending; __u8 bast_pending; __u8 convert_pending; __u8 lock_pending; __u8 cancel_pending; __u8 unlock_pending; __u32 refs; char cookie[32]; struct list_head list; }; void dump_dlm_locks(char *uuid, FILE *out, char *path, int dump_lvbs, struct list_head *locklist); #endif /* _DUMP_DLM_LOCKS_H_ */ ocfs2-tools-ocfs2-tools-1.8.6/debugfs.ocfs2/include/dump_fs_locks.h000066400000000000000000000020731347147137200251410ustar00rootroot00000000000000/* * dump_fs_locks.h * * Function prototypes, macros, etc. for related 'C' files * * Copyright (C) 2005, 2008 Oracle. All rights reserved. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * */ #ifndef _DUMP_FS_LOCKS_H_ #define _DUMP_FS_LOCKS_H_ void dump_fs_locks(char *uuid_str, FILE *out, char *path, int dump_lvbs, int only_busy, struct list_head *locklist); #endif /* _DUMP_FS_LOCKS_H_ */ ocfs2-tools-ocfs2-tools-1.8.6/debugfs.ocfs2/include/dump_net_stats.h000066400000000000000000000023101347147137200253340ustar00rootroot00000000000000/* * dump_net_stats.h * * Function prototypes, macros, etc. for related 'C' files * * Copyright (C) 2011 Oracle. All rights reserved. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * */ #ifndef _DUMP_NET_STATS_H_ #define _DUMP_NET_STATS_H_ struct net_stats { int ns_valid; unsigned long ns_send_count; long long ns_aqry_time; long long ns_send_time; long long ns_wait_time; unsigned long ns_recv_count; long long ns_proc_time; }; void dump_net_stats(FILE *out, char *path, int interval, int count); #endif /* _DUMP_NET_STATS_H_ */ ocfs2-tools-ocfs2-tools-1.8.6/debugfs.ocfs2/include/find_block_inode.h000066400000000000000000000020371347147137200255610ustar00rootroot00000000000000/* * find_block_inode.h * * Function prototypes, macros, etc. for related 'C' files * * Copyright (C) 2006 Oracle. All rights reserved. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * */ #ifndef _FIND_BLOCK_INODE_H_ #define _FIND_BLOCK_INODE_H_ errcode_t find_block_inode(ocfs2_filesys *fs, uint64_t *blkno, int count, FILE *out); #endif /* _FIND_BLOCK_INODE_ */ ocfs2-tools-ocfs2-tools-1.8.6/debugfs.ocfs2/include/find_inode_paths.h000066400000000000000000000020761347147137200256110ustar00rootroot00000000000000/* * find_inode_path.h * * Function prototypes, macros, etc. for related 'C' files * * Copyright (C) 2005 Oracle. All rights reserved. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * */ #ifndef _FIND_INODE_PATH_H_ #define _FIND_INODE_PATH_H_ errcode_t find_inode_paths(ocfs2_filesys *fs, char **args, int findall, uint32_t count, uint64_t *blkno, FILE *out); #endif /* _FIND_INODE_PATH_H_ */ ocfs2-tools-ocfs2-tools-1.8.6/debugfs.ocfs2/include/journal.h000066400000000000000000000020201347147137200237530ustar00rootroot00000000000000/* * journal.h * * Function prototypes, macros, etc. for related 'C' files * * Copyright (C) 2004 Oracle. All rights reserved. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * * Authors: Sunil Mushran, Mark Fasheh */ #ifndef _JOURNAL_H_ #define _JOURNAL_H_ errcode_t read_journal(ocfs2_filesys *fs, uint64_t blkno, FILE *out); #endif /* _JOURNAL_H_ */ ocfs2-tools-ocfs2-tools-1.8.6/debugfs.ocfs2/include/main.h000066400000000000000000000057641347147137200232470ustar00rootroot00000000000000/* * main.h * * Function prototypes, macros, etc. for related 'C' files * * Copyright (C) 2004, 2011 Oracle. All rights reserved. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * */ #ifndef __MAIN_H__ #define __MAIN_H__ #define _GNU_SOURCE #define _LARGEFILE64_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ocfs2/ocfs2.h" #include "ocfs2-kernel/ocfs1_fs_compat.h" struct dbgfs_gbls { char *progname; int allow_write; int imagefile; int interactive; char *device; ocfs2_filesys *fs; char *cwd; char *cmd; uint64_t cwd_blkno; char *blockbuf; uint64_t max_clusters; uint64_t max_blocks; uint64_t root_blkno; uint64_t sysdir_blkno; uint64_t hb_blkno; uint64_t slotmap_blkno; uint64_t jrnl_blkno[OCFS2_MAX_SLOTS]; }; struct dbgfs_opts { int allow_write; int imagefile; int no_prompt; uint32_t sb_num; char *cmd_file; char *one_cmd; char *device; }; #define DBGFS_FATAL(fmt, arg...) \ ({ fprintf(stderr, "ERROR at %s, %d: " fmt ". EXITING!!!\n", \ __FILE__, __LINE__, ##arg); \ raise(SIGTERM); \ exit(1); \ }) #define DBGFS_FATAL_STR(str) DBGFS_FATAL(str, "") #define DBGFS_WARN(fmt, arg...) \ fprintf(stderr, "WARNING at %s, %d: " fmt ".\n", \ __FILE__, __LINE__, ##arg) #define DBGFS_WARN_STR(str) DBGFS_WARN(str, "") #undef max #define max(a,b) ((a) > (b) ? (a) : (b)) #undef min #define min(a,b) ((a) < (b) ? (a) : (b)) #define dbfs_swap(a, b) \ do { \ typeof(a) c = (a); \ (a) = (b); \ (b) = c; \ } while (0); /* remaining headers */ #include #include "ocfs2-kernel/kernel-list.h" #include #include #include #include #include #include #include #include #include #endif /* __MAIN_H__ */ ocfs2-tools-ocfs2-tools-1.8.6/debugfs.ocfs2/include/ocfs2_internals.h000066400000000000000000000070771347147137200254150ustar00rootroot00000000000000/* * ocfs2_internals.h * * Kernel internal structures which we only export here for debug * purposes. In other words, this is stuff that userspace has no * business knowing. * * Copyright (C) 2005 Oracle. All rights reserved. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. */ #ifndef _OCFS2_INTERNALS_H_ #define _OCFS2_INTERNALS_H_ #include "ocfs2-kernel/sparse_endian_types.h" #include "ocfs2-kernel/ocfs2_lockid.h" /* * Values taken from fs/ocfs2/dlm/dlmapi.h */ #define LKM_IVMODE (-1) /* invalid mode */ #define LKM_NLMODE 0 /* null lock */ #define LKM_CRMODE 1 /* concurrent read unsupported */ #define LKM_CWMODE 2 /* concurrent write unsupported */ #define LKM_PRMODE 3 /* protected read */ #define LKM_PWMODE 4 /* protected write unsupported */ #define LKM_EXMODE 5 /* exclusive */ #define DLM_LVB_LEN 64 /* * Values taken from fs/ocfs2/ocfs2.h */ #define OCFS2_LOCK_ATTACHED (0x00000001) /* have we initialized * the lvb */ #define OCFS2_LOCK_BUSY (0x00000002) /* we are currently in * dlm_lock */ #define OCFS2_LOCK_BLOCKED (0x00000004) /* blocked waiting to * downconvert*/ #define OCFS2_LOCK_LOCAL (0x00000008) /* newly created inode */ #define OCFS2_LOCK_NEEDS_REFRESH (0x00000010) #define OCFS2_LOCK_REFRESHING (0x00000020) #define OCFS2_LOCK_INITIALIZED (0x00000040) /* track initialization * for shutdown paths */ #define OCFS2_LOCK_FREEING (0x00000080) /* help dlmglue track * when to skip queueing * a lock because it's * about to be * dropped. */ #define OCFS2_LOCK_QUEUED (0x00000100) /* queued for downconvert */ enum ocfs2_ast_action { OCFS2_AST_INVALID = 0, OCFS2_AST_ATTACH, OCFS2_AST_CONVERT, OCFS2_AST_DOWNCONVERT, }; enum ocfs2_unlock_action { OCFS2_UNLOCK_INVALID = 0, OCFS2_UNLOCK_CANCEL_CONVERT, OCFS2_UNLOCK_DROP_LOCK, }; /* * Values taken from fs/ocfs2/dlmglue.h */ #define OCFS2_LVB_VERSION 2 /* "version 1" lvb, used in ocfs2 1.0 and 1.1 */ struct ocfs2_meta_lvb_v1 { __be32 lvb_old_seq; __be32 lvb_version; __be32 lvb_iclusters; __be32 lvb_iuid; __be32 lvb_igid; __be16 lvb_imode; __be16 lvb_inlink; __be64 lvb_iatime_packed; __be64 lvb_ictime_packed; __be64 lvb_imtime_packed; __be64 lvb_isize; __be32 lvb_reserved[2]; }; /* "version 2" lvb, used in ocfs2 1.3 */ struct ocfs2_meta_lvb_v2 { __be32 lvb_version; __be32 lvb_iclusters; __be32 lvb_iuid; __be32 lvb_igid; __be64 lvb_iatime_packed; __be64 lvb_ictime_packed; __be64 lvb_imtime_packed; __be64 lvb_isize; __be16 lvb_imode; __be16 lvb_inlink; __be32 lvb_reserved[3]; }; #endif /* _OCFS2_INTERNALS_H_ */ ocfs2-tools-ocfs2-tools-1.8.6/debugfs.ocfs2/include/stat_sysdir.h000066400000000000000000000017461347147137200246670ustar00rootroot00000000000000/* * stat_sysdir.h * * Function prototypes, macros, etc. for related 'C' files * * Copyright (C) 2011 Oracle. All rights reserved. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * */ #ifndef _STAT_SYSDIR_H_ #define _STAT_SYSDIR_H_ void show_stat_sysdir(ocfs2_filesys *fs, FILE *out); #endif /* _STAT_SYSDIR_H_ */ ocfs2-tools-ocfs2-tools-1.8.6/debugfs.ocfs2/include/utils.h000066400000000000000000000064671347147137200234640ustar00rootroot00000000000000/* * utils.h * * Function prototypes, macros, etc. for related 'C' files * * Copyright (C) 2004, 2008 Oracle. All rights reserved. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * */ #ifndef __UTILS_H__ #define __UTILS_H__ struct rdump_opts { ocfs2_filesys *fs; char *fullname; char *buf; int verbose; }; struct strings { char *s_str; struct list_head s_list; }; void get_incompat_flag(struct ocfs2_super_block *sb, char *buf, size_t count); void get_tunefs_flag(struct ocfs2_super_block *sb, char *buf, size_t count); void get_compat_flag(struct ocfs2_super_block *sb, char *buf, size_t count); void get_rocompat_flag(struct ocfs2_super_block *sb, char *buf, size_t count); void get_cluster_info_flag(struct ocfs2_super_block *sb, char *buf, size_t count); void get_journal_block_type (uint32_t jtype, GString *str); void get_tag_flag (uint32_t flags, GString *str); void get_journal_compat_flag(uint32_t flags, char *buf, size_t count); void get_journal_incompat_flag(uint32_t flags, char *buf, size_t count); void get_journal_rocompat_flag(uint32_t flags, char *buf, size_t count); void ctime_nano(struct timespec *t, char *buf, int buflen); FILE *open_pager(int interactive); void close_pager(FILE *stream); int inodestr_to_inode(char *str, uint64_t *blkno); errcode_t string_to_inode(ocfs2_filesys *fs, uint64_t root_blkno, uint64_t cwd_blkno, char *str, uint64_t *blkno); errcode_t dump_file(ocfs2_filesys *fs, uint64_t ino, int fd, char *out_file, int preserve); errcode_t read_whole_file(ocfs2_filesys *fs, uint64_t ino, char **buf, uint32_t *buflen); void inode_perms_to_str(uint16_t mode, char *str, int len); void inode_time_to_str(uint64_t mtime, char *str, int len); errcode_t rdump_inode(ocfs2_filesys *fs, uint64_t blkno, const char *name, const char *dumproot, int verbose); void crunch_strsplit(char **args); void find_max_contig_free_bits(struct ocfs2_group_desc *gd, int *max_contig_free_bits); void print_contig_bits(FILE *out, struct ocfs2_group_desc *gd); errcode_t get_debugfs_path(char *debugfs_path, int len); errcode_t open_debugfs_file(const char *debugfs_path, const char *dirname, const char *uuid, const char *filename, FILE **fd); void init_stringlist(struct list_head *strlist); void free_stringlist(struct list_head *strlist); errcode_t add_to_stringlist(char *str, struct list_head *strlist); int del_from_stringlist(char *str, struct list_head *strlist); errcode_t traverse_extents(ocfs2_filesys *fs, struct ocfs2_extent_list *el, FILE *out); errcode_t traverse_chains(ocfs2_filesys *fs, struct ocfs2_chain_list *cl, FILE *out); enum dump_block_type detect_block (char *buf); #endif /* __UTILS_H__ */ ocfs2-tools-ocfs2-tools-1.8.6/debugfs.ocfs2/journal.c000066400000000000000000000070451347147137200223370ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * journal.c * * reads the journal file * * Copyright (C) 2004, 2007 Oracle. All rights reserved. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * * Authors: Sunil Mushran, Mark Fasheh */ #include "main.h" extern struct dbgfs_gbls gbls; static void scan_journal(FILE *out, journal_superblock_t *jsb, char *buf, int len, uint64_t *blocknum, uint64_t *last_unknown) { char *block; char *p; enum ocfs2_block_type type; journal_header_t *header; p = buf; while (len) { block = p; header = (journal_header_t *)block; if (header->h_magic == ntohl(JBD2_MAGIC_NUMBER)) { if (*last_unknown) { dump_jbd_unknown(out, *last_unknown, *blocknum); *last_unknown = 0; } dump_jbd_block(out, jsb, header, *blocknum); } else { type = ocfs2_detect_block(block); if (type == OCFS2_BLOCK_UNKNOWN) { if (*last_unknown == 0) *last_unknown = *blocknum; } else { if (*last_unknown) { dump_jbd_unknown(out, *last_unknown, *blocknum); *last_unknown = 0; } dump_jbd_metadata(out, type, block, *blocknum); } } (*blocknum)++; p += gbls.fs->fs_blocksize; len -= gbls.fs->fs_blocksize; } return; } errcode_t read_journal(ocfs2_filesys *fs, uint64_t blkno, FILE *out) { char *buf = NULL; char *jsb_buf = NULL; char *p; uint64_t blocknum; uint64_t len; uint64_t offset; uint32_t got; uint64_t last_unknown = 0; uint32_t buflen = 1024 * 1024; int buflenbits; ocfs2_cached_inode *ci = NULL; errcode_t ret; journal_superblock_t *jsb; ret = ocfs2_read_cached_inode(fs, blkno, &ci); if (ret) { com_err(gbls.cmd, ret, "while reading inode %"PRIu64, blkno); goto bail; } ret = ocfs2_malloc_block(fs->fs_io, &jsb_buf); if (ret) { com_err(gbls.cmd, ret, "while allocating journal superblock buffer"); goto bail; } buflenbits = buflen >> OCFS2_RAW_SB(gbls.fs->fs_super)->s_blocksize_bits; ret = ocfs2_malloc_blocks(fs->fs_io, buflenbits, &buf); if (ret) { com_err(gbls.cmd, ret, "while allocating %u bytes", buflen); goto bail; } offset = 0; blocknum = 0; jsb = (journal_superblock_t *)jsb_buf; while (1) { ret = ocfs2_file_read(ci, buf, buflen, offset, &got); if (ret) { com_err(gbls.cmd, ret, "while reading journal"); goto bail; }; if (got == 0) break; p = buf; len = got; if (offset == 0) { memcpy(jsb_buf, buf, fs->fs_blocksize); dump_jbd_superblock(out, jsb); ocfs2_swap_journal_superblock(jsb); blocknum++; p += fs->fs_blocksize; len -= fs->fs_blocksize; } scan_journal(out, jsb, p, len, &blocknum, &last_unknown); if (got < buflen) break; offset += got; } if (last_unknown) { dump_jbd_unknown(out, last_unknown, blocknum); last_unknown = 0; } bail: if (jsb_buf) ocfs2_free(&jsb_buf); if (buf) ocfs2_free(&buf); if (ci) ocfs2_free_cached_inode(fs, ci); return ret; } ocfs2-tools-ocfs2-tools-1.8.6/debugfs.ocfs2/main.c000066400000000000000000000254021347147137200216060ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * main.c * * entry point for debugfs.ocfs2 * * Copyright (C) 2004, 2007 Oracle. All rights reserved. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * * Authors: Sunil Mushran, Manish Singh */ #include "main.h" #include #include #define PROMPT "debugfs: " extern struct dbgfs_gbls gbls; static int decodemode = 0; static int encodemode = 0; static int arg_ind = 0; static int logmode = 0; struct log_entry { char *mask; char *action; }; static GList *loglist = NULL; static void usage(char *progname) { g_print("usage: %s -l [ ... [allow|off|deny]] ...\n", progname); g_print("usage: %s -d, --decode \n", progname); g_print("usage: %s -e, --encode \n", progname); g_print("usage: %s [-f cmdfile] [-R request] [-i] [-s backup#] [-V] [-w] [-n] [-?] [device]\n", progname); g_print("\t-f, --file \t\tExecute commands in cmdfile\n"); g_print("\t-R, --request \t\tExecute a single command\n"); g_print("\t-s, --superblock \tOpen the device using a backup superblock\n"); g_print("\t-i, --image\t\t\tOpen an o2image file\n"); g_print("\t-w, --write\t\t\tOpen in read-write mode instead of the default of read-only\n"); g_print("\t-V, --version\t\t\tShow version\n"); g_print("\t-n, --noprompt\t\t\tHide prompt\n"); g_print("\t-?, --help\t\t\tShow this help\n"); exit(0); } static void print_version(char *progname) { fprintf(stderr, "%s %s\n", progname, VERSION); } static void process_one_list(GList *list, char *action) { GList *tmp; struct log_entry *entry = NULL; while (list) { tmp = loglist; while (tmp) { entry = tmp->data; if (!strcasecmp(entry->mask, list->data)) break; tmp = tmp->next; } if (tmp) { entry->action = action; } else { entry = g_new(struct log_entry, 1); entry->action = action; entry->mask = list->data; loglist = g_list_append(loglist, entry); } list = list->next; } } static void fill_log_list(int argc, char **argv, int startind) { int i; GList *tmplist = NULL; for (i = startind; i < argc; i++) { if (!strcasecmp(argv[i], "allow") || !strcasecmp(argv[i], "deny") || !strcasecmp(argv[i], "off")) { process_one_list(tmplist, argv[i]); g_list_free(tmplist); tmplist = NULL; } else { tmplist = g_list_append(tmplist, argv[i]); } } } static void process_decode_lockres(int argc, char **argv, int startind) { int i; errcode_t ret; enum ocfs2_lock_type type; uint64_t blkno = 0; uint32_t generation = 0; uint64_t parent = 0; if (startind + 1 > argc) { usage(gbls.progname); exit(1); } for (i = startind; i < argc; ++i) { ret = ocfs2_decode_lockres(argv[i], &type, &blkno, &generation, &parent); if (ret) continue; printf("Lockres: %s\n", argv[i]); printf("Type: %s\n", ocfs2_lock_type_string(type)); if (blkno) printf("Block: %"PRIu64"\n", blkno); if (generation) printf("Generation: 0x%08x\n", generation); if (parent) printf("Parent: %"PRIu64"\n", parent); printf("\n"); } return ; } static void process_encode_lockres(int argc, char **argv, int startind) { int i; errcode_t ret; enum ocfs2_lock_type type; uint64_t blkno; uint64_t extra; /* generation or parent */ char lock[OCFS2_LOCK_ID_MAX_LEN]; char tmp[OCFS2_LOCK_ID_MAX_LEN]; if (startind + 3 > argc) { usage(gbls.progname); exit(1); } i = startind; type = ocfs2_get_lock_type(argv[i++][0]); blkno = strtoull(argv[i++], NULL, 0); extra = strtoull(argv[i++], NULL, 0); if (type == OCFS2_LOCK_TYPE_DENTRY) { ret = ocfs2_encode_lockres(type, blkno, 0, extra, tmp); if (!ret) ret = ocfs2_printable_lockres(tmp, lock, sizeof(lock)); } else ret = ocfs2_encode_lockres(type, blkno, (uint32_t)extra, 0, lock); if (ret) { com_err(gbls.progname, ret, "while encoding lockname"); return ; } printf("%s\n", lock); return ; } static void get_options(int argc, char **argv, struct dbgfs_opts *opts) { int c; char *ptr = NULL; static struct option long_options[] = { { "file", 1, 0, 'f' }, { "request", 1, 0, 'R' }, { "version", 0, 0, 'V' }, { "help", 0, 0, '?' }, { "write", 0, 0, '?' }, { "log", 0, 0, 'l' }, { "noprompt", 0, 0, 'n' }, { "decode", 0, 0, 'd' }, { "encode", 0, 0, 'e' }, { "superblock", 1, 0, 's' }, { "image", 0, 0, 'i' }, { 0, 0, 0, 0} }; while (1) { if (decodemode || encodemode || logmode) break; c = getopt_long(argc, argv, "lf:R:deV?wns:i", long_options, NULL); if (c == -1) break; switch (c) { case 'f': opts->cmd_file = strdup(optarg); if (!strlen(opts->cmd_file)) { usage(gbls.progname); exit(1); } break; case 'R': opts->one_cmd = strdup(optarg); if (!strlen(opts->one_cmd)) { usage(gbls.progname); exit(1); } break; case 'd': decodemode++; break; case 'e': encodemode++; break; case 'i': opts->imagefile = 1; break; case 'l': logmode++; break; case 'w': opts->allow_write = 1; break; case 'n': opts->no_prompt = 1; break; case '?': print_version(gbls.progname); usage(gbls.progname); exit(0); break; case 'V': print_version(gbls.progname); exit(0); break; case 's': opts->sb_num = strtoul(optarg, &ptr, 0); break; default: usage(gbls.progname); break; } } if (optind < argc) { if (logmode) fill_log_list(argc, argv, optind); else opts->device = strdup(argv[optind]); } if (decodemode || encodemode) arg_ind = optind; return ; } static char *get_line(FILE *stream, int no_prompt) { char *line; static char buf[1024]; int i; if (stream) { while (1) { if (!fgets(buf, sizeof(buf), stream)) return NULL; line = buf; i = strlen(line); if (i) buf[i - 1] = '\0'; g_strchug(line); if (strlen(line)) break; } } else { if (no_prompt) line = readline(NULL); else line = readline(PROMPT); if (line && *line) { g_strchug(line); add_history(line); } } return line; } #define LOG_CTL_PROC "/proc/fs/ocfs2_nodemanager/log_mask" static int set_logmode_proc(struct log_entry *entry) { FILE *f; f = fopen(LOG_CTL_PROC, "w"); if (!f) { fprintf(stderr, "%s: Unable to open \"%s\": %s\n", gbls.progname, LOG_CTL_PROC, strerror(errno)); return 1; } fprintf(f, "%s %s\n", entry->mask, entry->action); fclose(f); return 0; } #define LOG_CTL_SYSFS_DIR_OLD "/sys/o2cb/logmask" #define LOG_CTL_SYSFS_DIR "/sys/fs/o2cb/logmask" #define LOG_CTL_SYSFS_FORMAT "%s/%s" static int set_logmode_sysfs(const char *path, struct log_entry *entry) { FILE *f; char *logpath; logpath = g_strdup_printf(LOG_CTL_SYSFS_FORMAT, path, entry->mask); f = fopen(logpath, "w"); g_free(logpath); if (!f) { fprintf(stderr, "%s: Unable to write log mask \"%s\": %s\n", gbls.progname, entry->mask, strerror(errno)); return 1; } fprintf(f, "%s\n", entry->action); fclose(f); return 0; } static int get_logmode_sysfs(const char *path, const char *name) { char *logpath; char *current_mask = NULL; logpath = g_strdup_printf(LOG_CTL_SYSFS_FORMAT, path, name); if (g_file_get_contents(logpath, ¤t_mask, NULL, NULL)) { fprintf(stdout, "%s %s", name, current_mask); } g_free(logpath); g_free(current_mask); return 0; } static void run_logmode_proc(void) { GList *tmp; char *current_mask; if (loglist) { tmp = loglist; while (tmp) { if (set_logmode_proc(tmp->data)) break; tmp = tmp->next; } } else { if (g_file_get_contents(LOG_CTL_PROC, ¤t_mask, NULL, NULL)) { fprintf(stdout, "%s", current_mask); } } } static void run_logmode_sysfs(const char *path) { GList *tmp; DIR *dir; struct dirent *d; if (loglist) { tmp = loglist; while (tmp) { if (set_logmode_sysfs(path, tmp->data)) break; tmp = tmp->next; } } else { dir = opendir(path); if (dir) { while ((d = readdir(dir)) != NULL) get_logmode_sysfs(path, d->d_name); closedir(dir); } } } static void run_logmode(void) { struct stat stat_buf; if (!stat(LOG_CTL_SYSFS_DIR, &stat_buf) && S_ISDIR(stat_buf.st_mode)) run_logmode_sysfs(LOG_CTL_SYSFS_DIR); else if (!stat(LOG_CTL_SYSFS_DIR_OLD, &stat_buf) && S_ISDIR(stat_buf.st_mode)) run_logmode_sysfs(LOG_CTL_SYSFS_DIR_OLD); else if (!stat(LOG_CTL_PROC, &stat_buf) && S_ISREG(stat_buf.st_mode)) run_logmode_proc(); } int main(int argc, char **argv) { char *line; struct dbgfs_opts opts; FILE *cmd = NULL; initialize_o2cb_error_table(); initialize_ocfs_error_table(); #define INSTALL_SIGNAL(sig) \ do { \ if (signal(sig, handle_signal) == SIG_ERR) { \ printf("Could not set " #sig "\n"); \ goto bail; \ } \ } while (0) INSTALL_SIGNAL(SIGTERM); INSTALL_SIGNAL(SIGINT); setbuf(stdout, NULL); setbuf(stderr, NULL); memset(&opts, 0, sizeof(opts)); memset(&gbls, 0, sizeof(gbls)); gbls.progname = basename(argv[0]); get_options(argc, argv, &opts); if (logmode) { run_logmode(); goto bail; } if (decodemode) { process_decode_lockres(argc, argv, arg_ind); goto bail; } if (encodemode) { process_encode_lockres(argc, argv, arg_ind); goto bail; } gbls.allow_write = opts.allow_write; gbls.imagefile = opts.imagefile; if (!opts.cmd_file) gbls.interactive++; if (opts.device) { if (opts.sb_num) line = g_strdup_printf("open %s -s %u", opts.device, opts.sb_num); else line = g_strdup_printf("open %s", opts.device); do_command(line); g_free(line); } if (opts.one_cmd) { do_command(opts.one_cmd); goto bail; } if (opts.cmd_file) { cmd = fopen(opts.cmd_file, "r"); if (!cmd) { com_err(argv[0], errno, "'%s'", opts.cmd_file); goto bail; } } if (!opts.no_prompt) print_version(gbls.progname); while (1) { line = get_line(cmd, opts.no_prompt); if (line) { if (!gbls.interactive && !opts.no_prompt) fprintf(stdout, "%s%s\n", PROMPT, line); do_command(line); if (gbls.interactive) free(line); } else { printf("\n"); raise(SIGTERM); exit(0); } } bail: if (cmd) fclose(cmd); if (opts.cmd_file) free(opts.cmd_file); if (opts.device) free(opts.device); return 0; } ocfs2-tools-ocfs2-tools-1.8.6/debugfs.ocfs2/stat_sysdir.c000066400000000000000000000101511347147137200232250ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * stat_sysdir.c * * Shows all the objects in the system directory * * Copyright (C) 2011 Oracle. All rights reserved. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * */ #include "main.h" #include "ocfs2/image.h" #include "ocfs2/byteorder.h" extern struct dbgfs_gbls gbls; static int show_system_inode(struct ocfs2_dir_entry *rec, uint64_t blocknr, int offset, int blocksize, char *block_buf, void *priv_data) { struct list_dir_opts *ls_opts = (struct list_dir_opts *)priv_data; ocfs2_filesys *fs = ls_opts->fs; FILE *out = ls_opts->out; char *buf = ls_opts->buf; struct list_dir_opts ls; struct ocfs2_dinode *di; char tmp = rec->name[rec->name_len]; struct ocfs2_slot_map_extended *se = NULL; struct ocfs2_slot_map *sm = NULL; int num_slots; errcode_t ret = 0; rec->name[rec->name_len] = '\0'; if (!strcmp(rec->name, "..")) goto out; memset(buf, 0, fs->fs_blocksize); ocfs2_read_inode(fs, rec->inode, buf); di = (struct ocfs2_dinode *)buf; if (!strcmp(rec->name, ".")) fprintf(out, "\n //\n"); else fprintf(out, "\n //%s\n", rec->name); dump_inode(out, di); if ((di->i_flags & OCFS2_LOCAL_ALLOC_FL)) dump_local_alloc(out, &(di->id2.i_lab)); else if ((di->i_flags & OCFS2_CHAIN_FL)) ret = traverse_chains(fs, &(di->id2.i_chain), out); else if (S_ISLNK(di->i_mode) && !di->i_clusters) dump_fast_symlink(out, (char *)di->id2.i_symlink); else if (di->i_flags & OCFS2_DEALLOC_FL) dump_truncate_log(out, &(di->id2.i_dealloc)); else if (!(di->i_dyn_features & OCFS2_INLINE_DATA_FL)) ret = traverse_extents(fs, &(di->id2.i_list), out); if (ret) com_err(gbls.cmd, ret, "while traversing inode at block " "%"PRIu64, (uint64_t)rec->inode); if (S_ISDIR(di->i_mode)) { ls.fs = ls_opts->fs; ls.out = ls_opts->out; ls.long_opt = 1; ret = ocfs2_malloc_block(fs->fs_io, &ls.buf); if (ret) return ret; ret = ocfs2_dir_iterate(fs, rec->inode, 0, NULL, dump_dir_entry, (void *)&ls); if (ret) com_err(gbls.cmd, ret, "while iterating // at block " "%"PRIu64, (uint64_t)rec->inode); ocfs2_free(&ls.buf); } if (!strcmp(rec->name, ocfs2_system_inodes[SLOT_MAP_SYSTEM_INODE].si_name)) { num_slots = OCFS2_RAW_SB(fs->fs_super)->s_max_slots; if (ocfs2_uses_extended_slot_map(OCFS2_RAW_SB(fs->fs_super))) ret = ocfs2_read_slot_map_extended(fs, num_slots, &se); else ret = ocfs2_read_slot_map(fs, num_slots, &sm); if (ret) com_err(gbls.cmd, ret, "while reading //slotmap"); else dump_slots(out, se, sm, num_slots); ocfs2_free(&sm); ocfs2_free(&se); } out: rec->name[rec->name_len] = tmp; return 0; } void show_stat_sysdir(ocfs2_filesys *fs, FILE *out) { errcode_t ret; struct ocfs2_dinode *di; struct ocfs2_super_block *sb; struct list_dir_opts ls; di = fs->fs_super; sb = OCFS2_RAW_SB(di); fprintf(out, "Device: %s\n", gbls.device); fprintf(out, " superblock\n"); dump_super_block(out, sb); dump_inode(out, di); ret = ocfs2_check_directory(fs, fs->fs_sysdir_blkno); if (ret) { com_err(gbls.cmd, ret, "while checking system directory at " "block %"PRIu64"", fs->fs_sysdir_blkno); goto bail; } ls.fs = fs; ls.out = out; ls.buf = gbls.blockbuf; ls.long_opt = 1; ret = ocfs2_dir_iterate(fs, fs->fs_sysdir_blkno, 0, NULL, show_system_inode, (void *)&ls); if (ret) com_err(gbls.cmd, ret, "while iterating system directory at " "block %"PRIu64"", fs->fs_sysdir_blkno); bail: return ; } ocfs2-tools-ocfs2-tools-1.8.6/debugfs.ocfs2/utils.c000066400000000000000000000554641347147137200220350ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * utils.c * * utility functions * * Copyright (C) 2004, 2011 Oracle. All rights reserved. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * */ #include "main.h" #include "ocfs2/bitops.h" extern struct dbgfs_gbls gbls; void get_incompat_flag(struct ocfs2_super_block *sb, char *buf, size_t count) { errcode_t err; ocfs2_fs_options flags = { .opt_incompat = sb->s_feature_incompat, }; *buf = '\0'; err = ocfs2_snprint_feature_flags(buf, count, &flags); if (err) com_err(gbls.cmd, err, "while processing incompat flags"); } void get_tunefs_flag(struct ocfs2_super_block *sb, char *buf, size_t count) { errcode_t err; *buf = '\0'; err = ocfs2_snprint_tunefs_flags(buf, count, sb->s_tunefs_flag); if (err) com_err(gbls.cmd, err, "while processing tunefs flags"); } void get_compat_flag(struct ocfs2_super_block *sb, char *buf, size_t count) { errcode_t err; ocfs2_fs_options flags = { .opt_compat = sb->s_feature_compat, }; *buf = '\0'; err = ocfs2_snprint_feature_flags(buf, count, &flags); if (err) com_err(gbls.cmd, err, "while processing compat flags"); } void get_rocompat_flag(struct ocfs2_super_block *sb, char *buf, size_t count) { errcode_t err; ocfs2_fs_options flags = { .opt_ro_compat = sb->s_feature_ro_compat, }; *buf = '\0'; err = ocfs2_snprint_feature_flags(buf, count, &flags); if (err) com_err(gbls.cmd, err, "while processing ro compat flags"); } void get_cluster_info_flag(struct ocfs2_super_block *sb, char *buf, size_t count) { errcode_t err = 0; *buf = '\0'; if (ocfs2_o2cb_stack(sb)) err = ocfs2_snprint_cluster_o2cb_flags(buf, count, sb->s_cluster_info.ci_stackflags); if (err) com_err(gbls.cmd, err, "while processing clusterinfo flags"); } /* * get_journal_block_type() * */ void get_journal_block_type(uint32_t jtype, GString *str) { switch (jtype) { case JBD2_DESCRIPTOR_BLOCK: g_string_append(str, "JBD2_DESCRIPTOR_BLOCK"); break; case JBD2_COMMIT_BLOCK: g_string_append(str, "JBD2_COMMIT_BLOCK"); break; case JBD2_SUPERBLOCK_V1: g_string_append(str, "JBD2_SUPERBLOCK_V1"); break; case JBD2_SUPERBLOCK_V2: g_string_append(str, "JBD2_SUPERBLOCK_V2"); break; case JBD2_REVOKE_BLOCK: g_string_append(str, "JBD2_REVOKE_BLOCK"); break; } if (!str->len) g_string_append(str, "none"); return ; } /* * get_tag_flag() * */ void get_tag_flag(uint32_t flags, GString *str) { if (flags == 0) { g_string_append(str, "none"); goto done; } if (flags & JBD2_FLAG_ESCAPE) g_string_append(str, "JBD2_FLAG_ESCAPE "); if (flags & JBD2_FLAG_SAME_UUID) g_string_append(str, "JBD2_FLAG_SAME_UUID "); if (flags & JBD2_FLAG_DELETED) g_string_append(str, "JBD2_FLAG_DELETED "); if (flags & JBD2_FLAG_LAST_TAG) g_string_append(str, "JBD2_FLAG_LAST_TAG"); done: return ; } void get_journal_compat_flag(uint32_t flags, char *buf, size_t count) { size_t out = 0; *buf = '\0'; if (flags & JBD2_FEATURE_COMPAT_CHECKSUM) out += snprintf(buf + out, count - out, "checksum "); if (flags & ~JBD2_FEATURE_COMPAT_CHECKSUM) out += snprintf(buf + out, count - out, "unknown"); } void get_journal_incompat_flag(uint32_t flags, char *buf, size_t count) { size_t out = 0; *buf = '\0'; if (flags & JBD2_FEATURE_INCOMPAT_REVOKE) out += snprintf(buf + out, count - out, "revoke "); if (flags & JBD2_FEATURE_INCOMPAT_64BIT) out += snprintf(buf + out, count - out, "block64 "); else out += snprintf(buf + out, count - out, "block32 "); if (flags & JBD2_FEATURE_INCOMPAT_ASYNC_COMMIT) out += snprintf(buf + out, count - out, "async-commit "); if (flags & ~(JBD2_FEATURE_INCOMPAT_REVOKE | JBD2_FEATURE_INCOMPAT_64BIT | JBD2_FEATURE_INCOMPAT_ASYNC_COMMIT)) out += snprintf(buf + out, count - out, "unknown"); } void get_journal_rocompat_flag(uint32_t flags, char *buf, size_t count) { size_t out = 0; *buf = '\0'; if (flags) out += snprintf(buf + out, count - out, "unknown"); } /* * Adds nanosec to the ctime output. eg. Tue Jul 19 13:36:52.123456 2011 * On error, returns an empty string. */ void ctime_nano(struct timespec *t, char *buf, int buflen) { time_t sec; char tmp[26], *p; sec = (time_t)t->tv_sec; if (!ctime_r(&sec, tmp)) return; /* Find the last space where we will append the nanosec */ if ((p = strrchr(tmp, ' '))) { *p = '\0'; snprintf(buf, buflen, "%s.%ld %s", tmp, t->tv_nsec, ++p); } } /* * open_pager() -- copied from e2fsprogs-1.32/debugfs/util.c * * Copyright (C) 1993, 1994 Theodore Ts'o. This file may be * redistributed under the terms of the GNU Public License. * */ FILE *open_pager(int interactive) { FILE *outfile = NULL; const char *pager = getenv("PAGER"); if (interactive) { signal(SIGPIPE, SIG_IGN); if (pager) { if (strcmp(pager, "__none__") == 0) { return stdout; } } else pager = "more"; outfile = popen(pager, "w"); } return (outfile ? outfile : stdout); } /* * close_pager() -- copied from e2fsprogs-1.32/debugfs/util.c * * Copyright (C) 1993, 1994 Theodore Ts'o. This file may be * redistributed under the terms of the GNU Public License. */ void close_pager(FILE *stream) { if (stream && stream != stdout) pclose(stream); } /* * inodestr_to_inode() * * Returns ino if string is of the form */ int inodestr_to_inode(char *str, uint64_t *blkno) { int len; char *buf = NULL; char *end; int ret = OCFS2_ET_INVALID_LOCKRES; len = strlen(str); if (!((len > 2) && (str[0] == '<') && (str[len - 1] == '>'))) goto bail; ret = OCFS2_ET_NO_MEMORY; buf = strndup(str + 1, len - 2); if (!buf) goto bail; ret = 0; if (ocfs2_get_lock_type(buf[0]) < OCFS2_NUM_LOCK_TYPES) ret = ocfs2_decode_lockres(buf, NULL, blkno, NULL, NULL); else { *blkno = strtoull(buf, &end, 0); if (*end) ret = OCFS2_ET_INVALID_LOCKRES; } bail: if (buf) free(buf); return ret; } /* * string_to_inode() * * This routine is used whenever a command needs to turn a string into * an inode. * * Code based on similar function in e2fsprogs-1.32/debugfs/util.c * * Copyright (C) 1993, 1994 Theodore Ts'o. This file may be * redistributed under the terms of the GNU Public License. */ errcode_t string_to_inode(ocfs2_filesys *fs, uint64_t root_blkno, uint64_t cwd_blkno, char *str, uint64_t *blkno) { uint64_t root = root_blkno; /* * If the string is of the form , then treat it as an * inode number. */ if (!inodestr_to_inode(str, blkno)) return 0; /* // is short for system directory */ if (!strncmp(str, "//", 2)) { root = fs->fs_sysdir_blkno; ++str; } return ocfs2_namei(fs, root, cwd_blkno, str, blkno); } /* * fix_perms() * * Code based on similar function in e2fsprogs-1.32/debugfs/dump.c * * Copyright (C) 1994 Theodore Ts'o. This file may be redistributed * under the terms of the GNU Public License. */ static errcode_t fix_perms(const struct ocfs2_dinode *di, int *fd, char *name) { struct utimbuf ut; int i; errcode_t ret = 0; if (*fd != -1) i = fchmod(*fd, di->i_mode); else i = chmod(name, di->i_mode); if (i == -1) { ret = errno; goto bail; } if (*fd != -1) i = fchown(*fd, di->i_uid, di->i_gid); else i = chown(name, di->i_uid, di->i_gid); if (i == -1) { ret = errno; goto bail; } if (*fd != -1) { close(*fd); *fd = -1; } ut.actime = di->i_atime; ut.modtime = di->i_mtime; if (utime(name, &ut) == -1) ret = errno; bail: return ret; } /* * dump_file() * */ static errcode_t dump_symlink(ocfs2_filesys *fs, uint64_t blkno, char *name, struct ocfs2_dinode *inode); errcode_t dump_file(ocfs2_filesys *fs, uint64_t ino, int fd, char *out_file, int preserve) { errcode_t ret; char *buf = NULL; int buflen; uint32_t got; uint32_t wrote; ocfs2_cached_inode *ci = NULL; uint64_t offset = 0; ret = ocfs2_read_cached_inode(fs, ino, &ci); if (ret) { com_err(gbls.cmd, ret, "while reading inode %"PRIu64, ino); goto bail; } if (S_ISLNK(ci->ci_inode->i_mode)) { ret = unlink(out_file); if (ret) goto bail; ret = dump_symlink(fs, ino, out_file, ci->ci_inode); goto bail; } buflen = 1024 * 1024; ret = ocfs2_malloc_blocks(fs->fs_io, (buflen >> OCFS2_RAW_SB(fs->fs_super)->s_blocksize_bits), &buf); if (ret) { com_err(gbls.cmd, ret, "while allocating %u bytes", buflen); goto bail; } while (1) { ret = ocfs2_file_read(ci, buf, buflen, offset, &got); if (ret) { com_err(gbls.cmd, ret, "while reading file %"PRIu64" " "at offset %"PRIu64, ci->ci_blkno, offset); goto bail; } if (!got) break; wrote = write(fd, buf, got); if (wrote != got) { com_err(gbls.cmd, errno, "while writing file"); ret = errno; goto bail; } if (got < buflen) break; else offset += got; } if (preserve) ret = fix_perms(ci->ci_inode, &fd, out_file); bail: if (fd > 0 && fd != fileno(stdout)) close(fd); if (buf) ocfs2_free(&buf); if (ci) ocfs2_free_cached_inode(fs, ci); return ret; } /* * read_whole_file() * * read in buflen bytes or whole file if buflen = 0 * */ errcode_t read_whole_file(ocfs2_filesys *fs, uint64_t ino, char **buf, uint32_t *buflen) { errcode_t ret; uint32_t got; ocfs2_cached_inode *ci = NULL; ret = ocfs2_read_cached_inode(fs, ino, &ci); if (ret) { com_err(gbls.cmd, ret, "while reading inode %"PRIu64, ino); goto bail; } if (!*buflen) { *buflen = (((ci->ci_inode->i_size + fs->fs_blocksize - 1) >> OCFS2_RAW_SB(fs->fs_super)->s_blocksize_bits) << OCFS2_RAW_SB(fs->fs_super)->s_blocksize_bits); } /* bail if file size is larger than reasonable :-) */ if (*buflen > 100 * 1024 * 1024) { ret = OCFS2_ET_INTERNAL_FAILURE; goto bail; } ret = ocfs2_malloc_blocks(fs->fs_io, (*buflen >> OCFS2_RAW_SB(fs->fs_super)->s_blocksize_bits), buf); if (ret) { com_err(gbls.cmd, ret, "while allocating %u bytes", *buflen); goto bail; } ret = ocfs2_file_read(ci, *buf, *buflen, 0, &got); if (ret) { com_err(gbls.cmd, ret, "while reading file at inode %"PRIu64, ci->ci_blkno); goto bail; } bail: if (ci) ocfs2_free_cached_inode(fs, ci); return ret; } /* * inode_perms_to_str() * */ void inode_perms_to_str(uint16_t mode, char *str, int len) { if (len < 11) DBGFS_FATAL("internal error"); if (S_ISREG(mode)) str[0] = '-'; else if (S_ISDIR(mode)) str[0] = 'd'; else if (S_ISLNK(mode)) str[0] = 'l'; else if (S_ISCHR(mode)) str[0] = 'c'; else if (S_ISBLK(mode)) str[0] = 'b'; else if (S_ISFIFO(mode)) str[0] = 'f'; else if (S_ISSOCK(mode)) str[0] = 's'; else str[0] = '-'; str[1] = (mode & S_IRUSR) ? 'r' : '-'; str[2] = (mode & S_IWUSR) ? 'w' : '-'; if (mode & S_ISUID) str[3] = (mode & S_IXUSR) ? 's' : 'S'; else str[3] = (mode & S_IXUSR) ? 'x' : '-'; str[4] = (mode & S_IRGRP) ? 'r' : '-'; str[5] = (mode & S_IWGRP) ? 'w' : '-'; if (mode & S_ISGID) str[6] = (mode & S_IXGRP) ? 's' : 'S'; else str[6] = (mode & S_IXGRP) ? 'x' : '-'; str[7] = (mode & S_IROTH) ? 'r' : '-'; str[8] = (mode & S_IWOTH) ? 'w' : '-'; if (mode & S_ISVTX) str[9] = (mode & S_IXOTH) ? 't' : 'T'; else str[9] = (mode & S_IXOTH) ? 'x' : '-'; str[10] = '\0'; return ; } /* * inode_time_to_str() * */ void inode_time_to_str(uint64_t timeval, char *str, int len) { time_t tt = (time_t) timeval; struct tm *tm; static const char *month_str[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; tm = localtime(&tt); snprintf(str, len, "%2d-%s-%4d %02d:%02d", tm->tm_mday, month_str[tm->tm_mon], 1900 + tm->tm_year, tm->tm_hour, tm->tm_min); return ; } /* * dump_symlink() * * Code based on similar function in e2fsprogs-1.32/debugfs/dump.c * * Copyright (C) 1994 Theodore Ts'o. This file may be redistributed * under the terms of the GNU Public License. */ static errcode_t dump_symlink(ocfs2_filesys *fs, uint64_t blkno, char *name, struct ocfs2_dinode *inode) { char *buf = NULL; uint32_t len = 0; errcode_t ret = 0; char *link = NULL; if (!inode->i_clusters) link = (char *)inode->id2.i_symlink; else { ret = read_whole_file(fs, blkno, &buf, &len); if (ret) goto bail; link = buf; } if (symlink(link, name) == -1) ret = errno; bail: if (buf) ocfs2_free(&buf); return ret; } /* * rdump_dirent() * * Code based on similar function in e2fsprogs-1.32/debugfs/dump.c * * Copyright (C) 1994 Theodore Ts'o. This file may be redistributed * under the terms of the GNU Public License. */ static int rdump_dirent(struct ocfs2_dir_entry *rec, uint64_t blocknr, int offset, int blocksize, char *buf, void *priv_data) { struct rdump_opts *rd = (struct rdump_opts *)priv_data; char tmp = rec->name[rec->name_len]; errcode_t ret = 0; rec->name[rec->name_len] = '\0'; if (!strcmp(rec->name, ".") || !strcmp(rec->name, "..")) goto bail; ret = rdump_inode(rd->fs, rec->inode, rec->name, rd->fullname, rd->verbose); bail: rec->name[rec->name_len] = tmp; return ret; } /* * rdump_inode() * * Code based on similar function in e2fsprogs-1.32/debugfs/dump.c * * Copyright (C) 1994 Theodore Ts'o. This file may be redistributed * under the terms of the GNU Public License. */ errcode_t rdump_inode(ocfs2_filesys *fs, uint64_t blkno, const char *name, const char *dumproot, int verbose) { char *fullname = NULL; int len; errcode_t ret; char *buf = NULL; char *dirbuf = NULL; struct ocfs2_dinode *di; int fd; struct rdump_opts rd_opts = { NULL, NULL, NULL, 0 }; len = strlen(dumproot) + strlen(name) + 2; ret = ocfs2_malloc(len, &fullname); if (ret) { com_err(gbls.cmd, ret, "while allocating %u bytes", len); goto bail; } snprintf(fullname, len, "%s/%s", dumproot, name); ret = ocfs2_malloc_block(fs->fs_io, &buf); if (ret) { com_err(gbls.cmd, ret, "while allocating a block"); goto bail; } ret = ocfs2_read_inode(fs, blkno, buf); if (ret) { com_err(gbls.cmd, ret, "while reading inode %"PRIu64, blkno); goto bail; } di = (struct ocfs2_dinode *)buf; if (S_ISLNK(di->i_mode)) { ret = dump_symlink(fs, blkno, fullname, di); if (ret) goto bail; } else if (S_ISREG(di->i_mode)) { if (verbose) fprintf(stdout, "%s\n", fullname); fd = open64(fullname, O_WRONLY | O_CREAT | O_TRUNC, S_IRWXU); if (fd == -1) { com_err(gbls.cmd, errno, "while opening file %s", fullname); ret = errno; goto bail; } ret = dump_file(fs, blkno, fd, fullname, 1); if (ret) goto bail; } else if (S_ISDIR(di->i_mode) && strcmp(name, ".") && strcmp(name, "..")) { if (verbose) fprintf(stdout, "%s\n", fullname); /* Create the directory with 0700 permissions, because we * expect to have to create entries it. Then fix its perms * once we've done the traversal. */ if (mkdir(fullname, S_IRWXU) == -1) { com_err(gbls.cmd, errno, "while making directory %s", fullname); ret = errno; goto bail; } ret = ocfs2_malloc_block(fs->fs_io, &dirbuf); if (ret) { com_err(gbls.cmd, ret, "while allocating a block"); goto bail; } rd_opts.fs = fs; rd_opts.buf = dirbuf; rd_opts.fullname = fullname; rd_opts.verbose = verbose; ret = ocfs2_dir_iterate(fs, blkno, 0, NULL, rdump_dirent, (void *)&rd_opts); if (ret) { com_err(gbls.cmd, ret, "while iterating directory at " "block %"PRIu64, blkno); goto bail; } fd = -1; ret = fix_perms(di, &fd, fullname); if (ret) goto bail; } /* else do nothing (don't dump device files, sockets, fifos, etc.) */ bail: if (fullname) ocfs2_free(&fullname); if (buf) ocfs2_free(&buf); if (dirbuf) ocfs2_free(&dirbuf); return ret; } /* * crunch_strsplit() * * Moves empty strings to the end in args returned by g_strsplit(), * */ void crunch_strsplit(char **args) { char *p; int i, j; i = j = 0; while(args[i]) { if (!strlen(args[i])) { j = max(j, i+1); while(args[j]) { if (strlen(args[j])) { p = args[i]; args[i] = args[j]; args[j] = p; break; } ++j; } if (!args[j]) break; } ++i; } return ; } /* * find_max_contig_free_bits() * */ void find_max_contig_free_bits(struct ocfs2_group_desc *gd, int *max_contig_free_bits) { int end = 0; int start; int free_bits; *max_contig_free_bits = 0; while (end < gd->bg_bits) { start = ocfs2_find_next_bit_clear(gd->bg_bitmap, gd->bg_bits, end); if (start >= gd->bg_bits) break; end = ocfs2_find_next_bit_set(gd->bg_bitmap, gd->bg_bits, start); free_bits = end - start; if (*max_contig_free_bits < free_bits) *max_contig_free_bits = free_bits; } } void print_contig_bits(FILE *out, struct ocfs2_group_desc *gd) { int x, free_bits, start, end = 0, avg = 0, found = 0, total = 0; int max_contig_free_bits = 0; #define HEADER_FORMAT "%-3s %-6s %-6s" #define DATA_FORMAT "%-3d %-6d %-6d" #define LEFT_HEADER "\t"HEADER_FORMAT" " #define MIDDLE_HEADER HEADER_FORMAT" " #define RIGHT_HEADER HEADER_FORMAT"\n" #define LEFT_DATA "\t"DATA_FORMAT" " #define MIDDLE_DATA DATA_FORMAT" " #define RIGHT_DATA DATA_FORMAT"\n" while (end < gd->bg_bits) { start = ocfs2_find_next_bit_clear(gd->bg_bitmap, gd->bg_bits, end); if (start >= gd->bg_bits) break; end = ocfs2_find_next_bit_set(gd->bg_bitmap, gd->bg_bits, start); free_bits = end - start; if (!free_bits) continue; if (!found) { fprintf(out, LEFT_HEADER, "###", "Start", "Length"); fprintf(out, MIDDLE_HEADER, "###", "Start", "Length"); fprintf(out, RIGHT_HEADER, "###", "Start", "Length"); } found++; x = found % 3; if (x == 1) fprintf(out, LEFT_DATA, found, start, free_bits); else if (x == 2) fprintf(out, MIDDLE_DATA, found, start, free_bits); else fprintf(out, RIGHT_DATA, found, start, free_bits); total += free_bits; if (max_contig_free_bits < free_bits) max_contig_free_bits = free_bits; } if (found) { avg = total / found; if (found % 3) fprintf(out, "\n"); } fprintf(out, "\tFree Extent Count: %d Longest: %d Average: %d\n\n", found, max_contig_free_bits, avg); } #define SYSFS_BASE "/sys/kernel/" #define DEBUGFS_PATH SYSFS_BASE "debug" #define DEBUGFS_ALTERNATE_PATH "/debug" #define DEBUGFS_MAGIC 0x64626720 errcode_t get_debugfs_path(char *debugfs_path, int len) { errcode_t ret; int err; struct stat64 stat_buf; struct statfs64 statfs_buf; char *path = DEBUGFS_PATH; err = stat64(SYSFS_BASE, &stat_buf); if (err) path = DEBUGFS_ALTERNATE_PATH; ret = stat64(path, &stat_buf); if (ret || !S_ISDIR(stat_buf.st_mode)) return O2CB_ET_SERVICE_UNAVAILABLE; ret = statfs64(path, &statfs_buf); if (ret || (statfs_buf.f_type != DEBUGFS_MAGIC)) return O2CB_ET_SERVICE_UNAVAILABLE; strncpy(debugfs_path, path, len); return 0; } errcode_t open_debugfs_file(const char *debugfs_path, const char *dirname, const char *uuid, const char *filename, FILE **fd) { errcode_t ret = 0; char path[PATH_MAX]; if (uuid) ret = snprintf(path, PATH_MAX - 1, "%s/%s/%s/%s", debugfs_path, dirname, uuid, filename); else ret = snprintf(path, PATH_MAX - 1, "%s/%s/%s", debugfs_path, dirname, filename); if ((ret <= 0) || (ret == (PATH_MAX - 1))) return O2CB_ET_INTERNAL_FAILURE; *fd = fopen(path, "r"); if (!*fd) { switch (errno) { default: ret = O2CB_ET_INTERNAL_FAILURE; break; case ENOTDIR: case ENOENT: case EISDIR: ret = O2CB_ET_SERVICE_UNAVAILABLE; break; case EACCES: case EPERM: case EROFS: ret = O2CB_ET_PERMISSION_DENIED; break; } goto out; } ret = 0; out: return ret; } void init_stringlist(struct list_head *strlist) { INIT_LIST_HEAD(strlist); } errcode_t add_to_stringlist(char *str, struct list_head *strlist) { struct strings *s; if (!str || !strlen(str)) return 0; s = calloc(1, sizeof(struct strings)); if (!s) return OCFS2_ET_NO_MEMORY; INIT_LIST_HEAD(&s->s_list); s->s_str = strdup(str); if (!s->s_str) { free(s); return OCFS2_ET_NO_MEMORY; } list_add_tail(&s->s_list, strlist); return 0; } void free_stringlist(struct list_head *strlist) { struct strings *s; struct list_head *iter, *iter2; if (list_empty(strlist)) return; list_for_each_safe(iter, iter2, strlist) { s = list_entry(iter, struct strings, s_list); list_del(iter); free(s->s_str); free(s); } } int del_from_stringlist(char *str, struct list_head *strlist) { struct strings *s; struct list_head *iter, *iter2; if (!list_empty(strlist)) { list_for_each_safe(iter, iter2, strlist) { s = list_entry(iter, struct strings, s_list); if (!strcmp(str, s->s_str)) { list_del(iter); free(s->s_str); free(s); return 1; } } } return 0; } errcode_t traverse_extents(ocfs2_filesys *fs, struct ocfs2_extent_list *el, FILE *out) { struct ocfs2_extent_block *eb; struct ocfs2_extent_rec *rec; errcode_t ret = 0; char *buf = NULL; int i; uint32_t clusters; dump_extent_list(out, el); for (i = 0; i < el->l_next_free_rec; ++i) { rec = &(el->l_recs[i]); clusters = ocfs2_rec_clusters(el->l_tree_depth, rec); /* * In a unsuccessful insertion, we may shift a tree * add a new branch for it and do no insertion. So we * may meet a extent block which have * clusters == 0, this should only be happen * in the last extent rec. */ if (!clusters && i == el->l_next_free_rec - 1) break; if (el->l_tree_depth) { ret = ocfs2_malloc_block(gbls.fs->fs_io, &buf); if (ret) goto bail; ret = ocfs2_read_extent_block(fs, rec->e_blkno, buf); if (ret) goto bail; eb = (struct ocfs2_extent_block *)buf; dump_extent_block(out, eb); ret = traverse_extents(fs, &(eb->h_list), out); if (ret) goto bail; } } bail: if (buf) ocfs2_free(&buf); return ret; } errcode_t traverse_chains(ocfs2_filesys *fs, struct ocfs2_chain_list *cl, FILE *out) { struct ocfs2_group_desc *grp; struct ocfs2_chain_rec *rec; errcode_t ret = 0; char *buf = NULL; uint64_t blkno; int i; int index; dump_chain_list(out, cl); ret = ocfs2_malloc_block(gbls.fs->fs_io, &buf); if (ret) goto bail; for (i = 0; i < cl->cl_next_free_rec; ++i) { rec = &(cl->cl_recs[i]); blkno = rec->c_blkno; index = 0; fprintf(out, "\n"); while (blkno) { ret = ocfs2_read_group_desc(fs, blkno, buf); if (ret) goto bail; grp = (struct ocfs2_group_desc *)buf; dump_group_descriptor(out, grp, index); blkno = grp->bg_next_group; index++; } } bail: if (buf) ocfs2_free(&buf); return ret; } ocfs2-tools-ocfs2-tools-1.8.6/defragfs.ocfs2/000077500000000000000000000000001347147137200206555ustar00rootroot00000000000000ocfs2-tools-ocfs2-tools-1.8.6/defragfs.ocfs2/Makefile000066400000000000000000000011411347147137200223120ustar00rootroot00000000000000TOPDIR = .. include $(TOPDIR)/Preamble.make sbindir = $(root_sbindir) SBIN_PROGRAMS = defragfs.ocfs2 DEFINES += -DVERSION=\"$(VERSION)\" INCLUDES = -I$(TOPDIR)/include -I./include DEFINES = -DVERSION=\"$(VERSION)\" CFILES = main.c record.c libdefrag.c HFILES = \ include/libdefrag.h \ include/o2defrag.h \ include/record.h OBJS = $(subst .c,.o,$(CFILES)) DIST_FILES = $(CFILES) $(HFILES) defragfs.ocfs2.8.in DIST_RULES = dist-subdircreate MANS = defragfs.ocfs2.8 defragfs.ocfs2: $(OBJS) $(LINK) dist-subdircreate: $(TOPDIR)/mkinstalldirs $(DIST_DIR)/include include $(TOPDIR)/Postamble.make ocfs2-tools-ocfs2-tools-1.8.6/defragfs.ocfs2/defragfs.ocfs2.8.in000066400000000000000000000043671347147137200241610ustar00rootroot00000000000000.TH "defragfs.ocfs2" "8" "January 2012" "Version @VERSION@" "OCFS2 Manual Pages" .SH NAME defragfs.ocfs2 \- online defragmenter for ocfs2 filesystem .SH SYNOPSIS defragfs.ocfs2 [\-c] [\-v] [\-l] [\-g] [\-h][target]... .SH DESCRIPTION .PP .B defragfs.ocfs2 reduces fragmentation of ocfs2 based file. The file targeted by .B defragfs.ocfs2 is created on ocfs2 filesystem The targeted file gets more contiguous blocks and improves the file access speed. .PP .I target is a regular file, a directory, or a device that is mounted as ocfs2 filesystem. If .I target is a directory, .B defragfs.ocfs2 reduces fragmentation of all files in it. If .I target is a device, .B defragfs.ocfs2 gets the mount point of it and reduces fragmentation of all files in this mount point. .SH OPTIONS .TP .B \-c Print the numbers of files that should be processed .TP .B \-v verbose mode, the current fragmentation count and the ideal fragmentation count are printed for each file. .IP If this option is specified, .I target is never defragmented. .TP .B \-l .B defragfs.ocfs2 will run in low io mode, which means it will regularly yield cpu to allow other processes to run. .TP .B \-g .B defragfs.ocfs2 regularly records the current progress. So if it is interrupted, the next time you run .B defragfs.ocfs2 with this option, it will resume from the recorded progress. Note that if this option is specified, other options are ignored and replaced by those that were recorded. .TP .B \-h Print help info .SH NOTES .B defragfs.ocfs2 does not support files in lost+found directory. When .I target is a device or a mount point, .B defragfs.ocfs2 doesn't defragment files in mount point of other device. .PP It is completely .B not recommended to run against a file while it is actively in another application. Since the read/write of a file involves a dlm lock, this can result in a performance slowdown to both defragfs.ocfs2 and the application due to contention. .PP If the file system's free space is fragmented, or if there is insufficient free space available, defragfs.ocfs2 may not be able to improve the file's fragmentation. .PP Non-privileged users can execute .B defragfs.ocfs2 to their own file. Therefore, it is desirable to be executed by root user. .SH AUTHOR Written by Larry Chen ocfs2-tools-ocfs2-tools-1.8.6/defragfs.ocfs2/include/000077500000000000000000000000001347147137200223005ustar00rootroot00000000000000ocfs2-tools-ocfs2-tools-1.8.6/defragfs.ocfs2/include/libdefrag.h000066400000000000000000000015361347147137200243750ustar00rootroot00000000000000#ifndef __LIB_DEFERAG_H__ #define __LIB_DEFERAG_H__ #include #include #include #include #include #include #include #define PRINT_ERR(msg) \ fprintf(stderr, "[ERROR]\t%s\n", (msg)) #define PRINT_FILE_MSG(file, msg) \ fprintf(stdout, "\"%s\":%s\n", (file), msg) #define PRINT_FILE_ERRNO(file) \ fprintf(stderr, "[ERROR]\"%s\":%s\n", (file), strerror(errno)) #define PRINT_FILE_MSG_ERRNO(file, msg) \ fprintf(stderr, "[ERROR]%s:\"%s\" - %s\n", msg, file, strerror(errno)) #define PRINT_FILE_ERR(file, msg) \ fprintf(stderr, "[ERROR]\"%s\":%s\n", (file), msg) void *do_malloc(size_t size); int do_read(int fd, void *bytes, size_t count); int do_write(int fd, const void *bytes, size_t count); unsigned int do_csum(const unsigned char *buff, int len); #endif ocfs2-tools-ocfs2-tools-1.8.6/defragfs.ocfs2/include/o2defrag.h000066400000000000000000000011571347147137200241460ustar00rootroot00000000000000#ifndef __O2DEFRAG_H__ #define __O2DEFRAG_H__ #define FS_OCFS2 "ocfs2" #define DETAIL 0x01 #define STATISTIC 0x02 #define GO_ON 0x04 #define LOW_IO 0x08 #define DEVNAME 0 #define DIRNAME 1 #define FILENAME 2 #define FTW_OPEN_FD 2000 #define ROOT_UID 0 #define SHOW_FRAG_FILES 5 #define SCHEDULE_TIME_LIMIT 5 #define RECORD_EVERY_N_FILES 50 #ifndef OCFS2_IOC_MOVE_EXT #define OCFS2_IOC_MOVE_EXT _IOW('o', 6, struct ocfs2_move_extents) #endif struct o2defrag_opt { int o_num; char *o_str; }; #define declare_opt(n, s) {\ .o_num = n,\ .o_str = s\ } #define PROGRAME_NAME "defragfs.ocfs2" #endif ocfs2-tools-ocfs2-tools-1.8.6/defragfs.ocfs2/include/record.h000066400000000000000000000030721347147137200237310ustar00rootroot00000000000000#ifndef __RECORD_H__ #define __RECORD_H__ #include #include #include #include #include #include #include #include #include #define RECORD_FILE_NAME ".ocfs2.defrag.record" //under /tmp dir #define offset_of(type, member) (unsigned long)(&((type *)0)->member) #define calc_record_file_size(argc) (RECORD_HEADER_LEN + argc * PATH_MAX) /* * Because the main way to use this tool is like * defrag -a -b -c path1 path2 path3 * This struct is used to record every path */ struct argv_node { char *a_path; struct list_head a_list; }; struct resume_record { int r_mode_flag; /* mode flag, the binary of the combination of options */ ino_t r_inode_no; /* start from the file as a inode number */ int r_argc; /* how many argv_node in the r_argvs list */ struct list_head r_argvs; /* the list of argv_node */ }; #define RECORD_HEADER_LEN offset_of(struct resume_record, r_argvs) extern void dump_record(char *base_name, struct resume_record *rr, void (*dump_mode_flag)(int mode_flag)); extern void set_record_file_path(char *path); extern void free_record(struct resume_record *rr); extern void fill_resume_record(struct resume_record *rr, int mode_flag, char **argv, int argc, ino_t inode_no); extern void free_argv_node(struct argv_node *n); extern int store_record(struct resume_record *rr); extern int load_record(struct resume_record *rr); extern void mv_record(struct resume_record *dst, struct resume_record *src); extern int remove_record(void); #endif ocfs2-tools-ocfs2-tools-1.8.6/defragfs.ocfs2/libdefrag.c000066400000000000000000000041271347147137200227440ustar00rootroot00000000000000#include #include void *do_malloc(size_t size) { void *buf; buf = calloc(size, 1); if (buf == NULL) { fprintf(stderr, "No mem\n"); exit(-1); } return buf; } int do_read(int fd, void *bytes, size_t count) { int total = 0; int ret; while (total < count) { ret = read(fd, bytes + total, count - total); if (ret < 0) { ret = -errno; if ((ret == -EAGAIN) || (ret == -EINTR)) continue; total = ret; break; } if (ret == 0) break; total += ret; } return total; } int do_write(int fd, const void *bytes, size_t count) { int total = 0; int ret; while (total < count) { ret = write(fd, bytes + total, count - total); if (ret < 0) { ret = -errno; if ((ret == -EAGAIN) || (ret == -EINTR)) continue; else goto error; } total += ret; } return total; error: return ret; } static inline unsigned short from32to16(unsigned int x) { /* add up 16-bit and 16-bit for 16+c bit */ x = (x & 0xffff) + (x >> 16); /* add up carry.. */ x = (x & 0xffff) + (x >> 16); return x; } unsigned int do_csum(const unsigned char *buff, int len) { int odd; unsigned int result = 0; if (len <= 0) goto out; odd = 1 & (unsigned long) buff; if (odd) { #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ result += (*buff << 8); #else result = *buff; #endif len--; buff++; } if (len >= 2) { if (2 & (unsigned long) buff) { result += *(unsigned short *) buff; len -= 2; buff += 2; } if (len >= 4) { const unsigned char *end = buff + ((unsigned)len & ~3); unsigned int carry = 0; do { unsigned int w = *(unsigned int *) buff; buff += 4; result += carry; result += w; carry = (w > result); } while (buff < end); result += carry; result = (result & 0xffff) + (result >> 16); } if (len & 2) { result += *(unsigned short *) buff; buff += 2; } } if (len & 1) #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ result += *buff; #else result += (*buff << 8); #endif result = from32to16(result); if (odd) result = ((result >> 8) & 0xff) | ((result & 0xff) << 8); out: return result; } ocfs2-tools-ocfs2-tools-1.8.6/defragfs.ocfs2/main.c000066400000000000000000000355461347147137200217620ustar00rootroot00000000000000#define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ocfs2/ocfs2.h" #include "ocfs2/bitops.h" #include #include #include static char lost_found_dir[PATH_MAX + 1]; static int mode_flag; static unsigned int regular_count; static unsigned int succeed_count; static unsigned int skipped_count; static unsigned int processed_count; static uid_t current_uid; static int should_stop; struct resume_record rr; static void usage(char *progname) { printf("usage: %s [-c] [-v] [-l] [-g] [-h] [FILE | DIRECTORY | DEVICE]...\n", progname); printf("\t-c\t\tCalculate how many files will be processed\n"); printf("\t-v\t\tVerbose mode\n"); printf("\t-l\t\tLow io rate mode\n"); printf("\t-g\t\tResume last defrag progress\n"); printf("\t-h\t\tShow this help\n"); exit(0); } /* * get_dev_mount_point() - Get device's ocfs2 mount point. * @devname: the device's name. * @mount_point: the mount point. * * a block device may be mounted on multiple mount points, * this function return on first match */ static int get_dev_mount_point(const char *devname, char *mount_point) { /* Refer to /etc/mtab */ const char *mtab = MOUNTED; FILE *fp = NULL; struct mntent *mnt = NULL; struct stat64 sb; if (stat64(devname, &sb) < 0) { PRINT_FILE_MSG_ERRNO(devname, "While getting mount point"); return -1; } fp = setmntent(mtab, "r"); if (fp == NULL) { PRINT_ERR("Couldn't access /etc/mtab"); return -1; } while ((mnt = getmntent(fp)) != NULL) { struct stat64 ms; /* * To handle device symlinks, we see if the * device number matches, not the name */ if (stat64(mnt->mnt_fsname, &ms) < 0) continue; if (sb.st_rdev != ms.st_rdev) continue; endmntent(fp); if (strcmp(mnt->mnt_type, FS_OCFS2) == 0) { strncpy(mount_point, mnt->mnt_dir, PATH_MAX); return 0; } PRINT_FILE_MSG(devname, "Not ocfs2 format"); return -1; } endmntent(fp); PRINT_FILE_MSG(devname, "Is not mounted"); return -1; } /* * get_file_backend_info() - Get file's mount point and block dev that it's on. * @file: the device's name. * @dev: dev path * @mount_point: the mount point. * * The file mush be on ocfs2 partition */ static int get_file_backend_info(const char *file, char *dev, char *mount_point) { int maxlen = 0; int len, ret; FILE *fp = NULL; /* Refer to /etc/mtab */ const char *mtab = MOUNTED; char real_path[PATH_MAX + 1]; struct mntent *mnt = NULL; struct statfs64 fsbuf; /* Get full path */ if (realpath(file, real_path) == NULL) { PRINT_FILE_MSG_ERRNO(file, "Getting realpath failed"); return -1; } if (statfs64(real_path, &fsbuf) < 0) { PRINT_FILE_MSG_ERRNO(file, "Getting fs state failed"); return -1; } if (fsbuf.f_type != OCFS2_SUPER_MAGIC) return -1; fp = setmntent(mtab, "r"); if (fp == NULL) { PRINT_ERR("Couldn't access /etc/mtab"); return -1; } while ((mnt = getmntent(fp)) != NULL) { if (mnt->mnt_fsname[0] != '/') continue; len = strlen(mnt->mnt_dir); ret = memcmp(real_path, mnt->mnt_dir, len); if (ret != 0) continue; if (maxlen >= len) continue; maxlen = len; strncpy(mount_point, mnt->mnt_dir, PATH_MAX); strncpy(dev, mnt->mnt_fsname, strlen(mnt->mnt_fsname) + 1); } endmntent(fp); return 0; } /* * is_ocfs2() - Test whether the file on an ocfs2 filesystem. * @file: the file's name. */ static int is_ocfs2(const char *file) { struct statfs64 fsbuf; /* Refer to /etc/mtab */ char file_path[PATH_MAX + 1]; /* Get full path */ if (realpath(file, file_path) == NULL) return 0; if (statfs64(file_path, &fsbuf) < 0) return 0; if (fsbuf.f_type != OCFS2_SUPER_MAGIC) return 0; return 1; } /* * calc_entry_counts() - Calculate file counts. * @file: file name. * @buf: file info. * @flag: file type. * @ftwbuf: the pointer of a struct FTW. */ static int calc_entry_counts(const char *file, const struct stat64 *buf, int flag, struct FTW *ftwbuf) { if (lost_found_dir[0] != '\0' && !memcmp(file, lost_found_dir, strnlen(lost_found_dir, PATH_MAX))) { return 0; } if (S_ISREG(buf->st_mode)) regular_count++; return 0; } /* * print_progress - Print defrag progress * @file: file name. * @start: logical offset for defrag target file * @file_size: defrag target filesize */ static void print_progress(const char *file, int result) { char *str_res = "Success"; if (result) str_res = "Failed"; if (mode_flag & DETAIL) printf("[%u/%u]%s:%s\n", processed_count, regular_count, file, str_res); else printf("\033[79;0H\033[K[%u/%u]%s:%s\t", processed_count, regular_count, file, str_res); fflush(stdout); } static int do_file_defrag(const char *file, const struct stat64 *buf, int flag, struct FTW *ftwbuf) { /* Defrag the file */ int ret; int fd; struct ocfs2_move_extents me = { .me_start = 0, .me_len = buf->st_size, .me_flags = OCFS2_MOVE_EXT_FL_AUTO_DEFRAG, }; fd = open64(file, O_RDWR, 0400); if (fd < 0) { PRINT_FILE_MSG_ERRNO(file, "Open file failed"); return -1; } ret = ioctl(fd, OCFS2_IOC_MOVE_EXT, &me); if (ret < 0) PRINT_FILE_MSG_ERRNO(file,"Move extent failed"); close(fd); return ret; } /* * check_file() - Check file attributes. * @file: the file's name. * @file_stat: the pointer of the struct stat64. */ static int check_file(const char *file, const struct stat64 *file_stat) { const struct stat64 *buf; struct stat64 stat_tmp; int ret; buf = file_stat; if (!buf) { ret = stat64(file, &stat_tmp); if (ret < 0) { PRINT_FILE_MSG_ERRNO(file, "Get file stat failed"); return 0; } buf = &stat_tmp; } if (buf->st_size == 0) { if (mode_flag & DETAIL) PRINT_FILE_MSG(file, "File size is 0... skip"); return 0; } /* Has no blocks */ if (buf->st_blocks == 0) { if (mode_flag & DETAIL) PRINT_FILE_MSG(file, "File has no blocks"); return 0; } if (current_uid != ROOT_UID && buf->st_uid != current_uid) { if (mode_flag & DETAIL) { PRINT_FILE_MSG(file, "File is not current user's file or current user is not root"); } return 0; } return 1; } /* * defrag_file_ftw() - Check file attributes and call ioctl to defrag. * @file: the file's name. * @buf: the pointer of the struct stat64. * @flag: file type. * @ftwbuf: the pointer of a struct FTW. */ static int defrag_file_ftw(const char *file, const struct stat64 *buf, int flag, struct FTW *ftwbuf) { int ret; time_t run_limit = SCHEDULE_TIME_LIMIT; static time_t sched_clock; time_t now, diff; static int count; if (rr.r_inode_no) { if (buf->st_ino != rr.r_inode_no) goto skip; else rr.r_inode_no = 0; } if (should_stop || count >= RECORD_EVERY_N_FILES) { rr.r_inode_no = buf->st_ino; if (should_stop) { PRINT_FILE_MSG(file, "Interrupted"); printf("%s ---- interrupted\n", file); } else { rr.r_inode_no = 0; count = 0; } if (mode_flag & DETAIL) printf("\nRecording...\n"); ret = store_record(&rr); if (ret) PRINT_ERR("Record failed"); else if (mode_flag & DETAIL) printf("Record successfully\n" "Use -g option to resume progress\n"); if (should_stop) exit(0); } else count++; if (mode_flag & LOW_IO) { if(sched_clock == 0) sched_clock = time(NULL); now = time(NULL); diff = now - sched_clock; if (diff > run_limit) { printf("===========\n"); sched_yield(); sched_clock = now; } } if (lost_found_dir[0] != '\0' && !memcmp(file, lost_found_dir, strnlen(lost_found_dir, PATH_MAX))) { if (mode_flag & DETAIL) PRINT_FILE_MSG(file, "In lost+found dir... ignore"); return 0; } if (!S_ISREG(buf->st_mode)) { if (mode_flag & DETAIL) PRINT_FILE_MSG(file, "Not regular file... ignore"); return 0; } processed_count++; ret = check_file(file, buf); if (!ret) { skipped_count++; goto out; } ret = do_file_defrag(file, buf, flag, ftwbuf); if (ret == 0) succeed_count++; out: print_progress(file, ret); return 0; skip: if (mode_flag & DETAIL) PRINT_FILE_MSG(file, "already done... skip\n"); processed_count++; skipped_count++; return 0; } static int defrag_dir(const char *dir_path) { //lost+found is skipped! char *real_dir_path = do_malloc(PATH_MAX); char *dev_path = do_malloc(PATH_MAX); char *mount_point = do_malloc(PATH_MAX); char *lost_found = lost_found_dir; int mount_point_len; int flags = FTW_PHYS | FTW_MOUNT; int ret; if (!is_ocfs2(dir_path)) { PRINT_FILE_MSG(dir_path, "Not within ocfs2 fs"); return 1; } if (realpath(dir_path, real_dir_path) == NULL) { PRINT_FILE_MSG(dir_path, "Couldn't get full path"); return 1; } ret = get_file_backend_info(dir_path, dev_path, mount_point); if (ret < 0) { PRINT_FILE_MSG(dir_path, "can not get file back info"); return ret; } if (access(dir_path, R_OK) < 0) { perror(dir_path); return 1; } mount_point_len = strnlen(mount_point, PATH_MAX); snprintf(lost_found, PATH_MAX, "%s%s", mount_point, "/lost+found"); /* Not the case("defragfs.ocfs2 mount_point_dir") */ if (dir_path[mount_point_len] != '\0') { /* * "defragfs.ocfs2 mount_point_dir/lost+found" * or "defragfs.ocfs2 mount_point_dir/lost+found/" */ int lf_len = strnlen(lost_found, PATH_MAX); if (strncmp(lost_found, dir_path, lf_len) == 0 && (dir_path[lf_len] == '\0' || dir_path[lf_len] == '/')) { PRINT_FILE_MSG(dir_path, "defrag won't work within lost+found\n"); } } nftw64(real_dir_path, calc_entry_counts, FTW_OPEN_FD, flags); if (mode_flag & STATISTIC) { printf("%8d files should be defraged in [%s]\n", regular_count, real_dir_path); return 1; } nftw64(dir_path, defrag_file_ftw, FTW_OPEN_FD, flags); return 0; } static int defrag_block_dev(char *dev_path) { char *mnt_point; int ret; mnt_point = do_malloc(PATH_MAX); ret = get_dev_mount_point(dev_path, mnt_point); if (ret < 0) { PRINT_FILE_MSG(dev_path, "Could not find mount point"); return ret; } if (mode_flag & DETAIL) printf("ocfs2 defragmentation for device(%s)\n", dev_path); return defrag_dir(mnt_point); } static int defrag_file(char *file_path) { struct stat64 file_stat; int ret; if (!is_ocfs2(file_path)) { PRINT_FILE_MSG(file_path, "Not on ocfs2 fs\n"); return -1; } ret = stat64(file_path, &file_stat); if (ret < 0) { PRINT_FILE_MSG(file_path, "get file stat error"); return -1; } if (!S_ISREG(file_stat.st_mode)) { if (mode_flag & DETAIL) PRINT_FILE_MSG(file_path, "Not regular file... ignore"); return -1; } regular_count++; /* Empty file */ if (file_stat.st_size == 0) { if (mode_flag & DETAIL) PRINT_FILE_MSG(file_path, "File size is 0... skip"); skipped_count++; return -1; } /* Has no blocks */ if (file_stat.st_blocks == 0) { if (mode_flag & DETAIL) PRINT_FILE_MSG(file_path, "File has no blocks"); skipped_count++; return -1; } if (current_uid != ROOT_UID && file_stat.st_uid != current_uid) { if (mode_flag & DETAIL) { PRINT_FILE_MSG(file_path, "File is not current user's file or current user is not root"); } return -1; } ret = do_file_defrag(file_path, &file_stat, 0, 0); if (ret < 0) { PRINT_FILE_ERRNO(file_path); return -1; } printf("%s: Succeeded\n", file_path); return 0; } static void handle_signal(int sig) { switch (sig) { case SIGTERM: case SIGINT: printf("\nProcess Interrupted. signale = %d\n", sig); should_stop = 1; } } static struct o2defrag_opt opt_table[] = { declare_opt(DETAIL, "-v"), declare_opt(STATISTIC, "-c"), declare_opt(GO_ON, "-g"), declare_opt(LOW_IO, "-o"), }; static void dump_mode_flag(int mode_flag) { int i = 0; for (i = 0; i < sizeof(opt_table)/sizeof(opt_table[0]); i++) { if (mode_flag & opt_table[i].o_num) printf(" %s ", opt_table[i].o_str); } } static int parse_opt(int argc, char **argv, int *_mode_flag) { char opt; if (argc == 1) return 0; while ((opt = getopt(argc, argv, "gvclh")) != EOF) { switch (opt) { case 'v': *_mode_flag |= DETAIL; break; case 'c': *_mode_flag |= STATISTIC; break; case 'g': *_mode_flag |= GO_ON; break; case 'l': *_mode_flag |= LOW_IO; break; case 'h': usage(PROGRAME_NAME); exit(0); default: break; } } return optind; } static void init_signal_handler(void) { if (signal(SIGTERM, handle_signal) == SIG_ERR) { PRINT_ERR("Could not set SIGTERM"); exit(1); } if (signal(SIGINT, handle_signal) == SIG_ERR) { PRINT_ERR("Could not set SIGINT"); exit(1); } } static void print_version(char *progname) { printf("%s %s\n", progname, VERSION); } /* * main() - ocfs2 online defrag. * * @argc: the number of parameter. * @argv[]: the pointer array of parameter. */ int main(int argc, char *argv[]) { int ret; char dir_name[PATH_MAX + 1]; char dev_name[PATH_MAX + 1]; struct stat64 buf; int _mode_flag = 0; int index; struct resume_record rr_tmp; struct list_head *pos; init_signal_handler(); print_version(PROGRAME_NAME); index = parse_opt(argc, argv, &_mode_flag); if (_mode_flag & GO_ON) { ret = load_record(&rr_tmp); if (ret) { PRINT_ERR("Load record failed...exit"); exit(0); } mv_record(&rr, &rr_tmp); printf("Record detected...\n Start as:\n"); dump_record(PROGRAME_NAME, &rr, dump_mode_flag); } else { if (index == argc || index == 0) { usage(PROGRAME_NAME); exit(0); } fill_resume_record(&rr, _mode_flag, &argv[index], argc - index, 0); } mode_flag = rr.r_mode_flag & ~GO_ON; current_uid = getuid(); /* Main process */ list_for_each(pos, &rr.r_argvs) { struct argv_node *n = list_entry(pos, struct argv_node, a_list); char *path = n->a_path; succeed_count = 0; regular_count = 0; skipped_count = 0; memset(dir_name, 0, PATH_MAX + 1); memset(dev_name, 0, PATH_MAX + 1); memset(lost_found_dir, 0, PATH_MAX + 1); if (lstat64(path, &buf) < 0) { perror("Failed to get file info:"); printf("%s\n", path); continue; } /* Handle i.e. lvm device symlinks */ if (S_ISLNK(buf.st_mode)) { struct stat64 buf2; if (stat64(path, &buf2) == 0 && S_ISBLK(buf2.st_mode)) buf = buf2; } if (S_ISBLK(buf.st_mode)) { /* Block device */ ret = defrag_block_dev(path); } else if (S_ISDIR(buf.st_mode)) { /* Directory */ ret = defrag_dir(path); } else if (S_ISREG(buf.st_mode)) { /* Regular file */ ret = defrag_file(path); continue; } else { /* Irregular file */ printf("irregular file\n"); continue; } if (!ret) { printf("\n\tSuccess:\t\t\t[ %u/%u ]\n", succeed_count, regular_count); printf("\n\tSkipped:\t\t\t[ %u/%u ]\n", skipped_count, regular_count); printf("\n\tFailure:\t\t\t[ %u/%u ]\n", regular_count - succeed_count - skipped_count, regular_count); } free_argv_node(n); } remove_record(); return 0; } ocfs2-tools-ocfs2-tools-1.8.6/defragfs.ocfs2/record.c000066400000000000000000000112351347147137200223010ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include #include #include #include #define MAX_RECORD_FILE_SIZE (2<<20) static char record_path[PATH_MAX] = "/tmp/"RECORD_FILE_NAME; void mv_record(struct resume_record *dst, struct resume_record *src) { dst->r_argc = src->r_argc; dst->r_inode_no = src->r_inode_no; dst->r_mode_flag = src->r_mode_flag; INIT_LIST_HEAD(&dst->r_argvs); list_splice(&src->r_argvs, &dst->r_argvs); } void dump_record(char *base_name, struct resume_record *rr, void (*dump_mode_flag)(int mode_flag)) { struct list_head *pos; struct argv_node *n; int mode_flag = rr->r_mode_flag; printf("%s", base_name); dump_mode_flag(mode_flag); list_for_each(pos, &rr->r_argvs) { n = list_entry(pos, struct argv_node, a_list); printf(" %s ", n->a_path); } puts(""); } void free_argv_node(struct argv_node *n) { list_del(&n->a_list); if (n->a_path) free(n->a_path); free(n); } static inline struct argv_node *allocate_argv_node(int len) { struct argv_node *n; n = do_malloc(sizeof(struct argv_node)); n->a_path = do_malloc(len); return n; } void free_record(struct resume_record *rr) { struct list_head *pos; struct argv_node *n; list_for_each(pos, &rr->r_argvs) { n = list_entry(pos, struct argv_node, a_list); free_argv_node(n); } } static int read_record(int fd, struct resume_record **p, int *len) { struct stat s; void *content_buf = NULL; if (fstat(fd, &s)) goto error; content_buf = do_malloc(s.st_size); if (do_read(fd, content_buf, s.st_size) != s.st_size) goto error; if (p) *p = content_buf; if (len) *len = s.st_size; return 0; error: if (content_buf) free(content_buf); if (p) *p = NULL; if (len) *len = 0; return -errno; } static int is_record_file_valid(void *buf, int len) { int data_len = len - sizeof(unsigned int); unsigned int check_sum = *(int *)(buf + data_len); if (check_sum == do_csum(buf, data_len)) return 1; return 0; } static int __store_record(int fd, struct resume_record *rr) { struct list_head *pos; struct argv_node *n; int index = 0; unsigned char *buf = do_malloc(MAX_RECORD_FILE_SIZE); int len; int ret; memcpy(buf, rr, RECORD_HEADER_LEN); index += RECORD_HEADER_LEN; list_for_each(pos, &rr->r_argvs) { n = list_entry(pos, struct argv_node, a_list); len = strlen(n->a_path) + 1; if (index + len > MAX_RECORD_FILE_SIZE) { PRINT_ERR("Arg too long"); ret = -1; goto out; } strcpy((char *)&buf[index], n->a_path); index += len; } *(int *)&buf[index] = do_csum(buf, index); index += sizeof(int); ret = do_write(fd, buf, index); out: free(buf); if (ret < 0) return ret; return 0; } void fill_resume_record(struct resume_record *rr, int mode_flag, char **argv, int argc, ino_t inode_no) { int i; int len; struct argv_node *node; rr->r_mode_flag = mode_flag; rr->r_argc = argc; rr->r_inode_no = inode_no; INIT_LIST_HEAD(&rr->r_argvs); for (i = 0; i < argc; i++) { len = strlen(argv[i]) + 1; node = do_malloc(sizeof(struct argv_node)); node->a_path = do_malloc(len); strcpy(node->a_path, argv[i]); list_add_tail(&node->a_list, &rr->r_argvs); } } int remove_record(void) { int ret; ret = unlink(record_path); if (ret && errno != ENOENT) { perror("while deleting record file"); return -errno; } return 0; } int store_record(struct resume_record *rr) { int fd; fd = open(record_path, O_TRUNC | O_CREAT | O_WRONLY, 0600); if (fd < 0) { perror("while opening record file"); return -errno; } if (__store_record(fd, rr)) goto out; if (fsync(fd)) goto out; close(fd); return 0; out: return -1; } static int __load_record(int fd, struct resume_record *rr) { int ret = -1, i; struct resume_record *rr_tmp = NULL; char **argv = NULL; int argc; char *p; int len = 0; if (read_record(fd, &rr_tmp, &len)) goto out; if (!is_record_file_valid(rr_tmp, len)) goto out; argc = rr_tmp->r_argc; argv = do_malloc(sizeof(char *) * argc); p = (void *)rr_tmp + RECORD_HEADER_LEN; for (i = 0; i < argc; i++, p++) { argv[i] = (char *)p; p += strlen(p) + 1; } fill_resume_record(rr, rr_tmp->r_mode_flag, argv, argc, rr_tmp->r_inode_no); ret = 0; out: if (rr_tmp) free(rr_tmp); if (argv) free(argv); return ret; } int load_record(struct resume_record *rr) { int ret; int record_fd = open(record_path, O_RDONLY, 0700); if (record_fd < 0) { perror("while opening record file"); return -errno; } ret = __load_record(record_fd, rr); close(record_fd); return ret; } ocfs2-tools-ocfs2-tools-1.8.6/documentation/000077500000000000000000000000001347147137200207325ustar00rootroot00000000000000ocfs2-tools-ocfs2-tools-1.8.6/documentation/.gitignore000066400000000000000000000000061347147137200227160ustar00rootroot00000000000000*.pdf ocfs2-tools-ocfs2-tools-1.8.6/documentation/01-cluster_start_stop.odt000066400000000000000000000310741347147137200256300ustar00rootroot00000000000000PK4^2 ''mimetypeapplication/vnd.oasis.opendocument.textPK4Configurations2/statusbar/PK4'Configurations2/accelerator/current.xmlPKPK4Configurations2/floater/PK4Configurations2/popupmenu/PK4Configurations2/progressbar/PK4Configurations2/menubar/PK4Configurations2/toolbar/PK4Configurations2/images/Bitmaps/PK4 layout-cachecd`d(Pa``d``1x$0 PK(PK4 content.xmlioF B-9N'qM&X.X#r(͚3CCDvd]hUs7>~y3e6`wG"6.&w>?/LX$&rU~l p<|ŁNBWg,~<[y9Mnܯvir{ubl8^Uz^J/`q f`70v VveH/&+K SiW e^:{\+ۚuz,/#F0PLGB+"n&ʭ8\.&oOyY eIh2]co L|sTkfGדr͟ܓqOsGtÙ*$)1jU̱,ב  Iђ~ {+'>\y"}|D%hɑI#T) 0k.j >x q#m,R)S9 55cS9| "H8u7&s$(π r=1Sp)a\fI13LB삨(rWVj2޼:zǏ pƩQl #K:Q(3q5\FASA@;eb*S KwHL%A`Jm,Ũ,UA별h >>U40OqKH/ 8qu&x񷷢NuAdlsªLIaShHAV|DW:zudіeұJ &ǛtȟGdI;DU!FB`7=xՑp0 õ >gۯy.}CQTD2CqxmYb$x|"FB?z(|@$ ~y9]7Eo4Cux% ަ8#10 ]1?1O1jVu*MʰMx b2ŵ" ·CSQppp=]iؤ2=ePԳ~ɶ7_f;$ Ҍm!5"_4n%Ƕ"`?Xr{s|m?;4a . ^)G]vL/2ws1 S S3 1Ƌz qaLnWV0|o!C#o&1|#=:eщM*)-Hb LX=rIQp-,)5ѹ9q,'LyM[ʑ0,W9r"zl**bܖUPn]?uM6/k6$h\]Þ@ |˄lLmDǸ}F_B nx8KV@rgZ:ߣV pdj1xF'o fraҩEAzl6;QuԑrZ.j.$aPp}ǁO3my`aSN"Ȯݑ 6vhho_RC) Ow<:2mh8aoPTj!g@XcXLy3kFǑ.m&㺘-t'ar 5} G͍7fΔՇl@Ÿ "?M4 ҔUFU,^CȤ!kJ&&OT*_@~{cY'ܱ&SPOg9qm$4<~U%-440L!k66Wi `CĦ$5l`ۏB&c;":?qg:ƹ(= .!$a +QWg #w+KF2@9E< WuFD9.dm"L gH FqK{d#bmhsQ>XJ|Q%jLnhjڦ'DM [o;KLF"O۬A@_h8+ȻLu^B/f~>lν&OmxÁ\5OҒS$t Eo{>%wӰdՁ{XcGpK3Ng!q8Rd֗;NJDDq+ih@3{1Op  Ɏ ٯQtbH^xEDiNOė-l~H0w|eh~6Bw$F}^h]afA6xCH V%gZj5BCt }Xk`#%/T /ex3kb,.R ҃Q*M10[g783)m7 .\TX EITJb6=CNIDcSNk"ek HH: C!fj۬RN4kE*,(q4eD{[1H<&hdHj5EIcCsX '%8<`XFgcBEg rQ?)A鐜fbb\-L(6ֳz u^̩>X$s'S uwڜ9.]æߙG`BA_?~pr!e8<?jbR-| 0kmRJ,QQ=͜ڪrMKTė]m {;}!hh}8 XbmW1zk&Y7tfe!كM3"s߀mXå-3uН7һ?iZ"bu ~op{ _kZc?4-VK *M~2ֳ˞ Xo X -V  r QEĄAn(x8O5N|+t]9߰V>1 wh*xE VYNJ@k!(߿hnom0=^6)W=-fƞDsNu߁\0l@˟M(,BjMlzr GX(&VigUH~yoImqaM0wZر Uy5zWJTOPGw1ݧ;B[#xvQWP7OA34+[OM)O;1L^[mc6'TdԀJ.^6ɯR?I"ɞI9LT9Ӳ@ ;~dy Js "ȄX^#/J*'jЈ~`ziBflGk6s`״*x4M* Ӗf%xm Yr$|s:`9[*#tR(Ϳ53a ΁ۂ:FE5uM' )*hjFa26, bM춧.s d [0]JwaUeP0ip}60ĵugJ(1)tkTk&&]Cji5˧LKe;&8hlRI؂!S5!}c^ɬ6U@x`a%9B XᒷSasD^_#sHB̦C}}V./\aTi蹑L]Zt2XN)Ӭ\<5B['Hw+`La4juD"heP?:0p28d7*b՞e  r&:O.f5AtsJegdh% 9GNbE ^I0xTCBqm#*6R*:1O%pvOa]NAj76qd 5CU$mT5N_r1`94HZۤuwomzso6in|bRYf`;;[MZ N)Caspx'~FRhH Uǝ>YygK0=Fc043i>5-Mt O҅A 6.3jF'3I#鿟`@A/YUΓɹY<LJ!\k9/-xY49аJ[MM^؀ 4lў]g>HJzlgM- w>4lšI@s@w,, 7xV3O'C ̫`.#'5;x.`(i}Fts$%t窧PK@tbPK4T##meta.xml OpenOffice.org/2.0$Linux OpenOffice.org_project/680m5$Build-90112006-05-15T14:11:522006-06-30T13:52:21en-US3PT15M35SPK4Thumbnails/thumbnail.pngi4ŵ1HT$UTN6b5t5DƾMQKRK"jVhB,4Zk_9>{iP)qEqN?NEq֖.gK^Ku m1nw;G5)gP䋣~:IL}Bip.ԡ$ ? MЉH1fo{U7ˡIcch Vv0 }q垌tо-uV<++qC@֮RFlDQ<%qcp`/!,"aY4xV:JM7[5(bO2uKc9 f,eM|nͿsG%֜AH/9>~d r3&vuvu28t[ 6E{6 :獥1va~. HzΌ(Hݳ^XN󡏽 9d^OW<"3|NDm<8YU꿙uc v 9Ơ8s#T?z f l4qFfZ!) s;/Ujd%mL^xߡM,PvSVeSʂuUPZxa,q HӘ~l!oi9BIOx5AIZo&փã|;~;`cZÉy*x˴*dc ζgܼP^4S1uIEU^z=ߑGD+_ e8g$/`ݒPТ(p2lx܅K*I5"v@3Y6MR[w5::(xCx=}Ch N!F; VJˆ^ ο"&MC'u2D#(IҊsW$B.mOH@[+ %"B;ݎ:ǻ +GRq},\xJ<'`4\ *ٰ}Ү 8$%,y?Ჯ@ bAD"X>:񮸛(4D=44{\2g.9eA9` E6(?5*EܕM*U) B+;a>Q'_6vĈvD̺q,3V]^{Џe_vAgs&vwpTt]͙$20I$.p,Rx j+7%x/Qeey~N1QQg]LC)eV 6Ix,)4m]Uz nՓ:fgLL =b7{{މ*51J_䨲Tre3k)S @85l"&Ü̥Rz_mbkF?qdDO{vet{ap[Ue[L1\VD5X.]  y+'w;#7COP<,eQȓcգoPKع]PK4META-INF/manifest.xmlMn F9ަDj =$ ő6@X)H;ۈAhky+zX`uiP}F`k0 լjѶN-ןדi(.N,0J=67Z:޶U]+*.Sz]#wnS^tñzA)4.HCݕE'3RJgL6G/q'k^PSE]kj|-" TLa'8 NG\ ͎alⰵ I> +o6ӛ{nR~{rWPKOPK4^2 ''mimetypePK4MConfigurations2/statusbar/PK4'Configurations2/accelerator/current.xmlPK4Configurations2/floater/PK4Configurations2/popupmenu/PK4JConfigurations2/progressbar/PK4Configurations2/menubar/PK4Configurations2/toolbar/PK4Configurations2/images/Bitmaps/PK4( -layout-cachePK43.U content.xmlPK4@tb styles.xmlPK4T##meta.xmlPK4ey^6 J YThumbnails/thumbnail.pngPK4ع] 'settings.xmlPK4Ol,META-INF/manifest.xmlPK(-ocfs2-tools-ocfs2-tools-1.8.6/documentation/02-mount_umount.odt000066400000000000000000000252211347147137200244340ustar00rootroot00000000000000PK<4^2 ''mimetypeapplication/vnd.oasis.opendocument.textPK<4Configurations2/PK<4 Pictures/PK<4 layout-cachecd`d(g``d``Q1܀ PKp0PK<4 content.xmlko6 b -ڇ4)`;WvYKJR^p?fm(>׳p/n6؍FEo<(bbwyq{oUX&*rQ(Vgp0nEšFÂPЇt[1.8O[qk7=Kgt }:|aj÷&RRKn=*n3Y\ͭ-b1XLJφッ!5\Y錠x(2x0\X)}&s3"nf&smz'$9t? {r^3uNutYhÇtyeqDxjeҿ績^rg \#@3֊jktu .DV REY.♜)ƀѝ2VwTw,HyfZڠ-ժ""d[ @d -Mo in}ri=d|{Le ,z&()``kG+5;ZW aGEK-.궆堄6$,0Dus>.Ed*pA{ ioVb蘢h4OHd,y H(lo=Pƻr=y\tzܹw]z pչ]z p=\Ou==?[۾>?J>bUY*mLة 40~RY OI /_^̥aC!bU$L˓8C>`t'Ni$/ Tp; 2lSQÝ*6Nt"3iY*"1~1`ESf T-àܸU\8őruL8Zߦ87DC1@->KaWc/E9q34I5Zpday6лYfس}>ll&i  'Jj$J/4>Emͧ ؛ @6:)F.B9:1"5gFˬtQMe&#BcԫKp ax;XȆt(p~hN=qD ';Uf b&-9:HJ|LHK+ VC㲰?!rEٴCp-m7JA({C:VbX{&.㜈.漘Z< m(Df!a[<FB/:v%$*X⹈\ѩ2pDvD7?}3(Tqi.ODXOՂߡ p$ B'Bg\c"];7`X[HYxAx9A;h2l|wD6Bh=\ eO-PU4~X]nfi$EA4,{h5UC[VYM]$Hϔ1ˀ(}|duBr[d7`.TV(ZlD-Vz_)* 89ǐ(ЙwPX l&Z@9mO̜,׀ P|qH):.k̸Y7BP*ghiQWQ,Tittp* {u`Q都& 7b3  LBzN! xW;M%j.xk.(2( ǺG\=&_ō7nMVORCZVi ,^9 ݗ*,p li,nsEC9离BUAޱ} xgx&/]ZNPN`e,`($q\ܧޜ^xyWx@䴨ygZN3x1RP$WG/ћ{8ɩ}v7 {sgb-˭ >[Ao]j-Ƕ]j_cx._[ސMG'+RS-2q {RpNE _脩On4jyUh;wE,tH&oBW]=*vJDG?Cu|JBUL&}$7EW? !Q|:2a1OxSV^ G? ]kY4 A㎙LY7%x*~\FaLsK$ %AjV"Ǧ2nQs૕՝׎ǀ} v>ۅ[/)(Ɍ$G U QC_ [ѻg㐌,ñO4 a蒱e)aq]яW..<o !6?h4`LrE94\LPa&tخ>]r<<@P^MU!EN=^@ `Kc~7:HU&(L)bJZSN6ԡL+ݞ Q':ٮ{&B? ]ѕT>w7D-;MS8/JAu2BSdДtƸ'ؙ^.Ul&Q8Wf-uaD0a4z&Ù{҅>J$%E畵R\4%"2c*p#Ѿ=Օ*SambuBRD U[/cn]XTl$,ڏpg/U ;¢ ౯h=A@bLY;2 hk./ND8lˬ3[Rq ѱ͠"|:\+fm,ǻqGڏ ¾[`/kd=Cؠ'؈w'[o_D ˆ%@o܏V"l}*LvE('  tJVtm糧d:vNӝ8k;M$XnA1x I~х[d{xwhAoZoK?.vs4PKu>PK<4 styles.xmlYM6W*-˻xs) nrhP@Rko~}([n[8<oCǒE/D**&Ng8"<9MhxhFֹp~eDE03nZ5%QkEEx3iv)7bMnlMzd+[p8;0uBL;1uQ1(e5=aqdą:Ip7r*֖pZ2ʳ0bSI:K[3ؐ-C5>;UHhڰӏ.X#gL>cYN]`{$&oӡB]I5<0ڈr(hK@ bdGSSvg$cV;&Fc}'EPI&WB"83H'H ,EN$A*3PŽI'gt/qU,n7$Nj )kU\WD&tR:`=k*AtTI0sŰa1΁R:F% LpYCs"Kc+ ;Hi8zXL!0LVlb&@yNL}2Di]8rQ)#q->MDg c`.*map;k ۉb46k4#uo؅DS2u|Q4*kXppY6pI-LVULp9A"׈Eˌ>+h@DٍWط*xn$^.5i ©$)14ngV `aeb${"l47 "aRFo]Iq8YFN;!bOtazpet}ɱbJ=ȗ.{!i[$bZ1ΑQ , ]UJ D͜tc[iQRԁ[!>E1koM۳eP0;i fHܑ&cOMZwI6i=&wI6i-o6iWB}-m Ż f! UBQm? ?Û 'ƚc0ƽH`hkfv.Å=ջ3 #;mg~60RcuhLw >iC\Ao4`r=&̎ tto;N#!)SRKLu|#ހi"4S#*ݝi0wHոs Mi [-@x18Vڝӄ~/ ξqf耛}W uJ<7X>tr$$d/cOPKGYPK<4f>##meta.xml OpenOffice.org/1.9.104$Linux OpenOffice.org_project/680m104$Build-89132006-05-15T14:22:092006-05-15T14:25:56en-US1PT0SPK<4Thumbnails/thumbnail.pngVgTXfg4Q`G J4(DbBDRBPbB\ZHBT b9c9}mg4Bz> i~mg! ؑ)E KTe ۙ1KZw7`PĶyf+RD[bxt!#/7͢- ADy{SpV? 8ʰ m\. :5C_hZ~I3*-xi1OBTe - YHWGy(9Q@>h qECD\Lꗃ/3iLX(Jnj:E*+QU!v>M'܎Y-r&ӣen/Nc .d2wZJvfg+n=bW`idKSVaKyY +l8|p] Rq7PamEϧ7'<G;8uC0lVC5A)!MX|~ԫMB+_Mo7wG,O/_oqeAԞ9Ձ׊T5Sjjy8sh,ob-Z5یJ.u5 %-&fT:EYj5uX }*89ONcZy:嵻%_O xF8eWa+Mz'.SP"͑h{#~o>z!D=S?釈8+( (0gG84lL/W=XoZП0A,^ ~5@}O,ox,j IΫ_o|>LQM{ {ӥՒeao[p٫[=J SC7&dՌO1X~4)+4W3ax!Yv}yxu5|i" ΐTn#*2oiń!{D4cq=0tUTP@ mz)Z# 6|ouo hR><1~L`5l޾q pOŐX2zz nkN+ĆrhJ : =F;a鄛Q} ϫ9ĵ/t޶yR>Ρ|.I#ECƠ=[s &ۯN-x xT 4Bi- 5]kIXعk[Gel+9]{{B-qjh.hgL8G.TGi=a\܂-kPgI Tl`_Yp%yi=O_.SMgxoƛ+)6l*nWGݑB0+ $Ti„1͏CDžsMM|ݔO`M@5pǯ!@# o{ys͋tBxHB~RPoBL+nshU'׵o(jszrk4GW-y~' Ms猡~T`d h(Uެ0+S(:c{qy(X=|5>6Ssi, '=Ϟ(ڡPKePK<4 settings.xmlYr:}W0~'$sx 6--L }IweKĖJsήX4^Aio|2ĈU{w\4i!Aӥ]/U2@$A& 0yd'[Ϯ6& ZfsAj;N+{{\եoC!1/& vo_/p~a b]Zף+1k^Ѽ_|}lwxcv x=up9u]o|.\贈ָy5+'zK&4T $ 7b7c4` yٕ!]e)N"z=KꟲywjJ|!vduk:}.i\=@c0.^  fkTsaAG,4a)5g 4= ょo5Yz/TO.@T1COJQ?L9#*Z(ٿQiEgNS Dՠr(!ƉmڙgF.T"YBAlSf`S+# 16d$HV[7MCu9웒BCC(L_.y zL?5ȑ? g Y%})dN[n7`,zgG;F?nj1C.NR%cڑO{ѠaÏ8%gЉ .l(pl륓ÐB߻9T&Dǂ$wf0 sJQ,O؟3LUx&t}ve!ƈm!<5X2ſIW4;8*Ozp$IU ][Ҏ[:0 q'Tju\y袈 ,5C&C}N1Q)㚛'ӧ8ïη֖k5CW(R[/@2؀?W SYz UG6}z (#j߫ҫu|PK \ PK<4META-INF/manifest.xmlo {ai!VKlC=k%ДI^=>2dΦ=RV\2-yd\/͊T< Єu v]ZsM2[^ٜ(1~͌4Lihpk/ TEܩPEW/6fr@M{Z&֐RYF+19;zK p@z]cZ.|,6fB9.-.%VaF+ {ZV60LT;Ѣ˝x10nc+@0,ۅJm<Ǿd-dž, ƿ_/PK[vEPK<4^2 ''mimetypePK<4MConfigurations2/PK<4 {Pictures/PK<4p0 layout-cachePK<4u> content.xmlPK<4GY @styles.xmlPK<4f>##~meta.xmlPK<4eThumbnails/thumbnail.pngPK<4 \  r"settings.xmlPK<4[vE&META-INF/manifest.xmlPK O,(ocfs2-tools-ocfs2-tools-1.8.6/documentation/03-disk_format.odt000066400000000000000000000411271347147137200241710ustar00rootroot00000000000000PKa4^2 ''mimetypeapplication/vnd.oasis.opendocument.textPKa4Configurations2/statusbar/PKa4'Configurations2/accelerator/current.xmlPKPKa4Configurations2/floater/PKa4Configurations2/popupmenu/PKa4Configurations2/progressbar/PKa4Configurations2/menubar/PKa4Configurations2/toolbar/PKa4Configurations2/images/Bitmaps/PKa4 layout-cachecd`d(d``e`28 ,0Fqxc2B^0F9PK"-.UPKa4 content.xml=koHW4`gI|z$l[OS4<;]ƈy ԸN%5{2K,DžpXضTvl]> T@Pд~O^h:tc۷6gg+, =h׈BD5=:k9]^%w7~sWF}MQ/{0ٙq[ta{ѫ_$g7YrNyhz'?URwز3]؇e/5Y :sos G-*Q#]EJz`4ؗ`})Qw! ^ݞ<ص;Owi:X z L>xPg_3uX/evN2c܉@['>pad ˜`s+S`տ.܈x!7t/D`"Er2A}ĦZylͽ+xDX2X{Xz%HipA2GL,,2ǚviSSROhz{$ fS@™%p`"m~J3+Tx ]mӟ (`UBa%N=5 w~V \bI(ݚx8ʶB 3lG>:%3W-Z #CH \]KBZ;Ad%sv>.o6Mh;5E~mmLlBcՋ99s.wnm2"x~E۱{-L憉ch&J'\ފűP+7^`_km u' ǒ]$ORI4 h wCa$<8@;y$4qk iݱ A- >7h XPQ0 BX]s3:` uy3}095m \JE{vyuѷo}sA[ ƄJ0.$xyQ[ 熈 *1 cHx= ZbK-DSՅ@9S5J|yK#w`sl5K1F>|x2wcGRvZޞ$)%1t0NVS yUF vxuPN0ZsE VW@cwFzƆ'(*1~M)\HaB6fCJa <ܗҒ_ ,L)"7%jfI(j!|ƽ 5%^p$is,$(k)Rs?(v\{ TIڍDžrI>Sع0X |GRj%7lxJW$8 \@pi´q刉~LTo5ǒ3c^2>Мp}vOAΙi?fmжlvdCIJ:vOk}Ou>ΥQ͛64 ڎ,}<NcS]Mp '6t4U\UsOCj58 x*/MlaPj D!88jK&0C9͙1 c4bVh[[siBk \&R-&fR}|k`Yy[}{&M}ߙ4qNV}g91[ݩ-L``UǾl5uZ6Nu˨؟4rd32G6{:{Ql#^R{>껿 u 6;IsS}{*Vȁi{*MpAk?5&Iջω4qJ^{N3u_78L| Vo}g)iw&V}gsro3G?w\%^~3 {=/J84h`5ht=N.{zڸ^)ٌ|K>ueصELPG{y D7r4W & %4ZUGߌ#!Puy4n-'b%wiI;>aKBjўLՒkh5ьCMOpB4?N kcn} Mmy0Kۚz|2' Es a;&hd>ߚhlI|N 7~1XD4>ƌ¦ۄ(3j#2Gh|, \?c]db|x07|*R @ư)G6g L֡XrGDx%¥Kab `%~|J1buNٜ|'<Ř6wapǽ] b%?#.ʱԿљd Aa2-uZ 3CkX-SmPyI>2[k؏ڍe곁j77|8LfE/)-aK=iˇ;kROۃd6@ڰ3$Q0D*߷47g61`?.::h [0 *a>E9 jaHrؓ%o'EC NشeBK?6B`ַ2H-F8r[goy)M<ڋH 7X5iZqy > ^ŋ4ڲ ~z|jx>fwHKt$-u$fhV$b3%Y'=^P̩,9EI.86RpqPBaDy̳4焸f(QOaH)%l(A yĄsg/D U3^ ]r&RvzSW-+V7~?X,ҢZ y"K\ r1Y~hy_>~>뵩OTR>x*6}JRqQi`NEarTyOLHз IUd HF_Ca" Ͳ̑1lK^ >pblC]}8}G9G--BӴZWbIbQ[; VԊoㆣ֝Z@mTh/T-\j,w-jUQ<5Ԩ-D@8n@F ~!zol bۡZЫ2O͠-P };4s&+ u [ =S)q-St A p B~W v \I-PcP>dȖ wӚL;=E -(&ƭV t5 Wz\gTм=Lf+ u>Saza,{<(YīD 3VS*}jxen"^UV1)J *B 2D*U@0lEZq;NWX#Di;9{U*nVd<A(b枧RPb琡4˘6b ko}"p5sH Zz+Ҳc9&XɈRKHlDV(?)J XcJ&awF\&cV>'3(KVV^9?ĵBKx j+)Ŝ0,Q?ͿAtpp'7S0#gpJ٤e!{C;VLF:A&,K]1mpsAx%ăCT sMW(m+7ypqI]Έ* ]Ä+Fx j)0LVxzpX>jbyp; #Z`3U.P(('o23;jbymyG>oטGNցYs „ިՐ58.BwqKw,Yl"F ˎď$3vCgjexBRlś*`y]9[=c? &;}ha/`Mriw G =7߈k][rezjҍ`3~1pW&#rZ:Bʯ KfEGƐT kR  mR)W˛oѨM4"7b*2(A + [RQF։v / D,[\RI!O΂y^pEްI&7w&iQN~(Ahdt\ *k.+6Ыk*FwV_o| h|6~!;g{4 Y9J|êE"93菄w Y5T1|*Gac/Dd .oZ&/]^ _\)J4#qS#Cr[&'ɘvMOﲊ>Epˋ/&65>lwl6i]0vŤ_p*tD6xbWLIWvwf٣_/bXz:ŵJJ˟^%T8L:.Vb) Y#-!H!O&gI˧=nJr+gƃPԛV;XCo?zp(F)#:Fm i} #4q~vBR&K֕) r&s2;`y`S.i-N Z%@E7vG),Un3dwNODkoxKZIk T fYH ,;%;AJ@CМ@tWƈٽB*z]h&FuK?~rt[WrpA[Ee>Z=XH\]{ P\>C ѬvzR+kBU0?*"_C$q+YU,:ND}=Ev.y$ҜMP^\<*]ddFu4f1m ~HR~nlml‡qrꤽJO%0[%QN'@HJhl~! #ʤiUV y%n, JngYnxAJ"##;jn\phvUP"/2}KFu\Uaט Tq0{Yrd %9̡-ᅌ?Β?ʹlo /O;Ry2XuGOQr#/0x"fLEɽ s\53K/fHV3oxQOL/NevJq붹J @ЀDѐ`c LS3KYhE\tZ7LXIJWLd>nhu>S4H UPªp^04 \ek'U[*Defe݈E@򵁥ܷYmU]} ay E"aʖ$kmMҚdXCk!eb2LR}^{V>P-E7Ca;/Fj+_'iIޭ__׆]K"1~E-i~b>V$$-]ޘ4+nBЁd]AZlůc܏{R$vA赧Ȭ;ESWQPEhGˬ=ZxW;VѐFGAΚd4jjPĖ %u7|THY]>)A`ޓK까&OC4H[M! )Uz`QSK".%%PjeՓB/2$$%]Íq蠡2SbN9*Xbq;9z##?m,̴{?|o;h替 E/P,Û:c HY0gsd..A\,+JoS_M!%Cӎ*S ]sR";Y3}#t Qʀ SM2*hzJ`j[ 2 8T,3<}S§yV]3.*a96Jz##\0(OD=C"# 7T'98L9E~yoImqaM0wZر Uy5zWJTOPGw1ݧ;B[#xvQWP7OA34+[OM)O;1L^[mc6'TdԀJ.^6ɯR?I"ɞI9LT9Ӳ@ ;~dy Js "ȄX^#/J*'jЈ~`ziBflGk6s`״*x4M* Ӗf%xm Yr$|s:`9[*#tR(Ϳ53a ΁ۂ:FE5uM' )*hjFa26, bM춧.s d [0]JwaUeP0ip}60ĵugJ(1)tkTk&&]Cji5˧LKe;&8hlRI؂!S5!}c^ɬ6U@x`a%9B XᒷSasD^_#sHB̦C}}V./\aTi蹑L]Zt2XN)Ӭ\<5B['Hw+`La4juD"heP?:0p28d7*b՞e  r&:O.f5AtsJegdh% 9GNbE ^I0xTCBqm#*6R*:1O%pvOa]NAj76qd 5CU$mT5N_r1`94HZۤuwomzso6in|bRYf`;;[MZ N)Caspx'~FRhH Uǝ>YygK0=Fc043i>5-Mt O҅A 6.3jF'3I#鿟`@A/YUΓɹY<LJ!\k9/-xY49аJ[MM^؀ 4lў]g>HJzlgM- w>4lšI@s@w,, 7xV3O'C ̫`.#'5;x.`(i}Fts$%t窧PK@tbPKa4K""meta.xml OpenOffice.org/2.0$Linux OpenOffice.org_project/680m5$Build-90112006-05-15T14:26:592006-06-30T14:03:03en-US2PT7M6SPKa4Thumbnails/thumbnail.png? %Duc;qHai-6m.ј\rr#Zn#L+T&\rv<|:dɈ]1~oJ$vԬ+'(<\di+O3L710>hgg{s݇>&$(UJP$?wLvksk*gO0gYiS]=ˌ_n˵I6lo =C /YԀy쀫[Ypc[1t 'XLY ԝCs|bjVYfեカ>1ߍi%:d,`WN]4 _c%ߊ>Գ T@T=i%qmR^?o"˘ ]$3L_r5i"`*GhiF::/目"Ž^uT[z5ePrR+xٳUZ$ F{K `mX] E~bl\>R'$^gsh}k ck/Q_&;iC$=rш/j Qy[.+s]$h|,@MH<XUXSP{a.m|]Cm.rҬc,c 38&.'E||Kœ#tn;<ޏNTUc:̀p-A8pPgѻ41~$+ǻ&,PeJVgL i>~_p .QjDt*MG6.qOcs-*;9n͔|$ ϤNXݶ }rQXy̏$Fxe,,NԲsezc(0WaSRž`xX:獶x* ˣMG՘qp,YGLHĹ{`8]|ݚKVA/f$Grs 3&~+ یC'(q]9_AҀ-U=c;"gNĆuF٨ƸK 8 6#ndef>U(䛣Ä1.mnx< ViQ.楬!!νX>A9 9jpzVrɖ7o0m-Ben(&r7_8LiJU³S7Ufq=|2o ՛͌;>=zy9 GAdpX^ab0wHȳ@ ۮ30yaآY_YfZcӬ;3LR/G.<*Tt>h!I+H7&lHgY { 5NW >+HT/Prhs&##/7A__AsV6hD#cIt;zm]v7 O2+Y%%ȁaLBl iiDJ<4=̎HnSHMѱ.~}%y! 1VMN&'Jn|-)jIBtU_6zBBR)G,$Kcl @U;w8ÖYMUxCSԛ`8Ifb2d.`C{u~gMhTtyl^)X 1H"Z/huKl"\7%@7Mn[f,{\>1qZ]V%EhTӷ9_*[V"*2cRe5߿fVAQ"ckZ#רyy~w)Kkzz(tC<~9O<4\ԮbktoOz@% { fs&4S2]+ _1D,p`C,Cɡ6|"zm;z,UFQ=SRNR;p4K(Dw^/?)ڇU0[}Tbza_уmn{MT\N/GكD1CoR' L)#,LbX:8QIA˘)d;"^(݁ԍ1AYE0~E9JF9`~"nmf_ ps咩W=kstlڠp`*T. Ĺ#%TЂL0D> QH'wlV)puqMaL #'ŬbA1/oKO |F .h*$p6NJλ9D&I_H`@)֥j<)h?EU5MG@W]E*e?01CA{ui؍Owx4;Qf]4饎N*AZ+W?Q +o6ӛ{nR~{rWPKOPKa4^2 ''mimetypePKa4MConfigurations2/statusbar/PKa4'Configurations2/accelerator/current.xmlPKa4Configurations2/floater/PKa4Configurations2/popupmenu/PKa4JConfigurations2/progressbar/PKa4Configurations2/menubar/PKa4Configurations2/toolbar/PKa4Configurations2/images/Bitmaps/PKa4"-.U -layout-cachePKa4uI = content.xmlPKa4@tb #styles.xmlPKa4K"":)meta.xmlPKa4HD#* 9 -Thumbnails/thumbnail.pngPKa4K[ 7settings.xmlPKa4O<META-INF/manifest.xmlPK(>ocfs2-tools-ocfs2-tools-1.8.6/documentation/migrate_from_ocfs.sxw000066400000000000000000000174561347147137200251770ustar00rootroot00000000000000PK319mimetypeapplication/vnd.sun.xml.writerPK3$F  layout-cachepP PK3 content.xmlZ[S8~_VMuW}tp;-l+ _GGCS/$;W9~y.r2aJs)΂~o? L2b||{ ?_;/LSDU c) |o\ ;$I5 /)r6 KL NBݳ 3<" bd+`?rρ?,L5af#%hHQ'N7R[OeC=N{CG o+ sI5S5.'uF8j3~H"raRY޴b#sK+^nܑu9,pr ew0jS0aqrBܳչgܲi@RRZ|vDK#q h1L,(d”Į{dTϜ .52Knb Ua_3*^=[^*MdA˜U-Tzuݥ``ƀ#+oB51r_&Z~V1<EwA~#k[RENJY CN  UIMR@]f*,x]*(_pKG[[a% )5c]{qs$dF+2S(xTl,2gݧw4Bi4$ms,tfS4׬"[>z}Γz \/۪w8jE߷*[V%fOhs slR XY־?|C|7G?6>lFwRX(L*H/zc?GHF23rwފOT3ցĮ- l6qe ̦1]/ꊀxe;}=4.U;f{Y+}exF߰i38hՓ"2PVMXngLsx[!fc\,?¿[HwV# c+[DĘ)C\PCI 7rw~5$Fg]vskE# &TyrF5#j)B3'=/!f!42ȼ]-Q ņ\GwPloL ^ ϵ.%9 < Qh}3jxNM=ՀtcBg0I4tByn@t(߄N̬ķv0ZXuOӃ~'ࢣTDu̬[O8Lf ouB=Le ,#0ٗsLk"TA vN9u~_C 1}[EYP3#F:0BȘŋ4,3v?՚wCUJsgv.hF$n?  !+N3'C+D/Kn z uUh̿MǨ_++Mg u '.@֔mkQ'd1ܡ?mG>̓Q2Z P-(D )nC_یv)ݍ%]y8ߖ]_MИ$7hmw7z(^'S6sP_O.j`= {L85W vsY ] =l h2=gb fV[1f}mhYWKm%0+9(Y*[4e))TzEz}^>\_AZoы?LލFI6/PKPU &PK3 styles.xml[ms6~NS)eI8iqi{!p%ZV-J$vCb.oIbsAg[,$tqkyL>{"Yt\XXp;v\S'0pݻ;<,u?ؖm Bot 1[{)ez Fa(C\lt^Fp_d/ZJ0Э}FqUaz2VZqtte=6KL~Q8NGi1IBm4Gdq}"+5)D*H-^p?KH%L.ٕ"eó=T3q$sXMOEY2V]Tw@' BSC]HzKVDȉ1Tg}E嫉9Z lGd\rDywo g[-%=vQcZBOnEU! p#0Fi㰸y6Sn-lhw8PKA4PK3Y-meta.xml OpenOffice.org 1.1.0 (Linux)Migrating Data from OCFS to OCFS21995-11-21T17:41:002005-08-04T17:32:042005-08-04T14:52:00en-US12PT51M13SPK3 settings.xmlYmw8՞{VK[iC†PM/vUٞS$q}XjeR݋gT| oTp)ice}$yr!ujUҵC*Xm3k g0) +aY̯#S$T L]]^nկ ~, Y;vl{Q.^}}o9V\nI҇.h`C0#A1<GР$#z(:" O.\@gnj|t=/j;S橉<"Sx}}8/ZPȟ!xؗ9U/őr`ԯ  2|}Y>{JH0H6'p P 8lآv p+!ErƗrx ո;2>_NH3·Dqd@A#i!<`>EϦkk's/5isM~=:h2E|ñǡe4z[Y&c Kj:ӗX7dƌE:TTq=7.ĵ'./~VP{ܶPHĝ "?q *o__7o?[~9O\Vݬߓ[~)3ө泐[~O=l|:[ooo?٬_;1&LV33VCF?,"7py&"iϯ$MDfa+ʤ=Bcr(awmF.cJo%>mS-ږ&ĐNayDY`sjNS+z3q|ݎHg6eU^3%Q[ϼ&wr !H޵;P v3Zcݬ"ś ?Ļ{8wGI4Q-v\\)Hk|@,2{ߞ_~PRPKNHOPK3META-INF/manifest.xmln0>{{rЩB*UjK:΅XrȾPu@%Db|scĉ|f0Og *Wj{MazX>؊FZ]QŰm# AܵdwU}.>F~(-eF). jv"]OWR %d2ᾥ d$[ɖilC<\Θ\g2)Vy SixV~od:NT5ML`:3!tWnP;!w%1.Ky_PK4RPK319mimetypePK3$F  Dlayout-cachePK3PU & content.xmlPK3A4 styles.xmlPK3Y-meta.xmlPK3NHO settings.xmlPK34R.META-INF/manifest.xmlPKocfs2-tools-ocfs2-tools-1.8.6/documentation/ocfs2_dlm_design_draft.odt000077500000000000000000001021041347147137200260240ustar00rootroot00000000000000PKp|4^2 ''mimetypeapplication/vnd.oasis.opendocument.textPKp|4Configurations2/statusbar/PKp|4'Configurations2/accelerator/current.xmlPKPKp|4Configurations2/floater/PKp|4Configurations2/popupmenu/PKp|4Configurations2/progressbar/PKp|4Configurations2/menubar/PKp|4Configurations2/toolbar/PKp|4Configurations2/images/Bitmaps/PKp|4 layout-cachecd`d(fd`d``Qc2A ^ cS d,)14`a"Ƙ5b3Srxc1ؘd0:@L!ac8N0+Q c10!QeL2PK /PKp|4 content.xmlKsʲ.8_Љ8!uP4 >$jl^[u,yzHP6dѝqﴣ/|T RX/2ƃi7[; ݽvip翽}4򇳉ECߋ ׷;; |W'ɛMn0kjpWX"fxoe^NNogT|z;#ۉϙ7#,`R/|7aL ֿ}_%rV~\cDv>\r$i^ o?*GFs>0p4] <ㅑ!gJKwRi27#{_=6oķ;W\=Mn}wAoM)cJrhD7;yY }i߭-?K>u!,=8v`\؏s;ݿ=;p@H' :FkWNҔ|m> nϯss{ToTL\9Ҵ: g -ysGzI;|> ]-l<|f`dyybS5`#gO(]KMI64Vۑ__ r@ 3;oLs81>py0H>{<8_ZZs4ryK=@#w6RٱbHfl}c`z4k'4E<Va{n<W19:h vBBtڭ%8_mM t2aZw?FHпvZ#[@[?T@䌌k_=[QP;E98m ĺc$Ye+RRzoП}oTEQa.ۃ8Ct0okG Ɩe0nv{i󝕀^o~_zCE[]Hxow "J~,O)F\6XMs6u$)"IB|xoK=کUDS+UGW wcejSI!|^{Z%y C۵Z=Aws%gkx}+q|vۋ]jlz% g;藆 }Pj 5; +y<\/m˷k#[[v;y)F8M7>~j" ZvCZfo~UWQvBb+Ծڷfq^[>bJ; *ߚ̓'nt+Pz9]l/fiBNϽ 0̃~N 88S}X-଻,j~y7ܰ7keFkz/`DWW卬`0ٝ]ONF7fASBg_0rO! ^iS0L]{Ĉ#z:C5gbMzy ˓h?WoM1k}_X\fmdS^tOIҔz)zS>,=%ɲS2e$SO\?,̔\lTRtCqtvg_=} ,Q/p 5wxSxf= ٷӄ]I^);GuT^b%#߇*Ftu\BaYjd*S礈L]yt돞+-,wRNLC1lf·WZ?s9cZfͶkj/Vҷ*OT,LM3$m“6 tMa9Yʎ P&V#!r3n 5R\EY!`'N|:hRfOuMW|ML 霵S ;wBj eS.;K!bC<\Phupf<{>Eb~g#p3]tsdHqҊirYc:(xM̃\dz%,;׵%+ݟ8衏-Hy Ǣi 5xʄFՓleOw9B^EDKa)BKa`u %8[&L/BkDՊRJ|ș[}(+B=e#,ڹWyFgfCgd fb ;sS:dVO n9 Y&x]z>ry:szFMG)gd2jn3Ҧ"$xǙ5\J_V\g\Û{e65k ȡBr{Mr!Ǩ eSўYSuͯg% 2akaTk?خTɭ#>SO^« H*M] 7l}q؋dFH"-2,ohS$JoD,S/4b2u'&*7rA讕 Dom'kD8qVNl'ʉ `l'0}hV "Ԝ @ogkGI.5Ň;Fꈻ s6Sy9ktϩ<œ7T~aN6W<Œ4$ WrK pBI`Dϲ ˝! pn޺yغ死y'死yn~vn2~gy? 7vgB>>g^G}|2ŁwAyQ #%j mkNnZ2Si*e#sOGI5ߙQ7>!_7` +w!~ ؔUJ`}=PNݧO/aCڲ?aHug_-%=q[Vn#ȁ&B.צY貭~}%?؁Ɂ?4v|te &O#Xis;qgA΋YCzEeX8Ż&H 4[qdtǯ.\y粿xN^dON-/u_ws=]QwVP2=5j9q> k5n w 3|l$ Y9Xkyλ6v{BooK;8aNhD1Hp?=>hƠk oƧE8vv4i{2\ Ѓa:uP=#Z֮qF m*joރ80 o* S>l\')b=(:qLpZd qjfl7Pc˞鏚} J>i`v`5R_ƣ RHe$?p8?>ޣvk#e*qXrWqzC&rNNe|whd*/oxoLl ڜG15 :'M94N#o[&4϶g Ja2-D$3^dSאS_(t^aaփTr)x $(߆K^a[P$7 7g#}w9QiĘ!;B,в+x(~U!F ~eV6 yKY/D?!A0?{`vt$! V*!F5xorcM q8P4 5 `3d=ޣΒ x+ǰaA(|./N??=;8h|0|d6& V3v #O=pzyRV H>@Tf-gőn=Cؽ$]lLp‹8 9qid/:N. ÙYҒ '2jx!*E4hC9bK΄$(gF;u~.hy$(VEOŐ-nNxG8t[6M ԾmdG4G]`%>:0I )0ٔuu?'+ ;4#d ` 縃Y~)r&Rgi 9De4aoaO+P5⃜ljjao=_/hvB{]zT!@!'=oWNOcό;hbƹqf]L=|~SO-"K,~2SJ 6A]5K:W _~% HLqй3l<ˋ >941L {7elu\Q#&q{0d7;ԝ`@FD!xQ"j$wCER鬕V*_Bq̈́&I&Q-VzǸO)qhJOq(1`<l}dZkl{ĺ-D݄QX. 6҄ƄM8&i|R QOȧ!ab-DEaD)n0 (P|,KDigJk&Rn^0pL=*KD$!-2P3@ ^ҼD 2b ɫ+g=YT_dϤuB!s5 $@Yd rcB81` UzJ~D?#I|0|åph'I|bSR?"E:̀+ލ"p3=;p C (_*?h7o؅&;{4yHA|>j279}l3R I OH[$CB 6;H#8&C&Bn|w@/\ άZ:ɳj6O?>=7>|󪝩c0{4>bG ᧷GYIjܬ,~aJoK-"$phĈ iw]4+/\Bs2K!w='xDyq/V;@DG4xp6G$!^xT@ayyó\'M23=pxoF^ [bH{PzSXgI6RM9ثMGI[C:97ǛقRj-ѿ,)b̈uM<8VX|y, *s\)ju_J /k$<,'MΡoQxn t I=/]}#JuE4#s}kdKWlY 5Af4)SXc|BkPm3]qX3c]Xxو4lٞ, 3xcPسYf4\E <̠}D79 Mlk簨 N(LhBdy va{S5>.hc;Iш!#YCqp3tr=4\u@+hvV{rDŚ MP6/O?~8~E6e3ύ@^VhGbǤ g=F;n>v$mun kJzl ^i ĢF.vu{[{!c8TPkg䋭CKwp*yf&j,Rx,%wBma{8?ѹ=,=<ﰿEz0 xAF“BA:xcR n$l Լh"[%ɐQ >َ'b o 朵&wZ {&gB57h/Q; /$}p0>S2P{qkhQ7wEO+5̾MbDd#9ˣ(V rp} IfEC"l_rE?24O/62zOZؾY]EuRnX#NpH/&".ŖS96zB.`.%FD˜h^Ī7=&F ٬џ\F2/E~A`0e! 7Nŝp뷥ĒL VnNjtɹ'$I2/q&f|)nkD0Z`^|` 2g?ƛw25fHep DB%i8j{4ZIa'np/o­ HVhѼuWJ|A۞䱫*p"״ݦ𯼞(*1c?b<<"WB4'tB2cşP%g3X  hҭ<Q+0c(6G9I$@~D @ \}Pc*a|pDؘhc!E ٍd*:&"vUoY/ltBڛ*_!03ɼ˲TxbG#D.$lEyOl5QXxK)a^΄P25#8cTGsXQh,5 `zJZ(7欽y6C:9on8l+ю-T&ᩒ} g2vpN9HY52mNm@c0] mGdD#s&Cő!q*sy0lc% l5sI6 :RG])_5(FYKZ\J d:93 xC;Ɋ3/`ɂ2H ab 9)Q~_> ji }3F WbGK|N(P3i eκbH2M 5Z|0ssH]+rt%Cb4Ʊ,lb"IX* K(7rcBJ FɈeY՜c*%;Li3E^f^|<<\\|/cQuD^}D\,ԱXhc~k<񺈕8fږOz1yDPmL8+RuT8YōN E7Q:olXĠ6Bm[J|H O> QΚ\T4.R^bm rHy], aPdDhFqIy5ʅn*|բICYW{E8"Jb)LJ"'˞6|@OfX:UCI#}w"t9k"ȏ⑦%" DNxj4,MC6WgV^Mt>>-= #@OCF儱(i|%cŠ7(Y G6pa#venݟeTR SY+g+Ql$PGSI@}z镴;2N?\\HygPM<Ps/9MWYj PWX". 勒y2v}qT˔R;5B X-D f:Lћ7;o7b|4cu4STZ`BY -l0ވ'$bΐuM:K[n=eC"8ו𧺜IvQI |)ܔb3vUe?Pe?8I<07m}tFPgYpyT{fs68nފ5-j[qA}oq?5<1[7x:VH<~{ .j6^]Μ>C s#S$>F}874%2krTsN[[r[aO v#Ns'God$3֘.k:ϰ벦 k.kzϰ벦 k.ka˚gXsf k.kڭ0_3gk!E@}9dFƢc-/W]@-6 YlBSlc4AzKͶej{dzs,SΗ)gR/G/rcy;ږm]Wn#ux_m^S iڭeZ;`~Ggg߫q>yB,{XS|1!t=8vٕcLc**qtd$h_1Ӹh6w0ԉ mg>@j}*lP)Unw7ǥZ0 7VhIf!OLMYå4+,8HjudhݲS۳Ċ)n/)YA|e̞٣L7w(VGNLprER hq?yVs[i%~{ĺ;(+]kFb!eW^مBT&PQ[mZcsIћD $v]ATBP^_?=Ed$U^iF`u/F[923ά';+h^d+ ʹSzw]K94,`/ 49r'7՛ #o5jR\UYzg!B;,Gie@&=0Va2cRt=HNVu< +KgnRրY.%M-L7)mBȄ&g6VOjq?@[ %Bߌ|Ϯ*$ڮ"#lMG4$"$SI^Fcdf*]m]rhMf/wW\4:rYթV@<+`(>Grm+.ᦎk45PCq;:./fˇOW&UF0`]{tBܤ %>U5`wEGSK˯0S 4vV=U1m&Zxv;H+Iԝλg^e |L⸛9: wE]H.}$5.4{Iku7wC&X/ڍ @ZX|2)^[ЁX)hSvM3 /58UAL>GJe#g ^9Mh14*{Q"NkY2ʺ¸\ N iOèYLWi BR2nam^=,FUcIf%5@,saYp@.X%Yjv,l.ڑ[ٕ\8(f 6Ն n}G)yĞJλ'?,>Lm\̈́-_~=2.e*X֨/Ύ c &BKo×OӋzAj+H8uBl*gT7G5WY/#~KW;i&{W׊I/NCL>@ZSy_V B7|#v]OkW$ѴȘMu§v@DP'kg.bwúÂTk'B^( 궞\C(Qa #)ytLXnPU ӳYEBO/>~,*»U|* w\;f$x aBiTKv4gJ¸$>{](ی*a_]7_R(g튋l 45?\I!r#'3 5>pvKIsUkW4aľxZm~g ki㹪KD"Jm {vC^g_>|⯆o]-;sfkVӯ9uW/W^Vyn@@gqD6ťnUV(pz4mOT`6;FOWƽ [׆qφqq -іW}U(J_ ި ~UX3A{iF; AAI+\Qz/V-Bb[ٝI9я(6Suk5b $-=;P&;8SkMQGM 'Q~L~ PRvo[AtkæەFb؟P 㭄/;TE;=!F4l$ G'b<# v#T-Fּƒba: >ޭ DFYxRA%`a;+DS{rSwKC]Rwu< „L 7f='iC Tni]%']o]_4gt8B9S2&?4 gcdb-fvQ5vGr cnqK\(Hgͩ<0o8VEImb,_<ĠL)@őڍ" Fޢ"LHQl,94H'&sz6憻dZvvKϏɤ(r"r=͇/:q(k=:e$zW꙽fs's|._]Rj|RJz]pZeI޼-1?w`H16 )yOk2oxؐ/ Hv)vcTbǞO,Y+טy@)نk[ə ~xV^i1Eh^'O &<#f"Cp_ݾ-xNQ|W HyP*?Ï3+SfKܵ )g6I@H!_":c^Z;8ZAbk$4  hxZd'N"P-RsG+@]-TcOD eZc"XzS5jutۣrޑ mѬKo}8x ;5gw[(%x HG:ǴaZDN2˛;QA= Aw(& mVpq(Eah[pm8ތ h P2 *m~RΐXVroVpG+{XaGܔk"˓Q{a zKfZH|( daBܺVǛ=D#Gkf0'a>Q^'ĭcm.: GJxKo@ sJv^,͘n a+I֎gwQ3`^Y_{UYvG~7 9f&(pƋ']MZ䟤=.g@(h|tF S"qJV"&jd,>2|`LC6NHVuh%VںA˯䱃.{P7(KW"H'9H αe\'p$+z4?aKT`h^tŮTcłŭ!֋ u  'UBco7^ߜ艣pO7dd \( @3z3<#?]+ Gocw2+T{Sn?Ə]Tm ɈJ Z"y*'|=9>E?Hdw fcF>O#ޑw .lR߰Eb8 RZXȑrq+h ue+=QK5TJ?iFzRIg{q0PdEKEB o8@.:A[wn>栣0'@VLe;w;NUXҹ,Mi!J!X/k&ǼXǽFf ɉ #d5A9r 1W,<,C63_%$;_L (Wzš *V7gCG>Ap %$ջ.IY(}RwEI E1z vRwxqP} p&\& dldRFj~-{M?WܧQ;;̙"(7!4| $QZU!?}39k4vT154F`{?Ä``q_g|xr i-A$FcnsWѵkH rFѼʇ|H+8epi쑷͌%ѲȞOB![}V2ȡױrF8R!cҖ\~6rulr9|^JjJɐ/{0Xi,=QOj*}"PN82c#>H_(id1vןB 9 ΋%*ߚU@4ih:9jRaC)&B<~ *^~<ʏ4cg3{Mm~L6AWAA)RH\A.'}zz8A7_ =) H$W[A6rRd)I8EMc/"4TDž 2pXT85ڪŸ4$SʔvءH/Q;y^2/+M_͸ZrAZ熣zzʁ li'=Hr$}O UB ]s L&D؍醡w CFw-5?Sk6&y`r!'~pE2^HHHЏW/zUE ǘ /SeFjE[e">Ƽfΰ˞!YRo-B=jse''$|!Fuh#EZVq2ڈuNڔu{g&z}؄^PK$L!O H|"5/.KXh캜˵<ڙ\9PL2qrCCA/̸(? k](#l2ز%Wwu=1{RЛ+=8~IjÚ W;ȡlx >ޚ̳) yÌwڼVZgʍ6kǁ^'g{wRvx9@aUhT GMt2E$t"=*A-bG zi6Vq# ʊ*dDYsBRn:o#*jX-ˡHmUOpcoo|1-+:"/yqԔO陦$ƋkIUlxu0K*KJbst‡rGPP'>eѐym c"JrE*eL}?6gG" ULIM+Nt)C5  &ci}5դAnPE0SCZv1jڌI}]jιz.&(59:FRP@:|x J" BhLHOՖ㧯'bbBT[[ʢıpafGC؊]# QӠ]EX(&鸥G#f+Y0[ j٢A^/k$0\ xrii p}qL3/t`R؂RpHa=X`4q6"Dq8v-({XDjXDfKŚQ4Nе/O-',ؠK.2ZF[hkm-gcgSׇT9U6$RNGQiSN$1JYC*SSp?{*\dHG_ V' 8 FE`qRU$Y46̆yc۵R|`~-ugK%(>bu01\fY#ѵ8`ZO!%Zqekt$M,@ƥ5$cҶ  rՊ6䦬0r$d.y_ $r)ﭑuP9QtvΦ#r+IIS$RtAJx-e kIOBP \EFy4Cr`( .>LXFE(%r^)PQ󠞸_"/H#++L=PdQH{U}?CtB-ᦎ!iׇYZM+]C#~>ip^0򝓰U4$07gw$a zO*-gq(Q.8(w-)vFl΃|/r5 okAeE%J{;:Di{$o-<ʤ ˯4GgG rOܡ2.XfgoSK%;;#e>֦ՕƾL0bG ZOS% ;.lmR@ˬr +B!y˴taSXz.MLNFRQH%n--+6wý&` 2FJu*B켙K.=O@ՐsBnMypeHz"5v 玜`N7ԸWr,fZ,O$ee~6ۻ҅;f=\EUM#U'tqiۅ{T=U?U Â88neex/~qD-}=i|iBJ,y~Il]qrĉ;u7ncDյ:⢂՚`M22B*Wa=Ʒ1=\y6]~'v,0u^gZRvȊ6OkՃcrdV.]ڢLW&$jQo62$G h($'63#y$na ~&S< ($iP9O;'x7GK E83WĻ.tc:p60!K\22H$r8LVRL)2BG$P4.gЭƍ=4Pq+cJ; eLu⑸(XdzbxšS3/|Xy[5/,^L/#]"xNW^YvUqsT@i5>WTZpͬ_YKewt`Wl^fnt{pڧT%EC3 ВFO[Oج'u0 zONUIXb#pD7]C_Q#J˺ π;FH;U( SWnbs7t˞8(<3bk4"V*UOUg+&qA|GL*ؗMGEqn^]IJ[9fz-BBe'gSuOBu;wu:9*.؄Dz$AzTDD Mmq^CUuEI%OsY=/p;> ۝(0|E'Eɛw.[^36Hi/Gs2ɔ ׫:h\&yUc:>rl2p3qpXEjʊ%oމW;ӲR3w]3ϭ,ʍP o+ b;ڈ淴NdHUh, љ&V$ثY@g9$Vt5-l#=?nu1ҨALb搶,rtM7Կ̂rA5Ts_I1P-pNsNkLA@ B@7hs KUزdt1b[z*T=vKsbܜShw\cd.x9lC!u/ȗIc|EEL s+ $JiP]\K!Jfh!"CK^PS$NPl2<ŁE9dҬ%[w,>҉#~1N=j@Q쏳1_v;@9t̄]o179=K I|?ěv_ޗC>xݼit.CǠ#rWvcEMBuosw7)I8:1ް "f=nTtl\+Rcn)/QZٹTǂDsz/+}y'ku:R' f_.%ďKg?Π|a-_ERPʢAY?(eQ,BRF?_PE*jB W׮ WPlNG]rҼۣR+*ѱ9gn('?/+m&vk.އ@v,/JB~$/J~$/J~"_-0h^u~*5UɨJFMFU2j21*ˆ29 ~Vdsae.T{rRۢ;k$jo, ~2\_F`eq_D\[fpl/IZz‚bt1.΢1ۧ(u w̢wX)r$nnL%q~2OpkOD͏/6^s睊(a*AsQ.#ݞo~g̱^.m~OK6>TԖZQOjjU*j2+RQOrjU*j.&'AE*6Cc(C*e+ajתt_+z/ūT\512?5g"_#|Ռ=Y{Kj*^}P.w=>;@j\6l%2x_ jg~l-]nGx^3, |-ґ=t-vU4zjKcxodF})];L-E:hG?U7|GL&2 m/xL?址lT(0-m"{b̹>̌!h`ef!lY6x,;d 2C T( s#"=*RL\ Qnm3z{U0^zb*9R~F!1 uE20¨/"5_ s AKǓQSkӨ1ت"9**Qg4 ޸uKj6sb.w_M~WK.}R(OկY/KqQ/D*ܺXgHU2U0AFmh*RbEQSHU2jJf(f(5eTXD*jWyV x蒅:Xf'ui;0Ngc~TGa>|#S1:uk]ηΏ)ݗ}Sp%TTP#頴GgC-*AV7Hj}pC1;|BkiJnuI҂,³|>9}k6* ;?>/7&ɣC4gB|8vH#4JҁwfCK-v'0uߞoL=A%F™m0tc*V~N {ߌȺ4wGn&}T6_>sm!$m&CClHLNKebm*cdz\/e{vז[ʅ؟Xw /͞7Կs?Ѽ#<`91X59$}}s9Oe&w#4$nwbՊ6Aㄻp@$Uw%e$MP['Э)}+R)ťYg޷E[f鐡mBlj_Ѿ]Q7q8ihhyz;lv6l=D \HJGX4M?Ѳ1樯~BT)Gzqj͈4ܐuxgaxgY6F|cd1+.o[Bfw7YRZ* *՝z|~N-#3C<Pq@ΟMGNBjn^BvCDŭ2QF8b^GIȬ*D0ThCc䷴r$D[d4-4g]LO +}m9#9㭵AC5%q4,/I"|BIUO5$Z[,%o'TM%PSbMCojX-ɲnmDNwxGn,DIWVobsEZ޿pںn7ٟSO0 IFm8Oow#<sqC담ZY0+GŲTw֚tC-~)qtzeօswEY2_0OpZR2PFP$DN'F+ swZwD[,A?o\p^(3"L,!S@GBL(MIG#q#VB71܍"Ib` 3ZI#,ifvKqNo iDaY3*24)iJ9Zc!&' Q΅Jt6E:/ yE]mYUbgKen[tg@u̳yI78u9M0R  =IsUa}'& D4⏧W~52Lհ!:|,D.;ˣ%ldTl5Ʉ;h2D.5ͦ;Bim"G y$`,Q' Z;D(gm؅84S5%cl_f6 ‘h" D jŚ/h}] $3$$'RnV|,c^9wYo իSI{,LR>."kP3NVe犟b 1Y.K91-bL٘ݖWT5= 7, ?} )Ʀ+l?rTUT"WQ(ڢP}^oD-ν^Z?bi6 z05*͹\DIxadC9~PbiQELUJg0.d! MA^HӾ wD XA. v}bA3Er0G֍П6U{ez{ɛ)f[g&[8ڝTnR/T.eac<ʢOP+&˃j0> Ó!tݍ!t9!KFZt#<?BnCx 'bFʞ`͓)(wr4 2Խw6Z#/yfVfKLe @doif|l[`ê2q~ |6ΥӔD̎=6_Լ4qݵYv+-݈o- o{uiqYvoQ*5;eU95xՌ4}&8MkoIfsAOOځ[ʫ6lGp6bxm!ߠ'D0sI 3C57Ǒ+#GE,NZ3,bõw)\8|߱f/"=0BuqS8V]m8aD>'_wËA DK G _S;.)0(x|" .rz> %$\":(&|2D˛s#zz=!j !!)#-s蠘]s":1]!j QQv*%]":(}2DٹSA1a:Qzzjvɘ>0,@ig&A YS1NpXqtX'!ͣuCL|h':"`QR9@wx{>t]7aT0y)46kEmGu6қ)ݑgULB/Z%z6i)e9y@~w,Jw/spL}[F#ZD$O 8%/ $5c{FE]g9d;W3տt#=jU9Mu`YQdH@kKҊU\IŦ~FIzWTq,_& VN*iF&c@S}_g _M2[ztzZgh]g,puV \]j Y-psX X^āM`|*6g Ɋs&ߴq 8!Xw eV@z4V.-[m6S=(.f/-e+ 7NBV iWPW/L^i֌zθDbNQIeqW$`E^1ڪ$3l[A,^Xv:0^`:E)S7Ŗoz36B ,&*ܥ@QAj8C=`IH."bƥHsc\vFi籅-/Frř7PhȠ-"(|PVdW ܞ<.<v8u6!NlYSuiu2W |![ڰ_]N/EVT.IlH55N1 S]߬+&twE De*'\AW4ZT f mt?Ք!Xicԕ?;2yn 1%'R!mV)o|ZX 4@ PK{ &E aPKp|4 ;meta.xml OpenOffice.org/2.0$Linux OpenOffice.org_project/680m5$Build-9011Ozgur Yuksel2006-03-23T12:36:42Ozgur Yuksel2006-03-28T15:59:32Ozgur Yuksel2006-03-28T14:48:15tr-TR252P1DT10H32M53SPKp|4Thumbnails/thumbnail.png sb``p 5@ H{񊁁91bۻ0<>5jWW{،>"&7wpkF=y $K{`nH:?^ln*]TtQ}*<-t?K?-.e{cž_>ݖ}\@iֳ9ZxKOozp<<27eOd_suή~9eB6ƶ]_yh˽ۗxLlߑ۾lڝ7&N?N]4S7YǿjJ\KϽO7q(pSu8ϒXibR~ܽ5;і^=>&M_ei3~9qŗ{Sew\9V<gDWF_yQhƷO+/˭Uww̖'Gs]1>۷TfIcuϻ2ƃ}GV3gy?/F{o>"&u˿)YrNa!YMߗKN_{K_m}pdu}W/XjfooT˖uv9=m/ϔU~6ɏSD*uj=ǫWH 3:&PKjLzPKp|4 settings.xmlY[s8 ~_NKL7nZx3!)+P82ɒOsk铋c^^6s<:=벨*uǦS%١XJ".`9rZbHxDEI%`]V> [?Y5WʠDQteadTѩ7;VzwQ/Ajz3_g&3Myk廍ץ'lsyvїմ}~_3&Ǩm;"WxTjB9r~s7WO~ݻlx{ 벨p1\ua#`jeCL3p `>>ED ң^b秭W"U!98VVK~/5=(ٛUx(1j qvH7N3DIM21c~v}e<l~X#!bmÃ<4  B$dLi#z< [Qى #;X%]K?ɘwV(Ay8 P}Ŀqń䪍IY38<.$|i /żcꮳSCl.(@ 4 jOkNAHF+A],tr)1ѵS`42lJnڜImpWڕx9r1 _ %[{QJ [enIa1<{xw&of'yU{#_QnI~s1y3WD͡5>g:|}c56Ÿ[ԡi6h')Z5Ϲҩ^G]sYa ָnu Y{UXؾ1y쇣!  ۃ¦M*~$1.I5@_(G k;r =S4QT-{EG5}4;@o`&9gaqsO[.Z0]gMszHkObL2)e2[ӄYΐa𨪝6eSQ$YKAH4JL3$.ZbE4RHNJՃi$g m vHmAoFym&?9!o,_G1oٶwAq+E(W6O* -K;bǐ`K9(hLV _J:(Hb\*ҜN[`Sv%Tl7ڗJ[ѩƚ :n(j\\h,QDxZ`/׳Ȯ(cQ aQgO+|=d|/tF =6 pi)p6W교Jh]&cHXqҼ#3\A|YH^N˾51Bdȃ2^PKuJPKp|4META-INF/manifest.xmlMn F9ަDj =$ ő6@X)H;ۈAhky+zX`uiP}F`k0 լjѶN-ןדi(.N,0J=67Z:޶U]+*.Sz]#wnS^tñzA)4.HCݕE'3RJgL6G/q'k^PSE]kj|-" TLa'8 NG\ ͎alⰵ I> +o6ӛ{nR~{rWPKOPKp|4^2 ''mimetypePKp|4MConfigurations2/statusbar/PKp|4'Configurations2/accelerator/current.xmlPKp|4Configurations2/floater/PKp|4Configurations2/popupmenu/PKp|4JConfigurations2/progressbar/PKp|4Configurations2/menubar/PKp|4Configurations2/toolbar/PKp|4Configurations2/images/Bitmaps/PKp|4 / -layout-cachePKp|4 &`i content.xmlPKp|4{ &E a dstyles.xmlPKp|4 ;pmeta.xmlPKp|4jLzuThumbnails/thumbnail.pngPKp|4uJ xsettings.xmlPKp|4Ot~META-INF/manifest.xmlPK(ocfs2-tools-ocfs2-tools-1.8.6/documentation/ocfs2_faq.html000066400000000000000000001661041347147137200234730ustar00rootroot00000000000000
OCFS2 - FREQUENTLY ASKED QUESTIONS

OCFS2 - FREQUENTLY ASKED QUESTIONS

    GENERAL

  1. How do I get started?
    • Download and install the module and tools rpms.
    • Create cluster.conf and propagate to all nodes.
    • Configure and start the O2CB cluster service.
    • Format the volume.
    • Mount the volume.
  2. How do I know the version number running?
    	# cat /proc/fs/ocfs2/version
    	OCFS2 1.2.1 Fri Apr 21 13:51:24 PDT 2006 (build bd2f25ba0af9677db3572e3ccd92f739)
    
  3. How do I configure my system to auto-reboot after a panic?
    To auto-reboot system 60 secs after a panic, do:
    	# echo 60 > /proc/sys/kernel/panic
    
    To enable the above on every reboot, add the following to /etc/sysctl.conf:
    	kernel.panic = 60
    

    DOWNLOAD AND INSTALL

  4. Where do I get the packages from?
    For Oracle Enterprise Linux 4 and 5, use the up2date command as follows:
    	# up2date --install ocfs2-tools ocfs2console
    	# up2date --install ocfs2-`uname -r`
    
    For Novell's SLES9, use yast to upgrade to the latest SP3 kernel to get the required modules installed. Also, install the ocfs2-tools and ocfs2console packages.
    For Novell's SLES10, install ocfs2-tools and ocfs2console packages. For Red Hat's RHEL4 and RHEL5, download and install the appropriate module package and the two tools packages, ocfs2-tools and ocfs2console. Appropriate module refers to one matching the kernel version, flavor and architecture. Flavor refers to smp, hugemem, etc.
  5. What are the latest versions of the OCFS2 packages?
    For Enterprise Linux 5, the latest module package version is 1.2.6-1 and the latest tools/console package version is 1.2.6-1.
    For Enterprise Linux 4, the latest module package version is 1.2.5-1 and the latest tools/console package version is 1.2.4-1.
  6. How do I interpret the package name ocfs2-2.6.9-22.0.1.ELsmp-1.2.1-1.i686.rpm?
    The package name is comprised of multiple parts separated by '-'.
    • ocfs2 - Package name
    • 2.6.9-22.0.1.ELsmp - Kernel version and flavor
    • 1.2.1 - Package version
    • 1 - Package subversion
    • i686 - Architecture
  7. How do I know which package to install on my box?
    After one identifies the package name and version to install, one still needs to determine the kernel version, flavor and architecture.
    To know the kernel version and flavor, do:
    	# uname -r
    	2.6.9-22.0.1.ELsmp
    
    To know the architecture, do:
    	# rpm -qf /boot/vmlinuz-`uname -r` --queryformat "%{ARCH}\n"
    	i686
    
  8. Why can't I use uname -p to determine the kernel architecture?
    uname -p does not always provide the exact kernel architecture. Case in point the RHEL3 kernels on x86_64. Even though Red Hat has two different kernel architectures available for this port, ia32e and x86_64, uname -p identifies both as the generic x86_64.
  9. How do I install the rpms?
    First install the tools and console packages:
    	# rpm -Uvh ocfs2-tools-1.2.1-1.i386.rpm ocfs2console-1.2.1-1.i386.rpm
    
    Then install the appropriate kernel module package:
    	# rpm -Uvh ocfs2-2.6.9-22.0.1.ELsmp-1.2.1-1.i686.rpm
    
  10. Do I need to install the console?
    No, the console is not required but recommended for ease-of-use.
  11. What are the dependencies for installing ocfs2console?
    ocfs2console requires e2fsprogs, glib2 2.2.3 or later, vte 0.11.10 or later, pygtk2 (RHEL4) or python-gtk (SLES9) 1.99.16 or later, python 2.3 or later and ocfs2-tools.
  12. What modules are installed with the OCFS2 1.2 package?
    • ocfs2.ko
    • ocfs2_dlm.ko
    • ocfs2_dlmfs.ko
    • ocfs2_nodemanager.ko
    • configfs.ko (only Enterprise Linux 4)
    • debugfs.ko (only Enterprise Linux 4)
    The kernel shipped alongwith Enterprise Linux 5 includes configfs.ko and debugfs.ko.
  13. What tools are installed with the ocfs2-tools 1.2 package?
    • mkfs.ocfs2
    • fsck.ocfs2
    • tunefs.ocfs2
    • debugfs.ocfs2
    • mount.ocfs2
    • mounted.ocfs2
    • ocfs2cdsl
    • ocfs2_hb_ctl
    • o2cb_ctl
    • o2cb - init service to start/stop the cluster
    • ocfs2 - init service to mount/umount ocfs2 volumes
    • ocfs2console - installed with the console package
  14. What is debugfs and is it related to debugfs.ocfs2?
    debugfs is an in-memory filesystem developed by Greg Kroah-Hartman. It is useful for debugging as it allows kernel space to easily export data to userspace. It is currently being used by OCFS2 to dump the list of filesystem locks and could be used for more in the future. It is bundled with OCFS2 as the various distributions are currently not bundling it. While debugfs and debugfs.ocfs2 are unrelated in general, the latter is used as the front-end for the debugging info provided by the former. For example, refer to the troubleshooting section.

    CONFIGURE

  15. How do I populate /etc/ocfs2/cluster.conf?
    If you have installed the console, use it to create this configuration file. For details, refer to the user's guide. If you do not have the console installed, check the Appendix in the User's guide for a sample cluster.conf and the details of all the components. Do not forget to copy this file to all the nodes in the cluster. If you ever edit this file on any node, ensure the other nodes are updated as well.
  16. Should the IP interconnect be public or private?
    Using a private interconnect is recommended. While OCFS2 does not take much bandwidth, it does require the nodes to be alive on the network and sends regular keepalive packets to ensure that they are. To avoid a network delay being interpreted as a node disappearing on the net which could lead to a node-self-fencing, a private interconnect is recommended. One could use the same interconnect for Oracle RAC and OCFS2.
  17. What should the node name be and should it be related to the IP address?
    The node name needs to match the hostname. The IP address need not be the one associated with that hostname. As in, any valid IP address on that node can be used. OCFS2 will not attempt to match the node name (hostname) with the specified IP address.
  18. How do I modify the IP address, port or any other information specified in cluster.conf?
    While one can use ocfs2console to add nodes dynamically to a running cluster, any other modifications require the cluster to be offlined. Stop the cluster on all nodes, edit /etc/ocfs2/cluster.conf on one and copy to the rest, and restart the cluster on all nodes. Always ensure that cluster.conf is the same on all the nodes in the cluster.
  19. How do I add a new node to an online cluster?
    You can use the console to add a new node. However, you will need to explicitly add the new node on all the online nodes. That is, adding on one node and propagating to the other nodes is not sufficient. If the operation fails, it will most likely be due to bug#741. In that case, you can use the o2cb_ctl utility on all online nodes as follows:
    	# o2cb_ctl -C -i -n NODENAME -t node -a number=NODENUM -a ip_address=IPADDR -a ip_port=IPPORT -a cluster=CLUSTERNAME
    
    Ensure the node is added both in /etc/ocfs2/cluster.conf and in /config/cluster/CLUSTERNAME/node on all online nodes. You can then simply copy the cluster.conf to the new (still offline) node as well as other offline nodes. At the end, ensure that cluster.conf is consistent on all the nodes.
  20. How do I add a new node to an offline cluster?
    You can either use the console or use o2cb_ctl or simply hand edit cluster.conf. Then either use the console to propagate it to all nodes or hand copy using scp or any other tool. The o2cb_ctl command to do the same is:
            # o2cb_ctl -C -n NODENAME -t node -a number=NODENUM -a ip_address=IPADDR -a ip_port=IPPORT -a cluster=CLUSTERNAME
    
    Notice the "-i" argument is not required as the cluster is not online.

    O2CB CLUSTER SERVICE

  21. How do I configure the cluster service?
    	# /etc/init.d/o2cb configure
    
    Enter 'y' if you want the service to load on boot, the name of the cluster (as listed in /etc/ocfs2/cluster.conf) and the cluster timeouts.
  22. How do I start the cluster service?
    • To load the modules, do:
      	# /etc/init.d/o2cb load
      
    • To Online it, do:
      	# /etc/init.d/o2cb online [cluster_name]
      
    If you have configured the cluster to load on boot, you could combine the two as follows:
    	# /etc/init.d/o2cb start [cluster_name]
    
    The cluster name is not required if you have specified the name during configuration.
  23. How do I stop the cluster service?
    • To offline it, do:
      	# /etc/init.d/o2cb offline [cluster_name]
      
    • To unload the modules, do:
      	# /etc/init.d/o2cb unload
      
    If you have configured the cluster to load on boot, you could combine the two as follows:
    	# /etc/init.d/o2cb stop [cluster_name]
    
    The cluster name is not required if you have specified the name during configuration.
  24. How can I learn the status of the cluster?
    To learn the status of the cluster, do:
    	# /etc/init.d/o2cb status
    
  25. I am unable to get the cluster online. What could be wrong?
    Check whether the node name in the cluster.conf exactly matches the hostname. One of the nodes in the cluster.conf need to be in the cluster for the cluster to be online.

    FORMAT

  26. Should I partition a disk before formatting?
    Yes, partitioning is recommended even if one is planning to use the entire disk for ocfs2. Apart from the fact that partitioned disks are less likely to be "reused" by mistake, some features like mount-by-label only work with partitioned volumes.
    Use fdisk or parted or any other tool for the task.
  27. How do I format a volume?
    You could either use the console or use mkfs.ocfs2 directly to format the volume. For console, refer to the user's guide.
    	# mkfs.ocfs2 -L "oracle_home" /dev/sdX
    
    The above formats the volume with default block and cluster sizes, which are computed based upon the size of the volume.
    	# mkfs.ocfs2 -b 4k -C 32K -L "oracle_home" -N 4 /dev/sdX
    
    The above formats the volume for 4 nodes with a 4K block size and a 32K cluster size.
  28. What does the number of node slots during format refer to?
    The number of node slots specifies the number of nodes that can concurrently mount the volume. This number is specified during format and can be increased using tunefs.ocfs2. This number cannot be decreased.
  29. What should I consider when determining the number of node slots?
    OCFS2 allocates system files, like Journal, for each node slot. So as to not to waste space, one should specify a number within the ballpark of the actual number of nodes. Also, as this number can be increased, there is no need to specify a much larger number than one plans for mounting the volume.
  30. Does the number of node slots have to be the same for all volumes?
    No. This number can be different for each volume.
  31. What block size should I use?
    A block size is the smallest unit of space addressable by the file system. OCFS2 supports block sizes of 512 bytes, 1K, 2K and 4K. The block size cannot be changed after the format. For most volume sizes, a 4K size is recommended. On the other hand, the 512 bytes block is never recommended.
  32. What cluster size should I use?
    A cluster size is the smallest unit of space allocated to a file to hold the data. OCFS2 supports cluster sizes of 4K, 8K, 16K, 32K, 64K, 128K, 256K, 512K and 1M. For database volumes, a cluster size of 128K or larger is recommended. For Oracle home, 32K to 64K.
  33. Any advantage of labelling the volumes?
    As in a shared disk environment, the disk name (/dev/sdX) for a particular device be different on different nodes, labelling becomes a must for easy identification. You could also use labels to identify volumes during mount.
    	# mount -L "label" /dir
    
    The volume label is changeable using the tunefs.ocfs2 utility.

    RESIZE

  34. Can OCFS2 file systems be grown in size?
    Yes, you can grow an OCFS2 file system using tunefs.ocfs2. It should be noted that the tool will only resize the file system and not the underlying partition. You can use fdisk(8) (or any appropriate tool for your disk array) to resize the partition.
  35. What do I need to know to use fdisk(8) to resize the partition?
    To grow a partition using fdisk(8), you will have to delete it and recreate it with a larger size. When recreating it, ensure you specify the same starting disk cylinder as before and a ending disk cylinder that is greater than the existing one. Otherwise, not only will the resize operation fail, but you may lose your entire file system. Backup your data before performing this task.
  36. Short of reboot, how do I get the other nodes in the cluster to see the resized partition?
    Use blockdev(8) to rescan the partition table of the device on the other nodes in the cluster.
    	# blockdev --rereadpt /dev/sdX
    
  37. What is the tunefs.ocfs2 syntax for resizing the file system?
    To grow a file system to the end of the resized partition, do:
    	# tunefs.ocfs2 -S /dev/sdX
    
    For more, refer to the tunefs.ocfs2 manpage.
  38. Can the OCFS2 file system be grown while the file system is in use?
    No. tunefs.ocfs2 1.2.2 only allows offline resize. i.e., the file system cannot be mounted on any node in the cluster. The online resize capability will be added later.
  39. Can the OCFS2 file system be shrunk in size?
    No. We have no current plans on providing this functionality. However, if you find this feature useful, file an enhancement request on bugzilla listing your reasons for the same.

    MOUNT

  40. How do I mount the volume?
    You could either use the console or use mount directly. For console, refer to the user's guide.
    	# mount -t ocfs2 /dev/sdX /dir
    
    The above command will mount device /dev/sdX on directory /dir.
  41. How do I mount by label?
    To mount by label do:
    	# mount -L "label" /dir
    
  42. What entry to I add to /etc/fstab to mount an ocfs2 volume?
    Add the following:
    	/dev/sdX	/dir	ocfs2	noauto,_netdev	0	0
    
    The _netdev option indicates that the devices needs to be mounted after the network is up.
  43. What do I need to do to mount OCFS2 volumes on boot?
    • Enable o2cb service using:
      	# chkconfig --add o2cb
      
    • Enable ocfs2 service using:
      	# chkconfig --add ocfs2
      
    • Configure o2cb to load on boot using:
      	# /etc/init.d/o2cb configure
      
    • Add entries into /etc/fstab as follows:
      	/dev/sdX	/dir	ocfs2	_netdev	0	0
      
  44. How do I know my volume is mounted?
    • Enter mount without arguments, or,
      	# mount
      
    • List /etc/mtab, or,
      	# cat /etc/mtab
      
    • List /proc/mounts, or,
      	# cat /proc/mounts
      
    • Run ocfs2 service.
      	# /etc/init.d/ocfs2 status
      
      mount command reads the /etc/mtab to show the information.
  45. What are the /config and /dlm mountpoints for?
    OCFS2 comes bundled with two in-memory filesystems configfs and ocfs2_dlmfs. configfs is used by the ocfs2 tools to communicate to the in-kernel node manager the list of nodes in the cluster and to the in-kernel heartbeat thread the resource to heartbeat on. ocfs2_dlmfs is used by ocfs2 tools to communicate with the in-kernel dlm to take and release clusterwide locks on resources.
  46. Why does it take so much time to mount the volume?
    It takes around 5 secs for a volume to mount. It does so so as to let the heartbeat thread stabilize. In a later release, we plan to add support for a global heartbeat, which will make most mounts instant.
  47. Why does it take so much time to umount the volume?
    During umount, the dlm has to migrate all the mastered lockres' to an another node in the cluster. In 1.2, the lockres migration is a synchronous operation. We are looking into making it asynchronous so as to reduce the time it takes to migrate the lockres'. (While we have improved this performance in 1.2.5, the task of asynchronously migrating lockres' has been pushed to the 1.4 time frame.) To find the number of lockres in all dlm domains, do:
    	# cat /proc/fs/ocfs2_dlm/*/stat
    	local=60624, remote=1, unknown=0, key=0x8619a8da
    
    local refers to locally mastered lockres'.

    ORACLE RAC

  48. Any special flags to run Oracle RAC?
    OCFS2 volumes containing the Voting diskfile (CRS), Cluster registry (OCR), Data files, Redo logs, Archive logs and Control files must be mounted with the datavolume and nointr mount options. The datavolume option ensures that the Oracle processes opens these files with the o_direct flag. The nointr option ensures that the ios are not interrupted by signals.
    	# mount -o datavolume,nointr -t ocfs2 /dev/sda1 /u01/db
    
  49. What about the volume containing Oracle home?
    Oracle home volume should be mounted normally, that is, without the datavolume and nointr mount options. These mount options are only relevant for Oracle files listed above.
    	# mount -t ocfs2 /dev/sdb1 /software/orahome
    
    Also as OCFS2 does not currently support shared writeable mmap, the health check (GIMH) file $ORACLE_HOME/dbs/hc_ORACLESID.dat and the ASM file $ASM_HOME/dbs/ab_ORACLESID.dat should be symlinked to local filesystem. We expect to support shared writeable mmap in the OCFS2 1.4 release.
  50. Does that mean I cannot have my data file and Oracle home on the same volume?
    Yes. The volume containing the Oracle data files, redo-logs, etc. should never be on the same volume as the distribution (including the trace logs like, alert.log).
  51. Any other information I should be aware off?
    The 1.2.3 release of OCFS2 does not update the modification time on the inode across the cluster for non-extending writes. However, the time will be locally updated in the cached inodes. This leads to one observing different times (ls -l) for the same file on different nodes on the cluster.
    While this does not affect most uses of the filesystem, as one variably changes the file size during write, the one usage where this is most commonly experienced is with Oracle datafiles and redologs. This is because Oracle rarely resizes these files and thus almost all writes are non-extending.
    In OCFS2 1.4, we intend to fix this by updating modification times for all writes while providing an opt-out mount option (nocmtime) for users who would prefer to avoid the performance overhead associated with this feature.

    MIGRATE DATA FROM OCFS (RELEASE 1) TO OCFS2

  52. Can I mount OCFS volumes as OCFS2?
    No. OCFS and OCFS2 are not on-disk compatible. We had to break the compatibility in order to add many of the new features. At the same time, we have added enough flexibility in the new disk layout so as to maintain backward compatibility in the future.
  53. Can OCFS volumes and OCFS2 volumes be mounted on the same machine simultaneously?
    No. OCFS only works on 2.4 linux kernels (Red Hat's AS2.1/EL3 and SuSE's SLES8). OCFS2, on the other hand, only works on the 2.6 kernels (RHEL4, SLES9 and SLES10).
  54. Can I access my OCFS volume on 2.6 kernels (SLES9/SLES10/RHEL4)?
    Yes, you can access the OCFS volume on 2.6 kernels using FSCat tools, fsls and fscp. These tools can access the OCFS volumes at the device layer, to list and copy the files to another filesystem. FSCat tools are available on oss.oracle.com.
  55. Can I in-place convert my OCFS volume to OCFS2?
    No. The on-disk layout of OCFS and OCFS2 are sufficiently different that it would require a third disk (as a temporary buffer) inorder to in-place upgrade the volume. With that in mind, it was decided not to develop such a tool but instead provide tools to copy data from OCFS without one having to mount it.
  56. What is the quickest way to move data from OCFS to OCFS2?
    Quickest would mean having to perform the minimal number of copies. If you have the current backup on a non-OCFS volume accessible from the 2.6 kernel install, then all you would need to do is to retore the backup on the OCFS2 volume(s). If you do not have a backup but have a setup in which the system containing the OCFS2 volumes can access the disks containing the OCFS volume, you can use the FSCat tools to extract data from the OCFS volume and copy onto OCFS2.

    COREUTILS

  57. Like with OCFS (Release 1), do I need to use o_direct enabled tools to perform cp, mv, tar, etc.?
    No. OCFS2 does not need the o_direct enabled tools. The file system allows processes to open files in both o_direct and bufferred mode concurrently.

    EXPORTING VIA NFS

  58. Can I export an OCFS2 file system via NFS?
    Yes, you can export files on OCFS2 via the standard Linux NFS server. Please note that only NFS version 3 and above will work. In practice, this means clients need to be running a 2.4.x kernel or above.
  59. Is there no solution for the NFS v2 clients?
    NFS v2 clients can work if the server exports the volumes with the no_subtree_check option. However, this has some security implications that is documented in the exports manpage.

    TROUBLESHOOTING

  60. How do I enable and disable filesystem tracing?
    To list all the debug bits along with their statuses, do:
    	# debugfs.ocfs2 -l
    
    To enable tracing the bit SUPER, do:
    	# debugfs.ocfs2 -l SUPER allow
    
    To disable tracing the bit SUPER, do:
    	# debugfs.ocfs2 -l SUPER off
    
    To totally turn off tracing the SUPER bit, as in, turn off tracing even if some other bit is enabled for the same, do:
    	# debugfs.ocfs2 -l SUPER deny
    
    To enable heartbeat tracing, do:
    	# debugfs.ocfs2 -l HEARTBEAT ENTRY EXIT allow
    
    To disable heartbeat tracing, do:
    	# debugfs.ocfs2 -l HEARTBEAT off ENTRY EXIT deny
    
  61. How do I get a list of filesystem locks and their statuses?
    OCFS2 1.0.9+ has this feature. To get this list, do:
    • Mount debugfs is mounted at /debug (EL4) or /sys/kernel/debug (EL5).
      	# mount -t debugfs debugfs /debug
      	- OR -
      	# mount -t debugfs debugfs /sys/kernel/debug
      
    • Dump the locks.
      	# echo "fs_locks" | debugfs.ocfs2 /dev/sdX >/tmp/fslocks
      
  62. How do I read the fs_locks output?
    Let's look at a sample output:
    	Lockres: M000000000000000006672078b84822  Mode: Protected Read
    	Flags: Initialized Attached
    	RO Holders: 0  EX Holders: 0
    	Pending Action: None  Pending Unlock Action: None
    	Requested Mode: Protected Read  Blocking Mode: Invalid
    
    First thing to note is the Lockres, which is the lockname. The dlm identifies resources using locknames. A lockname is a combination of a lock type (S superblock, M metadata, D filedata, R rename, W readwrite), inode number and generation.
    To get the inode number and generation from lockname, do:
    	#echo "stat " | debugfs.ocfs2 -n /dev/sdX
    	Inode: 419616   Mode: 0666   Generation: 2025343010 (0x78b84822)
    	....
    
    To map the lockname to a directory entry, do:
    	# echo "locate " | debugfs.ocfs2 -n /dev/sdX
    	419616  /linux-2.6.15/arch/i386/kernel/semaphore.c
    
    One could also provide the inode number instead of the lockname.
    	# echo "locate <419616>" | debugfs.ocfs2 -n /dev/sdX
    	419616  /linux-2.6.15/arch/i386/kernel/semaphore.c
    
    To get a lockname from a directory entry, do:
    	# echo "encode /linux-2.6.15/arch/i386/kernel/semaphore.c" | debugfs.ocfs2 -n /dev/sdX
    	M000000000000000006672078b84822 D000000000000000006672078b84822 W000000000000000006672078b84822
    
    The first is the Metadata lock, then Data lock and last ReadWrite lock for the same resource.

    The DLM supports 3 lock modes: NL no lock, PR protected read and EX exclusive.

    If you have a dlm hang, the resource to look for would be one with the "Busy" flag set.

    The next step would be to query the dlm for the lock resource.

    Note: The dlm debugging is still a work in progress.

    To do dlm debugging, first one needs to know the dlm domain, which matches the volume UUID.
    	# echo "stats" | debugfs.ocfs2 -n /dev/sdX | grep UUID: | while read a b ; do echo $b ; done
    	82DA8137A49A47E4B187F74E09FBBB4B
    
    Then do:
    	# echo R dlm_domain lockname > /proc/fs/ocfs2_dlm/debug
    
    For example:
    	# echo R 82DA8137A49A47E4B187F74E09FBBB4B M000000000000000006672078b84822 > /proc/fs/ocfs2_dlm/debug
    	# dmesg | tail
    	struct dlm_ctxt: 82DA8137A49A47E4B187F74E09FBBB4B, node=79, key=965960985
    	lockres: M000000000000000006672078b84822, owner=75, state=0 last used: 0, on purge list: no
    	  granted queue:
    	    type=3, conv=-1, node=79, cookie=11673330234144325711, ast=(empty=y,pend=n), bast=(empty=y,pend=n)
    	  converting queue:
    	  blocked queue:
    
    It shows that the lock is mastered by node 75 and that node 79 has been granted a PR lock on the resource.

    This is just to give a flavor of dlm debugging.

    LIMITS

  63. Is there a limit to the number of subdirectories in a directory?
    Yes. OCFS2 currently allows up to 32000 subdirectories. While this limit could be increased, we will not be doing it till we implement some kind of efficient name lookup (htree, etc.).
  64. Is there a limit to the size of an ocfs2 file system?
    Yes, current software addresses block numbers with 32 bits. So the file system device is limited to (2 ^ 32) * blocksize (see mkfs -b). With a 4KB block size this amounts to a 16TB file system. This block addressing limit will be relaxed in future software. At that point the limit becomes addressing clusters of 1MB each with 32 bits which leads to a 4PB file system.

    SYSTEM FILES

  65. What are system files?
    System files are used to store standard filesystem metadata like bitmaps, journals, etc. Storing this information in files in a directory allows OCFS2 to be extensible. These system files can be accessed using debugfs.ocfs2. To list the system files, do:
    	# echo "ls -l //" | debugfs.ocfs2 -n /dev/sdX
            	18        16       1      2  .
            	18        16       2      2  ..
            	19        24       10     1  bad_blocks
            	20        32       18     1  global_inode_alloc
            	21        20       8      1  slot_map
            	22        24       9      1  heartbeat
            	23        28       13     1  global_bitmap
            	24        28       15     2  orphan_dir:0000
            	25        32       17     1  extent_alloc:0000
            	26        28       16     1  inode_alloc:0000
            	27        24       12     1  journal:0000
            	28        28       16     1  local_alloc:0000
            	29        3796     17     1  truncate_log:0000
    
    The first column lists the block number.
  66. Why do some files have numbers at the end?
    There are two types of files, global and local. Global files are for all the nodes, while local, like journal:0000, are node specific. The set of local files used by a node is determined by the slot mapping of that node. The numbers at the end of the system file name is the slot#. To list the slot maps, do:
    	# echo "slotmap" | debugfs.ocfs2 -n /dev/sdX
           	Slot#   Node#
                0      39
           	    1      40
                2      41
           	    3      42
    

    HEARTBEAT

  67. How does the disk heartbeat work?
    Every node writes every two secs to its block in the heartbeat system file. The block offset is equal to its global node number. So node 0 writes to the first block, node 1 to the second, etc. All the nodes also read the heartbeat sysfile every two secs. As long as the timestamp is changing, that node is deemed alive.
  68. When is a node deemed dead?
    An active node is deemed dead if it does not update its timestamp for O2CB_HEARTBEAT_THRESHOLD (default=7) loops. Once a node is deemed dead, the surviving node which manages to cluster lock the dead node's journal, recovers it by replaying the journal.
  69. What about self fencing?
    A node self-fences if it fails to update its timestamp for ((O2CB_HEARTBEAT_THRESHOLD - 1) * 2) secs. The [o2hb-xx] kernel thread, after every timestamp write, sets a timer to panic the system after that duration. If the next timestamp is written within that duration, as it should, it first cancels that timer before setting up a new one. This way it ensures the system will self fence if for some reason the [o2hb-x] kernel thread is unable to update the timestamp and thus be deemed dead by other nodes in the cluster.
  70. How can one change the parameter value of O2CB_HEARTBEAT_THRESHOLD?
    This parameter value could be changed by adding it to /etc/sysconfig/o2cb and RESTARTING the O2CB cluster. This value should be the SAME on ALL the nodes in the cluster.
  71. What should one set O2CB_HEARTBEAT_THRESHOLD to?
    It should be set to the timeout value of the io layer. Most multipath solutions have a timeout ranging from 60 secs to 120 secs. For 60 secs, set it to 31. For 120 secs, set it to 61.
    	O2CB_HEARTBEAT_THRESHOLD = (((timeout in secs) / 2) + 1)
    
  72. How does one check the current active O2CB_HEARTBEAT_THRESHOLD value?
    	# cat /proc/fs/ocfs2_nodemanager/hb_dead_threshold
    	7
    
  73. What if a node umounts a volume?
    During umount, the node will broadcast to all the nodes that have mounted that volume to drop that node from its node maps. As the journal is shutdown before this broadcast, any node crash after this point is ignored as there is no need for recovery.
  74. I encounter "Kernel panic - not syncing: ocfs2 is very sorry to be fencing this system by panicing" whenever I run a heavy io load?
    We have encountered a bug with the default CFQ io scheduler which causes a process doing heavy io to temporarily starve out other processes. While this is not fatal for most environments, it is for OCFS2 as we expect the hb thread to reading from and writing to the hb area atleast once every 12 secs (default). This bug has been addressed by Red Hat in RHEL4 U4 (2.6.9-42.EL) and Novell in SLES9 SP3 (2.6.5-7.257). If you wish to use the DEADLINE io scheduler, you could do so by appending "elevator=deadline" to the kernel command line as follows:

    • For SLES9, edit the command line in /boot/grub/menu.lst.
      title Linux 2.6.5-7.244-bigsmp (with deadline)
      	kernel (hd0,4)/boot/vmlinuz-2.6.5-7.244-bigsmp root=/dev/sda5
      		vga=0x314 selinux=0 splash=silent resume=/dev/sda3 elevator=deadline showopts console=tty0 console=ttyS0,115200 noexec=off
      	initrd (hd0,4)/boot/initrd-2.6.5-7.244-bigsmp
      
    • For RHEL4, edit the command line in /boot/grub/grub.conf:
      title Red Hat Enterprise Linux AS (2.6.9-22.EL) (with deadline)
      	root (hd0,0)
      	kernel /vmlinuz-2.6.9-22.EL ro root=LABEL=/ console=ttyS0,115200 console=tty0 elevator=deadline noexec=off
      	initrd /initrd-2.6.9-22.EL.img
      
    To see the current kernel command line, do:
    	# cat /proc/cmdline
    

    QUORUM AND FENCING

  75. What is a quorum?
    A quorum is a designation given to a group of nodes in a cluster which are still allowed to operate on shared storage. It comes up when there is a failure in the cluster which breaks the nodes up into groups which can communicate in their groups and with the shared storage but not between groups.
  76. How does OCFS2's cluster services define a quorum? The quorum decision is made by a single node based on the number of other nodes that are considered alive by heartbeating and the number of other nodes that are reachable via the network.
    A node has quorum when:
    • it sees an odd number of heartbeating nodes and has network connectivity to more than half of them.
      OR,
    • it sees an even number of heartbeating nodes and has network connectivity to at least half of them *and* has connectivity to the heartbeating node with the lowest node number.
  77. What is fencing?
    Fencing is the act of forecefully removing a node from a cluster. A node with OCFS2 mounted will fence itself when it realizes that it doesn't have quorum in a degraded cluster. It does this so that other nodes won't get stuck trying to access its resources. Currently OCFS2 will panic the machine when it realizes it has to fence itself off from the cluster. As described above, it will do this when it sees more nodes heartbeating than it has connectivity to and fails the quorum test.
    Due to user reports of nodes hanging during fencing, OCFS2 1.2.5 no longer uses "panic" for fencing. Instead, by default, it uses "machine restart". This should not only prevent nodes from hanging during fencing but also allow for nodes to quickly restart and rejoin the cluster. While this change is internal in nature, we are documenting this so as to make users aware that they are no longer going to see the familiar panic stack trace during fencing. Instead they will see the message "*** ocfs2 is very sorry to be fencing this system by restarting ***" and that too probably only as part of the messages captured on the netdump/netconsole server.
    If perchance the user wishes to use panic to fence (maybe to see the familiar oops stack trace or on the advise of customer support to diagnose frequent reboots), one can do so by issuing the following command after the O2CB cluster is online.
    	# echo 1 > /proc/fs/ocfs2_nodemanager/fence_method
    
    Please note that this change is local to a node.
  78. How does a node decide that it has connectivity with another?
    When a node sees another come to life via heartbeating it will try and establish a TCP connection to that newly live node. It considers that other node connected as long as the TCP connection persists and the connection is not idle for 10 seconds. Once that TCP connection is closed or idle it will not be reestablished until heartbeat thinks the other node has died and come back alive.
  79. How long does the quorum process take?
    First a node will realize that it doesn't have connectivity with another node. This can happen immediately if the connection is closed but can take a maximum of 10 seconds of idle time. Then the node must wait long enough to give heartbeating a chance to declare the node dead. It does this by waiting two iterations longer than the number of iterations needed to consider a node dead (see the Heartbeat section of this FAQ). The current default of 7 iterations of 2 seconds results in waiting for 9 iterations or 18 seconds. By default, then, a maximum of 28 seconds can pass from the time a network fault occurs until a node fences itself.
  80. How can one avoid a node from panic-ing when one shutdowns the other node in a 2-node cluster?
    This typically means that the network is shutting down before all the OCFS2 volumes are being umounted. Ensure the ocfs2 init script is enabled. This script ensures that the OCFS2 volumes are umounted before the network is shutdown. To check whether the service is enabled, do:
           	# chkconfig --list ocfs2
           	ocfs2     0:off   1:off   2:on    3:on    4:on    5:on    6:off
    
  81. How does one list out the startup and shutdown ordering of the OCFS2 related services?
    • To list the startup order for runlevel 3 on RHEL4, do:
      	# cd /etc/rc3.d
      	# ls S*ocfs2* S*o2cb* S*network*
      	S10network  S24o2cb  S25ocfs2
      
    • To list the shutdown order on RHEL4, do:
      	# cd /etc/rc6.d
      	# ls K*ocfs2* K*o2cb* K*network*
      	K19ocfs2  K20o2cb  K90network
      
    • To list the startup order for runlevel 3 on SLES9/SLES10, do:
      	# cd /etc/init.d/rc3.d
      	# ls S*ocfs2* S*o2cb* S*network*
      	S05network  S07o2cb  S08ocfs2
      
    • To list the shutdown order on SLES9/SLES10, do:
      	# cd /etc/init.d/rc3.d
      	# ls K*ocfs2* K*o2cb* K*network*
      	K14ocfs2  K15o2cb  K17network
      
    Please note that the default ordering in the ocfs2 scripts only include the network service and not any shared-device specific service, like iscsi. If one is using iscsi or any shared device requiring a service to be started and shutdown, please ensure that that service runs before and shutsdown after the ocfs2 init service.

    NOVELL'S SLES9 and SLES10

  82. Why are OCFS2 packages for SLES9 and SLES10 not made available on oss.oracle.com?
    OCFS2 packages for SLES9 and SELS10 are available directly from Novell as part of the kernel. Same is true for the various Asianux distributions and for ubuntu. As OCFS2 is now part of the mainline kernel, we expect more distributions to bundle the product with the kernel.
  83. What versions of OCFS2 are available with SLES9 and how do they match with the Red Hat versions available on oss.oracle.com?
    As both Novell and Oracle ship OCFS2 on different schedules, the package versions do not match. We expect to resolve itself over time as the number of patch fixes reduce. Novell is shipping two SLES9 releases, viz., SP2 and SP3.
    • The latest kernel with the SP2 release is 2.6.5-7.202.7. It ships with OCFS2 1.0.8.
    • The latest kernel with the SP3 release is 2.6.5-7.283. It ships with OCFS2 1.2.3. Please contact Novell to get the latest OCFS2 modules on SLES9 SP3.
  84. What versions of OCFS2 are available with SLES10? SLES10 is currently shipping OCFS2 1.2.3. SLES10 SP1 (beta) is currently shipping 1.2.5.

    RELEASE 1.2

  85. What is new in OCFS2 1.2?
    OCFS2 1.2 has two new features:
    • It is endian-safe. With this release, one can mount the same volume concurrently on x86, x86-64, ia64 and big endian architectures ppc64 and s390x.
    • Supports readonly mounts. The fs uses this feature to auto remount ro when encountering on-disk corruptions (instead of panic-ing).
  86. Do I need to re-make the volume when upgrading?
    No. OCFS2 1.2 is fully on-disk compatible with 1.0.
  87. Do I need to upgrade anything else?
    Yes, the tools needs to be upgraded to ocfs2-tools 1.2. ocfs2-tools 1.0 will not work with OCFS2 1.2 nor will 1.2 tools work with 1.0 modules.

    UPGRADE TO THE LATEST RELEASE

  88. How do I upgrade to the latest release?
    • Download the latest ocfs2-tools and ocfs2console for the target platform and the appropriate ocfs2 module package for the kernel version, flavor and architecture. (For more, refer to the "Download and Install" section above.)

    • Umount all OCFS2 volumes.
      	# umount -at ocfs2
      
    • Shutdown the cluster and unload the modules.
      	# /etc/init.d/o2cb offline
      	# /etc/init.d/o2cb unload
      
    • If required, upgrade the tools and console.
      	# rpm -Uvh ocfs2-tools-1.2.2-1.i386.rpm ocfs2console-1.2.2-1.i386.rpm
      
    • Upgrade the module.
      	# rpm -Uvh ocfs2-2.6.9-42.0.3.ELsmp-1.2.4-2.i686.rpm
      
    • Ensure init services ocfs2 and o2cb are enabled.
      	# chkconfig --add o2cb
      	# chkconfig --add ocfs2
      
    • To check whether the services are enabled, do:
      	# chkconfig --list o2cb
      	o2cb      0:off   1:off   2:on    3:on    4:on    5:on    6:off
      	# chkconfig --list ocfs2
      	ocfs2     0:off   1:off   2:on    3:on    4:on    5:on    6:off
      
    • To update the cluster timeouts, do:
      	# /etc/init.d/o2cb configure
      
      
    • At this stage one could either reboot the node or simply, restart the cluster and mount the volume.
  89. Can I do a rolling upgrade from 1.2.3 to 1.2.4?
    No. The network protocol had to be updated in 1.2.4 to allow for proper reference counting of lockres' across the cluster. This fix was necessary to fix races encountered during lockres purge and migrate. Effectively, one cannot run 1.2.4 on one node while another node is still on an earlier release (1.2.3 or older).
  90. Can I do a rolling upgrade from 1.2.4 to 1.2.5?
    No. The network protocol had to be updated in 1.2.5 to ensure all nodes were using the same O2CB timeouts. Effectively, one cannot run 1.2.5 on one node while another node is still on an earlier release. (For the record, the protocol remained the same between 1.2.0 to 1.2.3 before changing in 1.2.4 and 1.2.5.)
  91. After upgrade I am getting the following error on mount "mount.ocfs2: Invalid argument while mounting /dev/sda6 on /ocfs".
    Do "dmesg | tail". If you see the error:
    ocfs2_parse_options:523 ERROR: Unrecognized mount option "heartbeat=local" or missing value
    
    it means that you are trying to use the 1.2 tools and 1.0 modules. Ensure that you have unloaded the 1.0 modules and installed and loaded the 1.2 modules. Use modinfo to determine the version of the module installed and/or loaded.
  92. The cluster fails to load. What do I do?
    Check "demsg | tail" for any relevant errors. One common error is as follows:
    SELinux: initialized (dev configfs, type configfs), not configured for labeling audit(1139964740.184:2): avc:  denied  { mount } for  ...
    
    The above error indicates that you have SELinux activated. A bug in SELinux does not allow configfs to mount. Disable SELinux by setting "SELINUX=disabled" in /etc/selinux/config. Change is activated on reboot.

    PROCESSES

  93. List and describe all OCFS2 threads?
    [o2net]
    One per node. Is a workqueue thread started when the cluster is brought online and stopped when offline. It handles the network communication for all threads. It gets the list of active nodes from the o2hb thread and sets up tcp/ip communication channels with each active node. It sends regular keepalive packets to detect any interruption on the channels.
    [user_dlm]
    One per node. Is a workqueue thread started when dlmfs is loaded and stopped on unload. (dlmfs is an in-memory file system which allows user space processes to access the dlm in kernel to lock and unlock resources.) Handles lock downconverts when requested by other nodes.
    [ocfs2_wq]
    One per node. Is a workqueue thread started when ocfs2 module is loaded and stopped on unload. Handles blockable file system tasks like truncate log flush, orphan dir recovery and local alloc recovery, which involve taking dlm locks. Various code paths queue tasks to this thread. For example, ocfs2rec queues orphan dir recovery so that while the task is kicked off as part of recovery, its completion does not affect the recovery time.
    [o2hb-14C29A7392]
    One per heartbeat device. Is a kernel thread started when the heartbeat region is populated in configfs and stopped when it is removed. It writes every 2 secs to its block in the heartbeat region to indicate to other nodes that that node is alive. It also reads the region to maintain a nodemap of live nodes. It notifies o2net and dlm any changes in the nodemap.
    [ocfs2vote-0]
    One per mount. Is a kernel thread started when a volume is mounted and stopped on umount. It downgrades locks when requested by other nodes in reponse to blocking ASTs (BASTs). It also fixes up the dentry cache in reponse to files unlinked or renamed on other nodes.
    [dlm_thread]
    One per dlm domain. Is a kernel thread started when a dlm domain is created and stopped when destroyed. This is the core dlm which maintains the list of lock resources and handles the cluster locking infrastructure.
    [dlm_reco_thread]
    One per dlm domain. Is a kernel thread which handles dlm recovery whenever a node dies. If the node is the dlm recovery master, it remasters all the locks owned by the dead node.
    [dlm_wq]
    One per dlm domain. Is a workqueue thread. o2net queues dlm tasks on this thread.
    [kjournald]
    One per mount. Is used as OCFS2 uses JDB for journalling.
    [ocfs2cmt-0]
    One per mount. Is a kernel thread started when a volume is mounted and stopped on umount. Works in conjunction with kjournald.
    [ocfs2rec-0]
    Is started whenever another node needs to be be recovered. This could be either on mount when it discovers a dirty journal or during operation when hb detects a dead node. ocfs2rec handles the file system recovery and it runs after the dlm has finished its recovery.

    BUILD RPMS FOR HOTFIX KERNELS

  94. How to build OCFS2 packages for a hotfix kernel?
    • Download and install all the kernel-devel packages for the hotfix kernel.
    • Download and untar the OCFS2 source tarball.
      	# cd /tmp
      	# wget http://oss.oracle.com/projects/ocfs2/dist/files/source/v1.2/ocfs2-1.2.3.tar.gz
      	# tar -zxvf ocfs2-1.2.3.tar.gz
      
    • Ensure rpmbuild is installed and ~/.rpmmacros contains the proper links.
      	# cat ~/.rpmmacros
      	%_topdir        /home/jdoe/rpms
      	%_tmppath       /home/jdoe/rpms/tmp
      	%_sourcedir     /home/jdoe/rpms/SOURCES
      	%_specdir       /home/jdoe/rpms/SPECS
      	%_srcrpmdir     /home/jdoe/rpms/SRPMS
      	%_rpmdir        /home/jdoe/rpms/RPMS
      	%_builddir      /home/jdoe/rpms/BUILD
      
    • Configure and make.
      	# ./configure --with-kernel=/usr/src/kernels/2.6.9-42.X.EL_rpm
      	# make rhel4_2.6.9-42.X.EL_rpm
      
    The packages will be in %_rpmdir.
  95. Are the self-built packages officially supported by Oracle Support?
    No. Oracle Support does not provide support for self-built modules. If you wish official support, contact Oracle via Support or the ocfs2-users mailing list with the link to the hotfix kernel (kernel-devel and kernel-src rpms).

    BACKUP SUPER BLOCK

  96. What is a Backup Super block? A backup super block is a copy of the super block. As the super block is typically located close to the start of the device, it is susceptible to be overwritten, say, by an errant write (dd if=file of=/dev/sdX). Moreover, as the super block stores critical information that is hard to recreate, it becomes important to backup the block and use it when the super block gets corrupted.
  97. Where are the backup super blocks located? In OCFS2, the super blocks are backed up to blocks at the 1G, 4G, 16G, 64G, 256G and 1T byte offsets. The actual number of backups depend on the size of the device. It should be noted that the super block is not backed up on devices smaller than 1G.
  98. How does one enable this feature? mkfs.ocfs2 1.2.3 or later automatically backs up super blocks on devices larger than 1G. One can disable this by using the --no-backup-super option.
  99. How do I detect whether the super blocks are backed up on a device?
    	# debugfs.ocfs2 -R "stats" /dev/sdX | grep "Feature Compat"
            	Feature Compat: 1 BackupSuper
    
  100. How do I backup the super block on a device formatted by an older mkfs.ocfs2? tunefs.ocfs2 1.2.3 or later can attempt to retroactively backup the super block.
    	# tunefs.ocfs2 --backup-super /dev/sdX
    	tunefs.ocfs2 1.2.3
    	Adding backup superblock for the volume
    	Proceed (y/N): y
    	Backed up Superblock.
    	Wrote Superblock
    
    However, it is quite possible that one or more backup locations are in use by the file system. (tunefs.ocfs2 backs up the block only if all the backup locations are unused.)
    	# tunefs.ocfs2 --backup-super /dev/sdX
    	tunefs.ocfs2 1.2.3
    	tunefs.ocfs2: block 262144 is in use.
    	tunefs.ocfs2: block 4194304 is in use.
    	tunefs.ocfs2: Cannot enable backup superblock as backup blocks are in use
    
    If so, use the verify_backup_super script to list out the objects using these blocks.
    	# ./verify_backup_super /dev/sdX
    	Locating inodes using blocks 262144 1048576 4194304 on device /dev/sdX
            	Block#            Inode             Block Offset   
            	262144            27                65058          
            	1048576           Unused                           
            	4194304           4161791           25             
    	Matching inodes to object names
            	27      //journal:0003
            	4161791 /src/kernel/linux-2.6.19/drivers/scsi/BusLogic.c
    
    If the object happens to be user created, move that object temporarily to an another volume before re-attempting the operation. However, this will not work if one or more blocks are being used by a system file (shown starting with double slashes //), say, a journal.
  101. How do I ask fsck.ocfs2 to use a backup super block? To recover a volume using the second backup super block, do:
    	# fsck.ocfs2 -f -r 2 /dev/sdX
    	[RECOVER_BACKUP_SUPERBLOCK] Recover superblock information from backup block#1048576?  n
    	Checking OCFS2 filesystem in /dev/sdX
      	label:              myvolume
      	uuid:               4d 1d 1f f3 24 01 4d 3f 82 4c e2 67 0c b2 94 f3 
      	number of blocks:   13107196
      	bytes per block:    4096
      	number of clusters: 13107196
      	bytes per cluster:  4096
      	max slots:          4
    
    	/dev/sdX was run with -f, check forced.
    	Pass 0a: Checking cluster allocation chains
    	Pass 0b: Checking inode allocation chains
    	Pass 0c: Checking extent block allocation chains
    	Pass 1: Checking inodes and blocks.
    	Pass 2: Checking directory entries.
    	Pass 3: Checking directory connectivity.
    	Pass 4a: checking for orphaned inodes
    	Pass 4b: Checking inodes link counts.
    	All passes succeeded.
    
    For more, refer to the man pages.

    CONFIGURING CLUSTER TIMEOUTS

  102. List and describe all the configurable timeouts in the O2CB cluster stack? OCFS2 1.2.5 has 4 different configurable O2CB cluster timeouts:
    • O2CB_HEARTBEAT_THRESHOLD - The Disk Heartbeat timeout is the number of two second iterations before a node is considered dead. The exact formula used to convert the timeout in seconds to the number of iterations is as follows:
              O2CB_HEARTBEAT_THRESHOLD = (((timeout in seconds) / 2) + 1)
      
      For e.g., to specify a 60 sec timeout, set it to 31. For 120 secs, set it to 61. The default is 12 secs (O2CB_HEARTBEAT_THRESHOLD = 7).
    • O2CB_IDLE_TIMEOUT_MS - The Network Idle timeout specifies the time in miliseconds before a network connection is considered dead. The default is 10000 ms.
    • O2CB_KEEPALIVE_DELAY_MS - The Network Keepalive specifies the maximum delay in miliseconds before a keepalive packet is sent. As in, a keepalive packet is sent if a network connection between two nodes is silent for this duration. If the other node is alive and is connected, it is expected to respond. The default is 5000 ms.
    • O2CB_RECONNECT_DELAY_MS - The Network Reconnect specifies the minimum delay in miliseconds between connection attempts. The default is 2000 ms.
  103. What are the recommended timeout values? As timeout values depend on the hardware being used, there is no one set of recommended values. For e.g., users of multipath io should set the disk heartbeat threshold to atleast 60 secs, if not 120 secs. Similarly, users of Network bonding should set the network idle timeout to atleast 30 secs, if not 60 secs.
  104. What were the timeouts set to during OCFS2 1.2.5 release testing? The timeouts used during release testing were as follows:
    	O2CB_HEARTBEAT_THRESHOLD = 31
    	O2CB_IDLE_TIMEOUT_MS = 30000
    	O2CB_KEEPALIVE_DELAY_MS = 2000
    	O2CB_RECONNECT_DELAY_MS = 2000
    
    The default cluster timeouts in OCFS2 1.2.6 for EL5 have been set to the above. The upcoming release, OCFS2 1.2.7 (EL4 and EL5), will make the above default for both EL4 and EL5.
  105. Can one change these timeout values in a round robin fashion? No. The o2net handshake protocol ensures that all the timeout values for both the nodes are consistent and fails if any value differs. This failed connection results in a failed mount, the reason for which is always listed in dmesg.
  106. How does one set these O2CB timeouts? Umount all OCFS2 volumes and shutdown the O2CB cluster. If not already, upgrade to OCFS2 1.2.5 and OCFS2 TOOLS 1.2.4. Then use o2cb configure to set the new values. Do the same on all nodes. Start mounting volumes only after the timeouts have been set on all nodes.
    	# service o2cb configure
    	Configuring the O2CB driver.
    
    	This will configure the on-boot properties of the O2CB driver.
    	The following questions will determine whether the driver is loaded on
    	boot.  The current values will be shown in brackets ('[]').  Hitting
    	 without typing an answer will keep that current value.  Ctrl-C
    	will abort.
    
    	Load O2CB driver on boot (y/n) [n]: y
    	Cluster to start on boot (Enter "none" to clear) []: mycluster
    	Specify heartbeat dead threshold (>=7) [7]: 31
    	Specify network idle timeout in ms (>=5000) [10000]: 30000
    	Specify network keepalive delay in ms (>=1000) [5000]: 2000
    	Specify network reconnect delay in ms (>=2000) [2000]: 2000
    	Writing O2CB configuration: OK
    	Starting O2CB cluster mycluster: OK
    
  107. How to find the O2CB timeout values in effect?
    	# /etc/init.d/o2cb status
    	Module "configfs": Loaded
    	Filesystem "configfs": Mounted
    	Module "ocfs2_nodemanager": Loaded
    	Module "ocfs2_dlm": Loaded
    	Module "ocfs2_dlmfs": Loaded
    	Filesystem "ocfs2_dlmfs": Mounted
    	Checking O2CB cluster mycluster: Online
    	  Heartbeat dead threshold: 31
    	  Network idle timeout: 30000
    	  Network keepalive delay: 2000
    	  Network reconnect delay: 2000
    	Checking O2CB heartbeat: Not active
    
  108. Where are the O2CB timeout values stored?
    	# cat /etc/sysconfig/o2cb 
    	#
    	# This is a configuration file for automatic startup of the O2CB
    	# driver.  It is generated by running /etc/init.d/o2cb configure.
    	# Please use that method to modify this file
    	#
    
    	# O2CB_ENABELED: 'true' means to load the driver on boot.
    	O2CB_ENABLED=true
    
    	# O2CB_BOOTCLUSTER: If not empty, the name of a cluster to start.
    	O2CB_BOOTCLUSTER=mycluster
    
    	# O2CB_HEARTBEAT_THRESHOLD: Iterations before a node is considered dead.
    	O2CB_HEARTBEAT_THRESHOLD=31
    
    	# O2CB_IDLE_TIMEOUT_MS: Time in ms before a network connection is considered dead.
    	O2CB_IDLE_TIMEOUT_MS=30000
    
    	# O2CB_KEEPALIVE_DELAY_MS: Max time in ms before a keepalive packet is sent
    	O2CB_KEEPALIVE_DELAY_MS=2000
    
    	# O2CB_RECONNECT_DELAY_MS: Min time in ms between connection attempts
    	O2CB_RECONNECT_DELAY_MS=2000
    
    

    ENTERPRISE LINUX 5

  109. What are the changes in EL5 as compared to EL4 as it pertains to OCFS2?
    • The in-memory filesystems, configfs and debugfs, have new mountpoints. configfs is mounted at /sys/kernel/config, instead of /config, and debugfs at /sys/kernel/debug, instead of /debug. (dlmfs still mounts at the old mountpoint /dlm.)
    • While not related to EL5 per se, it just so happens that the default O2CB Cluster timeouts for OCFS2 on EL5 are different than on EL4. This difference is temporary in nature as in the next release of OCFS2 (1.2.7), the same timeouts will be made defaults for both EL4 and EL5.
ocfs2-tools-ocfs2-tools-1.8.6/documentation/ocfs2_faq.txt000066400000000000000000001074011347147137200233410ustar00rootroot00000000000000 OCFS2 - Frequently Asked Questions ================================== General ------- Q01 How do I get started? A01 a) Download and install the module and tools rpms. b) Create cluster.conf and propagate to all nodes. c) Configure and start the O2CB cluster service. d) Format the volume. e) Mount the volume. Q02 How do I know the version number running? A02 # cat /proc/fs/ocfs2/version OCFS2 1.2.1 Fri Apr 21 13:51:24 PDT 2006 (build bd2f25ba0af9677db3572e3ccd92f739) Q03 How do I configure my system to auto-reboot after a panic? A03 To auto-reboot system 60 secs after a panic, do: # echo 60 > /proc/sys/kernel/panic To enable the above on every reboot, add the following to /etc/sysctl.conf: kernel.panic = 60 ============================================================================== Download and Install -------------------- Q01 Where do I get the packages from? A01 For Novell's SLES9, upgrade to SP3 to get the required modules installed. Also, install ocfs2-tools and ocfs2console packages. For Red Hat's RHEL4, download and install the appropriate module package and the two tools packages, ocfs2-tools and ocfs2console. Appropriate module refers to one matching the kernel version, flavor and architecture. Flavor refers to smp, hugemem, etc. Q02 What are the latest versions of the OCFS2 packages? A02 The latest module package version is 1.2.2. The latest tools/console packages versions are 1.2.1. Q03 How do I interpret the package name ocfs2-2.6.9-22.0.1.ELsmp-1.2.1-1.i686.rpm? A03 The package name is comprised of multiple parts separated by '-'. a) ocfs2 - Package name b) 2.6.9-22.0.1.ELsmp - Kernel version and flavor c) 1.2.1 - Package version d) 1 - Package subversion e) i686 - Architecture Q04 How do I know which package to install on my box? A04 After one identifies the package name and version to install, one still needs to determine the kernel version, flavor and architecture. To know the kernel version and flavor, do: # uname -r 2.6.9-22.0.1.ELsmp To know the architecture, do: # rpm -qf /boot/vmlinuz-`uname -r` --queryformat "%{ARCH}\n" i686 Q05 Why can't I use "uname -p" to determine the kernel architecture? A05 "uname -p" does not always provide the exact kernel architecture. Case in point the RHEL3 kernels on x86_64. Even though Red Hat has two different kernel architectures available for this port, ia32e and x86_64, "uname -p" identifies both as the generic "x86_64". Q06 How do I install the rpms? A06 First install the tools and console packages: # rpm -Uvh ocfs2-tools-1.2.1-1.i386.rpm ocfs2console-1.2.1-1.i386.rpm Then install the appropriate kernel module package: # rpm -Uvh ocfs2-2.6.9-22.0.1.ELsmp-1.2.1-1.i686.rpm Q07 Do I need to install the console? A07 No, the console is not required but recommended for ease-of-use. Q08 What are the dependencies for installing ocfs2console? A08 ocfs2console requires e2fsprogs, glib2 2.2.3 or later, vte 0.11.10 or later, pygtk2 (EL4) or python-gtk (SLES9) 1.99.16 or later, python 2.3 or later and ocfs2-tools. Q09 What modules are installed with the OCFS2 1.2 package? A09 a) configfs.ko b) ocfs2.ko c) ocfs2_dlm.ko d) ocfs2_dlmfs.ko e) ocfs2_nodemanager.ko f) debugfs Q10 What tools are installed with the ocfs2-tools 1.2 package? A10 a) mkfs.ocfs2 b) fsck.ocfs2 c) tunefs.ocfs2 d) debugfs.ocfs2 e) mount.ocfs2 f) mounted.ocfs2 g) ocfs2cdsl h) ocfs2_hb_ctl i) o2cb_ctl j) o2cb - init service to start/stop the cluster k) ocfs2 - init service to mount/umount ocfs2 volumes l) ocfs2console - installed with the console package Q11 What is debugfs and is it related to debugfs.ocfs2? A11 debugfs is an in-memory filesystem developed by Greg Kroah-Hartman. It is useful for debugging as it allows kernel space to easily export data to userspace. For more, http://kerneltrap.org/node/4394. It is currently being used by OCFS2 to dump the list of filesystem locks and could be used for more in the future. It is bundled with OCFS2 as the various distributions are currently not bundling it. While debugfs and debugfs.ocfs2 are unrelated in general, the latter is used as the front-end for the debugging info provided by the former. For example, refer to the troubleshooting section. ============================================================================== Configure --------- Q01 How do I populate /etc/ocfs2/cluster.conf? A01 If you have installed the console, use it to create this configuration file. For details, refer to the user's guide. If you do not have the console installed, check the Appendix in the User's guide for a sample cluster.conf and the details of all the components. Do not forget to copy this file to all the nodes in the cluster. If you ever edit this file on any node, ensure the other nodes are updated as well. Q02 Should the IP interconnect be public or private? A02 Using a private interconnect is recommended. While OCFS2 does not take much bandwidth, it does require the nodes to be alive on the network and sends regular keepalive packets to ensure that they are. To avoid a network delay being interpreted as a node disappearing on the net which could lead to a node-self-fencing, a private interconnect is recommended. One could use the same interconnect for Oracle RAC and OCFS2. Q03 What should the node name be and should it be related to the IP address? A03 The node name needs to match the hostname. The IP address need not be the one associated with that hostname. As in, any valid IP address on that node can be used. OCFS2 will not attempt to match the node name (hostname) with the specified IP address. Q04 How do I modify the IP address, port or any other information specified in cluster.conf? A04 While one can use ocfs2console to add nodes dynamically to a running cluster, any other modifications require the cluster to be offlined. Stop the cluster on all nodes, edit /etc/ocfs2/cluster.conf on one and copy to the rest, and restart the cluster on all nodes. Always ensure that cluster.conf is the same on all the nodes in the cluster. ============================================================================== O2CB Cluster Service -------------------- Q01 How do I configure the cluster service? A01 # /etc/init.d/o2cb configure Enter 'y' if you want the service to load on boot and the name of the cluster (as listed in /etc/ocfs2/cluster.conf). Q02 How do I start the cluster service? A02 a) To load the modules, do: # /etc/init.d/o2cb load b) To Online it, do: # /etc/init.d/o2cb online [cluster_name] If you have configured the cluster to load on boot, you could combine the two as follows: # /etc/init.d/o2cb start [cluster_name] The cluster name is not required if you have specified the name during configuration. Q03 How do I stop the cluster service? A03 a) To offline it, do: # /etc/init.d/o2cb offline [cluster_name] b) To unload the modules, do: # /etc/init.d/o2cb unload If you have configured the cluster to load on boot, you could combine the two as follows: # /etc/init.d/o2cb stop [cluster_name] The cluster name is not required if you have specified the name during configuration. Q04 How can I learn the status of the cluster? A04 To learn the status of the cluster, do: # /etc/init.d/o2cb status Q05 I am unable to get the cluster online. What could be wrong? A05 Check whether the node name in the cluster.conf exactly matches the hostname. One of the nodes in the cluster.conf need to be in the cluster for the cluster to be online. ============================================================================== Format ------ Q01 How do I format a volume? A01 You could either use the console or use mkfs.ocfs2 directly to format the volume. For console, refer to the user's guide. # mkfs.ocfs2 -L "oracle_home" /dev/sdX The above formats the volume with default block and cluster sizes, which are computed based upon the size of the volume. # mkfs.ocfs2 -b 4k -C 32K -L "oracle_home" -N 4 /dev/sdX The above formats the volume for 4 nodes with a 4K block size and a 32K cluster size. Q02 What does the number of node slots during format refer to? A02 The number of node slots specifies the number of nodes that can concurrently mount the volume. This number is specified during format and can be increased using tunefs.ocfs2. This number cannot be decreased. Q03 What should I consider when determining the number of node slots? A03 OCFS2 allocates system files, like Journal, for each node slot. So as to not to waste space, one should specify a number within the ballpark of the actual number of nodes. Also, as this number can be increased, there is no need to specify a much larger number than one plans for mounting the volume. Q04 Does the number of node slots have to be the same for all volumes? A04 No. This number can be different for each volume. Q05 What block size should I use? A05 A block size is the smallest unit of space addressable by the file system. OCFS2 supports block sizes of 512 bytes, 1K, 2K and 4K. The block size cannot be changed after the format. For most volume sizes, a 4K size is recommended. On the other hand, the 512 bytes block is never recommended. Q06 What cluster size should I use? A06 A cluster size is the smallest unit of space allocated to a file to hold the data. OCFS2 supports cluster sizes of 4K, 8K, 16K, 32K, 64K, 128K, 256K, 512K and 1M. For database volumes, a cluster size of 128K or larger is recommended. For Oracle home, 32K to 64K. Q07 Any advantage of labelling the volumes? A07 As in a shared disk environment, the disk name (/dev/sdX) for a particular device be different on different nodes, labelling becomes a must for easy identification. You could also use labels to identify volumes during mount. # mount -L "label" /dir The volume label is changeable using the tunefs.ocfs2 utility. ============================================================================== Mount ----- Q01 How do I mount the volume? A01 You could either use the console or use mount directly. For console, refer to the user's guide. # mount -t ocfs2 /dev/sdX /dir The above command will mount device /dev/sdX on directory /dir. Q02 How do I mount by label? A02 To mount by label do: # mount -L "label" /dir Q03 What entry to I add to /etc/fstab to mount an ocfs2 volume? A03 Add the following: /dev/sdX /dir ocfs2 noauto,_netdev 0 0 The _netdev option indicates that the devices needs to be mounted after the network is up. Q04 What do I need to do to mount OCFS2 volumes on boot? A04 a) Enable o2cb service using: # chkconfig --add o2cb b) Enable ocfs2 service using: # chkconfig --add ocfs2 c) Configure o2cb to load on boot using: # /etc/init.d/o2cb configure d) Add entries into /etc/fstab as follows: /dev/sdX /dir ocfs2 _netdev 0 0 Q05 How do I know my volume is mounted? A05 a) Enter mount without arguments, or # mount b) List /etc/mtab, or # cat /etc/mtab c) List /proc/mounts, or # cat /proc/mounts d) Runs ocfs2 service # /etc/init.d/ocfs2 status mount command reads the /etc/mtab to show the information. Q06 What are the /config and /dlm mountpoints for? A06 OCFS2 comes bundled with two in-memory filesystems configfs and ocfs2_dlmfs. configfs is used by the ocfs2 tools to communicate to the in-kernel node manager the list of nodes in the cluster and to the in-kernel heartbeat thread the resource to heartbeat on. ocfs2_dlmfs is used by ocfs2 tools to communicate with the in-kernel dlm to take and release clusterwide locks on resources. Q07 Why does it take so much time to mount the volume? A07 It takes around 5 secs for a volume to mount. It does so so as to let the heartbeat thread stabilize. In a later release, we plan to add support for a global heartbeat, which will make most mounts instant. ============================================================================== Oracle RAC ---------- Q01 Any special flags to run Oracle RAC? A01 OCFS2 volumes containing the Voting diskfile (CRS), Cluster registry (OCR), Data files, Redo logs, Archive logs and control files must be mounted with the "datavolume" and "nointr" mount options. The datavolume option ensures that the Oracle processes opens these files with the o_direct flag. The "nointr" option ensures that the ios are not interrupted by signals. # mount -o datavolume,nointr -t ocfs2 /dev/sda1 /u01/db Q02 What about the volume containing Oracle home? A02 Oracle home volume should be mounted normally, that is, without the "datavolume" and "nointr" mount options. These mount options are only relevant for Oracle files listed above. # mount -t ocfs2 /dev/sdb1 /software/orahome Q03 Does that mean I cannot have my data file and Oracle home on the same volume? A03 Yes. The volume containing the Oracle data files, redo-logs, etc. should never be on the same volume as the distribution (including the trace logs like, alert.log). ============================================================================== Moving data from OCFS (Release 1) to OCFS2 ------------------------------------------ Q01 Can I mount OCFS volumes as OCFS2? A01 No. OCFS and OCFS2 are not on-disk compatible. We had to break the compatibility in order to add many of the new features. At the same time, we have added enough flexibility in the new disk layout so as to maintain backward compatibility in the future. Q02 Can OCFS volumes and OCFS2 volumes be mounted on the same machine simultaneously? A02 No. OCFS only works on 2.4 linux kernels (Red Hat's AS2.1/EL3 and SuSE's SLES8). OCFS2, on the other hand, only works on the 2.6 kernels (Red Hat's EL4 and SuSE's SLES9). Q03 Can I access my OCFS volume on 2.6 kernels (SLES9/RHEL4)? A03 Yes, you can access the OCFS volume on 2.6 kernels using FSCat tools, fsls and fscp. These tools can access the OCFS volumes at the device layer, to list and copy the files to another filesystem. FSCat tools are available on oss.oracle.com. Q04 Can I in-place convert my OCFS volume to OCFS2? A04 No. The on-disk layout of OCFS and OCFS2 are sufficiently different that it would require a third disk (as a temporary buffer) inorder to in-place upgrade the volume. With that in mind, it was decided not to develop such a tool but instead provide tools to copy data from OCFS without one having to mount it. Q05 What is the quickest way to move data from OCFS to OCFS2? A05 Quickest would mean having to perform the minimal number of copies. If you have the current backup on a non-OCFS volume accessible from the 2.6 kernel install, then all you would need to do is to retore the backup on the OCFS2 volume(s). If you do not have a backup but have a setup in which the system containing the OCFS2 volumes can access the disks containing the OCFS volume, you can use the FSCat tools to extract data from the OCFS volume and copy onto OCFS2. ============================================================================== Coreutils --------- Q01 Like with OCFS (Release 1), do I need to use o_direct enabled tools to perform cp, mv, tar, etc.? A01 No. OCFS2 does not need the o_direct enabled tools. The file system allows processes to open files in both o_direct and bufferred mode concurrently. ============================================================================== Troubleshooting --------------- Q01 How do I enable and disable filesystem tracing? A01 To list all the debug bits along with their statuses, do: # debugfs.ocfs2 -l To enable tracing the bit SUPER, do: # debugfs.ocfs2 -l SUPER allow To disable tracing the bit SUPER, do: # debugfs.ocfs2 -l SUPER off To totally turn off tracing the SUPER bit, as in, turn off tracing even if some other bit is enabled for the same, do: # debugfs.ocfs2 -l SUPER deny To enable heartbeat tracing, do: # debugfs.ocfs2 -l HEARTBEAT ENTRY EXIT allow To disable heartbeat tracing, do: # debugfs.ocfs2 -l HEARTBEAT off ENTRY EXIT deny Q02 How do I get a list of filesystem locks and their statuses? A02 OCFS2 1.0.9+ has this feature. To get this list, do: a) Mount debugfs is mounted at /debug. # mount -t debugfs debugfs /debug b) Dump the locks. # echo "fs_locks" | debugfs.ocfs2 /dev/sdX >/tmp/fslocks Q03 How do I read the fs_locks output? A03 Let's look at a sample output: Lockres: M000000000000000006672078b84822 Mode: Protected Read Flags: Initialized Attached RO Holders: 0 EX Holders: 0 Pending Action: None Pending Unlock Action: None Requested Mode: Protected Read Blocking Mode: Invalid First thing to note is the Lockres, which is the lockname. The dlm identifies resources using locknames. The lockname is a combination of a lock type (S superblock, M metadata, D filedata, R rename, W readwrite), inode number and generation. To get the inode number and generation from lockname, do: #echo "stat " | debugfs.ocfs2 /dev/sdX Inode: 419616 Mode: 0666 Generation: 2025343010 (0x78b84822) .... To map the lockname to a directory entry, do: # echo "locate " | debugfs.ocfs2 /dev/sdX debugfs.ocfs2 1.2.0 debugfs: 419616 /linux-2.6.15/arch/i386/kernel/semaphore.c One could also provide the inode number instead of the lockname. # echo "locate <419616>" | debugfs.ocfs2 /dev/sdX debugfs.ocfs2 1.2.0 debugfs: 419616 /linux-2.6.15/arch/i386/kernel/semaphore.c To get a lockname from a directory entry, do: # echo "encode /linux-2.6.15/arch/i386/kernel/semaphore.c" | debugfs.ocfs2 /dev/sdX M000000000000000006672078b84822 D000000000000000006672078b84822 W000000000000000006672078b84822 The first is the Metadata lock, then Data lock and last ReadWrite lock for the same resource. The DLM supports 3 lock modes: NL no lock, PR protected read and EX exclusive. If you have a dlm hang, the resource to look for would be one with the "Busy" flag set. The next step would be to query the dlm for the lock resource. Note: The dlm debugging is still a work in progress. To do dlm debugging, first one needs to know the dlm domain, which matches the volume UUID. # echo "stats" | debugfs.ocfs2 -n /dev/sdX | grep UUID: | while read a b ; do echo $b ; done 82DA8137A49A47E4B187F74E09FBBB4B Then do: # echo R dlm_domain lockname > /proc/fs/ocfs2_dlm/debug For example: # echo R 82DA8137A49A47E4B187F74E09FBBB4B M000000000000000006672078b84822 > /proc/fs/ocfs2_dlm/debug # dmesg | tail struct dlm_ctxt: 82DA8137A49A47E4B187F74E09FBBB4B, node=75, key=965960985 lockres: M000000000000000006672078b84822, owner=79, state=0 last used: 0, on purge list: no granted queue: type=3, conv=-1, node=79, cookie=11673330234144325711, ast=(empty=y,pend=n), bast=(empty=y,pend=n) converting queue: blocked queue: It shows that the lock is mastered by node 75 and that node 79 has been granted a PR lock on the resource. This is just to give a flavor of dlm debugging. ============================================================================== Limits ------ Q01 Is there a limit to the number of subdirectories in a directory? A01 Yes. OCFS2 currently allows up to 32000 subdirectories. While this limit could be increased, we will not be doing it till we implement some kind of efficient name lookup (htree, etc.). Q02 Is there a limit to the size of an ocfs2 file system? A02 Yes, current software addresses block numbers with 32 bits. So the file system device is limited to (2 ^ 32) * blocksize (see mkfs -b). With a 4KB block size this amounts to a 16TB file system. This block addressing limit will be relaxed in future software. At that point the limit becomes addressing clusters of 1MB each with 32 bits which leads to a 4PB file system. ============================================================================== System Files ------------ Q01 What are system files? A01 System files are used to store standard filesystem metadata like bitmaps, journals, etc. Storing this information in files in a directory allows OCFS2 to be extensible. These system files can be accessed using debugfs.ocfs2. To list the system files, do: # echo "ls -l //" | debugfs.ocfs2 /dev/sdX 18 16 1 2 . 18 16 2 2 .. 19 24 10 1 bad_blocks 20 32 18 1 global_inode_alloc 21 20 8 1 slot_map 22 24 9 1 heartbeat 23 28 13 1 global_bitmap 24 28 15 2 orphan_dir:0000 25 32 17 1 extent_alloc:0000 26 28 16 1 inode_alloc:0000 27 24 12 1 journal:0000 28 28 16 1 local_alloc:0000 29 3796 17 1 truncate_log:0000 The first column lists the block number. Q02 Why do some files have numbers at the end? A02 There are two types of files, global and local. Global files are for all the nodes, while local, like journal:0000, are node specific. The set of local files used by a node is determined by the slot mapping of that node. The numbers at the end of the system file name is the slot#. To list the slot maps, do: # echo "slotmap" | debugfs.ocfs2 -n /dev/sdX Slot# Node# 0 39 1 40 2 41 3 42 ============================================================================== Heartbeat --------- Q01 How does the disk heartbeat work? A01 Every node writes every two secs to its block in the heartbeat system file. The block offset is equal to its global node number. So node 0 writes to the first block, node 1 to the second, etc. All the nodes also read the heartbeat sysfile every two secs. As long as the timestamp is changing, that node is deemed alive. Q02 When is a node deemed dead? A02 An active node is deemed dead if it does not update its timestamp for O2CB_HEARTBEAT_THRESHOLD (default=7) loops. Once a node is deemed dead, the surviving node which manages to cluster lock the dead node's journal, recovers it by replaying the journal. Q03 What about self fencing? A03 A node self-fences if it fails to update its timestamp for ((O2CB_HEARTBEAT_THRESHOLD - 1) * 2) secs. The [o2hb-xx] kernel thread, after every timestamp write, sets a timer to panic the system after that duration. If the next timestamp is written within that duration, as it should, it first cancels that timer before setting up a new one. This way it ensures the system will self fence if for some reason the [o2hb-x] kernel thread is unable to update the timestamp and thus be deemed dead by other nodes in the cluster. Q04 How can one change the parameter value of O2CB_HEARTBEAT_THRESHOLD? A04 This parameter value could be changed by adding it to /etc/sysconfig/o2cb and RESTARTING the O2CB cluster. This value should be the SAME on ALL the nodes in the cluster. Q05 What should one set O2CB_HEARTBEAT_THRESHOLD to? A05 It should be set to the timeout value of the io layer. Most multipath solutions have a timeout ranging from 60 secs to 120 secs. For 60 secs, set it to 31. For 120 secs, set it to 61. O2CB_HEARTBEAT_THRESHOLD = (((timeout in secs) / 2) + 1) Q06 What if a node umounts a volume? A06 During umount, the node will broadcast to all the nodes that have mounted that volume to drop that node from its node maps. As the journal is shutdown before this broadcast, any node crash after this point is ignored as there is no need for recovery. Q07 I encounter "Kernel panic - not syncing: ocfs2 is very sorry to be fencing this system by panicing" whenever I run a heavy io load? A07 We have encountered a bug with the default "cfq" io scheduler which causes a process doing heavy io to temporarily starve out other processes. While this is not fatal for most environments, it is for OCFS2 as we expect the hb thread to be r/w to the hb area atleast once every 12 secs (default). Bug with the fix has been filed with Red Hat and Novell. For more, refer to the tracker bug filed on bugzilla: http://oss.oracle.com/bugzilla/show_bug.cgi?id=671 Till this issue is resolved, one is advised to use the "deadline" io scheduler. To use deadline, add "elevator=deadline" to the kernel command line as follows: 1. For SLES9, edit the command line in /boot/grub/menu.lst. title Linux 2.6.5-7.244-bigsmp elevator=deadline kernel (hd0,4)/boot/vmlinuz-2.6.5-7.244-bigsmp root=/dev/sda5 vga=0x314 selinux=0 splash=silent resume=/dev/sda3 elevator=deadline showopts console=tty0 console=ttyS0,115200 noexec=off initrd (hd0,4)/boot/initrd-2.6.5-7.244-bigsmp 2. For RHEL4, edit the command line in /boot/grub/grub.conf: title Red Hat Enterprise Linux AS (2.6.9-22.EL) root (hd0,0) kernel /vmlinuz-2.6.9-22.EL ro root=LABEL=/ console=ttyS0,115200 console=tty0 elevator=deadline noexec=off initrd /initrd-2.6.9-22.EL.img To see the current kernel command line, do: # cat /proc/cmdline ============================================================================== Quorum and Fencing ------------------ Q01 What is a quorum? A01 A quorum is a designation given to a group of nodes in a cluster which are still allowed to operate on shared storage. It comes up when there is a failure in the cluster which breaks the nodes up into groups which can communicate in their groups and with the shared storage but not between groups. Q02 How does OCFS2's cluster services define a quorum? A02 The quorum decision is made by a single node based on the number of other nodes that are considered alive by heartbeating and the number of other nodes that are reachable via the network. A node has quorum when: * it sees an odd number of heartbeating nodes and has network connectivity to more than half of them. or * it sees an even number of heartbeating nodes and has network connectivity to at least half of them *and* has connectivity to the heartbeating node with the lowest node number. Q03 What is fencing? A03 Fencing is the act of forecefully removing a node from a cluster. A node with OCFS2 mounted will fence itself when it realizes that it doesn't have quorum in a degraded cluster. It does this so that other nodes won't get stuck trying to access its resources. Currently OCFS2 will panic the machine when it realizes it has to fence itself off from the cluster. As described in Q02, it will do this when it sees more nodes heartbeating than it has connectivity to and fails the quorum test. Q04 How does a node decide that it has connectivity with another? A04 When a node sees another come to life via heartbeating it will try and establish a TCP connection to that newly live node. It considers that other node connected as long as the TCP connection persists and the connection is not idle for 10 seconds. Once that TCP connection is closed or idle it will not be reestablished until heartbeat thinks the other node has died and come back alive. Q05 How long does the quorum process take? A05 First a node will realize that it doesn't have connectivity with another node. This can happen immediately if the connection is closed but can take a maximum of 10 seconds of idle time. Then the node must wait long enough to give heartbeating a chance to declare the node dead. It does this by waiting two iterations longer than the number of iterations needed to consider a node dead (see Q03 in the Heartbeat section of this FAQ). The current default of 7 iterations of 2 seconds results in waiting for 9 iterations or 18 seconds. By default, then, a maximum of 28 seconds can pass from the time a network fault occurs until a node fences itself. Q06 How can one avoid a node from panic-ing when one shutdowns the other node in a 2-node cluster? A06 This typically means that the network is shutting down before all the OCFS2 volumes are being umounted. Ensure the ocfs2 init script is enabled. This script ensures that the OCFS2 volumes are umounted before the network is shutdown. To check whether the service is enabled, do: # chkconfig --list ocfs2 ocfs2 0:off 1:off 2:on 3:on 4:on 5:on 6:off Q07 How does one list out the startup and shutdown ordering of the OCFS2 related services? A07 To list the startup order for runlevel 3 on RHEL4, do: # cd /etc/rc3.d # ls S*ocfs2* S*o2cb* S*network* S10network S24o2cb S25ocfs2 To list the shutdown order on RHEL4, do: # cd /etc/rc6.d # ls K*ocfs2* K*o2cb* K*network* K19ocfs2 K20o2cb K90network To list the startup order for runlevel 3 on SLES9, do: # cd /etc/init.d/rc3.d # ls S*ocfs2* S*o2cb* S*network* S05network S07o2cb S08ocfs2 To list the shutdown order on SLES9, do: # cd /etc/init.d/rc3.d # ls K*ocfs2* K*o2cb* K*network* K14ocfs2 K15o2cb K17network Please note that the default ordering in the ocfs2 scripts only include the network service and not any shared-device specific service, like iscsi. If one is using iscsi or any shared device requiring a service to be started and shutdown, please ensure that that service runs before and shutsdown after the ocfs2 init service. ============================================================================== Novell SLES9 ------------ Q01 Why are OCFS2 packages for SLES9 not made available on oss.oracle.com? A01 OCFS2 packages for SLES9 are available directly from Novell as part of the kernel. Same is true for the various Asianux distributions and for ubuntu. As OCFS2 is now part of the mainline kernel (http://lwn.net/Articles/166954/), we expect more distributions to bundle the product with the kernel. Q02 What versions of OCFS2 are available with SLES9 and how do they match with the Red Hat versions available on oss.oracle.com? A02 As both Novell and Oracle ship OCFS2 on different schedules, the package versions do not match. We expect to resolve itself over time as the number of patch fixes reduce. Novell is shipping two SLES9 releases, viz., SP2 and SP3. The latest kernel with the SP2 release is 2.6.5-7.202.7. It ships with OCFS2 1.0.8. The latest kernel with the SP3 release is 2.6.5-7.257. It ships with OCFS2 1.2.1. ============================================================================== What's New in 1.2 ----------------- Q01 What is new in OCFS2 1.2? A01 OCFS2 1.2 has two new features: a) It is endian-safe. With this release, one can mount the same volume concurrently on x86, x86-64, ia64 and big endian architectures ppc64 and s390x. b) Supports readonly mounts. The fs uses this feature to auto remount ro when encountering on-disk corruptions (instead of panic-ing). Q02 Do I need to re-make the volume when upgrading? A02 No. OCFS2 1.2 is fully on-disk compatible with 1.0. Q03 Do I need to upgrade anything else? A03 Yes, the tools needs to be upgraded to ocfs2-tools 1.2. ocfs2-tools 1.0 will not work with OCFS2 1.2 nor will 1.2 tools work with 1.0 modules. ============================================================================== Upgrading to the latest release ------------------------------- Q01 How do I upgrade to the latest release? A01 1. Download the latest ocfs2-tools and ocfs2console for the target platform and the appropriate ocfs2 module package for the kernel version, flavor and architecture. (For more, refer to the "Download and Install" section above.) 2. Umount all OCFS2 volumes. # umount -at ocfs2 3. Shutdown the cluster and unload the modules. # /etc/init.d/o2cb offline # /etc/init.d/o2cb unload 4. If required, upgrade the tools and console. # rpm -Uvh ocfs2-tools-1.2.1-1.i386.rpm ocfs2console-1.2.1-1.i386.rpm 5. Upgrade the module. # rpm -Uvh ocfs2-2.6.9-22.0.1.ELsmp-1.2.2-1.i686.rpm 6. Ensure init services ocfs2 and o2cb are enabled. # chkconfig --add o2cb # chkconfig --add ocfs2 7. To check whether the services are enabled, do: # chkconfig --list o2cb o2cb 0:off 1:off 2:on 3:on 4:on 5:on 6:off # chkconfig --list ocfs2 ocfs2 0:off 1:off 2:on 3:on 4:on 5:on 6:off 8. At this stage one could either reboot the node or simply, restart the cluster and mount the volume. Q02 Can I do a rolling upgrade from 1.0.x/1.2.x to 1.2.2? A02 Rolling upgrade to 1.2.2 is not recommended. Shutdown the cluster on all nodes before upgrading the nodes. Q03 After upgrade I am getting the following error on mount "mount.ocfs2: Invalid argument while mounting /dev/sda6 on /ocfs". A03 Do "dmesg | tail". If you see the error: >> ocfs2_parse_options:523 ERROR: Unrecognized mount option >> "heartbeat=local" or missing value it means that you are trying to use the 1.2 tools and 1.0 modules. Ensure that you have unloaded the 1.0 modules and installed and loaded the 1.2 modules. Use modinfo to determine the version of the module installed and/or loaded. Q04 The cluster fails to load. What do I do? A04 Check "demsg | tail" for any relevant errors. One common error is as follows: >> SELinux: initialized (dev configfs, type configfs), not configured for labeling >> audit(1139964740.184:2): avc: denied { mount } for ... The above error indicates that you have SELinux activated. A bug in SELinux does not allow configfs to mount. Disable SELinux by setting "SELINUX=disabled" in /etc/selinux/config. Change is activated on reboot. ============================================================================== Processes --------- Q01 List and describe all OCFS2 threads? A01 [o2net] One per node. Is a workqueue thread started when the cluster is brought online and stopped when offline. It handles the network communication for all threads. It gets the list of active nodes from the o2hb thread and sets up tcp/ip communication channels with each active node. It sends regular keepalive packets to detect any interruption on the channels. [user_dlm] One per node. Is a workqueue thread started when dlmfs is loaded and stopped on unload. (dlmfs is an in-memory file system which allows user space processes to access the dlm in kernel to lock and unlock resources.) Handles lock downconverts when requested by other nodes. [ocfs2_wq] One per node. Is a workqueue thread started when ocfs2 module is loaded and stopped on unload. Handles blockable file system tasks like truncate log flush, orphan dir recovery and local alloc recovery, which involve taking dlm locks. Various code paths queue tasks to this thread. For example, ocfs2rec queues orphan dir recovery so that while the task is kicked off as part of recovery, its completion does not affect the recovery time. [o2hb-14C29A7392] One per heartbeat device. Is a kernel thread started when the heartbeat region is populated in configfs and stopped when it is removed. It writes every 2 secs to its block in the heartbeat region to indicate to other nodes that that node is alive. It also reads the region to maintain a nodemap of live nodes. It notifies o2net and dlm any changes in the nodemap. [ocfs2vote-0] One per mount. Is a kernel thread started when a volume is mounted and stopped on umount. It downgrades locks when requested by other nodes in reponse to blocking ASTs (BASTs). It also fixes up the dentry cache in reponse to files unlinked or renamed on other nodes. [dlm_thread] One per dlm domain. Is a kernel thread started when a dlm domain is created and stopped when destroyed. This is the core dlm which maintains the list of lock resources and handles the cluster locking infrastructure. [dlm_reco_thread] One per dlm domain. Is a kernel thread which handles dlm recovery whenever a node dies. If the node is the dlm recovery master, it remasters all the locks owned by the dead node. [dlm_wq] One per dlm domain. Is a workqueue thread. o2net queues dlm tasks on this thread. [kjournald] One per mount. Is used as OCFS2 uses JDB for journalling. [ocfs2cmt-0] One per mount. Is a kernel thread started when a volume is mounted and stooped on umount. Works in conjunction with kjournald. [ocfs2rec-0] Is started whenever another node needs to be be recovered. This could be either on mount when it discovers a dirty journal or during operation when hb detects a dead node. ocfs2rec handles the file system recovery and it runs after the dlm has finished its recovery. ============================================================================== ocfs2-tools-ocfs2-tools-1.8.6/documentation/samples/000077500000000000000000000000001347147137200223765ustar00rootroot00000000000000ocfs2-tools-ocfs2-tools-1.8.6/documentation/samples/cluster.conf000066400000000000000000000011271347147137200247270ustar00rootroot00000000000000cluster: heartbeat_mode = global node_count = 3 name = webcluster node: number = 7 cluster = webcluster ip_port = 7777 ip_address = 192.168.0.107 name = node7 node: number = 6 cluster = webcluster ip_port = 7777 ip_address = 192.168.0.106 name = node6 node: number = 10 cluster = webcluster ip_port = 7777 ip_address = 192.168.0.110 name = node10 heartbeat: cluster = webcluster region = 77D95EF51C0149D2823674FCC162CF8B heartbeat: cluster = webcluster region = DCDA2845177F4D59A0F2DCD8DE507CC3 heartbeat: cluster = webcluster region = BBA1DBD0F73F449384CE75197D9B7098 ocfs2-tools-ocfs2-tools-1.8.6/documentation/users_guide.odt000066400000000000000000012406561347147137200237760ustar00rootroot00000000000000PKʌ5^2 ''mimetypeapplication/vnd.oasis.opendocument.textPKʌ5Configurations2/statusbar/PKʌ5'Configurations2/accelerator/current.xmlPKPKʌ5Configurations2/floater/PKʌ5Configurations2/popupmenu/PKʌ5Configurations2/progressbar/PKʌ5Configurations2/menubar/PKʌ5Configurations2/toolbar/PKʌ5Configurations2/images/Bitmaps/PKʌ50}-Pictures/10000000000002A7000001E7CD53C040.jpgJFIFHHxwdumpC    $.' ",#(7),01444'9=82<.342C  2!!22222222222222222222222222222222222222222222222222"\  !V1AQ"SU267Rau#3BTqrt4$8b5Cs%&Ec&Q1!Aa2"R ?a9O#Tq͔ZޑZ=h"lTWVk&mؖ%ln;H\6,cA*OYX˟ J;>a-^tNI!++\֟)"m-^tKm-^tKm-^tKm-^tKm-^tKm-^tKm-^tKm-^tKm-^tKm-^tKm-^tKm-^tKm-^tKm-^tKm-^tKm-^t| *4qFvHϘFLZ=Sj&Sw&ךڹ-z/Pk/E wGbQѠz6Nxgv׬vKJD2̳=%GmYmqz/Pk/E wDlV͌O^mً mő\Ktm4t8V~)lG l5mKDz,yʴ3eJJT%n_$8'C8f:uYY r+38[eApG l5'TLo:d#']9s*S䥧ҷٚs%*R ^o8[eAVHq В'5[ŗXb-ns$Z󭠔kI/]\zs^"- ٌmHpG l5mZQMy2SFw6*H5,"8#^pz]."8#^pz]."8#^pz]."8#^pz]."8#^pz]."8#^pz]."8#^pz]."8#^pz]."8#^pz]."8#^pz]."8#^pz]."8#^pz]."8#^pz]."8#^pz]."8#^V}Ӈ(HM̈ў1 QnJnBm.H%D/}|pÛ?A-cM>lq^ؕȬ%Ierdg%_'_ 'pvYqtH a [Uh4W#2;ܪ\w^LzAdhZJK.DH[[4H̸YV41!Ӄ0SLJ-6NhEfv+qhh* 1{?Iҟm+9nG~:TdӹA%QK![|oHjcLhVJ6mIQ%MHVe&s"0}+4>^Ɍ9ԓM̕%Fsgy_Ўה?BɱiW*lcGE&bd_qiڔ#Ywhd{+J`J$ِƬcێcq JͺMТQvRNܤdded}d{ r rSң|l4]q-$Ɣz"[ ~5V ̡IBnBGd32".S1j72=zTvGOJ UGOJ)Qd|GqQGOJ)Q= rSңFLĴ|ś[ƐgihBr\Zؓ""T),Ɏu hUc-Gp2=zTvGOJ܆]u֛yʉ.*#4dz C#ܧGhhd{ySң42=zTvFQ^rtA+1ⱋF} rSңDFFFGHd1<پhY&fD.; >2=zTvGOJ G=*;CC#ܧGh42=zTvGOJhd{ r <)Q=*;Gcr#6mKAN u0N:20-#ܡmk7JmtÌ=M?+Pd, (qE@ 56ÚFmtlC,N+nѣ#/qF'*B2<.ܭmeKl6 bdg.6@(øR'(I+:T6ƴIJl\jժ#1'm{"`4=(EvNY4Ù"GZ\^<yC*/ }թ 1[KiffBӨwZENXCfLήUOB6- ez*]f"N2MBt+ҙ#1H1_h5%L˩gKj=_tIj.>%]: ]Q)n2hZyfV>SFEO%MH42͝*ʓɥ Dfe"#\MhTT8Ym ~{Xl+XGn#uȽnL]-,gZ=Pse|_UZcFjR7eeVNRwؔn[5P!Zj&I'eߌQZs`S5-;M˸%? -F^5j0؟QA&ْS,ܩam]YSQY]:-*{yRma+VCM̯mgE*US!RM9u UB3ϕ)u l,nx3WMϐeUq4*MC2d$œ91 Jʍ&XG4j$Ȓ|D%9_tI)Q/1ǡQ0#RˋJqДI;̈\laPҝowJIJ߉$E '԰?UhȋJK^iEqJ{e.-w13P=P9qf2҉9s$ʔGcVQj uMT$䲣SRgGdlx4!$Er||BԚY\dE.̷XZZԳq.cMk٩oV`4.{&sUQ)\#"#>+~D23i)$Hô/"BQ)[I2tȈի2$k.` Df̔)Rf>K jʜ%DeŬ[iJ@鐎qqI6.O]ks,rٝL̮ᖕۑ,"#=v<ӅsW!wf[G ffŹw3;ߎ(U*&OL!{rijqJ5Q]%'_U$TL9\:F7|H+W/- VZ74:;KV$KqCP 1 PhTI.KsomgpzנzU&gS1GBt JDiE+95O$88崮4PnrgF9^$ڿL_׿&haj<PQN7w)עFDZ,w3}Z_׉ȧ=(E T})7L/O|@o^'ژ_"z>91zj̚sԲ$ )!&$׉ȧb5T\8s )#IXZ\Z̍*U\@o^'ژ_"z>'T@o^'ژ_"z>'})7L/O|U$$֬Q&NO',9g]$,YK)_ Msz>׉ȧNqRK(5S4eǟ6qW%(h(ՑW#JJi2# Tf})7L/O|T})7L/O|@o^'ژ_"z>'})7L/O|@o^'ژ_"z>&*+K";:BDo^'ژ_"z>%|?7|?F})7L/O_w펐w펐Do^'ژ_"z>%|?7|?F})7L/O_w펐w펐Do^'ژ_"z>%|?7|?F})7L/O_w펐w펐Do^'ژ_"z>%|?7|?F})7L/O_w펐w펐Do^'ژ_"z>%|?7|?F})7L/Oy*%#IE׉ȧ޼O0E? xja|x S S׉ȧ޼O0E? xja|x S S׉ȧ޼O0E? xja|x S S׉ȧ޼O0E? xja|x S S׉ȧ޼O0E? xja|x S S׉ȧ޼O0E? xja|x S S׉ȧ޼O0E? xja|x S S׉ȧ޼O0E? ʱ>U )7Q=ynv;sq'm{"`4=(E =(E PkΘ>a8"+Tffz,%lBҢXAq>5jM; SB$)d;)"Vk2')INKuzc+lHl͊ddzV2 *St 4ܒi2."(\I%Aȹl$DEb!= IYVf3+jW.#RjPOԙ([m(ӛE]߆M!V228QR$;fU{}wLmY.OIƑԇ ܎)I4=|{kUʉ5\g\IT\Ɍ׫hJ'td5Pe-]DJ_Q"G΍,7+jc417"1AMו*3+JQUX]KmMTm*+Ue{Q߯oZqYm[d_9 R$""!37gw6[cYJh4u_Q!JFF"Dpd)#$w%!.\dYLo7\__,U)-TՌq*ˈRƕbůY8Nt>!EVKK`g q7̂׭EW.2|9QNtG%9[xS-*K*4lec#I-I.ydwI$WMQ\Z=M6.z>$G)V3+,)C9腃 I_o^+)C9p{0~PNg)C9 0~PNg)C9 0~PNg)C9 0~PNg)C9 0~PNg)C9 0~PNg)C9 0~PNg)C9 0~PNg1pGt c*{\ߪ,I?sQe)?_׿&?kt<yC*/ HyC*/ HXҿ?"FfmN82ޮ2 cZM& #ǑՙM8d܏RKVioTeQmM!NdEVˎڮ"c`iR;!}wYQR"I<Z%T&$iFWFf҇r̉+.S16KיCȢxR]/UX(sʔnȳjii"G3\+ VF}p7ő-n>emHeW;;걄,aC$]'S̙EtԤA|uq7W2aB%F";-ƐKJHWT,܄u3 ̅NQs]ңMZe붫+NGFNDkCm,ZT[2i,Ũ_41Bdidh3ŝI#_5{Fg6h՗9\ֵq\bbf$W\`[gZE #Ie07gnqؓa@wL+6ԳmYIDfFFZGOlЖ!H4j2#r8DxJIg>^JxFuíz3?3 >9J5-9T)+mIQ>) tedSs~3#!Iy)NBPjRSQfDgs>mD !$v*(RMJUW|*̴:%R 1;|D7f8ȜA#"2ra:B㩲QmjQV;rϔsdڍD_ȏR긒 A%S!(azWM6"pbQ̈ |JmzcLo6 O) k\؎_JMuH724RP\jQ *}'_ iTzۖ\R3 I4X9ҝW!P59U&vSڏ)rb!+A%mWZ)Y ?N7i9f:Q6(Yiԣ22/FFd>ӆ$".9/t,RYH4v+j"ըXJ%91%üWM$f\U T52wJYQ# )  4lKm5J-*RJ2)Av3|$1E#r>Խ!۶feǖxȵE|!Jғ2Id\EY\y>[NlQ)%$F""#Q򝈋_! rNL,)Y\U,jQUό8ƚE5dIM>"_Ls(u>= \ZVێJI&dE|7;'%2MKI3#4̈"iWTn,vɩ- *:E Q)Eb24궻9zFD=7_hn9zBps篴7k=}D=7_hn9zB{oݮs| k=}\ 'A9zCvNݮs|s篴(\ 7_hQ81I 7_hřH־УN>q,u|9^$ڿL_׿&h'H΀WMi/I+jW=mG{ß$ũ $A;uU]mGݍZ4 j *<@U҆v6Qhң74 jWz_J]ڏ[EJ Ʈң7_Qq}(owcj=n/*<CJ Ʈ}mGݍHናr!\I3Q%$IJKVң7_Qq}(owcj=n/*<CJ Ʈ}mGݍZ4 j *<@U҆v6Qhң74 jWz_J]ڏ[EJ Ʈң7_Qq}(owcj=n/*<CJ Ʈ}mGݍZ4 j *<@U҆v6Qhң74 jWz_J]ڏ[EJ Ʈң7_Qq}(owcj=n/*<CJ Ʈ}mGݍZ4 j *<@U҆v6Qhң74 jWz_J]ڏ[EJ Ʈң7_Qq}(owcj=n/*<CJ Ʈ}mGݍZ4 j *<@U҆v6Qhң74 jWz_J]ڏ[EJ Ʈң7_Qq}(owcj=n/*<CJ Ʈ}mGݍZ4 j *<@U҆v6Qhң74 jWz_J]ڏ[EJ Ʈң7_Qq}(owcj=n/*<CJ Ʈ}mGݍZ4 j *<@U҆v6Qhң74 jWz_J]ڏ[EJ Ʈң7_Qq}(owcj=n/*<CJ Ʈ}mGݍZ4 j *<@U҆v6Qhң74 jWz_J]ڏ[EJ Ʈң7_Qq}(owcj=n/*<CJ Ʈ}mGݍZ4 j *<@U҆v6Qhң74 jWz_J]ڏ[EJ Ʈң7_Qq}(owcj=n/*<CJ Ʈ}mGݍZ4 j *<@U҆v6Qhң74 jWz_J]ڏ[EJ ƮZ06n)ak3zpRx36:9 tRI.vtfWjj>ݰD> IbOR.3oNTP|RnwA(ß$1U:|שӑǠDnCi+NhdV;gFJȵw1N[r#m6$2TgfV>Km)N%A^Q$?&4%az ;*Z-,zԣR&3$Y>]faSq(ԕDu )\TNR!,Pmr#2r3o{ɘO[-HyžzD;(%'=BчkKPN ʲXKjKy%V#QUV dTfJBڮV."n^'B)IYT&V4JEM-!.),*MJ6ܨ3JiuI1ѥ ,.$%ckֻ%$SEb(ꓨ e5Si#4Qqd>F\8tQaS 36ȬeeY^Z2_x\']dVn]v6d[[S2zEJ4 CoTXή2J𖑾[uK͢^KɟwNY+M[[-@pfެ|~έ=|2f>U&pjd^Ӻ #.[ǘb+A2,4"ZШ8H'Ik˔r;Vi@f&D&b[ q,OhyW3ƛ|pffM$}!7=զ˫"kʝ7 ⨡UqJVgfyVR+n2QD&;\m7۠im-ۡDJɫ_%%bZ{^~<`y)1]% ZR6*6kk#!/ *O-q97;M-N4[u9ғc#ec.> I:;?uqn*wNA#Tӊm)$줨d|@n2u C;)*+1^8vӐp;!EHmnRFE|{\_Qx*wNA#Tej;e :Q$>"'c{EׇGh9Sr LzmQ'ص pŅq&Omґ-F*SI򑑗d|cGh9 (t?;ENQ@J/\?;EN8vӐQz*wNA#TSrt(^vӐp;!E#TGh9 (t?;ENQ@(p; *wNBEGh9SrP /\?;EN8vӐQz*wNA#TSrt(^vӐp;!E#TGh9 (t?;ENQ@(p; *wNBEGh9SrP /\?;EN8vӐQz*wNA#TSrt(^vӐp;!E#TGh9 (t?;ENQ@(p; *wNBEGh9SrP /\?;EN8vӐQz*wNA#TSrt(^vӐp;!E#TGh9 (t?;ENQ@(p; *wNBEGh9SrP /\?;EN8vӐQz*wNA#TSrr%pTIn7-- J2-s ȗȢ o ՟}`}?vmxG67$TŒ4v[Η:f/Eo-G67$Q/8^MN:nR{"qv'H1mQdԓcj;^嫈J!@) :>n$ǫFG|zqTb\ZXIpmfQ{mTg256CnwqiuDw%Ts[q]tFqY,U}ECU4*KD:6Ffii1=fJ:>M4d,|d|cži,  s`,+A&1Dm*[l-S W,yob=iըl@b<©ۙc2 k##3;׮-D+R'̖L$zqV_sKpk%FJjuGcO2W;s\*7iь\[f5\G: Y7\ gR+2ǪT" tPf4F|k8qN8-k3R@EP__@%.SVx]є> I%2=3Veu=JG67$X3Y5';ƻHnY 2/'5|wwNxi"R)? "%fl,iVwmyK+#*if=PUUIk3JU̮ÀwNx8|wܘ*fWa+[kΜԤB}BuF@܆":3n<4f,YX͠68|wwNxE1ejYNj ":SW&U14gb7×\|6(Ӧ8yf\$"2ʒ$\FERwNx8|wlAT[CʞOICMқdBr²Is"X}_8!GwBrlhV6\ҝ­Gu(Ŭ& 7};>wӹ |;sþnw;0W|wwNxX& p _>wӹ7};`+þnw;|;s,wNx8|w`L7};>wӹ |;sþnw;0W|wwNxX& p _>wӹ7};`+þnw;|;s,wNx8|w`L7};>wӹ |;sþnw;0W|wwNxX& p _>wӹ7};`+þnw;|;s,wNx8|w`L7};>wӹ |;sþnw;0W|wwNxX& p _>wӹ7};`+þnw;|;s,wNx8|w`L7};>wӹ |;sþnw;0W|wwNxX& p _>wӹ*´ZjN\HZ7Ҭ}GFb5\A3MY۶wښlxG67$YVNmM 02V+4~2R|9%_> IXZBA?S,TjG=ӡ>󈴤h̒;Xԫ)6Qf=cN6 OeRؐۅeM)o4%Фc$,(RszҢZVDȲZңBLȭ{ޏIK5zEO)nCMhAGZ X.z>uUNCr l,l-{-gs1#ArA=PԪNKqs'Buq굤SX³`d*cNRe8٩R6DI+dfv JUt%ZMݮ7),e729FL*V_XPR#Z]MY۶& 9loH  V> IgFC NX$G0|>W`dE4|"_}6@ko_+O7/dE4|"_}6@ko_+O7/dE4|"_}6@ko_+O7/dE4|"_}6@ko_+O7/dE4|"_}6@ko_+O7/dE4|"_}6@ko_+O7/dE4|"_}6@ko_+O7/dE4|"_}6@ko_+O7/dE4|"_}6@ko_+O7/dNi5ȸ)CɨJUW"ȭe_wG˛9彯{%MJJY kQȏ<W`  ~i|>W`  ~i|>W`  ~i|>W`  ~i|>W`  ~i|>W`  ~i|>W`  ~i|>W`  ~i|>W`  ~i|>W` PR ~i &0U]GkAμ.SVx]D> IjKM*JyHJ"+L#|&a~?t31 31 W[xJUJT$ӻmfj+ZRd|ntnt1.f=cpGCRiĢO/+~&Y^<+W>sat58n3TuN-gL$e56JK)ֶ&5UO*jK) 7U-D"Hқv-z]MY۶h>0"bRs[]˓!}aσc}D/GΎpo))$&+# hD2IBIULF"O*ʔjAhH=1HI~ D҉dE{],\굇!i8QJr4^tjgFWO|C!ϓ*{"I!%'YJ%Ys3#tFv& iIRI]+QDm>%tkƩ!TEĕd11P>)%)BA_1\ljFut##D)ioAmGZEgZ$F,x>FuqѢ*lШ4Me5 )f2-_̹QF 8FrJ2BLQz}+ C6&S2y^eq*Fcc"]*0(jfڐ*/'&$?P74nzjƨSƴÍ F2u3Q6VvcQI;(o+X.+XrtnéCi n)E-CAD)&;MyרK Z^ƅFSQJYI3/V0-d[︧rJZw5Y9*/8He*m"I6"H  ՟}`}?vmxG67$M?DI\#|&a~?tbDv%r<[yi[n$12,vӫTҤ_mqhI.3q@7,v?@2L2MX$j=~&foY 7~1ŦCm) J#NTZw;vj;U%?@f7#,vi ˨%M IX} .T4TXuP* גv|GR-+~NbK\S&sզ %MmeqX:P 9<1 6n10䒩n(I,If;"\a緤!u6wqfbIzYr xzS(\f [-TZ58egs8iRlgb.@X}T&KTXh6hKqy]>;D#FѣzgO#Vm"w̹,emVD]ۆVuR4*h7IHϤ[{ߒAZ=5QKVtJВ#5\vQ 5+54Vlq(iTIifj"2YwFO*FUŏʚ[8+*EB+k+eOtXr MY۶wښl7}aσc}D/G>0"i#HgF @Sl߀өqJ#s6h"2%dZza+Ki%Twݰ ՟}`6#|&a~?.> IO0QC:1N&7M?e4hYJ7+Q}"T:_F秪B]NR̭ +Ys.N  ebq^ҝ8/mfZ&n/\bye>[5ԢGV͕HRfW+﯈c4#]Pf2󭤛q(RRyO5Z` )f㛹S/qГٲew|(N* /sSr'Bo=vo]-'@A'CT旹Q)7l7oqx!K6ʙ{dN͓/[@OҪT٤Lo4͛"3Bx̵]Ex*6i0)3fȌ8P3-WQ_4+5VuJ\mmԥ(Ic"Qq[SQ*܅6j2h֢ᆴ!+A2liV:IjZHWA)Z%iS9 Sp6IuE+;5X!)E@kq$DJ%'Y&J##p" ~;RV8e$fd3."#3FbM 'JQ}UI1AnF&.rRf~7\RS mD݈TyԞ;zP  b<ثCiuZJz~-P 5I)$dF5ғ2Er  Z:*dJ io)&+#2-d\\fBFuM`0\5N4Xk]nW)DdjDg*bR̎ȸoCa.1O9Fu6J""Vn;1H:t;On)f\&,VVQc)b{ǕZ$(#Btc"Q t*mw( !D =v"+N\WQ ;ܬz.:b,Ÿ'Ml/q ]QwmYqT̼DKiekf\FGr3###jt"TNXb>\Q%J=jRRDIIˈ3##2>AxT.畠)% dNkž[_Upz~.s4[Quo)Il,3;XەҋIzP̂H&]ѭ ̉$mj#I"kK!"$Ɲ,y%ҫJI r Wa?_^}?v<.SV& 9loHG67$M?DI (fZ >jg~*ZJu+Rd22F#3ZܴKcb,DUdq)B[vJg{%$gs-v#NxVw~F64Cͣ_t' DNzm%QJTXM,e{jO=Fw~ysjJ'"Y/q3jPM!8d!y)$\J:*õHCF?QC/fV|ڐ(y,FVр:Jo?<)ekKmzLsQ[`ۗ6䗋kP 6R6%d%d";j|VL9NyI[2N QX%!Y9W^RNL:@&f-Uim&;IAbwW#Q6LI*%AH-z JRg.TFdW>(hM[։h!e $BI=o6_Y׿ OPJPv<ܱXr#IgZ踛6 6O(pLjI,O ]J\pգAH&fec;R#_mF%4UO#-eHhÕ=>EAjFY:ѥY]]l̵$ܙ}3p^) 9BkA*M=r!E&sU.dkљ.SNõ֊h -+QEFIr'W/9ոw=4SRId)8ؚ W}zgzNn5d%]gA\44Tj]ݺESd'z{9Zƕjw?_kտl Qq)VOVfߠ_Aqo5+1gQ{/'a#eESȟ|zȫrY{{\}t=ۋOgY/ /yWgWc3[_2K\Ǚbի_ku͠:h\-PiqT+Mv@z ]1JbLq *Q}Ť7M՗s;IZ>so9?n6z]`g W}ASl~\6JIReM)p̊">,lHR SN2*ӫGŮD/==: _]A ףR跽@5AN3Ȝ9uV9JV3""ˬ߼. 3$Til2wH%DI=DI+cE/\zdљ]KRSS's?*BSqM9D!ԙTn;.q=//|Z/?gUBU;$QUӔjr^c>>73cnzgҳqL_ Y*5. [nENj"źO֧{kҿ_b>BuɠRr׫p3n{DwNEG Il=D?Q!E8|VɫOkլ WQf"=fWy̽n1q]"edIXĕI}e-c!ܙc2-Mn^M_\bONc)r+ ׏eζ l 擊nFi-!63b?/G8$ ΋R<լme'm`OFyEpU~ Q?b# Ժ21h[*igFys;7k_2cԊ@cDjQ(2XF_g_)9e\M)EUF%%fL7@Wa?_c'kںK5gnjj>ݰD> IO0QBß$_!P$)ABD)J֥*<{iȽqĐ4YR`J,IJId23³PÕZf&(->TI[Di2Ȍ36D|Ne10NiK!+R`71JV*%doV׫P+ n%%*6V3w2ըUZs.oO<32E !ɣN% ܺlW3!# CÍ̏YR)h%=4(ԛ\{ՆIT%okP^#YBҎfJlQUTWcS)"_f; Y 4:"j \gnS@s+kγNyICj\wSJ7 $dJ,w>#"co8*b& [VѓD6\ZOesTJL-FJRj2+kTLnH%hGkƕAJeƟr{UM Sɑ_Txo9L/w-hAc:QCrJ5 @UJ4i\R 晳dFyq(O 6=mG]+&fu;Z}0N!4竻˘RUtI=!R}FW9T%.b;θtJJI9-vIȬWg)pSU)OJ}o˓MqGBW{$\\/&v"O*q#F4dDk;E[OlRR`Z4woL|JͤVc3%™*دӥ9O%nV7)ód˨BTȸ?A@@ bd؄^~B+3Q'jmlwQ/}WD|ǥF;'>KqM\/Qy0;1F%*4ȏe~u)<˖o%v|OY%kj-/-\ooQpjqߍrjL_=\y+s<9;2է{_ nu֖6||c|){\7;_gyrpnu†5zQ>gxsSCMLҫl4lTZm'I/TF˜STZj!DKFJ֬LL#RSHrnvI\]KrM3,Tm2hnRڈի|.$赃'#Udz+; *wNAr>K$B NUeġ[>FRgn;G).z-\u2WhvZGh9Sr?QV#}r+K(4YeHAq%*'U.Q\ IQdayM%(htFf3; _Srt'WWx)!0q $Iꭡ$DV$""K4m.\6Ͳhu&O oSrt/?b&f̤VdVt&EAs)"1tP *F5 .#$F^ _Srt'*jh5>Թ1ffw337ffg}ooQpnv^c~6 Fp*wNA#T~TyS'\OCy+d|7Srt3{<\n αWk_uws2ssZ4k$n-/-\p; *wNAʝUEK]Ϝ &5st Jdַ㝒G|#TGh9)T _Ze9Z?h*wNBjB%V&A}#9|*|^7Wqb^<rŬ3<36;+b"w GlPR@'kں,b_QmW@ix]MY۶h>0"i#HB`sd4 $3ڃ-ME6k7 GժZaN4taFVdgb̊=QzSHRScѭh|ex3;o@%ʜfHѤ-*QVS,J}Qs[y+3#si"Ukz>~AJG6B7!DDhsI؈.@&2 > Jb{% ,/OϋdX;fK1b;\xcjMq3Iq֤܌7%|UTNKI6I=H32#3QgNN%k:͔Z -\QdJ# Uq4eLhzFķ%y3f5gmu6iޟFDb} |N)qFf6fW;,;ĸT3'XhK%8,d3Oy\\ķM1h\47%Im(,B;(Yq깙(ju(S%>+[nCz$$iXȑY"jC 5&dfӨ gFUIz\Ko)BIIߋY_l)dM ǎҝuv3ʄZQr~FSE BUE^uuOR.,V/T0|D~9ХWvkLi[iq:Yj+Q KJwJXܠm[> g18JRbI.+qkͩGnt@{b9UIΥ45!nW-%ԷY~FvUvA=Sb&BH^m_#_*ȮfcHT*1iq7TtLk6SWV"3֥$x"B#5q.s%6m8wQzψv"3ʰ^ y|C.|ɧrzq|cBɥ^)qqjj̢ʯST Gyz[-C%YTH/|`4=ϩC22YrqY֔HWrFutbJubS^h*R]4be-2.Q(ZŨ:tjYaBq-HNS,R+XU$_mm>L臍݌㬯}`Km2lF'W wC6]Úrd.J锝5\g`H(]EfZ)mCɞAiOIk#3IDV QfJLC1Ӿ*O3O/R52Q9/BpAqjD. J+{% Q5@r Wa?_^}?v<.SV& 9loHG67$M?DI (+rTv,K̩2В5YDN2:G8=RڎP<ɠf2܏)؎Ǩ 0 9 QmZDŪ2̣҆:̬32k-w2!=JFIvJdZ⾧FO(f+X~Q7Sv5TZX4JLWh|Tȥj%k(2m> 1皷D5hmMdDW$#Ah7>,CSk DvYhb-D|b=Y):Rɉkƌ(X:Dzd/CJRE l$Z:2I#U+VNbK\S&sզ %MmeqX:PѤhޙ(Hgs.K[U2U/_Jr Jܬ)nSfɗP:/)Iq\c-"ЖP+&tcs";;۔JspjN,vmJi&MU|V24!!1lAlP6 6O(Qax3??a hT}$?ݯhn_fO=ctI+$"? mmĸq$%Ǫz˘ȏZ26kkZ=:u7ѧj_3}{Nf4:u7ѧ( 2T2ݎ&Y|r2#u7ѧ4oO`QH5I.bK6^BzLFsJ/$HJRDYv"-fbXWoO`iF_3}{Nf4:u7ѧ(Fӯ=ENf4|i ,oPǧ_3}{SFj+TF. J+{% PW+0j豊F]wښl5gn oß$_! }aσc}D/GΌ@"#+5Qv\Qf5dR֤|LUr}58#S'8)xB4/oW4˜QX!Sz,WSQSmQ;NtUj~P4,lQ?i$'us+ s̯s$՟44RhI)J@tALUaXKKRYec=WIsSq掟%0mHIY&2hd3<ƕ :-q갤u9jiVpŢQ(ğz.5j4S<&XaՙخdE&w3"bT^T7tkC;9fF"Le@Oe5ӝm/r S,W">1\)Tdď]5vcFO}fpFEFDj"=e~2~AåJŋ"6SfpTwY-dyq$E{GO R'1Ua9jHy f&fȸȬI҄LE vGq/.T2F$inRw|0ؤhޙ(Hgs.K[U2U/_Jr Jܬ)nSfɗP:/)Iq\>9-͑4Y*ȵW=Ed? V#Pt!y"C4kUD|X!Ht%? Ř2Ȏ~\\.RlɇSom' JX$*$ЅҴq}8]eh37q:;>777S1TgQ{:;#?_({:;kQ{:;kQ{:;kQ{:;kQ{:; 2'ЄMv%*3$c4r0+l C|i5lhBnv+Y~¼9G /i2'7KY o0M8W|:hɺ_t1tÅx7s,OTf}f-ַT$W33>""iQ,Z߃%$ԥ $\fD\0tÅx7s,,7KY o0M8W|:hɺ_t1t>B7Ds%k彯cKn1ϰ{wf>[RեWQMb")$SdؒV"K]'%C@\(+eOtjj>ݰ ՟}`6#|&a~?.> IO0QC:1Tz=@%ʖ0MQGm&{]' &B.bډLHsIY+*K2#L  iF*eeo![I6Pk%A'CT旹Q)7l7oq%SGܛ'>K*JRr:Xn4tsWJ)&GcF|G0̯AeBKI%<4GřKRSsK˰jR&knZ I+6i"ֲ$)UF+.:\Bs6r B*JnUib,*ZZYAj"P)Lqtxx}ʄۋ3I$IQ*H&w#4̯jܪԟDJukm23<.lV5<=Ə%ŧ.Ck#dTewNSHJr%f9ZQE"RilOE"&f)i)umғWSHA)?Zq~I""]%1"URpifᒓFF|ZȮ3S\j|cSE,Puh F]I#QYZQk\3 $J$d8GI3_ߏ^lQ!+41F(ʔۻ3l2LbZ>2sqj4*f]¤IÔdyQģIdjI\v=B Fy̘-m)HBH$j+=<5LʬTFqM)Ec22Aܪ5fl)FffLu*o!,-M4{!$+l C|AV"\Db/0xȗ5}4i5ŨfgDCG֩{NTt\wRqRY+N(#%(b\S6L =4bN1 4HJ$+҈U0A8d6YAKQT);OַsdRaJA3c;%IZ$iVrQZ;Xpz#Yer=w"$%<-Fwݭ-鸊ELFy侦66mK-dWrc#K_,V)͒USG*r-wǓ,XSH|Xĸ[RzuDIи$w#F#aʓv5N,|_jD֦YN:"e"#3-cm0UbvLyɉc4hbyNYQQGEFWPZ9DZveZrԻj3#Fv1"  ǿ?b(83+-8#6O(J]'%C@\(+eOtjj>ݰ ՟}`6#|&a~?.> IO0QC:1Zcl=S/%J^Uhd24]zO8{sӉqڬ%%n%+4EdV<*P7iz)i3TS7ʣIk3Vd5CJ"uFÒMm3CC%7JqFM|s;sWRjkҩ1˅ pE+1gmB9m>#o¦JߒodC%)ZKIKI3;]e3ګ"ԡsev_)܈L5=ލ<,wZjQ33E.35i;J}uLymO\5F٥#JHYnZ)HV*LݫM!*AJ'$ۅ#""'tIK ha3Q+6Y;\23^!/r O&8Y4rF[Y*b!lu"cQ%"&sOZݝ'WsT;)#;kWZ=5QKVtJВ#5\vQ S*jθi5Tl365$rs<ܖ)-Zۨ!QNBz$6i4Wc,+s;+ZpA.=oCsLIjerlñRdžNn$K=w52a }A9̗;f(݈V|G2af-B7w;Q{#t i A)II_fg-$$$"RP“ IKhS2-mгCRM$i#W3s.[C$StPUHiq+K1kk S]iuyPMTo>i4=weHU'C>+v;S+Imq 5%%rs3;D:\x;㰜]BLB7RRGhh%3Pt!Վ"rK/S]D hI3VQu:S֧3T%ry.8Yv%dqFjD&#gM?M>D"Q QCW֦\fƍ PLR|W2ˈDNUGݢhrm3լ֔nb2n$mV22(K6-=4=Č;L"sH#+/˔Z0Wa\I7ӱܿJ[hu9\BVEr bd؋ߨaJ($HM"Y\4#Kf4nlmeʓ+/0DsDs|/0/0>K?4K? DѝZXenD2z JzLLSLt ϽRl&)&S6DsDssT z^lYi5 s3Llu4ʕ~;79_a_`!w GlPXV զqI;FkRh+eOtXr MY۶wښl7}aσc}D/G>0"i#HgF @ ] %.I*9h79IkY!)JsO%~0ɂVRnB5X j+kqa8EsvDVVj;&Kfgb"#3Ɩ(՚c(O! S䗏&E)g'fܴ hz҃I(VΟlkS0Dҗ!BJ%dD,Dgnej[ȃ=+HKMJL]1':Y)geȈGT٪%ĥQ, 2 &1e[Q)  .bމ+%eWfDw#2IR;ѩ.4U&ZM&iS4ddd|bLm]K}YOJS.7n5F +~KN&U i.'!-&mw/Ŗ$jr jcRLIΧyJoY|r"#R3Vt]Tǖ[њm^"4$嬏P”Uikjb}ڴBDrImY2.2+xt "DybM6|vQSvIjQQZJXL F QvLNAINgeRܻV3:^m}WT#ҩN-)\DEffDEb7+{pӮ FZ-){xo{[Xĉ~BH9l8Mt't_ ]e8"JiǐIΒQ]*T )1=1`LIi$f)jJn|v#3 ԣi'j`Hb*NJeM-BRDJ"!̵ƪ\KIHbl(!H=4I+Kee{7bdZTIۮ!%ւlJͤZHc; UQˎmMBУJ##!Eqò u TZy.)91ʹjW!f;!xiK4l#sYZc+GYRU Ĵ3ZDySs+ؔ؊zXM:t$*M9Ti2;LqjOkq"m؈iD|&}b*ws91GN!f|`&v ZA-*Bԅ̸|JcV"T$S!L&2$IrJH5,iQ\u)-T*8]./Ƙo5yVͳ"<1A%M9mm&;pٲ6ә6M\+j,Js\2Tjri2qMK5Ȓip{XV#!>!))StLhJD&C-J>]CG63 PƒR纩i)VNTB2V0 %N*4jSm!sqĒi?i"bW)m#&ꊗN TimflNj#Q:ُNNi4gKk\%fd$ U~W-W=^8'PS%ĥn$ԔZ""#Ir0bU+bŨ5m2irsn8^b-/,#5[x6J˧φqP6ZNj#Q:zӊ!Ǖ $~3IDYlQ+1+;TS&< q]%mB+2*HIZ TctQ%Qf;ߒ.u7$qD.U6Mt0z*[Xaފ:~LQMt0oESkk ?@&(ފ:~7CESkk ?@TC ! mmcTCU6`}mmcz*[Xa0D>U6Mt0z*[Xaފ:~LQMt0oESkk ?@&(ފ:~7CESkk ?@TC ! mmchj[s&͗Rmԟ*,sYFWI+eOtXr MY۶wښl7}aσc}D/G>0"i#HgF @Ƨq[ѡǜ}eZJ=|fc`|hKpZJ"d\W?c|:!+M좹\Wa?_c'kںK5gnjj>ݰD> IO0QBß$_!EW+0j豊F]wښl5gn oß$_! }aσc}F&NHI]وˑ^n2.!`+]ڏ[CQq}(owcj=n/ mG,`+]ڏ[CQq}(  v6Pz_Jݍ7Ҁowcj=n/ mG,`+]ڏ[CQq}(  v6Pz_Jݍ7Ҁowcj=n/ mG,`+]ڏ[CQq}(  v6Pz_Jݍ7Ҁowcj=n/ mG,`+]ڏ[CQq}(  v6Pz_Jݍ7Ҁowcj=n/ mG,`+]ڏ[CQq}(  v6Pz_Jݍ7Ҁowcj=n/ mG,`+]ڏ[CQq}(  v6Pz_Jݍ7Ҁowcj=n/ mG,`+]ڏ[CQq}(  v6Pz_Jݍ7Ҁowcj=n/ mG,`+]ڏ[CQq}(  v6Pz_Jݍ7Ҁowcj=n/ mG,`+]ڏ[CQq}(  v6Pz_Jݍ7Ҁowcj=n/ mG,`+]ڏ[CQq}(  v6Pz_J+eOt7ҍEXt'òG!]u ,AjBu'υjj>ݰ ՟}`64]ObTj|u0*ZM<%uddDw#3"ϟT9xz޸9aϟTGϟTGGϟTϟTGGϟTϟTGGϟTϟTGGϟTϟTGGϟTϟTGGϟTϟTGGϟTϟTGGϟTϟTGGϟTϟTGGϟTϟTGGϟTϟTGGϟTϟTGGϟTϟTGGϟTϟTGGϟTϟTGGϟTϟTGGϟTϟTGGϟTϟTGGϟTϟTGGϟTbf2T-+JF)BJ"Qi|/^q͏PKʌ5X-Pictures/10000000000002A7000001E7A4935D3C.jpgJFIFHHxwdumpC    $.' ",#(7),01444'9=82<.342C  2!!22222222222222222222222222222222222222222222222222"^  !1AV"QSU267RTaeu#3Bbqrt4$%58CEc(1Q!ARabB ?a9O#Tq͔ZޑZ=h"lTWVk&m֖ln;H\6,c[o+k;sGb9n+y]Q;-$ds3?R|'0"7#E uCr8[^`TKn+y]P܎5#r8[^`T7#E uD܎5 n+y]Q."7#E uCr8[^`TKn+y]P܎5#r8[^`T7#E uD܎5 n+y]Q."7#E uCr8[^`TKn+y]P܎5#r8[^`T7#E uD܎5 n+y]Q."7#E uCr8[^`TKn+y]P܎5#r8[^`T7#E uD܎5 n+y]Q\2Ts/ TOk۹#>񘌘Bz%FL!-IlM5w-܎5 n+y]QthS]Ùu1`EQ ,IrQV[{$7#E uCr8[^`TFi\qܶ[호pنYĿFLC%k}GpopnG q^U-*͕))R}HqOćNquD눲&V=Df6pnG q^N\& NGb׮ʣ)ifIJת܎5 I!a\]$FoزU^%qkRui;e˫PnkPeT܃4[1x?M n+y]P܎5:Bk͸u 3YTGDyn܎5 n+y]Q."7#E uCr8[^`TKn+y]P܎5#r8[^`T7#E uD܎5 n+y]Q."7#E uCr8[^`TKn+y]P܎5#r8[^`T7#E uD܎5 n+y]Q."7#E uCr8[^`TKn+y]P܎5#r8[^`T7#E uD܎5 n+y]Q."7#E uCr8[^`TKn+y]Q O:p Z3vF+T&QB\L974 P˛kڶ{Zl˲TFYg!)FyZQ\e?` x= iXiWOdpȬ^,=AuZƊsEr=#|mʥ~%4ǤFԤIZ˄nD˅hCIQw7ʞ<=AisGb-3[x}FAPh;XȏxMƔoYțpJ<-CHH{NM;_%mdAGz~;ZFiDfԕT.DfRok2#ه}c@:r23xʙmI<@[i4ڙYZi;v FEO%MH42͝*ʓɥ Dfe"#\MhTT8Ym ~6D]Dyvld^7].3{Ne9>Mկk1ʣIi5)Q2+'HDkV;ElJZ-ckqӊ-i5lD(b9)šy掅.5o߼6'jPq 6dj1*X[WVTtF(fWN#-b.Je2zGJՐs+YQiJETg8a:NQaJP0f|y;#n RjI̪&IfTLg&2#qiRYQc+x\HL)KSI.Q~= \ZVێJI&dE|f#NJ{RJU"-['gWq?K l_۾#EZPa uk*D^PwRs5  sKk'ic)-(2MlDv5k0T۵BM^K*55&tvMWrBDW+^翼(IU=&DZR|%K74&񍚜fOlg9I%]E2)E®2"3޸9议:ܤ@Z!(aJ6N䋀xl;N$!qL%'LZ"IwVjP12STJe,-*rJ#pk3+Qn'E*UB9Ž$N=U7dXVftB32kSZWnDtN:q^x5lhY5DZ}.VU=3J1(]֝EtRԝ}ֳ-WԑR0rQe$V#[dJ/|HT&#UYjl-ZϼH$j"!RGAec9.i]4\$3$z"(ATLΧ*bF ӔVsks-cJIL%Hqqi\i2UrIo^Ș$ڿLxì$v49ndSSE7.:$Xmfgܾ6O{{P*#5PW ȧڼOƘ^E? x0h iS@O[2iR;Hh, Z|o\UDØ=Rl4-MHYej?ayW ȧ> 6Ox0hj?ayW ȧ> 6Ox0hziqIyt,q_Us;s]Iԥ&5*ɶk7yz,{W ȧڼOƘ^E? XsØIL1JIJ}W>pk24VAel@m^'L/"j?ay'T@m^'L/"j?ay'~4)6O|U$$֬Q&NO',9g]$,YK{)^ llͫiS@m^'L/"7q9ǥK},kHyL8Dі|D[從]ЗTՠ\cVE\*+)*#˾Fj3j?ayW ȧ>j?ayW ȧ> 6Ox0hj?ayW ȧ> 6Ox0hNIaeuQ쥑χ9BDm^'L/"j?ay%|?ul~c W ȧڼOƘ^E? }lr>F~4)6O_g[χ9Bx0h iS@(APDm^'L/"j?ay%|?ul~c W ȧڼOƘ^E? }lr>F~4)6O_g[χ9Bx0h iS@(APDm^'L/"j?ay%|?ul~c W ȧڼOƘ^E? 䨖RL&W##d=@m^'L/"j?ay'~4)6O|@m^'L/"j?ay'~4)6O|@m^'L/"j?ay'~4)6O|@m^'L/"j?ay'~4)6O|@m^'L/"j?ay'~4)6O|@m^'L/"j?ay'~4)6O|@m^'L/"j?ay'~4)6O|@m^'L/"j?ay'*Di8NLxd'-ߢn_׿&Hx3T_@3T_@ y1L6dWEq0ʌ؏EhZTW##+9.'ƻ`mIajy(D,[{2*A->Nulԉ,YcǾ;]6]ǔ~qu!w#qz9O_^tEĢ2fynZ[08]Ӊdj,oS d{qE9jmyL YLw)U Ϗ>TvDrm3TAQX]҉7>*Rj%'&,ɌH}(ZTTi4b=g#I-I.ydwI$WMQ\Z=M6.z>$G)V3+,ۄT9 I__^RzLJ +T9nRz+T9nRz+T9nRz+T9nRz+T9nRz+T9nRz+T9nRz+T9,+C#$wTY)?]C@;'S1 (9^$ڿL_׿&hx3T_@3T_@cJ; |v[nM8˺z$bՍi4(>B23Ve4㍒nr=J--YQGq6H;d:K3q-Z-jY=JLTlF쇍fG[)JLy$h"#$ hXP^mKJk2$9LV+,S^fNf"IKWr=vT9Uch\"D;*Q"ͩfIʌƫp T5Z9TGDY i"Ib\б8tiO2e'RiE^Ɇ1 xﰷBۍ-)#Q^RDr32; G[t{J5kGڮ7:}9 Ӭ:iRElI5~` Lǎ UV;u$SQiqFzCk6Ysek_V& H&bEu ,eyդZP4[X> ͏qvV:l UFdIYjJ#225Zս>|ceMfv^ Aȏ]+(7gloؓ>^JxFuíz3>3 >9J5-9T)+mIQ=ɕtedSs~S#!Iy)NBPjRSQfDgs>@\yo;D)FI&֥*i+>-0n!IuԃiddDv^ 䙩$&r'h?ȌNP:lgZwը\#p#sG6Q2#@=$IeTHy2^SMȜ%ؔf",.;R^q6[ͻjhC-'+-v#=Ri.o,R ̍2TH#ʟ|B*׺ZU&6rW <$M"#fk34Uj0NUI蝔6eJj<[Eu֭JOZ1#OVe,-Z-J4Jh)Y+x|cC3-V\gly({j;72" % x㥴pґ4DFvAxCӴNEYTM "qZu(̌%KNK '*T2A R &]ڈj73Inl9Ip;jl)nٿ-W,}Br8ML;ҺDe~zr eB.|.pjRJԄJPi5 ,;LzH؏/dv홙eq帝-vQ_&HRG4̒k5hW?eӮ-ǔJuIIȈ|'b"D2\,F #}l|-Zys<HL)KSI.Q~#Ш[PYeťkmJTfDŽGcprS$lJ i,ē23IȎ */%}|qzFn£yYPV#-Jwks5(\|s{5_Hl;=}!ps5_Hh\|s'A;=}!\|{5_Hl;=}!ps5_Hh\|s')?]CA\|Y|k wF!)gke@'m{"`j0Eց:DtŦȨ:lTL-~ 'jI[WẔwwҍ#|['6deܤf#Uvڏ;CoM󸾔Z4 *<*@Uڏ;CoM󸾔Z4 *<*@Uڏ;CoM󸾔Z4 *<*@Uڏ;CoM󸾔Z4 *<*@Uڏ;CoM󸾔Hናr!\I3Q%$ǼIJK]KQU&y_J]mGѥGoWHiQU&y_J]mGѥGoWHiQU&y_J]mGѥGoWHiQU&y_J]mGѥGoWHiQU&y_J]mGѥGoWHiQU&y_J]mGѥGoWHiQU&y_J]mGѥGoWHiQU&y_J]mGѥGoWHiQU&y_J]mGѥGoWHiQU&y_J]mGѥGoWHiQU&y_J]mGѥGoWHiQU&y_J]mGѥGoWHiQU&y_J]mGѥGoWHiQU&y_J]mGѥGoWHiQU&y_J]mGѥGoWHiQU&y_J]mGѥGoWHiQU&y_J]mGѥGoWHiQU&y_J]mGѥGoWHiQU&y_J]mGѥGoWHiQU&y_J]mGѥGoWHiQU&y_J]mGѥGoWHiQU&y_J]mGѥGoWHiQU&y_J]mGѥGoWHiQU&y_J]mGѥGoWHiQU&y_J]mGѥGoWHiQU&y_J]mGѥGoWHiQU&y_J]mGѥGoWHiQU&y_J]mGѥGoWHiQU&y_J]mGѥGoWHiQU&y_J]mGѥGoWHiQU&y_J]mGѥGoWHiQU&y_J]mGѥGoWHiQU&y_J]mGѥGoWHiQU&y_J]mGѥGoWHiQU&y_J]mGѥGoWHiQU&y_J]mGѥGoWHTߏ[JjdPԔꈵۺJOV])8͎B6K743;wؚl7}`aϋc}RG>Ԇ)ˌӥ"1!:'@*TE=>0ű#UN5CFq2$ېJӠZ-)U#^%^dZ̘-LڛIW*3i3I+xyvvBͯRHԟMhJSkb]vU= JZYBFMFfI"} v2㓛k*Q+˘AR-`j!seCvY)M)ԡ]j$Feg1:Z}n|!ǜ48lwM3JL)FGQ[|&aUSK8LKIC-:Jfk4rV^ iX lR]6qIdDQ'*HE̋c曉YWt%B|K܋Vfj4N"#+ѩa br9.mjQ?C}ޭ]L_P߁NAĕI$A8fԖݲe2QB;-` F]1\4.Vv]&MXsTIF_VC7tGˀ\#|[.µy#*w.A#*w.C!Q";ͼÉ%!JJFZ<&FnLg}RKm֔JJ{FZs1Sr 1Sr cvC,)m%֫+0xN7xNt& K&񓮥?EYܮ{r`nːnːMQ鮲xG7$MbYCUe%D}]2mDBXsTnQ R':WLC1?")1k.aUOR}Ѷ1]idA6}jl?ZC8~, bZIBNB#_uUM6OĔ>Vm}&e@'&DUhIabHhY;]X0̊h'* *Jgȍ7K7mgUcl7*N(ř$Ei>i$FUb""P-吱L"1DE[I2eI+%Hw&Ee>_m$_Ȁs%rN"dt5HvSE)>6IKc T0k&J"JHi#I(xf:M>Gi0aN*4t6k9H1h1SZK[n"6ZWl;Ub\CRBKMZ jӎ6Q3k1b@*U 8S;lKʒM; jUC*6cA6ɈۊJ I#QYċ 3;Q,4CmJP+"-VkT?Ԇ֨s}}?;.V #|[+z-BLvgiT'BḦX0 9loH;bY-^93V77qc Cw?@Tj$iDFHZmsZwVIfIjJ^95:Ͷ#mbl|$FYRW/>_w?@T7qc FYBoÈ%_w#S;j%Iy&Fa&טEڒE'nx1~+2eלuIldiwJb31J&0_)95R4ܗo b/}P"7nf48\%QOM},2ʝJY2!&zE^'mx1~mUw?@T7qc Exhx1~mb/}P"7 ûEo^1Xwqc Cw?@TW61~nxa"7 b/}Q^,;^1Eo+Ew?@T7qc Exhx1~mb/}P"7 ûEo^1Xwqc Cw?@TW61~nxa"7 b/}Q^,;^1Eo+Ew?@T7qc Exhx1~mb/}P"7 ûEo^1Xwqc Cw?@TW61~nxa"7 b/}Q^,;^1Eo+Ew?@T7qc Exhx1~mb/}P"7 ûEo^1Xwqc Cw?@TW61~nxa"7 b/}Q^,;^1Eo+Ew?@T7qc Exhx1~mb/}P"7 ûEo7踪R1\#f̝ Ik"|//(5ݗ}?;.V艼#|[)rJMIAַ#fF%>-Iv:Ϲ/mrin˘k׾3b"-MѶ:a摽*LjX˼ZxfrT!הd$-[%jc;vc;E:bDl!) 24N=v4dFd[pF$!R_Rg%㵇mõm(i6EBK${t޹+>G7#3)U=f}gk.]>o>oG>{X|1|;X|1|2:ko>oMsmõm)|c;vc;e5πt y y 66>{X|1|;X|1|2:ko>oMsmõm)|c;vc;e5πt y y 66>{X|1|;X|1|2:ko>oMsmõm)|c;vc;e5πt y y 66>{X|1|;X|1|2:ko>oMsmõm)|c;vc;e5πt y y 66>{X|1|;X|1|2:ko>oMs/qE Wk<&JECl+ e}!;.Vv]&MXsTu/#|[-ΌJH>👺[l"_}|/>F m~iE4l@m_ O m~iE4l@m_ O m~iE4l@m_ O m~iE4l@m_ O m~iE4l@m_ O m~iE4l@m_ O m~iE4l@m_ O m~iE4l@m_ O m~iE4l@m_ O rj%oS1Q$EZΏ6s{_) f2J4t̲+Q|39$Y=y|/> 5/a[l"_}|/> 5/a[l"_}|/> 5/a[l"_}|/> 5/a[l"_}|/> 5/a[l"_}|/> 5/a[l"_}|/> 5/a[l"_}|/> 5/a[l"_}|/> 5/a[l"_}|/>#Za&LaثB+|Aλ.Vv]&D>-IjKM*JyHJ"+&>-IO0QC431 31 W[xJUJT$ӳmfj+ZRd|nrnr1.f<tf<t CV=U*eELao("5RQ\]Q_GP8anzR,Er+3lOU 1ۄcۉj+Z[lR-3&+uJUV$g% *gDBʮWPfY6mJt-,g[^V{_]cp1PȮiyc,=W] *gDBʮ5XFLEF:eBڍJM6gZ-:'UtΉ]FW+ *&A7Pԓ˚J{w ] *gDBʮGr#+%ViO̕-- Gd\kÞOYU :-KC/!)KA<3$f"=v;ȵj;WeΖZr92!<%I=ql6f{nOi46ӉE)9a^Vy.WU̮}at58n3TuN-gL$e56JK)ֶ&5UO*jK) 7U-D"Hқv-z]&MY׶h>0ű"bRs[]˃!}`aϋc}RD/G߬SpSHMbW G&Љd))ʓ7 4plEH'U(ԃxѵHzc$GغXkCXqz6=iFՔϿXCI'UU+2 FE!0BKN6&K%+fGb`MDғ.I7ںV{tkƩ!TEĕd11P=%)BA_1\ljFut##D)ioAmGZEgZ$F,x>Fj L\KFBSpZC%5H$ȵUٗ*>ҡzIFBSIj5U"9CaRe3)y]7f<ّf2+NOc-Qim ҡIhQ܌z)8rbJjCq)L;ƫIjQe>5hR1zsJ*LԔfDv;w6Z/.PIa[iK H[e5&Z|7 *)TDK66Fnj/V,k1{jIMFdTM+LsAg#f_a-d[︧rJZw5Y9*.8He*mI6"H@S.Vv]&MXsT4 $!p 9loH r#.;8Jq$Gd~#Z&ndDFIoxfG@Ys4&Uzɒr{&I䚍Gԑ$ 7x:? !ɇ%Fn* b;w;X? ~#? ~#eRػV3:^~m}W Œ~GZYe[nFBk:u#u({dѣ)eBs)JQR.32""98!z\yP݂I7>ק,bߺbԹt$D]3zB2q=&Tf]EZ_C̚B ?o**Dlx̮hQp_|O>k 4K3ZnK.rX>Vq{.mI_ek?{#_{);:t;QZj2%kW z"\=%Ғ$~"IK湖6KN~L~q5QE{Lhtsد3 (Qzp j=Nϥy=E757m^u&׷ZS4ՔgktumgU^ϥy=eoN2)MȄԧ{JzgFW{e}BB̉)r"ej4"fdG}Dz2KSYdÐ'm}{6G+jp;yDk<ݵ{Wu'^_ha .$>WZRP]ҖjIWޠP#1Ĺ9\m\Q%JzVQ&3ԋ=ݭ~ԛe}@ ϗGby v:y=ReI.59z%󞫞־Τ+_wk_Τ\L*յK<\fGSȵ{'s,m^g; iJIZ-iA$| T^ǝsݭ1'fIFWѕ]:z\:e{~FzNN] rNAk[國YlV UݧK󹮑$R |,jU!VI|m)XFdԗ,v3jȏfOD|IKdG^èˍ}-FZ KΦIWm_ȷz9d>vͨ}OlSU-RYZJQMْR{QU\vIu6Rc#MbLw+?;{}Q30?`pF%N|zN|z wF]:i :i]:i :i]:i :i]:i^Q?5) $xȎ{ЮWta?_]}?;.V& 9loHG7$M?DI шP|$-+B? R'1Ua9jHy f&fȷȬI(жLHirIT7HQd}u$Io JDyTRP:yaHmC~1$I=es=Nq .dj*J^-Y2463a>\L%kL4u4J%8#qcbT78H-G_<LDl ꏅGc? qr[)žw;l_xVl ^ !w8H-CqcbT;osmw;W`~iw:N)žn? qr[ B-=M& DZe,f7"դݧM8Q6qfFʛ$̬W.R9}P~#+SgS)Z#΁EC(`G4՜ԃI+q=FMnif+@22V[1G1o\s__*)'#QS9 *zMȍGc*]EZ%}J˪gGao~# .R9}P+_u-1M)n)WQ&kǤBLW7u*n|K-w%Uoo )žn? qr[u1gqV6JuB +34$w}p4:y%iec+j/Ŗ$jr jcRLIΧyJoY|'r"#44\R͚Y )%]Njv2I"Cj;#2";2J&.xI%rZGIxSғ3+.%%*6V3w2ըUZs,"%N,zV ͪE@1FCm JE\S:[%$Ȗͻ$dV+3}0"f.%M%%"Q3+9*j֩R#Ӕ܈no m-㱟sͱE:2\C{/,E0 g3q(+yyLрrpXifT iOh!V%,%꺷JQ ܙ<_ۋ[L8P$F8V#lfvmdGr>  w1qF]JKh 'k\^uuD*JR8Ҝ$JQi#"Qe#hsbCCThԶFMӒ$j4%7"23լ%0d&J&8ȣ7IvHYfw3+X p*l/%DU;__[(J@1_^ioHv.R)d󆬏&JѦ"SJo(Ja:m)U̔i2]wւ" n)MFm:)CSKZRR͗#֍EwJ=w-VX\GICT"rVD#cMGʽZ-`)MbjJC㥉1l4]Cjzlَ_i_)Fi5ːRnQ=Je&W=FEp=eim>;+OLMm[dw7~ Eb#ݕYOKnK3O>e?%HW"n'<5p3)@E0z3D>-IO0QBß_!1N&7M?e4hYJ7+Q>C*Jc!.)fVJJv p2\8j/liN؎J3-ofQu7؇^nEA0%AO m5(DeR{,."CgTrPKdYTfDZǬ dKjLw&HdY#F}} mDVJgN{V,TЕ$QܐM4Ȳ-FG-تJTf[L%9.$ܒI\YFe1ȌѧPF6䜂$ؒ*oDvb(JB^TaOdѦn;5n5pXK•1jY Iͼgs5)$E|̸p`Ʀj6RvBwƜ?luV\iڒ %^,Uk$'+a3Bt}G{*mwF&c̔8Q oڬ݈̌w#F6ӍBJźZb+k3QY uY[&QSL3Jspeg{|yC\%C\eFKMXХ1"@8IoH̳(ed$gӈjU65"|~B m%w%VWO4 :c9S$ !&bV{Gbv8 LA.l0ڜqgg24~)ʹ$l*v2jt575Pn@ X9 RPZVȌ*kfMzPOPu*2^7`M %VcQY'{ #վFC%R5ѓLrsQsSlʺ|H;__[(JǪ⚔ئid;e$di22222 EF* 6+L6kRq)J4ꌋ&2S ;SgFzB~yY^i3[IIl;Z䡶ė2#ЍD8!Ң<ǬofJr zJd*YvV'0fvΛHZl5>ę)YokjX>]9Q~Z[:N YԶv#VUeErW yT͉fΝ6|ާ7}v0]EQkq&b=^o-}' }O8r}PK344Mw$[)o֔b"ʗ!4#M""5!j?xX Ds *%\Bn!fI)"GieNuDzͤ54Vfwmw+w}ᨈ`*UM>s6YfCA(FB)$wVH{n(-Z*ܟ4NTI+$d"+/w}?/*Ň*8b+uIY]eRDԸ#mt뽵Z"\en0FhVSMj+(2$T+]OtXr MY׶wؚl7}`aϋc}RD/G>0ű"i#Hf@"9Ve|W:ɔR,C#$j"3=e}D0"N(Du)IQE$Y2=G_Q%DNNu6q("3QE^'*y$H[Q)u_lfRSiBR%33,FѣzgO#Vm"tw̸,emV<8դ)U1=ptff(I#)ek#.zEA5rTqꌒbGy*B&沈ȷȬIXR9-s LTϳVCT4(NI7 k(|ĝҀ.PU7PĩE6 [##%:ㆂFu^GmfcԒ' eBN%nlÞiͳ.RVBk0K$7Q!Id aifٗpN$VBfN]V$NSqMz-B~4"4s2 s]+S=#dlig&cr\q*qŴjRlwOr2e=VhT%S.ߊt,R_"4>s%Z̻ԥʞqh"G)TEK);ԓXD7ltʛnN&ۺ,hkyRm1Pi,<"E%L&(^ $Djd뱝tE+G-Se4#LfJ;uEr26 yWM95[h]gK- 4M%FXZ|ajǔTI&Y.ًI&bȎگeANW@a5ZIf[#<-2nJ6iUJ"lo_eF6hήCSۜM;),QI؈dgk \Wq:eqYܖqh^ni$$yw2IbZ{Tztl)FW5%̲+k{p +M^7 jdˎA*JLYkvx$&K&ZT;-d"QBMEc2#j=F F# !ԓ%di#;j:^!UYPym'e:М34G-6<bz%1*zN<Ϗ Yp̷"$*Q%\ -=< =<TRmvVMh\K&~ z^1r+.Nl*Tu[3Vwblj2"V{yA@{yA@, :(:u]Ci-#mI%)J"#CKpۓ=i*nDhcdW. 8s8s'@r])[r\4h&┥ )c"#>舌FB^ZXKRDGXeJsW q0q0Qv<6<^2 .ǞÞPǞÞP AE8s8sz({yA@{yA@/@`pa(?`pa(? m*p34̈Y^ױ_~ż#q_@4 $!q_@"@@\+]Otebj>˾՟}{`6#|[&a~?.>-IO0QC4ACĐbZ}+Z$8Fv%"DwBeIy*fH%*%&wI CUj5zQ%l9vc"29 ¨A:sɥ.ITtm)Z4YIU[^@1SDp4lX1V-W;j̹>8 C8ѡDPIB˦{-s20ID<8ڠIL.jS٭cBI:`XdNrV־U5((dk&͵XEzx52!%|cՐC[2-F Ev081"&4GT6q8HpFDGs2-{1"`J .Ei-4O:KSe[ZW|ʧ=U*eELao("5RQ\]G&BJe/uGMDm,F;__4 MغS.4ݢ*lf6LR6yd{9kB Ҏ%{RQWiU*laSqH7f͑qġ;Z(\ͶKiv@M˓C{piQ8Ӟo.b#-IT}$V4yK%^ڮmR*6jhnR(:))$Dm&"_QLO#Abԛ8{~K$v3S5%K{49:Q&+tmi)g$IQ*QZ ? թpN uHmVC5%d226u@MaL+JDUERVZNK8l"re~Cr+zYwڧ hOin%eN *Ѓ3+n\EGSf yo)-"A.8e"2+[)f|7$@ PmVQ.t⫦qddlH2YXr2$VYه%_Vd"N;#e~i+1p٭z:F) `9kBDz'8b[Y c(ҭ^fW*8R+1JQ!N-(qSNJ)'kW!k٭z:CfsLזJ%("ĂGmΩ, ͘DWzZVUZdSXri4m2Mih+܏Qصֹk}٭z:@WCVFٔŜ4yi2Q0)%$k0ű"i#HBXsT4 $3F @Q[P&lnxĜĂ;fQIJJ_1ӡr$%6C"M Dv3GX Pl)$Δ4U$fgbzp(Z\m+I*"2?x}U٤œiݐ2 $ns"-I3{ҦQZ24rfw=  #ؕLQ6m)j;$,Dgb#4k2DĚl9 7-#D ԣY3IܵSYI]sVAnV#Pt!y"C4kUD|*l/$޿qKOGm;__[(JG{|]pr/k>Ay_,\/#܋돰y_,\/#܋돰y_,\/#܋돱I) ar/kEuV<#tV<#t5#܋{|]q&]!&] 5#܋{|]q&]!&] 5#܋{|]q&]!&] 5#܋{|]q&]!&] 5#܋{|]qN.m /bQYY5ֹ_$I) o#܋{|]q&]!&]"{"fy_,\lec9ɗHec9ɗH {"fy_,\lec9ɗHec9ɗH {"fy_,\lec9ɗHec9ɗH {"fy_,\lec9ɗHec9ɗH {"f7ߏTTm!0%\=k=k3=ذ[2!+]OtXr MY׶wؚl7}`aϋc}RD/G>0ű"i#Hf@"-NkGb~m0kʞS+mn #UNo!zhsۅ-5 S̚ &jc-MzPё-,`jݥLZ-(l3+,#3+s"L3ѫ4Tmi/H+xiqK]b7Lsz9cQO3Itv'̑JVYf,pJٞ˳ydC[HI)ƍFDErI(>@&N~sq<6padGe.G+՞r*릸h\,ȳȍDGB(48tXu$PlnJ%3;$b5XiJD*51Rg>Zi!R ТQ9$6,Q;å JXL F xJM-ǑXuRyJ%V34r$E-ķjĕSKQܔeҏHA(nxG{ZJ[MuzWnm+yo!j\v\PilĵfZwSOHBm%ԣp$w=%̍EΪ%@i$y[B h53%oL7]<'5aJL8DOWrdHUkZ4::[9uII%k cUK)!5{)xȔ+Ykܷ]خvIɧٙK|GIV%p*t-Q W~?P?dho(`ob[2"* F]1\4.Vv]&MXsT4 $!p 9loHetü92ݗ |Y$EdlmE N39T ^7 ś=%r7 D*|OEJj#Jt␔4~'r3 ΗRS`O*cdœ=' $ 82MYNKi),$Y\DTʛ5Vu!%YFV3tXq'9_?9hS odi#&K33{iPӢ~ KSi&h) Z-u+VIi8ϻ!eIYDZgs2+%EM!KMwF= -di.{Zx4 TSP9- :Ȏ-t\%r#q9IҕFLH\gf4dGخg dYdF#W :TX(NSe6n %GuGђLWD~Z"sZZ3f4hQ(nnk(|ĝ(DPWiwN3$hF(g}Wȫo)%,&G}ΞGDEf>c;pXڬ)x:SRVaOCr;6L%Iw JL|]'=96;f+%Y稬Ub=e=N2 "$?mFH^Gq(nbzvB[]C𮰛Imr?RDzH2Ӽ~6q,`+[NA`]Zwao(`ob[2#=S&.DG%kme$z4"ēRG+(2, Wta?_c'kںK5g^ebj>D>-IO0QBß_!1Tz=@%ʖ0MQGm&{]' &B.bډLHsIY+*K2#L  iF*eeo![I6Pk%A'CTƗQ)7l7oq%SGܛ'>K*JRp:Xn4tsWJ)&GcF{fW Ł2%DRE#̥)،%AHe HH5\CKm٤HkYZw .!9ڛu9VF%E## #Zh*J8PFkZ*ne{s;_Q ښGNܸDv;e_)*&GcW2&^ENUXfF4̇ ˾RfT͆HSM |%  q e&keLd9 dސwi2Y;gq|x&"=1oJze22^C#3=yX KjDF z{Fx)6Wƶ kˊ3lnݲyV=ON+'lYp^8wғYfdNE(ȈVbh5qzNu 7L;7*Jzǿr;XQ:KNv[2  HJMiJ2j%fXYoEis!/2!lԣqiBLQ^F] ZCieuK|bᲕ%hJТRTW##CSUIae(7E(Y!%w%ZWAģ/Dȏt}}t`DV밗n q P [\g%z??k%@4En q밗%z??k\gTDV밗n q P [\g%z??k%@4En q밗%z??k\gTDV밗n q P [\g%z??k%@4En q밗%z??k\gTDV밗n q P [\g%z??k%@4En q밗%z??k5ΏW1G"ʑ9藝mv2p&nD7r Wta?_]}?;.V& 9loHG7$M?DI шPW sg>H:եy-TE!~^~&MUP5<&d)+q)Y-"vP,:EQOM+NM2%&UHYȬ}0[(I5׹ T(m7BLm]K}YOJS.7n5F +nKN&U i.'!-&mw/Ŗ$jr jcRLIΧyJoY|'r"#2n+z48󏬳kQGff6E.35i;gJ}uLymO\5F٥#JHYnZ)HV*L٫M!*AJ'$ۅ#""'xtIK ha3Q+6Y;\23]!/b O&8Y4rF[Y*b!lu"cQ%"&sOZݝ'WsT;)#;kWZ=5QKVtJВ#5Q S*jθi5Tl365$Gs<)-Zۨ!QNBz$6i4Wc,+s;+ZpA.=oCcLIjerlñRdžNl$K=w52a }A9̗;f(݈V|G0TɊ:tȈ Fw33m qNKQ)DZ̋z}aI鍅U4)R Y)&4Q幗 c)sYU:Sc*48t%Dh5S(dtaNʩ i`Dæ65'~Řg~ i(aFI u8Rn~w=DdfZJhiOF%FV o9R[QNte-~+а.$؈n_%Go:!+M좹\+]OtXr MY׶wؚl7}`aϋc}RD/G>0ű"i#Hf@"BJ]r>Ts6oG6rֲBSe^ k߶ oLaN?OD܅6j2kVm;p;.:̈vLDFg,Q4ǪPFCO%/LSi#5#,.OF<i2qєQx?(5a镚+#.C9.00wS%\iJMq$LҧiQ_HpUȳ\I\(n -+j-]9's;j|i}2Vܖ#}hL")J]NBZM>_-HAY%Ǧ &.O,޳NDFf'lO-놷5(4DiBIK-Y)0I5i!NAeub"6֢gHjGƴTʍAds=]+.Clv6|BU9RhҗYIQD/cU:tW#-4Z[a+I*IVZ!\;iY ӑJ%f22"#ըf_Liӑ2&SrS%z+,dv=y). 2p}]δݚr$DY'"I7+UzT QcBv+yJ5(.ť"J>!))StLhJD&C-J>CLPRSl6hTIȵgs+,JfTh8JB!Ӑ%g"~EĮ; 2۴6Fȗ5ΔZRI#"HfM܀܃aG5Dk[{"tْebV] k,U&uPn'LCjTi4(IN.&TcHE/!ni$8e%$jQK|EMCUTzʼPܖ~?rdZD(I+Y{;\@NnN98A6WQ$6+)RV %%Er2;Ljz1,ش2u0Kx"ڈFw.h^q$NFr("?@$@@\+]Otebj>˾՟}{`6#|[&a~?.>-IO0QC4b j{N7yYԣ33B\SDJQ"޹@mq ZnGew/>r Wta?_]}?;.V& 9loHG7$M?DI шPr Wta?_]}?;.V& 9loHG7$a^k!.%حܹUv"lsoM󸾔6ڏ;DUsoM󸾔6ڏ;@XW6ڏ;CoM󸾔soM󸾔6ڏ;@XW6ڏ;CoM󸾔soM󸾔6ڏ;@XW6ڏ;CoM󸾔soM󸾔6ڏ;@XW6ڏ;CoM󸾔soM󸾔6ڏ;@XW6ڏ;CoM󸾔soM󸾔6ڏ;@XW6ڏ;CoM󸾔soM󸾔6ڏ;@XW6ڏ;CoM󸾔soM󸾔6ڏ;@XW6ڏ;CoM󸾔soM󸾔6ڏ;@XW6ڏ;CoM󸾔soM󸾔6ڏ;@XW6ڏ;CoM󸾔soM󸾔6ڏ;@XW6ڏ;CoM󸾔soM󸾔6ڏ;@XW6ڏ;CoM󸾔soM󸾔6ڏ;@XW6ڏ;CoM󸾔soM󸾔6ڏ;@XW6ڏ;CoM󸾔soM󸾔6ڏ;@XW6ڏ;CoM󸾔soM󸾔6ڏ;@XW6ڏ;CoM󸾔soM󸾔6ڏ;@XW6ڏ;CoM󸾔soM󸾔6ڏ;@XW6ڏ;CoM󸾔soM󸾔6ڏ;@XW6ڏ;CoM󸾔soM󸾔6ڏ;@Xr xQq}(Z5GBy;.xRu^a,Rx|]&MY׶hN!Ij|ɘrЕ-&FԒIIZ22";gǞhPrmuqyd;n??۽yW#cy@ǞhPcy@ǞhPcy@ǞhPcy@ǞhPcy@ǞhPcy@ǞhPcy@ǞhPcy@ǞhPcy@ǞhPcy@ǞhPcy@ǞhPcy@ǞhPcy@ǞhPcy@ǞhPcy@ǞhPcy@ǞhPcy@ǞhPcy@ǞhPcy@ǞhPcy@ǞhPcy@ǞhPcy@ǞhPcy@ǞhPcy@ǞhPcy@ǞhPcy@ǞhPcy@ǞhPcy@ǞhPcy@ǞhPcy@ǞhPcy@ǞhPcy@ǞhPcy@ǞhPcy@ǞhPcy@ǞhPcy@ǞhPcy@ǞhPcy@ǞhPcy@ǞhPcy@ǞhPkȸҊT1-TTK~R.)ܐDJ"33"$DFGr=I}_zDz٠F xNPFK gWJFIFHHxwdumpC    $.' ",#(7),01444'9=82<.342C  2!!22222222222222222222222222222222222222222222222222"]  !1AQV"SU267RTaqu#3Brt4b8$%5Cs&Ec&AQ!1a"bB ?a9O#Tq͔ZޑZ=h"lTWVk&mܖ%6ؘ"vpژ['gm ,eφk%kG r^i$5{%v+S=AQ-z/@kܗ"TnG r^p%ȷܗ!-z/@k-Fp%nG r^pQ-z/@kܗ"TnG r^p%ȷܗ!-z/@k-Fp%nG r^pQ-z/@kܗ"TnG r^p%ȷܗ!-z/@k-Fp%nG r^pQ-z/@kܗ"TnG r^p%ȷܗ!-z/@k-Fp%nG r^pR̾L7Q=gndŪ6*2e1 jKgrmyp ]nKz]܎5`ģ@m 2u(ïY3 ,BegzKڲa-z/@kܗ"+MR'ۏ/LV嶿lmő\Ktm4t8V' I܎5 nKz]ʥvJZ}+}g9R 꾬%G r^°CmXn|8,,F*Lf8Io: Feտ~[nU7 $Vdp%nG r^:Bk͸u 3YTGDyn1Q-z/@kܗ"TnG r^p%ȷܗ!-z/@k-Fp%nG r^pQ-z/@kܗ"TnG r^p%ȷܗ!-z/@k-Fp%nG r^pQ-z/@kܗ"TnG r^p%ȷܗ!-z/@k-Fp%nG r^pQ-z/@kܗ"TnG r^p%ȷܗ# O:p Z3v!j?F+T&QB\YL974 P˛kڶx?d5C2R \{c?0n<Æ۴4}+F_tV e/ڭcE9%ʥ~%4ǤFԤIZ˄MKkf *І24:pf[*x{m, f3vC)+!R6ؔyZ*E2Cri͠(o[%p||oHjcLhVJ6mIQ%MHVe&s"0{WY8{yqe\ඤ-mLd-Z4;v %Lrf4t[;8HBnv+-fEZv6,do]}F<:DPd%NhlBcVR،b11 JͺMТQvRN$dded?z<:C#ঢ়GXjhd| uc뭰ҝuĶ ZĒ3i[^e JrJ;$pSΣ42> yu&GO:)Qd{q4|)Q<: 42> yuGO:Y3imnB- Mr3j2?&ęIfLu#KBv;j=dev)Q<:͹ 6kq]JTFh3"2%SΣ42> yu&<:C#ঢ়GX#e'-wHY4X};d| u`FJ"222=dd?!fPN)Qf$fFW 4~2> yuGO:C#ঢ়GXhd|@4|)Q<: 42> yuGO:G uaSΣ}Ȏ<ٶM-8#`tdaZGBIC4FnN[|4d]VɶYiP#5Jm4C۟ qC,N+nѣ#/q(G'*B2<.meKl6 bdg\I`wc&[Z..$e!kΤdj={(2mq5mWmd!Yt~#7:]VҦG;d|@4|)Q<: 42> yuGO:G uaSΣ}C#ঢ়GXhd|@4|)Q<: 42> yuGO:G uaSΣ}C#ঢ়GXhd|@4|)Q<: 42> yuGO:G uaSΣ}C#ঢ়GXhd|@4|)Q<: 42> yuGO:G u`堬J8ɏgx_$_7{ͥEǾ?@gx_嬬>%h+!DN6cY<|OQZzVtmNh+粒erիVğWDi=(EE PKʌ5 -Pictures/10000000000002A7000001E7D4D9994D.jpgJFIFHHxwdumpC    $.' ",#(7),01444'9=82<.342C  2!!22222222222222222222222222222222222222222222222222"d  !1Q"ATV23RUau#46Sqr$578BstbdecC%DHEv&Q!1A2a ?i9o#\q͉,-LxSq9*Ou;߰->,~ͺv;|mLX6\OJ=g+mvMgt^cgoMp1nh`=ײ^NHCQ3$Ol#-޽i֎^ z5/Jtz_kѧZ:[{/5Խ(":|AFhn ףRGKweuײ^K҂#-޽i֎^ z5/Jtz_kѧZ:[{/5Խ(":|AFhn ףRGKweuײ^K҂#-޽i֎^ z5/Jtz_kѧZ:[{/5Խ(":|AFhn ףRGKweuײ^K҂#-޽i֎^ z5/Jtz_kѧZ:[{/5Խ(":|AFhn ףRGKweuײ^yV_QL7P{8A=dŪ6*! R[9 k:OA+֎^ z4GKwe%nTyR;|x0BEQYB8l栐GKweuײ^Fi\=ܶܜhvjنYĿvLq}GϚ tz_kѧZ:[{/5+Xe))Rc¾HqOćnquAlA-޽i֎^ z5n %lG8tW]KOo5L퐔HxgZ:[{/5ׅi}$ڴ݉./; 0YXZ]^%q֥h +I6~幯KaMSr H!=GKweuײ^kX7 6)Pr[*HkGZ:[{/5ӭ-޽j^hn ףNtz_kѩzPDuײ^:|AFA֎^ z4GKweGZ:[{/5ӭ-޽j^hn ףNtz_kѩzPDuײ^:|AFA֎^ z4GKweGZ:[{/5ӭ-޽j^hn ףNtz_kѩzPDuײ^:|AFA֎^ z4GKweGZ:[{/5ӭ-޽j^hn ףNtz_kѩzPDuײ^zkEiѶ9bBr@<:Jע+T&䀣ϏlvA&~Yc}bǷ͜^֥m/.Rn1eBA;Y=fwI-jMsvA5dVe/ =aucA 瞶Zpc Rm=KkfІANh!ӣ4SOXZm9]98cCiam OS&J~Haԭp(zձڤ[$= ;_ ;\ |]-lv 4UNRT@JF@Bi[?Tž2t[RN-m$pݧ;U?QQ2l[|Uʛ%э^p! <\mi)dse}M̏z =hD'*u!*st̄,d`|[fJTmԥjmE I8RN;`M2=|*zJhSQGOG&j[ RͽcHF6zZddInQ*d^ve+pzZ-NXul0AyjϹSQGOGA $2d0yPSACh$ F=d{Ty鹑SQ篴GOG>zJhSQGOGҚ>nd{Ty鹑SQ篴>znd{Ty)yۭ4t~lז`aZGBH#YMڛip!~?5ka,2P05Ѭթ53n{ZePbZ7`5Jh%HF2=ڇ-m `9-归~kfчv ڂ9ׅČ-{jI-UxuJhU2GhfJ+meJ![hsώ<+-)]Զycs*l(>zJhSQGOGҚ>nd{Ty鹑SQ篴>znd{Ty)GOG>zJhSQGOGҚ>nd{Ty鹑SQ篴>znd{Ty)GOG>zJhSQGOGҚ>nd{Ty鹑SQ篴>znd{Ty)GOG>zJhSQGOGҚ>nd{Ty鹑SQ篴>z%hBFsgJhl/^w3;[g=<ٝ_j)kZscJha k;T4sdHQz{wmN0V)B28pI_n^&t=XJv:Ua%.yKX$dd|Yngyc*/"ݭU[q.4K$ 82 GQS-Xٓ63sܺKIS[U-g;H בulݹlu8om emI-q{8#|9X$jխm6(B>A*SN8A9<9Zkz*jl"PY=wN#|pD,&,F쇋Ul) s$0OlKBԶč(niiC>vW5KיC cbx㚠zrP\!rˬEmM#i% #Q'Y#XZj zr3Ȭ,ˏvJ@IC#'Y~ݾov:PmHP# OlH-+ݬͲ􋻎95eG~~ZSF-~~ZSF-~~ZSF-~~ZSF-~~ZSF-~~ZSF-~~ZSF-~~ZSF-~~ZSF-~~ZSF-~~ZSF-~~ZSF-*`aJIֳҚ5(o?ֲ5BVY+3(_wRwR`"qm.ZN+RF8vrGr1~m?Xs߲MN-HlP{UUۏۏhޣ yGU}6q}m:}6q}mZ7}^zoQ~{͸_[N{͸_[V>W}=_n>7ӫn>7գz7`oAWۏۏ#.Nl ˔yre$Q%$ `tR}=_n>7ӫn>7գz7`oAWۏۏhޣ yGU}6q}m:}6q}mZ7}^zoQ~{͸_[N{͸_[V>W}=_n>7ӫn>7գz7`oAWۏ7RX%PuX'HpWoKJz`3_OrI4at5CۦH^dY.3$4bBiO¸8"vlh)V}?9UQj`MaRD#$N8$vyV:M"}7p޾ĿXqͤ'`8ճp"|/=^.$P옓%zKK9q;}ݑvѲUtշp"|/=M"zލ.Am-Ս,=\1k˸_K~>zS&ݼ*qDaiyØ=Ҳ߄O1R߄OJ˸_K~>zn߄O+.}-D_K~>z TjEu8^ R󀤨gEln߄OJ˸_K~>zn߄OJ˸_K~>z}I[KSl ,)gG'*t}-D T}-D1R߄OJ˸_K~>zn߄O+.}-D_K~>z T}-D1R߄OJMŻyuT+}-!IJ<Ê81}-DT}-D T}-D1R d|njm.6š`)*L7H vh6iZOe$݌dEl)JP)JP)JP)JP)JP)JP+3 fc_5X8̸o𶖤74۠drxt@MW[c٥i}#>M$Uq Cmґ!:'pe*NNP?n 9lo&jtZ"3A%);IJryU5]ElIe)[mMi*'dlFk[er<ħvyBˑA-}!II9+MMuE0{j*RTI uͦ]fav2:*QRWvF@$h!seCvXRSCh+PN2#A\+ղ, #j( W˧=dC;t'/UvJڦreA\:M5=.)^#MޕDv_mLoiv YmNp+I{֛Nif ޢ"4; .;֙γ)Q&)w#uB,F*P)JPVȰ4K2fIf4tJBs12OāՅRL%KCeCiI:ke>ͰvPe$TyF,e`i*<2Till7d2C6ڞ^iR+V 8oENOv&ugo{6R]HA'VSsE]D'JT 6QdsnjP)*OT;֙γ)Q&)w#uB,F*z٥?I IK|F;&|V[R*Dĝ=#A2C@&!jb T~#X)J)JHѾDzzCe\1zY޻!)Nddm pz*9_4o$RQ֨4쁫'l!Ki?Us%D}Tt8N$5!K,-*Dp5"z1u)ITtHBI`浴MK,%)MG$R@' xUbm];mAd)Q)j9#A+ uvTSFuύgkbuSJCM_viBR'% *%dd9UmIf' ]eF,G[IݧppH7:lVVd4d#BB^0h'h7YNP  p 6O=A:v2Ke- 6СvU%7k6TGxj7^Bvv~thL^@K 8q8ώ~jn|PX3 fc_5 庯6Jj+H 9lo&\59/kq*AYt67WXRT#Êu{xq] JmHRmAIPp"23rc<:iAIZO1p#+u{xqN|V+V7d2šK6ڞ^V3p @4=[8_G+w+ZM"D!C?KF6Td #Êu{xqW7\'1* (&;nNH'*$`WY:j;o%N@T{u1tS=[8_G+w)LW7HnWH0s~|V)tS7HnWH0s~|V)tS7Hn+֥QXLSlBDI?k$}uY՟Z[U&1![wwm+aAJ†yn5ff3)JP!j* '8cUquqAAT8=M6͢h0Q:?v_=sw–'YNw–'YSGHmhl47-%@$G$׸ТKM㜞ٮi6R)6R)CZϸl89n0Pv8rh"I=S: [}ekcSuU7#r)`!KB >[1?:{݆EyEnPmqR얩2C{)H\m]Sm]SGJf >71k NNvQ{Q2h1";1[r1{ю;Uz [}e: [}e4tFn 5ldBXHp*kzo\ڻKwϬ\ڻKwϬJsj-O>sj-O>-F;2ttv2ۭjq!C)|`TLfL?gk/qV $[cm]SGFiD{mkj'-mL2ڶBDy,$,mmc9ˆzkͫyUuJLW|)nySHc+z +mm7E. FZ\wiv֜l\8Ͳ:+bo\ڻKwϬ\ڻKwϬf#o7 e$'iGG֪vp,%QCG9F;\ڻKwϬ\ڻKwϬqr hN06c94xda8m9@'d5W|)nyW|)nyښ!"b!,$8XfQVuͫy6;vvMrެjnnV%1@)hYc'\ڻKwϬXَd,2IR rv$&\ڻKwϬ\ڻKwϬԛwTz;1{lg q]qw:i; *9QP $skuͫyc bS|uW|mKO V (E1f?U)l`~4"~4ɱdԳU2Kh: @jH`iϓc~5nQ RKg+ MOT옷l0oZ}m[ᕧ+h ;;'*WY4؛hgOŒALB\c(NGˉ3&m'r}$T)Yl'N&DUh%]!̈́'TTfʼnd]@zhKbIL IRs\F8jaRuE,"K[vC!!r];=el"1DE[I!$a*FGb@#532$)!O+lI|ۮ*H"ݥFR#+eL H?Gf-7Q5E)lik-4 k$(R B9&Mgm0a*4t6W@cgLc1dPBҼmq fX{$mRm⁵%Fz8Uʣ.:vwSJ٭Tv[2b́6f,viXi BR(2RRRRRRUgV9ioWUgV9ioW m>DIw smQ+g&V"+^|}-{C'b2CD񅲠#eĝF設7)%e%2dhEKRT2̓mv9lqJ\KjJ#꜆%ѧ7EFܦ[PRad8){7aR$#fK.=ٕa͝q# T*+}-DvmPTAX q13G2RVe$XȗvdqHt)}9^a7G(6s +NNr'Ad~* c>APq)QN@QHp:v Jǵ<8֔9*:<Vˉp fM eR!)'>AJRJRN!PCB A'8PRo|ʀI*9>p_,aD[JrBT\e!R٠$lV[mň;#[X9Q;|c81#ܜKnS JG>Qf,8qHjb-\!TRLϳ 9#=Pnl-8 8'A>$&퐥yn!a$HGsB%A&J4i lv̩#EiE;6`F6OkY^X}fBBR((sp8 ۠qG KLܗ Rr@: <㊋jJ8Xb;%Մ! yT8Ԅ۴'pu 5)l@ 08 Zq%Ċ ,m*“tPcj?&z9YbK[TȤF3Zb{SpdR21'Z#Eio: BA$q<l5ɺ_~]Jc/.ߥV ~o"H }´P;Mv[g*ž|쑮w;TQ|v*4l5Ǻ_~]Jc/.ߥ]톸X˷S}Ew]llR؆8*)BJ gVk6@v\}Ew>;U6Ҙ\YMZVyPQA;!X+RSB;o1JCRimNVsv\}Ew>;UiNku~:_~]JnXq݅a%[(R9횊Bշ d5Òc:̴%+ BT톹X˷S}EwҝQ|v*u~vSc/.ߥNߗoҮzԬ{ȖL 6JqvP j];ʆ3>~T"!*ܵ%*NTvN;au>;TQ|v*5:#Ԙ#+Ce ZJpNƥb<خ#m.̤(dv\}Ew>;UѬڕo߂-m.8Q8ZWvEan-r- 0Aep{DӶc/.ߥNߗoҫZ¹ɂAkR`y I)IWc *#i)*VxbmaH.:⹒;DNku~:_~]J38aeR99 mXΔuElRʂ Su~:_~]J4JMM ~) KݧqS;EY0;TQ|v*j X:WPR£RPOV>;aitprD軦@V*8k ?cv]*~~\1O򘱽'@?5=HVW}?TV*GU RS]r[s-H*NvcҳgdB l'd)J%8_fLЄxJvKMӅCז-6%IJBP2xvHO:ܵ %6eIFqjggw)&Ce[ 8<OVi e[Ii#gg<>8W2k[alm!Jjh=Ҕ\ϖ sY7[>X(,u|Ն1UV&viMW[c٥i}#>M$.\sRP ַ>hO~4ɱd.}}UߨsqرHsr4nN}ozwj8ՙ۲}RƐ^SO0ot?6ΦQNx,!$$8)HepF (Q' 5^>Ϳt*&B"b K!G+Np{%lߟK{FHe*sĞnnqgt?6ΙE]˫٤ceiIP ^$q#Y19Q'a h(uaX:} ?LRt?6ΙMsWA?6ΟC)}J?CaX:e5ϩ]aX:} ?L+ ?Oco>tco0~m2շc} ?Z24w[ڏMKn[;f$IEt53ow Lo&>ѳP:,zB䈒$+S ,Gl$zGZSVڅ(m+ (AJ1"4Y h(?&C:O%@GoʎQ$[\ݹ5rrUe; S).6+榵IN{Q)Z 'e0ˡjVJro(965z.IInH(B0I$c +r{Mh?5 $}) 9$Jg7MLZpČNSvpMKjiȉty%[#dѝ^5 ]nuL(*9 jYYIrjHR=Lqm%#!'e)h'gJ'SNDMK.%d9! B'iI5v((Yd]5߸t2;M4l/ufgPEv~;w`76PEv~;w`76՚AESXmL)݃THڂVjRF*~~\1O讇u?v?AyݧV9}oC?5=P0MOTPs!8{ImZR9Ճ[w[Ԕ-a<^7oZE_[wSuw|$]ViS~|~mH>Mۼ}uZ7oMiPUy u7WwnAh߯57oAWn]-U~|߯5_[w4.*nؔ̒텵(VV;8M$7ԭ}J?Xs߲M[Y 69OEOѻJ_)S~Tlҵ쿊|_)Plҵ쿊|_)Plҵ쿊|_)Plҵ쿊|_)Plҵ쿊|_)Plҵ쿊|_)Plҵ쿊|_)Plҵ쿊|_)Plҵ쿊|_)PlgV9ioW_)UsSeKk"GABMC;mPR}Hp95&"l< (VYxzu֘Y/1^lIxx2{S3.;r#<:mAIZO1p">8Z##>HBdFPJe]7؉Ra0Q"P%+_d+x\v86SRQz)T)\y:𦘱8D)p:E%DFO0K3Ua @bi&OҐ 5(;$Ս=Z#.B̈́kX+}W6$݃NFl ! lvӴrWP|( 46lofpxAw)Ajqť )J8 ZͮlC8Y\=dVēDxmow3WO_u+950(9N (Gi })J)J 9w{l 1LD0% uDrI<0)>lE!y[- М0v%[(@rgؑs@@>ØϾZ-o4#$HCj_s^gCX켉ziiC{)8BI9G+VewFVPAry`"Jv90V6<3k/lE JItȚ|m#;R{B4mmk8n6j|!kqŨ+NP4׻LtsԄ)c)#筙RrI+T-rLw-:S$K./'ya)Šsט}28ېzQۡ:> ,IqnT9 HeahPp5E{0$b31qK}IK<7-P8 *%!a?mҡYc[Ÿ[H_UwK-/6 + lvӴrWh:D{-`9dF\%g ''TR5@E{03RRm~](;Vid"O[/m AbzuR]M'nZ? IMe$Aj%mд2r2?=&7m,[BӐI U%tx(?(1JݣqTeI⛄,~%+vƂRnvk?PZol5-9S"zuR]M'nZ?Ph~OݏkC?iR5!C/WG?Ok+>*:sbږ)Ä(| }?TTmZRrA4qG=8qH[q=VsA1.y>^Yyl[mqL%A*9%J*$xyɥiGd.+!-HIV2^;V:6[x´1`Z_\/]ʇ |vƳcO K p;XEdzU'j;帪 y25 t>ӱMo(N>*1dc <}{Ul6ʔV)8J90KRY/('iH`Le%ce(2~k{I'<[-$ '$쎁V-M5nǖ((gee Np ffYqӑajF!;$ 'PS0!R F`s@ ,A:jYH9nMҚ6J.GN|I%%j; (q?Xs߲MM?CiL}<\NmৢZR%E<[88%)* Px$jz˴M>jJ.cs56yjjL-%D GqH @ d ܺ't'U vwsZ~4֥+ii+X @'$vQj;?59tNOUxehQ7g;Ig3^Lb \0^I8R_*bϚc%"n-3jf ?5g6kˌi !Am2GlY)TQUG _Dy(eCӧ k'++|1s --rq$$I'$[tRr(DϪRCOf1v =cfLVoca*qb2>jޥ| Hi)h8t-[S3㯓lk=>S6!j@#J ,֧nhm)L$ZtLrd?j)m6ֵ`22x>j7fTwciu6B<#Q7bT] NESpJ@8>!RRRi-ܺBFC#;= !J 98v `p%J$&EmPeEC N2O6Q˜GgPR[k` SpGEl҂&.l4(Ij#hp 5(4#X0>-CQЗ| ȫ]p1 ¢唖T`DNkn[6EDVYHCjJR`FAǍxrjv招k a2KxJyON\92~J6~kZQ<5LR R cbYaR-A9)@8-(1rXXXkA큶NJv>*rXXXkA큶NJv>*J \9%!{`m|dҔ7_ScEt;ԩ sP?czOꁇtjzZǚOZ ڵHR `q+[8_#Ы*ל?GS8_#Ы(+y*y:U= ҂ל?GS8_#Ы(+y*y:U= ҂ל?GT]-"ZAs/[m!)}D WzP+3 fc_5X9nMҚ6J.GN|II;9#-dvPGN|I}?TVhŷE1d%p<u JvRKBĤkj+D:EH/u>R-Lq$})J <@rIҊW> kI-WaL{@<ՇPgUJAxhzc$!#N6PXQsH8.gln&ԙw$Dm=zUsUƭ!VEĕabbr{m P' J >wtE@j T߰܇#W!Z *O}jǯ'?EyՈ1u-"} )N)‘!Ml#h.T}1C695qPG㟆FMZmr2˥ĩGg m0 fnū:[jBwRZ' g_iAISR)x$ 'Tx ִdf?Y[3J%IF;`c./XrtnéC$q>#h1t2DDibӥ{u9y^Bm]2* JVl>Էݓ>qN<)kYQ(I\UFf,t26Gi!8WH Rw|)/I1P1tV }Ϳjt53o5Ťn32O qA/J0@9ָCꅲT"rVlFOh+SG<]S ܑ(c+B_VWC Fq=^[\LR:@ (VRIm~::#ֶpIX  e g`:bޛhnJ)80NI8H%JUۦnR,z R ) ilG0ずdBrkHm'2v\V'oe I9Rzk~n:&B/T9GҒaÇbx~`hW)SVpq![) 첒4U\WRlЊDqgYi,Bx셠ђ+: T0rBE Y(J ps S4{{#q C#`+A![!#쌂+a,5Pn+qd*yIHHQBUg8[MK:E\K*Z}VUQ 穝wb[w/-Iݥ-LX@c*u d6U秓wy]q%˘co6{crx@KSacx3A:PA_h<:yTi †{`R+]=n$5AZad-.$oZQZR@iF*!"3\hg FF2HTMN"ӪrSSRae %- R 8MRRkviV{-HLX4BmH'֥2 6Թo1-F`;GӇP] m(g,- ;mRGp{`׹' 2ײm *;sVLn:٩nMngrn5! )GVF6NXRn;|.썼c-F12pEf2XSM'R^Fti.7";˟-rʒI); ǵ+6zPM|L waKTx$s nW$v SQI^ rsL.z7ۛB[ySv#oa=ACd0A<2M|aƸE:5pZw1ԠRJ@HQ<1AKҳ]tϿsĉe"\BC O}%GE: 8 Pw`d`WnmIp~-JTo8bRS!JT6·egZۆ٪qKӾiͲRvqRl9ҏ>7Sݶxe^pmЕ(QIpNT5V:\w+xm!IQ;p+%Dkp0 ΚrZfHV셅vN6HHIǀ|Wr`gyoIl2=퍬s] RwipKjrʝgoH' <{( TL 7*s$6KN) ,g'@ iJP*uMBuuMj;jt[@N,6B\޳Ǒ~wun8-6RTc;XH6xqkbr0c.Zڣ$Ӌ ]r imqL›utj3jt[@N,686v'Kx/n?eFvJgk 94zE-պ,HNIRa g<ƂmqL›utj3jt[@N,686v'0_[~+2d픕+s͞jBF*~~\1O讇u?v?AyݧV9}oC?5=P0MOTJRP)@)@)@)@)@k5|`庯6Jj+H 9lo&AQPGN|I}?TVhJR((1ȎĸGo0JVۉ JA <5+Nrjjq}\f2 * a 7<}#Z.odLTTxPI?joy?GN-2l0␤ANNN0s oy?GaxjT0˒y[$C36k9sh&K aQicem JAqiT}orl6F)D%)I Q{ʆg R¨߃O7樋]廛1D{@NBRJIT]iT-.s_2zJ=L|$R }Ϳjt53o4G_g\,Nʖe ^;v40]Շ@(܅"''ݡKA q*M]F:zJĹ#&R p-(X=RJvUxhAa)@@v“5hz+nN#0V;k )H!C$qY'%zKP{qMwJJ[kh UWVh:zwܼr o%0B m.-#e;D Nxs[fc̖PBC.-viIKymA9Jue[-!2Zy /HZYBFY V}Y$je[-NR0GfAo)@ NKMjwKsXeFfL;[ZFڸ -m'ڑAm)@)@VMg~.@`=64Y } Sz[; g'<{UzeAl‚.bږ;>欨c#Ro_.}Ejkn]3T-j$+H@<*hnX70 Z_;{I ?(7)U n}DBK7c־)y8m͝hhE)d[i\Okn1N6XmJ/rqPjͨҭ@ uZb^eKR("j nQpK"uQIJVI)Vxˤ'/b[l Bd^:%cJA栻RWII*Zy- `EGg+ R R:lR Ɖ -kqj p۫lB%H!@' JҨ(EwzFҠSNp:NiGC儺\$FPOcq­4)J uIΝmv0rLTa] U說29}ְlؠ'J I֕ە%n,8)h)$W5a9o\nkCm͉s/\DڒPRH@hyc-ҧ^h-V;(H$'5ޏ}*ZL7+c/1gN8PYm!D$@\F9~o"H }´P;Mv[g*ž|쑮w_Gc>:ϥAY̓|-m%ぴ풡#|v-S gN}DBUk$+iIJU kdmv89*T_ mR( $MI.UkJC;Rĩ!\\3pRbZ7(FLS $i*8A8#-b!2~pkL׶ &賩bط6 F@BvTE8'ڧE^-Z[Fal%Cme)B)V0ثKٵ+7---b[iF\+ RpqsگvB[tC*Zp)@ae$ol%3F;zXCREe m- m'F3PWTجt>ImqQv{;8nu+(DƸ& ǐԒv8QPR6¥o,yyna+)NII5qIFjÏd$p/kqr:IէP+5%MnuS{K%YWdxbZia0w2)V Aj6`gJaqf:"6[HIj)XmAG`N*4goWَ)l8n`8e(*9 jYj=;%ͬ])߳Pč{h:bu2LE\7bDZAZNN{xOW T4u([s"R%K%[Iqƺ/_z?Y,_z?Yb w8ḿ-q\8BH8PpA5W5^CWPUQVT@)Sd('\jUAESXmL)݃THڂAPj~N eD<N҂}?6Gj X:WPR£RPO^5YmMa0#vSx #k;j 5YmMa0#vSx #k;j A[}:~6XTbJTY;J jڪΠ"6æwUq*omsgmAxӫ5TP,l5*R\v׀8A[nR5&t&wQS1!mGv's~7{"a q9:4t$eRO7c=M&uv-5eeyeKyK[2D짡#9&P)JP)JP)JP)JP)JP)JP+3 fc_5X9nMҚ6J.GN|I}?TT.ӟ&jitp1R)J)J)J)J*:;iK/qFե 8GQC;v'B]NVJJFqĤ$v%P2\8n/riN/mm-o6r?H;r. * |VR>!JI###9A-JRJRRu Aˎ2O46|S6@]6K0f8B,)Hubgʞo鄄D~: nU}}S&ԇVe"K}wVe+{[T~|IW+>M˙0{C?6z3?T o_:/gGU?FmHubgu=κICaSKIp[ V9Q;Yfbc"[- K()%d8\Ǵ{U8Z6cܮ5S! 8.m׌cvl8Vv yV`5rRg}ZЀH;YsF躳*#w}P}!jh $!)9sW ;>꨹&vAK%8Tv)Ȏ(suqUW6of,)@mR~Эs2\xm*VeKS.oB1;rG G:nU~uNmHuf_ʟ)g<sV&ݮrw`MG\8@ɦDT3.ԇVfp"C# N\9txs1eШ"Vs3g+aC' tQ;a ov߼z?lXTH8ˇ]ښL#Q9V)eA?Js=,j&;cdI$v@O4)Ԗ̝9Rqq|5xme*3Wy-Ҕ-؞c>"]bWӫ{Ѭ63y/z4+^k):+(`)Յ(RI 8 ;D+^j}0?W Rf@oK݀[p z6x*KEW="PA-`쫏&#nyQJ``88^-_"4 h2|G@r%ąAI%*p;U?JT R R R R R R jYV[l`~4MҴӟ&jitp ~4ɱd#EfT*)JRJRJRJRfZK|ϊT>GXe(eԭKc`O½D45ߘ#)֦ԥ%E6¯9bjR"Jq&nu") T³SS*yH[Q-u_lm)M)X >ؒqq=(4m)a01;7t>VpNH`pVl3v;S뾪cjzʔ[)x$#dlq])Aڹ*1ŦU!E ;$Pv@|5)[Su| Z:9䶏skZ:䶏s< /lxAOuz\<(=hN?ɭ>穯#GjJL;0RȜOkr}DŽ)OK;:NMNycۧ9Ee ue)xIº'/lxAN_=.@Cp@Jfx:"Y }tB9c6/lxAN_=.h^.@X~|+uh(9߸sD)[Su|GÞu}/oɽ/9hc r}DŽ]_'ƒ֎6y/w?uG&q9?}DŽ)OK-IKh0?9|_IZd'H3~?nn)[Su| ]Kߣ^b̒ųagbcR'Z?5ZJUYI<\F~8/lxAN_˯ϗYӓȤ>QwѨREұ>jJ+Mchg'koƦYM7ϸtj{fO_̟O W\@up{OxdxUltb۞hq-eo{Čp`j侷ųMN m))$NJMXdxUltv;uٛvL-!-7eܒTO1'S¯fO_J'S¯fO_J'S¯fO_J'S¯fO_J'S¯fO_J'S¯fO_J'S¯fO_J'S¯fO_YZ2<*]lF[{]HV]l`~4MҴӟ&jitp ~4ɱd#Ef(zKp\kR)Q9Zs=Ԑ4YR`J@!)PRpNRBUYi˭~oP(JZ}%l98=әlr +ny6ސ0yJ+Ce* I ʱLJ +V7\[jh% ^ʊ p;G$p #󎙙w! ]F:whPt%g%"a@zq@*] mOmc *N0O bJ!V%u5p^Y/% BGmDp'++1 /:V29ltW81"&6눏 ZPڗƔD ACdblIUQ0-,CA Scyطq:h.W9ZS./jf# }i@E)IQ$qVL@3ځxҸ56Wt\i'eZTA.)ck=|3֩5}r"_.ZЂS8BFA+Jsf 2KAyp8Pr8eC?Q-S%ՍTw v9vZ}8Ӟm1?)Jw`763h:}+hDQfB%")R$9xII< d-pYƶRߗ&OG <$+qBGh DP_mwN$#vRV9z+bҖ{NA[[m U2UQJr Jܼ)nS ]B$sҔ!Ϛc%"CP1tSnGAsMQIiIVސPT0mcx1Y$+h.!y]m l )olO W5 ʁl!a8ۉ Jd AA1^l/Ny mNBvXaN<`JQ@Yʒ;UU(uԱ9Fbh,~NgѪu5M1bqƈ#RuIZJ%G $` AvgtjL!$jQBv x٭"zF1#;"NJu,(H$n VAjw ;˷SطF1qҢ6JvHOozwbXeFw-!y|I*)0Msnػ.LA-TXՀ2pds H^ ML1.GH[r2~ju#܇'<XH?HW{$[%Ϸ'FgxAb^=nݱv]0[ëd &R}оmFS-&SbV X?Ki&R}R&R}uԱ9FԱ9Fbh,~Ngѫ(+bh,~NgѧX?KjJ X?Ki&R}R&R}uԱ9FԱ9Fbh,~Ngѫ(+bh,~NgѧX?KjJ X?Ki&R}RԫuF@ ZZIQJ8H<_~X^~jj~maICHXG}?TV*.cs56yB[4ujX>*P+BO>TfkiL}<Ո8AvWk$&>K8;)K./㔧FxqȒIWjAn;L}<ԓSvMb217< SiCbܩ,6I쒓P$t5,Zϩecd@;H1 4 >jm17< TUůpښzD )b; [ dONm+ܫtJPGnr'cs56y* L}S6!j@\u\u5K*bL@IvLF^0ʁ5"m5m_umʈ?ӖFkiFUl 옍ajZk=9k=4NRk=9k=4NRk=9k=4NRk=9k=4NRk=9k=4NRk=9k=4NRk=9k=4NRk=9k=4NRk=9k=4NRk=9k=4e{ߏ_S0I'<']W(3EM?VW}?TV*ȥjŘLe,V2ҒvQ$a ooF]%j`ӏ*#jG!69"9S{WA 8`-Fai 2B$ vK/q.:Y)@m=@gsvBkRS{r C};)I;n)!D'#?^D8yLv^XFk m6ԭ LR9Fj:ld\jD[ђ;ZJN1dgk9_\hn1r"ܔm(;$98T#HPD"0$',8Rt$}jYDa+my(s+*' p8׺ IWjUFՎ'~YdTݟX9(֗XV .T%0(hq9>F3K=d6륿*2sq38T+פ?P/I0g pC-ssֵKcB-[*A9PPMLϰYǒqA9'P<2O,kS~h '`2QNAR ⠥u9[K돫ިɷ^퍝csͺ#Pʒ_S[iAG"A\Fp#R9s 3VCTPm8 殕JPW|'U|VJ<1V?R[l`~4MҴӟ&jitp ~4ɱd#EfT*)JR-NkGb~m0W;'<*Jّ-,hjݵLZ-(lΠada@0A#i3d۳^mPܩ._H"ָ.)|QWe1҃ܭͻ̫},YKLWlRaJ6*nf{.ςy\BBhڛYQ p{UiA۟i%M.7}v{Zw&${\gf4C?%pm80j xRcjrEl˭HӮ; %C8XpҖ]mkTɝZB$ n (9O5tPhR`b4wo||(+kxክ_.WJ꫔ipy@W}(D)=vqW:PR*|K{}P{28PIGqo@HZAT7nu_Y[kӞPSӞPTz, 6`K9$ j:.͊!m) QzG0e*Dc:4:4!W]9lS+>slgMZQy 9EeV[}Rdj!\inIJHF9gN:\jm?\ <)i^Q}0%)9WB@c82MKi),Bv\ ld۠蕥luaXKKR*me #N>*r8ڟ4mS oa IH!I;99+t[aI~r !b3}Z16\OŝݛKL)ƞ}XaVA8  '$_b\^T.ևy@)''=O{)Fix- :8XC28gELHҸh~Jp`6@*U+V.M[gu\wvJp ᧥-0;|iR_*mt5 w|)/I6~ b{D5Gn"N7)N <5Zč:#ĎvZi OԨ(hKE{|yJh6XK@HCcƳtD8f۱zoiża[P$$ A<5])TRf zbT䅬)RVȋyn- rt4r҂KmO.l;܌=u).$BvFqx O]D)f# n-yTIPyHI=IH"@۠^aչ-+CNqRCKVH~gn*~5)3"3[5tNZIR~0h$PLyv#_[qTˍ,'yNq Z-zt6Lm Ȗ&@mj;*Rm~:<~:dIj7-ه%_VSČKHGT_Ӌv\r:RBvpIP)'z/9.WxN]T;xn ?zr?zf[ty[ty06 ZQN=N{$슻jg!&"*ͭ RKo;] tV-o-o窍V-o-o٨k!>`U+a;!]6T-o)Υԕ91 OHEKH"MG~&OuF#M/m#iH ?zAJ~:<~:j Az "xRSkr=X{Wnb<;sse-Fq!# 8<樴R[-)en(JvR3³Ry)n4gRQ ,r2tbu U ueȖV$@@հw;v($\JwIc$ }~gn*C;NyAQNC;NyAQ[lR Ɖ -kqj pM$>+4b)QJR [cl=5imr^Ke*y{*mlRAgժ4M=6UuC 6%’1#ӨDUmT޽i3c)7QH#8 J+z2ۥI+i (m'2NCɍɯwO|5Te T7c) p(9m#olՑZ'UDo20NZJ3Nq5ԍND]zB]L{jPbviaM8_l tpc[q>NV($ئ]fnzj}wLymO\5Re/Rl )hֹL\M*A(PPrIp WJ,&#G}Ν‚= 3=!/ Mz*LaźH8E[)AgZ&?pE"_"dq-B[á<*I9R p YjfÈ&y Km%hH 㛟 9Jwnpw\oSqi5 4WQ"K$J;1Ib;j>R_*mt4)JT 컔E-m2`{_''VjPGڬ4p"$o93)olpj>Npծݮ0c2)}ZVFF!ijOXIZ qkRee*NU>rÍxi,wCٴƀ4 $svo+zzivJe.wFV0׽W#PB9b#5SWHm 8%'&qȸDSuNҤ(%X089PKETZ&nKv!@!*19ʇ ~:<~:M$>*H`iϓc~54GJTR5P*Ĕ|䶂rZn%YOky[SPVnB*#֡8jޓQq9dEll~ kKTEmͱِu”A*ppOTX\x sd6㍣dJ By6fk+mL/6WdGm[J\dU+(mO+UԍND]zB]L{jPbviaM8_l G\ٺ%ĥPX6xe'i1.bډlH2!-s,r #R;ѭ.4]&> $ԻھELu2 z*RrN8P_)\|¶Jo*C"Rw'`-%'8YnjF" Ƚ!.=(i1w;jw/rRvxqIꝩU1=pweJ-J6r8¢ts[Z1rgo6AA%ۃk p0AҪDěl9 Fn[FN +r8JXL F ;mo{#;X#1U jGovLuNx(&N.\ "g;YcgsRW+{UҔC'ddNI j9+{߮ʷ[R$m1v;9{XSz/,SB-jFtn`@ Gj]wz"$3 'm!P䔫RA=;Uk}+Se U ;V!VC*inE+e@(8t4wNZ{*}e{ӽ֏>{*}e{ӽ֏>{*}e{ӽ֏>{*}e{ӽ֏>{*}e{ӽ֏>{*}e{)![)= vGYMӽ֏>7}^zoQFGYMӽ֏>7}^zoQ{*}e7NZW}=GYMӽ֏> Se)@ecJQсKڥ.]i-]a-&;`oɴ%wNZ?I]>?%7NZ?I]>?%7NZ?I]>?%7NZ?I]>?%7NZ?I]>?%7NZNV($إ((((!Ϛc%"CP1tSnOjzxjw7+\x lcP|C=f}LX/%OԄ)-2J8UQrnG[q3O% BU{`}QyjŦ$X%B}>0\RR=*: K{7|Y2pYy+-<>zj8OqRU-d@ޤ{8/_np.7.嵫3Y C%ET0Bp6K԰YKIP߶6󏋵SUA^ʇ=Ot3$?hOr C2@c 'W q,ح˯RkOK8ꏖVM{)4*Vרڥn7̍FxR 󌂴b I][2\Puy< 4H S;$`3LV@/- ;Kqq[_26ge<(e')Q9 Ėa"\A%)%A*9$+O5U4ŕ[SKVw')qJҐT(93gkf2Fg 2*9()f=wa|q)_Vⰷ1HAURT|Ǧ]|60e)W$gWl fz!PKSRRvRR̡gRԥ* Ov~WEZu_~U'B*Ӫj+JRRRRRRRRRRRUrѤƿcIWh4k۷J{.DןnM 9lo&AQPGN|I}?TVhJR((((((((!Ϛc%"CP1tSnc /E~N[Ur6ѱ|·>_PPLl+SNN9@h_;ӧЋB< /lxAU:}/[|>c r}DŽQh_;ӧЋB< /lxAU:}/k7jO~UUoJ+gɣꑼZ'*$$}i)JJRJRJRJRJRJRJRJRJRJRUFՎ'5]]??n)^~ݺV7~4ɱd#EB 9lo&ο]5GwFD ̀q88D*yiyj*Ju~mu~m *yiyh,tW|n/W|n/ҫ_q񸾶_q񸾶Ju~mu~m *yiyh,tW|n/W|n/ҫ_q񸾶_q񸾶Zo7Kn$^YiJ C#9a|2rǞm9 iG.8\R9|yiyjgz7 Ӯ <$?_Z=_q񸾶_q񸾶7ލCgz7 ֏W|n/W|n/&wx}:ލCyiyi{ <$?_N&wx}h~mu~mhgz7 Ӯ <$?_Z=_q񸾶_q񸾶7ލCgz7 ֏W|n/W|n/&wx}:ލCyiyi{ <$?_N&wx}h~mu~mhgz7 Ӯ <$?_Z=_q񸾶_q񸾶7ލCgz7 ֏W|n/W|n/&wx}:ލCyiyi{ <$?_N&wx}h~mu~mhgz7 Ӯ <$?_Z=_q񸾶_q񸾶7ލCgz7 ֏W|n/W|n/&wx}:ލCyiyi{ <$?_N&wx}h~mu~mhgz7 Ӯ <$?_Z=_q񸾶_q񸾶7ލCgz7 ֏W|n/W|n/&wx}cϽˊ 2Ȉ㮺2\@%J%[')ۏۏcW:}6q}m:}6q}mAcW:}6q}m:}6q}m\ۏۏ:Usn>7ӫn>7XUί{͸_[N{͸_[AcW:}6q}m:}6q}m\ۏۏ:Usn>7ӫn>7XUί{͸_[N{͸_[AcW:}6q}m:}6q}m\ۏۏ:_4uv_q񸾶Q؞{Nˁ]uפ00,IPy]??n)^~ݺVц߫B @UArЕ-%; #+ JF3@]?sG Rty^\6<Ec?4Чw)J}}BEc?4Х(Ec?4Чw)Jw)]?sG R]?sG }}B}}BEc?4Х(Ec?4Чw)Jw)]?sG R]?sG }}B}}BEc?4Х(Ec?4Чw)Jw)]?sG R]?sG }}B}}BEc?4Х(Ec?4Чw)Jw)]?sG R]?sG }}B}}BEc?4Х(Ec?4Чw)Jw)]?sG R]?sG }}B}}BEc?4Х(Ec?4Чw)Jw)]?sG R]?sG }}B}}BEc?4Х(Ec?4Чw)Jw)]?sG R]?sG }}B}}BEc?4Х(Ec?4Чw)Jw)]?sG R]?sG }}B}}BEc?4Х(Ec?4Чw)Jw)]?sG R]?sG }}B}}BEc?4Х(Ec?4Чw)Jw)]?sG R]?sG }}B٬" z}#x!#!@I8A$)J^.}>q٠FֶOcv1VJFIFHHxwdumpC    $.' ",#(7),01444'9=82<.342C  2!!22222222222222222222222222222222222222222222222222"^ !1V"AQSU26RTau#37qrt$5Bb4s8%CdEc"!Q1A2 ?hƎ[vkΚWF*a=Pw[o9.&t~F{ߒ`mgXmk=gk39M.7cxqź!q|GE/eSg ~I'ڞ]* HEAU9#]Un*9#]T䎋t^uU 䎋t^uS:-{/EV肣:-{/ENHEAU[ HEAU9#]Un*9#]T䎋t^uU 䎋t^uS:-{/EV肣:-{/ENHEAU[ HEAU9#]Un*9#]T䎋t^uU 䎋t^uS:-{/EV肣:-{/ENHEAU[ HEAU9#]Un*9#]T䎋t^uU 䎋t^uS:-{/EV肣:-{/ENHEAU|uU[G$o''J{iq=͌Fln\̂ג:-{/ENHEAUsCIgfjm#_.qo~f>Tkj$fwuI--vxk] kvwk 轗"ꯇh2FF6GQ]| ]$Y*cH#{48ڻ/f]A-7q,9#]T䎋t^uTk[4rLC'&1wu 䎋t^uS:-{/EV肣:-{/ENHEAU[ HEAU9#]Un*9#]T䎋t^uU 䎋t^uS:-{/EV肣:-{/ENHEAU[ HEAU9#]Un*9#]T䎋t^uU 䎋t^uS:-{/EV肣:-{/ENHEAU[ HEAU9#]Un*9#]T䎋t^uU 䎋t^uS:-{/EV肣:-{/ENHEAU[ HEAU}EZ5;5ۣ&7$m2UGi{lQ=eg!4s5ƺ9m&,zں_9kVڋ̵.mƒa$9WoKQˢz9mZ5;_oc3F f=a7cA Kp5A{c+Z5u1q7dA2&w- vf>LtzmKI2 E`, cVrDD#9#Eڪ-mRFÎ650{Ck齢ߢoTP jjWTnk tl v`] )?{A"뎮XC3JB:`|m-Ųk4nٷUw}v},%R[}+jaf5@7'$qiby?ْ=1MGog@+Qhj6M01ڣs2jZA4rtn,ppiÚqGhƣ޷ƳҹE-\lj=|k=)e,6824wIPﶊ=5ֆhXGP5qA $풭~ƣ޷ƳқzJqƣ޷ƳҀrKlj=|k=)Q,qƣ޷Ƴҫ+4o}-mMPk5[ddJ[+*a~u%@;n;[Gvƣ޷ƳқzJuQ sF!plk,$hBZ55{zW(655{zW*?T8<YvR2C09V~ƣ޷ƳқzJwԺM: IsF~">55{zW(655{zW(8{zScQ[Y\XcQ[YMGogrcGog655%)$3DvF6W4&A L|mWr%ڣ]tt̓ߴϕG 1FHv"X8$EGM0~|"C Xics=0Gʔcj'T3Rh!JGL!'&<ǪOʒcvh9߸#S9~LdwNIDָbu3fev'%тve˵v[[fRӶWs8|GogrcGog655%655{zW(8{zScQ[Y\XcQ[YMGogrcGog655%655{zW(8{zScQ[Y\XcQ[YMGogrcGog655%655{zW(8{zScQ[Y\XcQ[YMGogrcGog655%655{zW(8{zQհa +K Zq/Fu|[jξ,5kĸtuo{s$c php$\wۉ,y}S+/U4õdnT8sHݻv"O>-(CϽ_bӱ!Wk(xLX$dd|YYgU/1}ݭkFcW-06B裉䖜89Ahޤo Mm4eu=)a+pY<˭u˲lE[N!뱻iHf-ko'lERPKʌ5 -Pictures/10000000000002A7000001E7A08E42E5.jpgJFIFHHxwdumpC    $.' ",#(7),01444'9=82<.342C  2!!22222222222222222222222222222222222222222222222222"] !1AV"QSU26aeq#37Trtu$BR4b&58s%CE'Q!1A"aB ?*:zۄQUȷQGE ;5:xvjsb?e!"Ə![hIo٪\eҳ'!>.:Az,i-٪@ϻw%Yij` b:ݾM16B;+V5]ڇY>vvtw݃oJַsiO,ֲr\,tkp ig"wr濷%?Ƿ? (sp׏`ðF?}ݿ'g\8zmAhijRg'Ŵj\k,Űj~Ӓ1jSy@w8&πm% q`cg6dpʽ{c7 xJn6Yxwۼ!ϱNNjQŭ2ӵ;u8hZܗI3O MpY7 Pm6V7o{<&p5\ 4QwkNI30wOy\v|v)fvY''&πmMPzɸZ[×-Wѷnxӭ!!RMn@m l`/&πl?5Z->ŇMnpʽe-ghZ`OxӰq Pm7;xCv~>X_ߔԐط}߾v5CR&qp5XA&j{Yo_\x ^GQ-rWI550w2ST8e.o{gڵߛ2k^wNjQ->ſOq,lKMt&R&jvV;|[8խ4kv,z;\[tGcW?J)upq=_|;O{՞_|ݿcԓ|dّ~5CR.kk^j;G-G$> 0y}߿p]e-|\ jsxCWqCұj?n+ 3Y7 _ڳ57 _{xXg MGuCr.j=g)ZjsxCZw655Cr.j=Ck=!V-I?&p]e-~!Sp*wGYT\r'j`eLS}Ptܘif 2v3u?4x} M{_C諅J,7+[)iwY `8F-ҏFoC3P6jMJV/bU{_Ck=!U4> dsKPa]H_)k(nWKuW8#sw@5KqkrIU•)ڿZk}p*=q3QCa#KN;Mn>Cǟd6/Y!Ro7KQgraVG3ˇlGnYmڦ~?Ede K;,6C7YnٟuY,L[5y*a:zi]q<}MCo e<᭷\ 5ʴ[ M{^?PoV]+Zml7 5xvpF It󧟥/JeYFYw _YB57 _{xXu5T8r.j=}-|\ jsxCV=I26mϿjrST8g.>-|\j/v|߿X$ϻ|~̎AO)3Yw _Z!Spr-X}X'aMI26`!tj M֬7;>Mq>kX]\9+%5Cr&qp=Jkb{֨qrYw Y,ּ;7xC,tm6EU|Zؗ~~&7ż2Y7Zhijv}Npq=rŰjz6-Gjgou狓 +܁@Ł^O6dpʽdc7 < Mv5 c`Ǩ֍\~E?&K hVKME 756g vۜ``9 [=V}i7?)_YGHҩa|4ulsm9"n#aPu._IyU/1mVV_U[S 5;1,18'`@qk5os]<tިu:7~JdkWKypca}{+Qhj7&j_QǀN8 ܆Mkpr#DJҿ5;ʞ؍I}{GwP޷jvt)L\í #GjujF1nq[wݍrm{غdNYlsp;*{Mu9t1kpH; $9IOiZ=&q/} p#G*@og?ND~[wP:ݪt;S =nc Iw7oN#>g.a`rTm}mlN4wtǎ]=zb=n_ʜ7؍IvoIW%K[{T3MV=l IYKpeUL54Τ`h#'4ܿ=nک0N[Zm>8!Ybh$. p%@p8 !>g.iiz'N/~Õ[8rȟ30nuUkruzݩI־T7YwAa!Lj``=M1s]nUc6?N/VOttC B*!5.Fgk8k@qx$p4:ܿdʭ_6n_jt]=zbn_ݩ'M'[.*']=zbn_ڬ3=ru~V/ttOiu~W'[ny]']=zbn_U=ruWz>ggHx|V=㭥kD Ě;STowgJw21sISS=,;?=]^E,-Yev䖾qf~.SOՍQsJ&0WkDNw)=&zim[ ub pr;SQ06atO>SU+@x,uұ"pWeґRjx.jKuww]`twFG+Jɪ.`4sHjG\TT!AJ6s;-WVM6#.tscn֑?j6tSOq+M͍ٓf`?~?S#Ē0\)~۷+"|cT\[O"u|, \mqXܿsrzZ~_?F[ny]'N/ܪ.SLí={=ruNy?l9tOiu~rI듭;Uw']=zb=n_rgM/n_jt]=zbn_ݩInV;oe>g.ao;/n_?V!6r'4:ۿcn{6iiv8IOiu~:ܪi 6'[VOtǎ]=zb=n_ʝoM}1VݮIõtOiuzݩ0?R` `rm}mb30sHO[Zv3?}9 7؍IwߏtOiuڬI~SsH2Hڝlaک0ьll秢|CL\íQ`ۃWIOɱ:NU8tx>_.b4rʐ}qoh4jwN)/<+MExasce<7*l:ǨSoĿ"R?M[|_I|ӎQ snkws~6c,wK!L 㭝2ICom Zk6Al d)ܾ;RKymxbR*Wy[粥vp٤d lلZIۿ%ZR·_bӱ!Wk(wX$dd{ U/1{[xVƮZ`$lG-91psѵf"pJ+LMt[ÞfGs..Bu4B#cwi3X56mY"d՗ZDʸYHv }JYi_ENy\ icF0I;I*5-S.LEPY 0U88Fdp2ֵO(aζawal+Z]Y7ޯ7޿ƣ=nM]sŦ{ρݹ}Pj_S}E*jlbmʪ<8HcPqXskK[cZbҺHᵥ8ZrӇ0#iKmzoᰎ/.qlWnT:BeaޭeڭՔ8Cnڬv[U|emC4 {rFqGYmNfm&eU\,'y)rX˪ ŝ wlܞAųUuj+MY05ijkLAvsaqin;Opsmʚ=KugBGqpV:{Mj׾8ֹ9i y H%dSUKU#skC&6qqrr:"QQvQKjΡuhU{(ds5Gܕ3pMpmG @$(sbpnKsvxl_Yt29nz\]Mu<&Hեh# 8/IE)p^S> 8/IE)p^S> 8/IE)p^S5%LVYgx a8/IE)—o?G?:hE?XN ~t|~px<`AzOΚ/O4_")}G0&Dp^S'Mȧ _w ;a8/IE)—o?G?:hE?XN ~t|~px<`AzOΚ/O4_")}G0&Dp^S'Mȧ _w ;a8/IE)—o?G?:hE?XN ~t|~42;_Ua8/IE)ŸDa8/IE)ŸDa8/IE)ŸDa8/IE)ŸDa8/IE)ŸDa8/IE)ŸDa8/IE)ŸDa8/IE)ŸDa8/IE)ŸDa8/IE)ŸDa8/IE)ŸDa8/IE)ŸDa8/IE)ŸDa8/IE)ŸDa8/IE)ŸDHo˳] Ɂxf;urqdQ}O>](_bQ<ʥ)"TVNqG3 Ij3   KkGMfxT7*&c nl.Yqv;_hQpduݚ|Aٍ6)t+GU!-8iocTLPY( | yVh )ls;25ٷdqs"rm*>w[gۜ͝k~[E}+!StY'q Ȓ-g`;s0s̛kJt5ED:iNH%ԑI#NSyru7LQV[ %r$q$rI&UZRfrʷVDD@\zie~WQO cŀQ>6qvsfjY+KɦCkݍJi"a-1q#qjŋAďTjd6`,he{ KØpxVhk@r)J,5oZ"u6wIwѨ+%%q[4Tɓ9ۑo_=U-1\ݦ:JZHEtrҺG49x;=ݼJ0v*::bAH0m1{8 )KdM=T :amSZ6wiQU\,'y)rX˪ ŝ wlܞA!Va"j&6҃$cv*KM5\1MwuH]ѰI!n2jxdm-6T {^lݠt7XK*EZ~_Cc:E:jXNVTWjXNN4??UWjXNN4??UWjXNN4??UWjXNN4??UWjXNN4??UWjXNN4??UWjXNN4??UWjXNX]򑢶0CdXe!*3S/XDDD@DDD@DDD@DDD@DDD@DDD@DDD@DDI?wvoDi'nȔ[GCU/1H XeRCUZK~P[r $RH\b=cX*4wiBym 9YI$a8' pRכ\<0*(y=w)i6eDhL6ɭM jj&3/-u;kZ@h, RZIm SUm̘K7r꒲}mhQt Fդ},.q&k4v$$r,VQG)}H2N i-k 9قZacFYD:hH ZїqwhƐǤJk)DǾ9"{Z p9<{Qh;/|߀|}ͽۍ[+nةU̦m)`Z5Ѫx"(4Y1QUaF0p5p32ۍ+# Zۛu#W[,PPTi)eKLΰ.8\`qRozxx ݵxT ֌fR^csn ^}1YKF9̈́#/#k˸k8WeJ|KvN=Neqs۪|nkqH_fwY5w-M\ߌKCQ3*fHT.s[d =́AQ,2M%03KJ94F9%;q P\naX1#o[%ԑFBөik%c֕87;69VM\"7 w]$O0hE|g7$q/ CȮ]\oՓLڹ53 miFpPO\Lg\&PW5 UQW '\MW }Av9 ,^Ikvg-[Q]N-TqSCΦ{ _ˆ^o 2TGAvBbEqk.Zdd O6:] \v\J]v#=P|X;cm<]K7 }lqj:VtCG۴{ӨGQ,,71H=H | Be7IW$Թ lfY %$uTsK)^7F[5\9A]Msa-<,xg;AFX.\,u> %sƇ{RqқNJ\N"߲w>{S~KP{NJo;=)q8~M's翥.'A;=)d|(='s翥7ϞXe#~CX-0^S/X#qN+O>](Iۿ%uWTPu3>QpV|l#m:Ľɓ6q͸]/[#G?f}Thi.Vjyq_Z?}m}jnnM՝?(*?}m}jp+ѺG7Vw7t+f|֫FY#҂f|֧yq_Z;SugxwJ yq_Z?}m}jыuuLISTp@ eLh4ZDވ@ٴDJ5SZ >9Ldҵ_tG~ͦ&Un=MCTʓDΉncYv^"F%޲5iuJj㪀tm!k-%Ʌ=-OQ8þd\*j3] jPC+s\I  W6 aILq' U52]TdN1sQEPG ̣9:V67 >VibG,{cW7;vCle]C4٩иc|["j5dwdsc06K; ^m%7ݷ][r~>öwwMWrT[7M˂+vwukkqr/'omJ3A@LǥYkE#*d `-1dvr[j->I ri,$-nNxgMҟt7α羷lq{-]j[.vҸQ&ESO8v~[8A-&KIWUQ| |O>,pɳo&V'i-jf)EM,2=h1W mlŷ;@!EU%}}\toMOt )䵤98ҳFWWVU(*DC|l RKA.9v;QA-UZ*ڊIi97U +ͷIa_vR(6CO.ג\[ݮ v!hܴfKJp7sG4ط- $TSICp\%͉STu DPrD]ƫm'eAwMyƉwMyƋhh96蚱\4E+,2Fۖ=9 ˢ>4sm75]ibyu9bps^AR8<'_#+w ,rClG~{/v 8 XsGVDžMŠWYO>b%1ط#'de,(8<'_#+w SxOMUʎ O ˉ;Xq=Ѫx*on,5 <|=*DyntK#*NtKYR*f۷zCCe6;VM'cA;)Y5 4{dki s2;RKT4T-ǮCCG9ZI|ۮ.TVT^c]3NYLxˡtC_T}}Zn="*4ZY(րXKAhp4sm75sy~ҸJJJVkJRzSO7NzSO7Zn\'PUU>:xX `V捙>▮SWwuҊ`tfm\ 5#l]8}jCҟ y^vCҟ y^ғFY+`c7d`%m%d=)d=)+x˫?d=)d=)&᜺Cҟ y^vCҟ y^"a˫?d=)d=)&᜺Cҟ y^vCҟ y^"a˫?d=)d=)&᜺Cҟ y^vCҟ y^"a˫?d=)d=)&᜺Cҟ y^vCҟ y^"a˫?d=)d=)&᜺Cҟ y^vCҟ y^"a˫?d=)d=)&᜺Cҟ y^vCҟ y^"a˫?d=)d=)&᜺Cҟ y^vCҟ y^"a˫?d=)d=)&᜺Cҟ y^vCҟ y^"a˫?d=)d=)&᜺Cҟ y^vCҟ y^"a˫?d=)d=)&᜺Cҟ y^vCҟ y^"a˫?d=)d=)&᜺Cҟ y^vCҟ y^"a˫?d=)d=)&᜺Cҟ y^vCҟ y^"a˫?d=)d=)&᜺Cҟ y^vCҟ y^"a˫?d=)d=)&᜺Cҟ y^vCҟ y^"a˫?d=)d=)&᜺Cҟ y^vCҟ y^"a˫?d=)d=)&᜺Cҟ y^vCҟ y^"a˫?d=)d=)&᜺Cҟ y^vCҟ y^"a˫?d=)d=)&᜺Cҟ y^2oU֞k>4Nk>4^77>4sm75sjE7ϒIk5A?~tG~ͦ& !W5o {SzWXƺyJn֔]䥤ΦnNHxFgHfՔt,wTnr{"3۱iӱi}r5Smua*Aiignͻ qikL40@λFIhˢv!>v!>lktx::jj QS9NI*[KKII:1;;n..suC}C}MOOQa4PI&$6qkNwzxx>{s2^c.'>v!>v!>lL'1Eӻm;m]jNC}C}1Eӻm;m6æQt>yN>yM|s];iӱil:jNC}C}1Eӻm;m6æQt>yN>yM|s];iӱil:jNC}C}1Eӻm;m6æQt>yN>yM|s];iӱil:jNC}C}1Eӻm;m6æQt>yN>yM|s];iӱil:jNC}C}1Eӻm;m6æQt>yN>yM|s];iӱil:jNC}C}1Eӻm;m6æQt>yN>yM|s];iӱil:jNC}C}1Eӻm;m6æQt>yN>yM|s];iӱil:jNC}C}1Eӻm;m6æQt>yN>yM|s];iӱil:jNC}C}1Eӻm;m6æQt>yN>yM|s=(nW>y[Vާ_υw}?-=#X֗Xʕl&D&Ezz#G?f}U֛/Z#G?f}UfA]&h=?[){= 枅F-n4'Rߚzl ^OBp/}-n4'Rߚzl ^OBp/}-n4'Rߚzl ^OBp/}-n4'Rߚzl ^OBp/}-n4'Rߚzl ^OBp/}-n4'Rߚzl ^OBp/}-n4'Rߚzl ^OBp/}-n4'Rߚzl ^OBp/}-n4'Rߚzl ^OBp/}-q]NZ\HgTK49p>$k}]msgT?!-.vZ2FtZ溜48x4 E构 ^OB Z!K~iN4 E构 ^OB Z!K~iN4 E构 ^OB Z!K~iN4 E构 ^OB Z!K~iN4 E构 ^OB Z!K~iN4 E构 ^OB Z!K~iN4 E构 ^OB Z!K~iN4 E构 ^OB Z!K~iN4 E构 ^OB Z!K~iN4 Z?w ^OBYYc$ˎ00{uwMyƉwMyƊim͊<5{9g4G~ͦ&?Ajw<`Mh;ܞ0t*t9\l1֗g;Z&T+.O:Z' (2A5rxб-[|VUev@\Z֗2FGڞǣ ]k2uv,r z^npgjԸ)mqH7i7(/v OIRտ?8o.MIwB\!R8֚HY{3pW*.PRɺA3~i8;Teԟ7t&@X.V\ii279XsKZ7' ?8o.P30ڂ$4ߍd`:9 d%,ԟ7t(r_Rmzʪ 2HG4 m[˞駴EЕ5k(Ꭹ9CZ\daa!kvd  ucw2۴Ƴ6xAmL)Suh&~}g:vHmTPd+]iI kkd7 h c uV\ Tp:2U'8v-DD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDDAO&D&EM菴 MMSM:#1drvC菴 MMSS0~?,s=ۡPY(_()j!,5kuZLcUV :bej hcGऍD $p:Z"GQW|\Z"q~iáuo0x!Z3p}S-VIC?$o'g9$[< mM4^I{L]qث[&d74GUh[^$6CZ269gX'uAVP[8y;U1>y1Q%;r n-`G 輬hy[5#j'cj#-T05 UxjcE{s4V$l.88`dR+FQ%TCTg FW k0 eU0Zaqn6"vr}+ si*dki[A{.% k{֬4Ue+k'_=4sm75MO QQ:x*䧩9F5øABɈ7 EvEgTgY!aA6#ޟ31! BOG5,nP\\vI?@t&#ޟ37HGG1q2V`21! 7 D@t&#ޟ3Қ1r޻ޫp"j7q9{\;3''W%3m[;(ʞo_sfcu8rHQҊ6QWOUG-if5c`TXww#mM[zEZ 5E%U1nOPᖻsA4Nk>4[Dވ@ٴD5?3Bh96蚦`HYĈ(Mu F=0B()o:YqXcuYq9.?ѹ!rKEt[[ C^DuA,ppckaxJEG4ъ*mo !TG;'$Խt!kpP[kR\)JZ qTV;vQܪe5\P:XHhÉ#X Hʢui-HI S$.qZHo:@ѹi9ζӌq[w #{" '$l$r(=[h07}NGjQ,&ȆQ6 lk$ \ZJ9&Nڭhn3csMFdnG>u!`3 b<A' `8ڨݶڨ1I7Y7I,v˞\NO uFV \].iu4c:28\kVQҚz=n5X QMHM﫝FZ  9# ;tު֊3! 綐8׌᧗A4SGn )ꤎ0DƘ郸AuApq(h竩GIx֌OToeC))S ow̃26mA:ۤQ\+G%mj!mSX7Xc8;F2&ߤTW;Lٌ!I Fbᆜ,Kv驛KSYWSb -n59dqPIW\4ot4]OgWskŅOK5p&5ԬkwWmph%vhṭaI[V#@f5% ܣ [! 'WSǭ㻱(Xv}΂_Y l"f'BF4d琞#g^KjVz dzF=nupη(,Hߥ|AWMOUTL0S։ \Ӯ憖;9<4VsSkw8ZsPO"2YA-CHjD$k=8x;ue Xl9 R׹mrAm'eV5\h?gk* .k>4Nk>4[Dވ@ٴD5?3Bh96蚦`HYĈ(V豷ֶzVNSBC+^1A 'hؽղ -+e;;@۬Av+*9҂M%"FVnp=F0R9\S.KZ*)h⤷Yk9Ѻ6x>ȒqmVtAil 5";17-Yb=vlV'M$MW0C(dyF;T`GV$ADAk)Ypj-M3'487nHXmuU)2reACGn 9v-" A׺SjbG S4v2kc[n8r*j)0˞@kق\IJD )ij"ltM-IL7YveMl{:E;ḊZ\HhzDLj7Jpl4VehcNvkv2JVD4T7G@N5Kub6WA [l[\q͜C30m# WU^$4zJqUou+eC{i]sZ:97;egGe)u] Sl6giX6.#GY*-28P|z"iHvvl[[m7i*rrKp# lSRr)euK,XˌK=&)+!ؓ`;NyVMyg;GmUT4Q5u#ll35B;RrZNO'" PZ8EM#c;L1[.kdq#+r" ѢUcUv&D&EM菴 MMSS0~?(]ijg􅚈*=$[t-kH Zs { ;ZMKUSB=Kɕkָ9'-!p}YhyT(k[5JHTnrkFZu(h&3mO1shwˣm$mkkCsmٱҖ9d|n6 $~ q:$l̜#Fk( wS;w68JCXdp3dh*YG>#]&0tc2A{Eݣ!*6qp~ E;v#1$=5zCMl&oǖJ˹ 㔠( MXmOP-,ϧ'HT!h 8j@ł:]4hP%,MJǴm;.W:Vˌ{. d >J(+(PcS @3;~5puT2E<^FXLA'T4֪ /OG4ʊQO#$qs:Һ\ѶvN5#f\3*7Mq}q;7jn=瓕kW ]5{}4`G53fPt\+EX Å eidt;ZR$lf<426}5NwWSm)Ãʇv38hh#koٟU\Hf[v8{bAjyvN;X8'$r`l¦UZ K}K:wGU!bXƹhִr6@tDPW/4OʬjѢT]W$ן}4sm75MO QQF\oqPE-Mml2 z``q5ٴgyC5utQ#Y3xA2ݨ%Q -5OԧJւIvyc$m{NZ>>w)Q @_+'$0KQ1ϵVӸ˹ny`ι-''DDE Q- et0K;ipc'6E .2TkSGW#:BpѱFݫvjkɭ񖁆Q˲v 0yn^),jH4~u2|qmh;3&T" """ """ """ """ """ """ """ """ """ """ """ """ """ """ ""_hXrD]4I?x:I?xmz#G?f}T D}hoj!f"(" պ[>tz*8/:  'kvt㥶:e%k,-%jcSpv̊sfe[`лk嵾FV=k#"V <8F$mr@ZP5כU}6[LdsLXc]Ms{9iη&*ieK#D3UΧn4sm75MO QQWElm}mt^S3c-tv孨Ai]HãSho{poh40F6HÚז$2[ nRIk*nSɽ/MN5^ݼɣ3 -[ͩ%c0289xդFO+ WE[=͎ǖKQ#Z AwjNHXal4wSjj)U$_b 1#sDe[ ڥdgI'lgai[Fj{],u1ČQ= `2庭h5߫ګRzHi&2C#@s18WA$._nRMP'm`-4pS##y.o9u!T̔ۻ 2n 9;" 5iZ[|6=X%t&1#Z4~j-Tw( }Vƽ#H""" """ """ """ """ """ """ """ """ """ ""_hXrD]4I?x:I?xmz#G?f}T D}hoj!f"(" MlZkɍjs{5o&6~j$jq mvq(0G*xdMHX\pژ8O ҊX&PM_fdS6X#tMG:6\0$ި_M ƞb9$kXZs1ߍlkMe*eQ ckfjKW&E\̽1D]7]ziN@˜7X&Q-378-8FںIlTS>rkõ]$4xJy͞"s4=Ӽ<A@TokeW4AG# u e/m9[*diepڌU;t̼A,I9ܯ3ɤW2+/Ll6v tãpS(:*]O ڧ}Փ>*ѾFe1k5r6D襢 \߯mf:88IRd15@H:RHO\*mIP)ܝ!8h h#nչil 5BH;U86W:k-2 9rdeiw~I hTT,7FZ=mx ʐ] P61WqkEY=Fi w"6Ibuѳl2CQRo&L'/nK#AGU.=ܑ7$ 8G.z7vl20ˌmZK)c4uKRJ<27e--$byFNkم5M5EDxɺTHj4klp'j*[]'u=ƲWUG%[[C=?jDqni-cCuA.U-2G \89p2rk5ps\29*=iZ[|6=X%t&1#Z4~j-Tw($Q@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DD\h?gk*F?YPiu]^u]^&G~ͦ&?A.@ٴD5?3BF$DQDD@DDD@DDt4=͒M$qs$DD@DDD@DDDAFO+"" """ """ +udc^܃ פDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DD\h?gk*F?YPiu]^u]^&G~ͦ&?A.@ٴD5?3BF$DQDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@Uv_hU5O'U5O-oD}hoj!B菴 MMXkvV}O0*i` fpqD*yq_Z?}m}jp+f|֠p+f|֠p+f|֠p+f|֠p+f|֠p+f|֠p+f|֠p+f|֠p+f|֠p+f|֠p+f|֠p+f|֠p+f|֠p+f|֠p+f|֠p+f|֠p+f|֠p+f|֠p+f|֠p+f|֠p+f|֠p+f|֠p+f|֠p+f|֠p+f|֠p+f|֠p+f|֠p+f|֠p+f|֠p+f|֠p+f|֠p+f|֠p+f|֠p+f|֠p+f|֠p+f|֠p+f|֠p+f|֠p+f|֠F?YS͸]/Z}v)ں z*eY xàcNr wMyƉwMyƋho~Z"k};4ulkHї5r fÞi?xʲFVw<A;.?O".GeާIÞi?w<A;.?O" v]4N˽O9zs'Sy"eާIÞi?w<A;.?O" v]4N˽O9zs'Sy"eާIÞi?w<A;.?O" v]4N˽O9zs'Sy"eާIÞi?w<A;.?O" v]4N˽O9zs'Sy"eާIÞi?w<A;.?O" v]4N˽O9zs'Sy"eާIÞi?w<A;.?O" v]4N˽O9zs'Sy"eާIÞi?w<A;.?O" v]4N˽O9zs'Sy"eާIÞi?w<A;.?O" v]4N˽O9zs'Sy"eާIÞi?w<A;.?O" v]4N˽O9zs'Sy"eާIÞi?w<A;.?O" v]4N˽O9zs'Sy"(fP2@e;ÙꝌhpN=IE}e~OR٠FcGCIEVJFIFHHxwdumpC    $.' ",#(7),01444'9=82<.342C  2!!22222222222222222222222222222222222222222222222222"d  !1Q"ATV23RUau#46Sqr$578BstbdecC%DHEv&Q!1A2a ?i9o#\q͉,-LxSq9*Ou;߰->,~ͺv;|mLX6\OJ=g+mvMgt^cgoMp1nh`=ײ^NHCQ3$Ol#-޽i֎^ z5/Jtz_kѧZ:[{/5Խ(":|AFhn ףRGKweuײ^K҂#-޽i֎^ z5/Jtz_kѧZ:[{/5Խ(":|AFhn ףRGKweuײ^K҂#-޽i֎^ z5/Jtz_kѧZ:[{/5Խ(":|AFhn ףRGKweuײ^K҂#-޽i֎^ z5/Jtz_kѧZ:[{/5Խ(":|AFhn ףRGKweuײ^yV_QL7P{8A=dŪ6*! R[9 k:OA+֎^ z4GKwe%nTyR;|x0BEQYB8l栐GKweuײ^Fi\=ܶܜhvjنYĿvLq}GϚ tz_kѧZ:[{/5+Xe))Rc¾HqOćnquAlA-޽i֎^ z5n %lG8tW]KOo5L퐔HxgZ:[{/5ׅi}$ڴ݉./; 0YXZ]^%q֥h +I6~幯KaMSr H!=GKweuײ^kX7 6)Pr[*HkGZ:[{/5ӭ-޽j^hn ףNtz_kѩzPDuײ^:|AFA֎^ z4GKweGZ:[{/5ӭ-޽j^hn ףNtz_kѩzPDuײ^:|AFA֎^ z4GKweGZ:[{/5ӭ-޽j^hn ףNtz_kѩzPDuײ^:|AFA֎^ z4GKweGZ:[{/5ӭ-޽j^hn ףNtz_kѩzPDuײ^zkEiѶ9bBr@<:Jע+T&䀣ϏlvA&~Yc}bǷ͜^֥m/.Rn1eBA;Y=fwI-jMsvA5dVe/ =aucA 瞶Zpc Rm=KkfІANh!ӣ4SOXZm9]98cCiam OS&J~Haԭp(zձڤ[$= ;_ ;\ |]-lv 4UNRT@JF@Bi[?Tž2t[RN-m$pݧ;U?QQ2l[|Uʛ%э^p! <\mi)dse}M̏z =hD'*u!*st̄,d`|[fJTmԥjmE I8RN;`M2=|*zJhSQGOG&j[ RͽcHF6zZddInQ*d^ve+pzZ-NXul0AyjϹSQGOGA $2d0yPSACh$ F=d{Ty鹑SQ篴GOG>zJhSQGOGҚ>nd{Ty鹑SQ篴>znd{Ty)yۭ4t~lז`aZGBH#YMڛip!~?5ka,2P05Ѭթ53n{ZePbZ7`5Jh%HF2=ڇ-m `9-归~kfчv ڂ9ׅČ-{jI-UxuJhU2GhfJ+meJ![hsώ<+-)]Զycs*l(>zJhSQGOGҚ>nd{Ty鹑SQ篴>znd{Ty)GOG>zJhSQGOGҚ>nd{Ty鹑SQ篴>znd{Ty)GOG>zJhSQGOGҚ>nd{Ty鹑SQ篴>znd{Ty)GOG>zJhSQGOGҚ>nd{Ty鹑SQ篴>z%hBFsgJhl/^w3;[g=<ٝ_j)kZscJha k;T4sdHQz{wmN0V)B28pI_n^&t=XJv:Ua%.yKX$dd|Yngyc*/"ݭU[q.4K$ 82 GQS-Xٓ63sܺKIS[U-g;H בulݹlu8om JZ}+}g9R 꾬%G r^°CmXn|8,,F*Lf8Io: Feտ~[nU7 $Vdp%nG r^:Bk͸u 3YTGDyn1Q-z/@kܗ"TnG r^p%ȷܗ!-z/@k-Fp%nG r^pQ-z/@kܗ"TnG r^p%ȷܗ!-z/@k-Fp%nG r^pQ-z/@kܗ"TnG r^p%ȷܗ!-z/@k-Fp%nG r^pQ-z/@kܗ"TnG r^p%ȷܗ# O:p Z3v!j?F+T&QB\YL974 P˛kڶx?d5C2R \{c?0n<Æ۴4}+F_tV e/ڭcE9%ʥ~%4ǤFԤIZ˄MKkf *І24:pf[*x{m, f3vC)+!R6ؔyZ*E2Cri͠(o[%p||oHjcLhVJ6mIQ%MHVe&s"0{WY8{yqe\ඤ-mLd-Z4;v %Lrf4t[;8HBnv+-fEZv6,do]}F<:DPd%NhlBcVR،b11 JͺMТQvRN$dded?z<:C#ঢ়GXjhd| uc뭰ҝuĶ ZĒ3i[^e JrJ;$pSΣ42> yu&GO:)Qd{q4|)Q<: 42> yuGO:Y3imnB- Mr3j2?&ęIfLu#KBv;j=dev)Q<:͹ 6kq]JTFh3"2%SΣ42> yu&<:C#ঢ়GX#e'-wHY4X};d| u`FJ"222=dd?!fPN)Qf$fFW 4~2> yuGO:C#ঢ়GXhd|@4|)Q<: 42> yuGO:G uaSΣ}Ȏ<ٶM-8#`tdaZGBIC4FnN[|4d]VɶYiP#5Jm4C۟ qC,N+nѣ#/q(G'*B2<.meKl6 bdg\I`wc&[Z..$e!kΤdj={(2mq5mWmd!Yt~#7:]VҦG;d|@4|)Q<: 42> yuGO:G uaSΣ}C#ঢ়GXhd|@4|)Q<: 42> yuGO:G uaSΣ}C#ঢ়GXhd|@4|)Q<: 42> yuGO:G uaSΣ}C#ঢ়GXhd|@4|)Q<: 42> yuGO:G u`堬J8ɏgx_$_7{ͥEǾ?@gx_嬬>%h+!DN6cY<|OQZzVtmNh+粒erիVğWDi=(EE vNY4Ù"GZ\_%x3T_@թk 1[KiffBӨwZĪ.Oz̙\Gm%MhV[)k;AˋUgS(()w"1M"2$/NW&RJtvШ[PYeťkmJTfDŽG`$aPҝowJIJy$E{{ 'İ;UhȋJK^iEqJ{e-w1sP=Pw9qf2҉9s$ʔGcVjQ຦ݪjYQ3n6ZԚ"_z=FjMz. q2"җLf[,-M-DjYHh5l䷎0{`Ɨ=9I*(ٔ܌J.p?Euq" C Tduw\[C/"BQ)[I2tȈի2$k.  Df̔)Rf>K jʜ%DekҕQ*!ޒl'H\2Y+: !iꙙ\5-+"Y:DFzyu|ӆsrW!wf[G ffűw3;~(U*&OL!{rijqJ5}Q]%'_}UHTrTu(To 2 +KnV2%_8תT&#UYjl-ZψI.ECRGAec9.i]4\$3$z*(ATLΧ*bF ӔVsk{5b>J80Ҹ)Ae«o&'>m{0 IO^LѸì$w49ndSSE7.:$XmfgܿWS ȧv ,Fj6Oj?*ay`_ SڼOʘ^E?X{q&u,HIy6̵Xz&ꔸal0RjB̯c+?;WS ȧx0~/~T)m^'L/" Ax0~WS ȧPm^'L/"~T)W&H+m|_5]35Ԛ[Bl6_|6rfvi̊$236#d!+eB%lm4X-O%`|GZe*jLp%; ,UY3}!PC?6+7F~Ɣ궽Z8+ǘMX' vrIxȷ r }$Mk"> |"$?!zē6rfVկ|[{]8ԡ&2PטQ6.7~[Q6<QXc\}FHʌN5V;]{#i3eݎyOoiHpqĞNSWޘ*FTLo9ǗJ?gE#<{+ Q?dӧ!6;)m $bVR}+?:4gϔqXbY98izc!nESRb44G*TfV;\#3.h98;ڛL.>TV<; ^WbDH%r#-J32(I%$DEBI|Ǎm}[sYJh4u_Q!JmqX0qܔse3q<e%%TYqSҶU8ٙU$Ii|5d;ap|-zYUr,;I;DrSE0Ed!2DD!BPa:JwmG&C9[5K4(s- U\M P̩,= 1dLe:FBҤIV;>"1Ęi꺤2᩷A8MtH%t/+ܤk=GSNa4y+KrK%i+(c2cMLJX>pг0%(~Ng9?LJ0%(~Ng4?LJ0%(~Ng4?LJ0%(~Ng4?LJ0%(~Ng4?LJ0%(~Ng4?LJ0%(~Ng4?LJ0%(~Ng4?LJo\ Q2´22Aʞ '+O\rdwG!O}"`jȘ g訿؁b+g訿؁b3Uƕv>em33hq屗}fo8Hoi0Q|23Ve4㍒nr=J-mYQGq6H;d:K3q-Z-jY=JLTlF쇍fG[)JLy$h"#$ hXP^mKJk2$9LV+,S^fNf"IKWz=vT9Uch\"D;*Q"ͩfIƫpT5Z9TGDY i"Ib\б8tiO2e'RiE_ 1 xﰷBۍ-)#Q^RDSr32; G[t{J5kGڮ'Zt= 5*r#ZieXuJҤٔIe=Dj]f#2 $E)}jf*TNvQ2NLD%h5dV'kRd2Z - T4eլDٕ@)uqU#:jJ;ZM̸Hc􂂉{%B8m17tFf'>k{kg3{6VnCjBEJ3#"̇8bB)kIRSBʼ(H%Ib-ZLVg6$w鿝6Dlߦ[꾡ytj&Cx] "2=dwX2TbF>cr mf)E[jBIFWQ%(4zŖ=HlGڗd;v̲N($)RRfI5xk+2뮶hqRRDk2""5 ؈ TIɖ#>6]늾eJ<ʹcHL)KSI.%_G؍J.-+[mBR$2"der>;'%2MKI3#4̈*W7C(XՓSZTt/:*ReiNmw\k^ٮq}s5(k_m}a\k (k_m}a\k (k_m}a\k (k_m}a\k (k_m}a\k (k_m}a\k (k_m}a\k ٮq}Y|k wG!ZfgaPy^$ڿ&?k6 w3.-6EAbami0ѱEԆ̒LԝD|c5Y]6P#j=./*<ZJ֮}6P#j=./*<ZJ֮}6P#j=./*<ZJ֮}6P#j=./*<ZJ֮}6P#j=./8bFܹHmǗ&RLdDIu "Rծ- j#j=./ 6Qң74 j#j=./ 6Qң74 j#j=./ 6Qң74 j#j=./ 6Qң74 j#j=./ 6Qң74 j#j=./ 6Qң74 j#j=./ 6Qң74 j#j=./ 6Qң74 j#j=./ 6Qң74 j#j=./ 6Qң74 j#j=./ 6Qң74 j#j=./ 6Qң74 j#j=./ 6Qң74 j#j=./ 6Qң74 j#j=./ 6Qң74 j#j=./ 6Qң74 j#j=./ 6Qң74 j#j=./ 6Qң74 j#j=./ 6Qң74 j#j=./ 6Qң74 j#j=./ 6Qң74 j#j=./ 6Qң74 j#j=./ 6Qң74 j#j=./ 6Qң74 j#j=./ 6Qң74 j#j=./ 6Qң74 j#j=./ 6Qң74 j#j=./ 6Qң74 j#j=./ 6Qң74 j#j=./ 6Qң74 j#j=./ 6Qң74 j#j=./ 6Qң74 j#j=./ 6Qң74 j#j=./ 6Qң74 j#j=./ 6Qң74 j#j=./ 6Qң7Q֪oǭ% 2[~fjJXuDZ%'^+18͎B6K743{wޚ> "ßFI1UF|' SJDcrC*u(NkT7;}>0ѱGUN5CFq2$ېJӠZ-)U# xyvvBͯRHԟ& JSkb]vU= JZYBFMFfI"~Q]faSq͵(ԕDu )\UNR!,Pmr#2r3oANc%JY\IK~Ǯ׵JI$x0ꓨe5Si#4Qqd?u m#.:k 0 4dV2,ȯhCL=Wy$u66n-#d[ 2p[-="c!B,ZvV -wKH-Mf/GzKdvNY+M[[ Pnfڬl~έ=|2fͽ?ՎKv52uN/Hi-cw oQ]Q-FT[$Q5J^^!@f&D&b[ q,OhyW3Ӿ~733j&>c|m[{e~k5eNqTP͇I8+3Po)6(mkR.V6mOТ%kկ㒱-=/?i0lNn-)IgE5YjW'ϖ8؛-麜iIe(j+o7Vtg r (eTLf3QVJڋm+QaMjK߳),$I(|~i~IP'ɤqȿɥffNTQ"25,32d \nG%mJ;'oիISq%RI"3j!Yu%7lLGx9_QmWFg+0]}?w]϶h>0ѱG*7'9E~Y MҢ"##2=F\:psi#U'~XnϐnϐøqM!ĝm]SNHqe%Ec#2FwGT|wGT|~% 2J̾Ѹ; ;!ZvqΔw3xsؾ@ѵ; ;!b)ZG65~{4h30pv$שR%rr%JyI=22 \nϐ€hnϐnϐ€7[-VMfhh\W;]2 1#l Ran'PJd{NBD#ˈl*]V3. (233mzmC5"|iJd}G'5ec &Ti4&Tv4S-{sQ6yOcQq_1k1]2F˚A#L[sXJD#NƖRHϏQ8\S-KZԥ(fgf` oEmwޚ>uzj<`:2>0ѱE` )@զY]GR6>4u𬚂c]|7 ȍ|;sþ.;B**b*.Oi_hQ̒fgqFǔRR;h~Qؙ!ڥQ1ȉ*Q(ֳ4\l7|]vpw~hDc@r;eL:B*:kuyӖڔV9'Tktmn"*36SHCJ;b̵ Fn f?LMB248aD\XjvSYJdМf=zئMİ!* 41Y *`Ȍ+gԣA>w߹ }6 IơOrt'&MI9}YeXjCĐ2Z"Ս-w߹ }3þ.;A|;s4pw~h7|]v d}>w߹ ?|;sþ.;C@g|]vpw~hh2 n A>w߹ }3þ.;A|;s4pw~h7|]v d}>w߹ ?|;sþ.;C@g|]vpw~hh2 n A>w߹ }3þ.;A|;s4pw~h7|]v d}>w߹ ?|;sþ.;C@g|]vpw~hh2 n A>w߹ }3þ.;A|;s4pw~h7|]v d}>w߹ ?|;sþ.;C@g|]vpw~hh2 n A>w߹ }3þ.;A|;s4pw~h7|]v ʰ;_Q3.~ }}?w]϶#}$iVNmM 02V+4}R|]%_FgßFIXZB<bR9Ν (ϜE$DfdfIƥYI1`[U*P ZvTҖHRY- I]V2I|cN ^E,?)J*%dJ|)խ*4$̊ױ )fQ-وimh#HYkWb3e!窓UT7 K6Ȓ˖ΒѡRw=[0$$ Jbv[4˙=.|U &Ptʓ)Lɶ"I\՞c#3|gZPҨsI*JnpyIc) Z6dyP.~!$Fs>՟y;VxG67HJ V>5ΌJH>!<#m_ O6/'(6Edl"_}@$|/>P #m_ O6/'Ha ~6Edl"_}@$|/>P #m_ O6/'Ha ~6Edl"_}@$|/>P #m_ O6/'Ha ~6Edl"_}@$|/>P #m_ O6/'Ha ~6Edl"_}@$|/>P #Q,̋|>MDREk/pt|[Nݘ(jUW2ȭE9ΎI%̈)EdIl"_}A|/>@FPm_ O/'a$m~EdIl"_}A|/>@FPm_ O/'a$m~EdIl"_}A|/>@FPm_ O/'a$m~EdIl"_}A|/>@FPm_ O/'a$m~EdIl"_}A|/>@FPm_ O$a&LaثB+λVMYlxG67HRZnRVGrQ_Rapsi"#Hff<uf<u qKoJJvc ;ғQ-E{JL3 p3 p uǃs.ǃs.EjJQ}+S1[JFdW2b~+h0WPlOP܈Ê0Ids6F$RFEk4ƗgD]A|a?ʮ@Gr#++-ViO̕-- Gd\kOqUЅ:-KC/!)KA<3$f"=v;ȵjK-9HyD$e{[k3=b`nOi46ӉE)9a^Vy.WU̮|f?at58n3TuN-gL$e56JK)ֶf"uTTԗSl*:n*[RJ"E򑙥7;\Z @wޚ>uzj<`6#}$\JNss+pw) 9lo4t 3Gn b J;:̅%9Rf␤Ub=f.R'eJ5 4m|R $$$k"iDQ"fdv/KWV5'=ת)SnS&kΘYLߵxq4qUR 4m|R $$$k"iDQ"fdv/Kghn0&u$NҵF_WI~־jIT\IXzZVNS ޒR!Q뱝zcR43DdeNQz(KK} mr:R+:$ԟ=1؜+91q-"} )N)Ii SQ c"veʏ04f7y&"3QfZGmDvc`EV\(l*Le/2˦TǗ2,Eb3\e `QcE:-!:T)-";Xo@'NLIMH~n%1ixs3R ?Q,4)~=D[9%F[&jJ3X;XbFM]W魴%R$--z>؄*)TDK66Fnj/n,k1*4Ȩ4VGjI|c o'}8Rֳz|U]qc0Ti.lEH@cVMYlw}aϣcy8_!K}aϣcy8_!9";K-É4JJouV5GIۮ3[c2-1f/GPY LG=%LV?5_"I'You L8)*3t9PeksemWou~a@ou~a@(*$̉Г{k\?,:*r2'N& J=&9hYPRd̈q\Q .Zi!R ТQ9$6,Q;ҀQ(`c;pXڬ2 3b :{0hUlIl{!{"k).248$FkIEk$Vkigl*1QҨDea;FO*FUŏʚ[8+*EB+k)Y KjOl uGhRHw$"Vwlw#'?Uk舿(oە-&#RHʹ[224@O6_1Qqk]rII\,|љ}]PDZL{ySIOW|6ƞ* HRm 鮦cw"> fzTioԝiY!kI=|Wb=ڑ&%7uś>1gl\ը1+{}?_.ϳ2{eNķZ!oIzJ=fFIqWo%$YEH.3u>4{bs? Rbbk?>RTM_걡S"D 7iHuS*&ۆ%fʂu7+$VdC(P٪aahlxym9ȶ#3N#g伻$mU<TXx=\v.E:/OT_&Z;h(ikR{䠘5L{A/_Dx˲GRqoꗶ>^&Ä*n1=s J+2>.%C](-s$J2Ep b-B^v}{X_,5T7*NoƧَ Kl:.)ºٮyneV 3mFP~$[%*dJj4&fyVǨ>)R!rˆ "ֻ";Xo^uߏ+ڄV rHlGnQ!v2;pݚLCmϊKlՑHk47V[^wGѕj_~tv#>3Z0[r Wa?_VMYlw}aϣcy8_!K}aϣcy8_!9:= :63f#"VEz>=vFs)m?@Qj8ǎMuopшOTboJiP"D٠}w ڸCHb5.ŸG;uԺY2$ue##W}kQ c GʋoU0ѱE/G>0ѱE/GES'B^]&ز4e,ZPWǾ?GWѿꐗS+B%yJi;\˃x Xh4N G[mF[57܊`J2-jQ#+fʤ)I3+׼T1{3Yx[yM)){;NkQ dR5 B%5PH n$ID;DdzV!TjSRFl̒FeDfzYdiY^"2 ڥYi4$bn"%-&g+Ja0aͨ6P_*:cQ|T`Gym.ZQ\1OOű*!0q0:r FRfYD XgYLCCza8m-0uuFE{|XΩLK6;f+-w={A )h8Uj>4G' ʲ;X3#-a>[KSQ)Mz([~Jgz:zʧɳq׈dJ++Y1Ա\=ʋ-[P:m1(VVSL|ֶ_ wѰͬB"=6{ϡD%V"AwcXf[W\iqPyDNI3I̷fFFAxGb-gkU[zo: ;ePW}54hcٖcRL]f+d(MeJƩ8ZZs=DK+'w3/_Pmd8*EmL:M4i=8Yge7Jl|bժT7(9s4iZ4egr"+@14 _R˦KT:%&RTZ7DymEkfR8i)l!3KwV+q U4[k[e6>Eii3f˗{|Ф9%FbI5^\"-"n$[i2&T+jJSs$Es*5)z̨R)-xѣ5RfDzB'Ib ::$ HۉE[+'ўVר6y~y>"8 CnJ:rJI7yU$Ԓ"ʯn{ָNҖq4gR^YI;҃,wI-r6y~=ǀ^_hj R.[qNڞr,ܗAXyRv3F¬t2)u6{ȖetDd\ciZ\'Xk2 6xIZ+ۄD6,1-=Q=썵$&(3UGO)d/;(\ۧyU29_QmWFg+0]}?w]϶h>0ѱE/G>0ѱE/GEyVe|W:ɔR,C#$j"3=e~[saMrte":"Ȓ,7( lĦQ%8ӝFB .e$EW <{R$E-:ʎ3)M)YkpѣzgO#Vm"|w̸,emV2gB^ŨMz*LͲqni,䌶U^mfCXDUdq)B[.vJg{%$gs-v#NxVvnF64CͣozH Q8Ӟo.b#-IT}$V4yK%^ڮ醢qQ=]۶\FZ%*Ih&K2\zx6J\5 7Q)wqLns"[6IXY*v)2mMNo*DfhZ'ZHVyri7Vvnl[%Bth""%k3E!;EْKK"[Qe4Y6e;k;]@ uNM90כYZrBJI2ٛP)4l ؘi6Jld6FV;\` 8\ P㍭.N- ̲K5Y_Y^TUvph(eϔR;OcTKx#BV\K+Vv׬T˧>j* }>#jwI]GT(Ȉ+$X+%IVCްasԨ*M>AumŠ) ͡2tgq;4Tu&#qYY_|F*m8TJ*d)UY'vZ]Gިȯk~0hM[։h!e $BI=n6_i׿ȑUP'ImTfږp-n9Mk3;x pPa 9lȒr{R>Fd-e+{!ā&3 ObrC,ʘ6Ե%]2eI| S+US'b13BI6N)ek b:ٴn8J~\W#&dZH6DE}F6Bl/;( 2u"M*L"r-KΥ^.qo `Uô*:- e?"CMcF"o6D> ׿Y->:)j>TQYz4m}w)NGv;b)C S--*T$τi%DVhj4R]$l{&@Oz(MƬ-DW; Tj[?bK˥^K4wɟֿ'EE{N3̼MjJKW2ӰJY՛)TQ)iH"5ב)+{^&5E"RfuG0O֑Dͽ/@QMR´jK9 J_q tYҕ(ˀ̮Zkܬ7K$-꒔ؔ:B-DK$(v-]Ű#̈́B*hdwm62;#/b|9JL(̝ykYѝJQ|gZ R6gSitt|&3GTҍ(JYZ՘ԒJ=fi64ca\r%JS)kq&F)fRwt%iAV*}hjӆ|-G6]eqYj=z.<'.tMtٸ&$DE\V; i('qe(ֵvດgӁGl[єnIR RٝZ2=U~e {t*FBg+0ьF] 5g}՟ymxG67HG67H$3((C)ZԴ!4Gs-7 ø&*L>Ts7RE)Q)63LFFv?fjrQפPԥJ+aȍ6SfȏCLr *c;pXڬ1x:SRVaOCr;6L%IwJL|u=Tw5ayBUSe J;!HA ~3/cƁ?0'2F?cm%(֓3HQ@fhߣ4oOPi6 ̿oц?1#N&|Mi@fhߣ4oOPi6 ̿oц?1#N&|Mi@fhߣ4oOP Q0=nDg CSFEr? }~3/aOe3~~r%.=R]vcUZIeAE[q)Qphߣ ~3/bs4:`/婾?0'2F*3Kyjo ZZhߣ ~3/bs4:`/婾?0'2F*3Kyjo ZZhߣ ~3/bs4:a̟JT1+i(ti#YؔYEiC2upeB*FB=~qLw_!yA[3uOthr wޚ>uzj<`6#}$]?Di \#}$]?Di PVTkmA&ɛ51'1 ٔjRRfWཌ~cיzt8NDDq(yY A({Bj8M:җIa$kufFv+jIȬCJҚBJFh{Gt[;,Ӽ]̂x ؊ >^\dHݒm> !O1Nz o%fsdlv$VJ-wUQY?zz#nd/#rDH~w4꽈,USe QUNsW6, m?1uwlq9Lf.,IAr0G]rdSwdM%&F\}Y]/R,”n5.,rBHիVV'C*yl$&3ZP[RI&ʢ!M Uij2"=.2L})Yd42/*xj2UdT0tU$J *Qm] `."/?epOb[NY[{lj1׿bsJ%mjQM;l"3;;?~ъ'E['E!UjU*Iy c<.;"|.'}U1"I԰Uwȣ$&$WޑU-FfͿAZw3k$#8jsa!̜$O dZW3"<ߦ-b5f*Zf#lpd-fobs$7GY0Gv&E>|e_[R(-eZ3˒m{)I\\S33l;IS/jgi>L |E㘶[ڥ*-.~?.>.~?h(3rTv,K̩2В5YDN#[C)mGDIRdi3SnGoGczsFD髦T[v1j(Π+(̬dfZ]̈BfYBlI}%2E-q_SEK^ji0*snƣ*KKfIO#*9De]ҕ7 =gf p2'E%)2-/A,yCsŤSӲuN|ndG}G{p\?.b. B ŗͩM6dézz_3U;_^vP4-UsB;P[dXKڍK";fP fPSh2lV+稈p >6|?n nMQ ]{>6|?n nMQ ]{>6|?n nMQ ]{>6|?n nMQ ]{>g*pR3>Nn nMQ ]c&'Qj؂=N#멒Tiؑ_>!s0)r)d.z#kZV+Q~D QS QS75G-vCq[T~dta6Lz3ʴ)devg6[M6BCJR+ {ܚ!-ɪ?Ak>(( ܚ!-ɪ?Ak>(( ܚ!-ɪ?Ak>( _uLYdJ}fJ&xFDo75G-vCq[T~d=~qLw_!yB?8lXLG~VPx F]1+!w]϶wޚ> ßFIOpQBßFIOpQC4rYYw&[v1"$c-衩g98Kx~G"\b>Leq5%:FqHJoeL(}TTʄ-9qdfICI%);\V);2̓V|ӴeJK%I'+Vk+*W#2Ua.%-Hv2dDfhQ]'of6dbWO}ǚ:|[!%dHɣ%wTkU% T4JgN-DeaU٤œiݐ2 $ns"-I3}QzSHRScѭh|ew3;%.yN܂:dj< +DuF1M.>CKrFu!*6!);1؉B dޑIY$dAWj‹nrUM52!NRL|46'UuƤG84 HJC٦3#3QXzB u7$qD.0ѱE/G>0ѱE/GEep 16sԃZ[\٥O/*YL.GPp0mȪaA6%I[J=oa"*~i^twm 譕1-M2FZFEcFoCT:t'Tl9$;^42YStj$W3 y15u.f=*LPAZW Z2sNv7i}2Vܖ#}hL")J]NBZM?/Ŗ$jr jcRLIΧyJoY|'r"# j{N7yYԣff$]fjvΔ몘ڞkq:3RKF$ܵ8R9-s LTϳVCT4(NI7 5FEEbNѣzgO#Vm"|w̸,emV2gB^ŨMz*LͲqni,䌶U^mfCXRfKR}͍[iKnDglN#0TɊ:tȈ Fw33m qNKQ)DZ̋z~d0R*̩ [g,IHިˆ9uê)1RyrsJ,h"4Zǚf0C]^Tm2:O%DZB7MFmj8UIϨَ9DDy[hBMII\\토a=]8'PS%ĥn$Ԕ""#Ip"n21ucQ/.ʚF Քm@?Ʃ)heI\^rKb]dn$Y,F[j;Ɉ.-EStqq5O':Hf-2 r_eƔKI*}f PIw\5Te† ҸfѕӘw3[OpDeYJVrhq~,1#S{UdVS4bNu?̲Sz;TÌZNҟ]uS[S n'FjQix҄2[=B R'1Ua9jHy f&fȷȬIBDĚl9 7-#D ԣY3Iܵ))a0-;7t>J%f+1{˂Vaz,Dy*QDQ2#: m':|v" 'aKbyZPfDI{^ʥPJ:RLW"4b-Z49cR3 2I}Mdzk5W|ec=`'*1TBq--,ֵT%&v"1o4=p%!*vʾSNULǼ*i(t|D“2\ZlhsH[v"#;eZu_m ݜELQӤ6DDFHm)%%}3 -]),mDKJ!Dv3-N1*)JoM޹%DDi4̮_.LTšHrK!))StLhJD&C-J/>BmfvQ$uS%4JS7 ﮄeNeq,JfTh8JB!Ӑ%g"~PEĮ;c*]:|5S:I!:5Gt3/lBL|BrvcI>*[ZDN)+3$'eޫDlCUKoFS9tN6&k[ ѭDj;VlCqD4Coy)7-J%f=Ec)Q}dǝ6AT2n:˺DEs4POLb5+54Vlq(iTIifj"2Yw .M,i%F˥$$UtI2;p ֙*٬%LE3VBR +Nh="Nk_ZLeq4$lr2BV2MI\J˿s-Q:YUvmʌVZQɻXnjSSNy-n8MI- +BVa5=lZ{h{ Xv:%DImDFW#m;aBtob#M  Wa?_ѣ'kuzj<`}?. 9lo4t )p 9lo4t 3G @OiⷣC81ֵzĀ&З!$Eȷ~VNWvQ\Gr3uOthr wޚ>uzj<`6#}$]?Di \#}$]?Di Pr Wa?_VMYlw}aϣcy8_!K}aϣcy3tMqAd%ғ$";j[4]mw҆yQq}(рmw҆yQq}(  yQq}(mwҀрmw҆yQq}(  yQq}(mwҀрmw҆yQq}(  yQq}(mwҀрmw҆yQq}(  yQq}(mwҀрmw҆yQq}(  yQq}(mwҀрmw҆yQq}(  yQq}(mwҀрmw҆yQq}(  yQq}(mwҀрmw҆yQq}(  yQq}(mwҀрmw҆yQq}(  yQq}(mwҀрmw҆yQq}(  yQq}(mwҀрmw҆yQq}(  yQq}(mwҀрmw҆yQq}(  yQq}(mwҀрmw҆yQq}(  yQq}(mwҀрmw҆yQq}(  yQq}(mwҀрmw҆yQq}(  yQq}(mwҀрmw҆yQq}(  yQq}(mwҀрmw҆yQq}(  yQq}(mwҀрmw҆yQq}(  yQq}(mwҀрmw҆yQq}(  yQq}(mwҀрmw҆yQq}( Wa?_#j=./cО{ˁ]uפ0q 3Ԟ՟y;Vq~5S㨙- RhmI+c#"#oe<#˧kwǖCw?`={W#w?`={={w?`w?`={={w?`w?`={={w?`w?`={={w?`w?`={={w?`w?`={={w?`w?`={={w?`w?`={={w?`w?`={={w?`w?`={={w?`w?`={={w?`w?`={={w?`w?`={={w?`w?`={={w?`w?`={={w?`w?`={={w?`w?`={={w?`w?`={={w?`w?`={={w?`w?`={={w?`(e[WO;ﬔ>SԄDD#3;{R"##3 ݽ}}=٠FpȦW3R$VJFIFHHxwdumpC    $.' ",#(7),01444'9=82<.342C  2!!22222222222222222222222222222222222222222222222222"\  !V1AQ"SU267Rau#3BTqrt4$8b5Cs%&Ec&Q1!Aa2"R ?a9O#Tq͔ZޑZ=h"lTWVk&mؖ%ln;H\6,cA*OYX˟ J;>a-^tNI!++\֟)"m-^tKm-^tKm-^tKm-^tKm-^tKm-^tKm-^tKm-^tKm-^tKm-^tKm-^tKm-^tKm-^tKm-^tKm-^tKm-^t| *4qFvHϘFLZ=Sj&Sw&ךڹ-z/Pk/E wGbQѠz6Nxgv׬vKJD2̳=%GmYmqz/Pk/E wDlV͌O^mً mő\Ktm4t8V~)lG l5mKDz,yʴ3eJJT%n_$8'C8f:uYY r+38[eApG l5'TLo:d#']9s*S䥧ҷٚs%*R ^o8[eAVHq В'5[ŗXb-ns$Z󭠔kI/]\zs^"- ٌmHpG l5mZQMy2SFw6*H5,"8#^pz]."8#^pz]."8#^pz]."8#^pz]."8#^pz]."8#^pz]."8#^pz]."8#^pz]."8#^pz]."8#^pz]."8#^pz]."8#^pz]."8#^pz]."8#^pz]."8#^V}Ӈ(HM̈ў1 QnJnBm.H%D/}|pÛ?A-cM>lq^ؕȬ%Ierdg%_'_ 'pvYqtPKʌ5K^-Pictures/10000000000002A7000001E7AE2A6DFD.jpgJFIFHHxwdumpC    $.' ",#(7),01444'9=82<.342C  2!!22222222222222222222222222222222222222222222222222"^ !1V"AQSU26RTau#37qrt$5Bb4s8%CdEc"!Q1A2 ?hƎ[vkΚWF*a=Pw[o9.&t~F{ߒ`mgXmk=gk39M.7cxqź!q|GE/eSg ~I'ڞ]* HEAU9#]Un*9#]T䎋t^uU 䎋t^uS:-{/EV肣:-{/ENHEAU[ HEAU9#]Un*9#]T䎋t^uU 䎋t^uS:-{/EV肣:-{/ENHEAU[ HEAU9#]Un*9#]T䎋t^uU 䎋t^uS:-{/EV肣:-{/ENHEAU[ HEAU9#]Un*9#]T䎋t^uU 䎋t^uS:-{/EV肣:-{/ENHEAU|uU[G$o''J{iq=͌Fln\̂ג:-{/ENHEAUsCIgfjm#_.qo~f>Tkj$fwuI--vxk] kvwk 轗"ꯇh2FF6GQ]| ]$Y*cH#{48ڻ/f]A-7q,9#]T䎋t^uTk[4rLC'&1wu 䎋t^uS:-{/EV肣:-{/ENHEAU[ HEAU9#]Un*9#]T䎋t^uU 䎋t^uS:-{/EV肣:-{/ENHEAU[ HEAU9#]Un*9#]T䎋t^uU 䎋t^uS:-{/EV肣:-{/ENHEAU[ HEAU9#]Un*9#]T䎋t^uU 䎋t^uS:-{/EV肣:-{/ENHEAU[ HEAU}EZ5;5ۣ&7$m2UGi{lQ=eg!4s5ƺ9m&,zں_9kVڋ̵.mƒa$9WoKQˢz9mZ5;_oc3F f=a7cA Kp5A{c+Z5u1q7dA2&w- vf>LtzmKI2 E`, cVrDD#9#Eڪ-mRFÎ650{Ck齢ߢoTP jjWTnk tl v`] )?{A"뎮XC3JB:`|m-Ųk4nٷUw}v},%R[}+jaf5@7'$qiby?ْ=1MGog@+Qhj6M01ڣs2jZA4rtn,ppiÚqGhƣ޷ƳҹE-\lj=|k=)e,6824wIPﶊ=5ֆhXGP5qA $풭~ƣ޷ƳқzJqƣ޷ƳҀrKlj=|k=)Q,qƣ޷Ƴҫ+4o}-mMPk5[ddJ[+*a~u%@;n;[Gvƣ޷ƳқzJuQ sF!plk,$hBZ55{zW(655{zW*?T8<YvR2C09V~ƣ޷ƳқzJwԺM: IsF~">55{zW(655{zW(8{zScQ[Y\XcQ[YMGogrcGog655%)$3DvF6W4&A L|mWr%ڣ]tt̓ߴϕG 1FHv"X8$EGM0~|"C Xics=0Gʔcj'T3Rh!JGL!'&<ǪOʒcvh9߸#S9~LdwNIDָbu3fev'%тve˵v[[fRӶWs8|GogrcGog655%655{zW(8{zScQ[Y\XcQ[YMGogrcGog655%655{zW(8{zScQ[Y\XcQ[YMGogrcGog655%655{zW(8{zScQ[Y\XcQ[YMGogrcGog655%655{zW(8{zQհa +K Zq/Fu|[jξ,5kĸtuo{s$c php$\wۉ,y}S+/U4õdnT8sHݻv"O>-(CϽ_bӱ!Wk(xLX$dd|YYgU/1}ݭkFcW-06B裉䖜89Ahޤo Mm4eu=)a+pY<˭u˲lE[N!뱻iHf-ko'lERɫ.w!։qS7k&~g1)e};s$.s$'T3=jZ]aھ=VDIʻQV~AGOKupFpkKzNżd1kWkuiwl/Pj_SEuuIt6UI$d1(8,oLsU6X䖘=dR8oipEmHh-m lRux[jv8n#˹R+7*!2UEkuT0VV 䌷psoZ -}emChjvy܎ڝq[(p*L w=2 EUph澞^7uc.,2VvX%ݓr{CMUI7 gTDMm3Iǵͅť#=VͶk*j*[ol-՞Q Ce{Mj׾8ֹ9i {EH%dSUKU#skCf7ssvgzDPgtEVΫse΢YCD*,E{Cjm*bXڎ;IQL]fn\Fetrn0*xL݋KFy̨T߮ZoTRڟls@\#eŘ-Ʀu Φ=:E74;.wh< 4~+%cYLbkywv vR%PʆKGO#j\8|M"Rwd@h5_qPiaୁeڭՔ8n޵ ڝql5Úݠ{leNPxvsᮡe ː%Wwƽ9g9)) ǃ^eQjH^Iv9$<*mV1[kL]#\;-Z5F췑1*.W($]%k)3׶9Avp@pZXhjÒk{`h sMp(ai*EdC,akGk!Ć6PTY5PMu(kάqٱ5T=:ըVM8QA$-a۱ """ """ """ """ """ """ """ """ """ """ """ """ """ """ ""+O>-(Iۿ%ѸnƖ[)K5vmy'vwo'MO v},%R̪4^e?'?JhW肃4^e?'?Jh^{:A_K{56iO* cK`ns͒7az&i=J' f<+~yPW))uh摺! mS[ F-s3R'MO zOҚ/2*+~yPW))_ +~yPW))_MҦI[ LZIe x.ڧo L~Gg?JhN+~yPsOjZ7L$"99+{eʞBʺbGdap8-# Yj+~yPW))_'MO zOҚ/2 'MO zOҚ/2 'MO zOҚ/2 'MO zOҚ/2ԕ0BYg79/G<`PTq^̧'MO ߇..Tq^̧'MO ߇..Tq^̧'MO ߇..Tq^̧'MO ߇..Tq^̧'MO ߇..Tq^̧'MO ߇..Tq^̧'MO ߇..Tq^̧'MO ߇..Tq^̧'MO ߇..Tq^̧'MO hsH-# ʢ4^e?'?JhW肃4^e?'?JhW肃4^e?'?JhW肃4^e?'?JhW肃4^e?'?JhW肃4^e?'?JhW肃4^e?'?JhW肃4^e?'?JhW肃4^e?'?JhW肃4^e?'?JhW肃4^e?'?JhW肃4^e?'?JhW肃4^e?'?JhW肃4^e?'?JhW肃4^e?'?JhWʴ>]VL 55߫EΒlEz},%RϽ_bYzȭEn *B骦jZ5ǵ 0BtMx⩲L2u@v߂n8Io &rm` mQVҎ&)k߻rB~Q`Ӎ\9>%E 咂KxR ?M `b|gvF#c~u$ZnTMESRcuY:οݨ :,r Isw89Mf&16]MSd]I$4-;~<NfUeY!p/MLw9I$I6T3[DU[,6j"[ilq2ذ?ύ/a_;6kn7$ż[!Ji"a-1q#qjƋAďTjd6`.ٴ2=eGjdØpyZhk@vgf&\"_6wIw˅HƆoO%S&N~F? Tsv+j"mc=١Ϋč iTi}1Wי3FY~ WdsS]r-J6Ia cjѻ UwzxޢG+!ՌY`vN `X QZnΩKS\gJ k KpF{u,4p5 C!tsF$Y~Zhq!+o>tK}%t ԽBZd3F~2܄XURDuV?&RzM襏VBhE,~nXUhK!4??7CZA&RzM襏Vg rC)ct=UDBhE,~nXUhK!4??7CZA&RzM襏Vg At@SF 8"sVuTRTKR'nȔM$ٿh= XJ+]{Tb*iGh(j-Njr $RH\b=]W;ie<h]褒0ÜsGw8s+kͮkUHew<ɻ)y>7eThL6ɭM jj&3/-u;kZ@h,`AmEki)~̘Ko 5꒻`j` Nw?C cwbwQZrQ>Pa8Q5Z;p];KգW74VIIa|m:4a;BڋL,wQ4N)eVepn].Mpe<>H$OkAp4 ٣5V||;ݿx;*~۫*kUs)GaX%sih<ہwAv (ͭFX`kg˵WMl'hbٻW\5uꂂCIK, Zguvch0_md-.n_υ@hve 57膏۶NDW#0q Ae7IW$Թ nv+tKn %s#. k K {eYhkENQ.c$Y&|~3 sZHiyql29:XፒL\ր^@h؊DMZHΝv2;:8;'mʚ=KugBGqp*b Y |֪a{㎝kӖATO mI3;XclX4KA |Av" ȕ__NK)-'sN'sKE*>'sN'sKl8l,^"w?mw?mxSR*>'sN'sKl8l,^"w?mw?mxeCʣP< AҖ:s}=I󌸒pVTDDD@DDD@DDD@DDD@DDD@DDD@DDD@DDW}[7"Q4OfJ-k`3>QpV|l#m:ĽCn>WKT&}SUhi.{2zq_Z}m}jmY#M;;(2}m}jq+Qgxw6+ކ|֭F՝?ޔڳG҃/ކ|֧zq_ZVwwzSjJ zq_Z}m}jыuuLISTp@ eLh4sojJ5SZ >9Ldҵ=k\菸 6ꚸҫu}l։̦jT&tM{vf5`vdGR^.iM\uPn5q:b0{X_qmJzT%<&. VV8kA-UJbsXqsK!0ߍqfљa2"~)N$ZWXKꌀI7.w:ᙔrՇ:'JF2 7dAn&ѱ+=kow,&٣:ʺyjM]ga676=s]:uU*Y⩕jˋ\;߻#oA.IU; 55"{]KcVrꪛauT2O4aٵ4|1ZDPabYm +E|DڀK #7.iFl$Ղ`I7Ȉ#y9ܾɮǔqB@w'y߆|xRjt.4MY W1%sE)me8Qmv'iM?o<'\/kj;M1goTl^ˇwmjj:K]`D-]# Ljss031eZHʷ8Cs0LuC;c9 [j->I dӬXI[ܜɚ(4J}qu6|+mom7m{+kU4kKZ=P05ÎqjkDu\(8@'rY]N[|3OOZ 0 RYCd{Zc:~wBћJ(("S+ukIs; sfݮ4PU>)\I/-F7VYhj)'se{#H\V7Ǹnp<QN"l]G$U@Brњ .9)88ogwdwgrAnP%Ҥ2J,nlL8@s9]"u'lʴk9Q!{.?=~ן}|h&}SWUM^Jj9$ak #WnϓiuO+2rDzEn9="xFtr1̑k=HWE+p`!K_H<'/GV}27X80k8QdH#+w Dž]S8P k;aNĖ6[xN_H<,l-{Vۘ. 8t]3њoQQVP܀꩜{j#+w cuDŽ¢7\[xN_H<,*%/GV#+w cuDŽ¢XrDzEn9="x𰨖7\[xN_H<,*%/GV#+w cuDŽ¢XrDzEn9="x𰨖7\[xN_H<,*%/GV#+w cuDŽ¢XrDzEn9="x𰨖7\[xN_H<,*%/GV#+w cuDŽ¢XrDzEn9="x𰨖7\[xN_H<,*%/GV#+w cuDŽ¢XrDzEn9="x𰨖7\[xN_H<,*%/GV#+w cuDŽ¢XrDzEn9="x𰨖7\[xN_H<,*%/GV#+w cuDŽ¢XrDzEn9="x𰨖7\[xN_H<,*%/GV#+w cuDŽ¢XrDzEn9="x𰨖7\[xN_H<,*%/GV#+w cuDžWW[ޖV%\C],ʷtTض]^A{.?mz#G>MadYQ:fwX\GkG|MMXkCwZ$F;Xa6&E-+5>ؗg#w2QP mQsq"'20wlryn;R fTKdx:] MmDrs#+~3Z QJze9ӝmPgp ,+ULŲemN6IlLϼUk*\iC++7PήpܤPAOWH`uB420=pmq,E}e[Zښ I J}ξBҸڑd㹔MS3ꦙ[&.仴w\p?UueG 3m#'8]K# )ac[h'Hdt={sI$QU ER ]^A{.?tew>4sojaj ie\wʛD}hɴTգ\͟Ejn nf&{@DnCNrG|u&]nVCTUM]S=YcF-v1ꏌeg-ȭ͂y覸:eDKiݒ2a}wNCNPIO%d#he \ſ]An݂G#)_qRuNc"qy1%@}wNCNij7 e5=-#ꥂ*TZ:XGc; v-KGW|u 5udSՙ k Ѵh kqJ>;'!wO'YF Ms|STx+v0AO8.d%l}\oq˜qͽ*CNrG|ugy:}wZJ>;'!wO'Yh*;陸};'!wO'Yh*;陸};'!wO'Yh*;陸};'!wO'Yh*;陸};'!wO'Yh*;陸};'!wO'Yh*;陸};'!wO'Yh*;陸};'!wO'Yh*;陸};'!wO'Yh*;陸}4Oe߽5_*.G|MMZj[nV䔲(';Md֞f3>4sojq뫨M9{Z'C]ӌ;n5Bim1.* i(fuC2{"訠]=So{\׀%n41:nsdQ pwb3cB樚k}uQBY&56lfs7멚 C%@q\Koԫ 5uرs'v5(鍲 mWȀY9w+ ,.?v?8N]J~q@?˽]0zһa "CWc=dއz4Y9w+ ,.?v?8N]J~q@?˽]0zһa "CWc=dއz4Y9w+ ,.?v?8N]J~q@?˽]0zһa "CWc=dއz4Y9w+ ,.?v?8N]J~q@?˽]0zһa "CWc=dއz4Y9w+ ,.?v?8N]J~q@?˽]0zһa "CWc=dއz4Y9w+ ,.?v?8W5w:8(*૥:A q~dF)*5O.?=~ן}|hD}hɴTյ,Vϓi[ u3 4؞5n0￲} OQ%n0￲} OQ—'R>QF ^'МaKdIE){Bq/}A%n0￲} OQ—'R>QF ^'МaKdIE){Bq/}A%n0￲} OQ—'R>QF ^'МaKdIE){Bq/}A%n0￲} OQ—'R>QF ^'МaKdIX-1 5,נel8ʗ q x8[N0￲} ~9ݬscTy$/xH_]P%:10 oZaԔ]P" @p }_,c#je*rL]ide =f1Ζy|~O[釜UdOmv9GʐݪӚɃ[ߴ;!?ҠKl[Tpis>Գ)5hӸ3?1t"8I[F` 28뻟8 ^xT:2lM$WN3m#Jc&8 tc7da7.NА>ہ)5gT# n۹=~ŭIi;tibdK\ Î*Nj Vfb;݇jty;ػ\=G%]\8qS3rN|M ]*c&yƶ7pϋ%6.H>վ?l%ÎjS>j6'㊼s`sh~a= bϏb=Uv\x?3{[C\q^SUcqM a ?N9L_~s;nM%]+9T߇hWm=W;`}T?s{|zؿ;GQ9mn'/,X9xB=\5\)ㆺfʉ5 +FWՉ"dn&Tl{w+An~5}( p̺1cLK+5 㚿M UYm5Z펖}0g}ѻp%8泱"r9vsv]ÞbbKOa߬{vM'dI???Sl+x95A!7?{<᫓w.#Lfm5t?k=p+27 Ǵa# ێ;<ݮkwSl+5l:EklI#=I^i ii~u5@hk{m$Gk;i`c}fnZx'љ!tMMZef7qeލ/ꂖSTh.3cl|G<~wBsHR$u|5a7G+K`Fgp+,T1,V0k =!peK弟(^lXzb(aKd){BJpN0￲} i^I``TuD]^AD}hɴTvT3T#G>M?d5rxMh;ܞ0zqSRZUTMqo 6 kK3i|փ5rx]Hփ5rx]J-ξ+U;^i |k.-kK#~vi@D{P5:wCUޅAr }r78} 3D)mqH6llPW9.k<'WCUބԟoвjf6ThF֚HYpWd (k)dA3~i8;ԵR|!BpO7]T*, jc.4tϋj279XK)ԟoМ: Wz9.V@PDW['8 ^I é>wP,eo ;Q 'pi*#z" """ """ """ ,qI7MPɋmvc\i*wɴ_[TL[QڦcGh6P8GnH?l|a;QG=-=#Kexαa"3ͫIpkF]BHftלKI5-8&}q # 4 t͹z:+jqaN!<Ӭwp3)#byw~uȟV<:GpkF]B䖒g<́M߃+ё>_N,/n\7Bo uqY'c|}rr/ijzʸ ~e'խŇt -61Vz02:+3?\^?2qxHSOW8䎒oh)#ѻtiW4̵=D\^?28䎑oԮy#ytiW4̵=D\^?2_:G8-IW]B䎑Ѽ.ZUM#-OQ9W4̵=E>oN,oN,/e\^?2.}J+#` /uu5C 08!>_N, kXKbw눃-8{]Ҙ<㨒 Kis~76#h~O_+2y[5#j'cj#-Bp`kr55uT1ڲ{s4V$n.89`2)bFQ%TCTg FW k0 ee z[0gylqT7j6"wr~,- si*dki[A{.% {Xi4VOOq0zxdqÁc `-+c'<2/0I][Q`|bF=(] S(&\)-zc14ɸ{w 9ݨk5V\sI`\ytIj{r\K7W7Ҫhht GGi^"" n&}mRUVw7hX}$zk2#+\5!sciVjKYMsikD8;Gٝ`~aTu54<2h{_K4.TgIUSpK {ntak_hcNvkw2JCKQSM8\S290ssٱXKNRRN$Z]g7v5]9OqSi-]U4t5Nm i3AsNIAw8kkA% R0KlW69)GsIdPإ.65waf٫TU2嬐S 8Ÿs](zW#wۢl0 uF[kwz].Zm5CI {c3Bijn4 h2DǍl$Fgf6[]q\S@ j5a\<g I9Y#;}\AUC[WM_fJJwLJ&v-7"Vyڨ A%{0d"XcsVelYhڬmf7#k*+-zCS=zVZ]WG5Qc$aǝ.t{#8!Ȱ57A{l6jw5~s wcYݬ_--},\qGcI Pk98Ʃ0QyUn^ꅊpqyS^ND]v^#3+C.uK}cn55GF#o.<n1Z-u7 0 0{cݓKc#89 p*mEA,Y͎8a|I`Z譢<&Ͽk? Eu*r]:gP}GSݠi$n#fsr;`n۴.(y0ꚧ74dὯNUƂo嵿Ji_?A_rT+?O TDP?wMyƉE]菸 6ꚮFT#G>M?du""""" 먧Jzc Zhs\;.GzB(wkV{UMxf$\9 &b?z3РV]C咾m",OS1C= OEڈH9&P[1rs7vC3И!D GzBb?zQ). QFD9{;''W%3oRvGU;=%mN͚Usց$UҊ6QWOUG-if5c`ThvGMEZ 5E%U1n֞7]-wb4{Dvݻn~-Ԋݻn~-Ԉ;vGMR ۿo6G]Hn~-ۿou"ݻn~-Ԉ;vGT/ӫ!ݔ\UVm+D@DDD@TnY.DtTVRSCMNE anNN7:D@DDD@DDة MM:ނ>Ѡej.ߣ`oYǥ8/ǏJՔ;ߣ`oYǥ8/ǏJYM G8L? 9xE~p;s~p;s~_<"C+k OxPFym=@ׇ:IZ23F~2G<`NG<`TЯ<BroB `X} o嵿JiQłs228-HGNƵ$JUur?Bm܏ŷкA۷r?B2T~S˵ 9pp{`ҺYeA wMyƉE]菸 6ꚮFT#G>M?du""""" /!ǴF_h %:g[襙6D3.U)hk}at(ukh%T r l89o2TyQSh M`dO IhsF>ԁ̭-\zn) p9Ǚ":kx- Ieck@]5rapvb]i \;IZ>]&m M ;άsAbx#Ju';XG8'$vF7adIYeU} [*5;\=o[AG|dWk5|2 3YL]5h㛟 (MMKhL1GSEBH;U87"n&}mRUVw7hXQa}(iqś.5UhC \av)%֒||@VjDFKd{朇A_1C,ѾH\+ZK 8 W{u&ѶZ(vL-l񯑭ٗ7Tn멸_ul$j82w{RtX{\SJ-mxT9n:K._շGWƚ#'`T0콶e4SA$ȡ kI$;k QWe%=+b׵C[Ľkg;wswF'.O_!3k^a9A 0!iGm| MKљ#G> g+|\1VpgPۙWPf(]G;TsoPi+WT:5IX1qAͼ u*!5.Fgk8k@q$[J᭧KLM%I;77eٳWSsO\$Ѻfc̓ƃ`=mjYU&ɺ hke8pO:""" (?HQ?z+U=QQViL$'914InI'~ULsoGc^api-sN =b}@aw86.;wcyUV+j;eTj#(a:|RZwg~7|[Ok!4??7CX=mJZr-4QsNcU0s =nȨw[=cM{mBxv'jFuH֗8#~_a84U%5;^ӪFw4 vԨ7B&hѽH+C 15ww+')5򊖘H--yi;:,kvJUWb~WPZhN> fD7pq=#y(4XU9 LM֝7S{YJ!q:.yW'quu%d3UWUIRabv]L 1# rC)ct=T&Rz@3XU9 " !4??7CNBhE,~n?M襏SK C)ct=T&Rz@3XU9 " !4??7CNBhE,~n6aAL⍌ k`iT_?]OAt?TB DEg/4OhrD-YPB]^A{.?mz#G>M?.ϓigHW\Aέі@vߓ =SVsKU z%nfUesvh)pw9V8eߜcwud4 E_Gm],jj4G,NvZdc#zh{U-4WZWLcnق!#j`F@ȣ)pw9V8eߜcwuy֫YEU37U UA%vɮ4HXGoWظj鮚8\j'Z}+Kc ^Ih7AY4=Ԏ$|e߂ c$ Ajym)Ԝ8;[hc݅7-WTW,*I/ Qn2u\Z=y!-W<07a߸~;-6PUi-]= HGni!;fF7k :M"HtVƵI!!emKCQjng|:ųn8 DPU/Vm+tlu<.c 1`/kO8r(!ZmUVT[fsI \~ciچ ;])6;M4&(-tQDLh:f?HJc$x0G+dhYۏtWr UeWSOWlQ^݆092GYiǚhdev[qSHt̥uƞ+M.DUUtjn5P=VG}3gU#p8b!ZmFF˅@pYEI㥅160͹?dso+|C TG2(chc#cCZƁD@DDD@Q?&M 8״ΊZoc]CMYR8GS dhp8p#*֫Fl5UYmAILֽ.p$I4&;m5 $B*jh 1uXFKlj.mSEM=X!Ocǒ\\F$Hɫq[ { 36sHpqv3@9{J? o o'[hɘ*g6i[<$v;ƨi?eQGۻv w?mw?mX :Y3@H$#Y`S(mVV)r" SR*>'sN'sKl8l,^"w?mw?mxSR*>'sN'sKl8l,^"w?mw?m?3Bҿs)P9 G0>!Q~Rr!V,F[?_h+*^˿zk>4Oe߽5_-D}hɴTu?3Bp9m75]OA#Q3t7G}Oj6jq{X܌qx::EF,w+~ouCenvkFq$v[Һ:j[SC<6YFYnrGmHGQpmU eFPg19$ddg;["*4)WP02ichFi:;[:[H*|FDښ_v9{kUFъFLƧݎuqAzUFъFLƧݎuq_'KiYV#]݈[SWkn:m(WK66N4P0 ֒Fnˆ~$\ѶvN5#v\3 ^nY-kd1:s4 b;9'AW5d.{}M #teclϝ-VPVVThcik1,; DP1WD[ Lsc(-"Z&ͩK>F$C8we*!բ[ke|l=H$mxAvrM}daG{I?CJCm mHR.Ϋσ2w/U/Sk Sҿ^6 Pޕ]Ϸl_(jݥV :Ң,Gm²p²x #Hq:X$ҙjʎ8.DӰB9SRmE:cC 4`;luZLvwnF.H3EaNq0?ap2+v/p,N2 @p{!Rkk)m4-\šCnd#W'pރhWpS29*Qx `i ~O04v|MK[Y!.#R>#w84HtYEK 5udnٝCq{8Vx piꍣgƣuZwc͂btBGELWVV2> ` FJT.kNҿs)P9 @DEg/4OhrD-YPB]^A{.?mz#G>M?.ϓigH<荟EҲwZ0l)wW63 d|m,Ѽm#]#Af$'G IAlת$k7 ư##vh V Nz+h6I#{e{1čPѭgy*%rA-ҒHhl,mgk1ve1ё:~MQZ}W0g)]dn7U5v˨8Ycv I^ Tb,sH'ekÎqiߝ2F<,\mc!d%kqn܂5qg$" #v Vkq=SgjjcSv5q|9Zk’IMNʼnֆW`KuyDW\*tJ[,`Ʈl0s~VsH,Ezgsl`i\9]k`̚(.VV5 穎vֱ f7ySeE5WJm=Y2X]07.-99bWEkK|c3m{H:uN0@V(" """ (?H]U1f=h6HA `nh[e4zx, Ai/c~5et8*S5WLCi\ o3pю~}Nj.mBs0Ha`nq8VgojF8[h&}ۍ#YnfGb}}Mc5ϑJgqO_go8EXлu =UC=&KݤӀH`?}I/Œ*37go,Y"?}I/Œ*37go,Y"?}I/Œ*37go,Y"?}I/Œ]:g/mTH洊 ђNZגs|v \SO%F>79i /鯃_7M:z4ܻ-jU;JA+EV tW^^ ޕJUj_h+*ѬF[?򲠅D]^A.G|MMWS~#?*]&}SUt 2(In2vUе{ -kNۜhQi5-UM 'lT.&VCZ fᣗ[Sop5lS )"MS ֌P-P\(Mfڟzc^GNHײ2 fU߻r+җ\>7DUŅ`c7nvNH݌}qњ(B]D#Nݛ%! c2ef2HV4T,AR{˟p.דa0tc2AEѐt\[8?HLcd;oI~5rG[-2ᧂ,1咵] q(=^ Tߥqy"tN29< *\'B릑@-dCv1 ξ+U;^i |k.-kK#~Ҋ +ET-t-Ǹ#fq:pغ"Jlk,&GJa sgϕM5Kq%=a-Sqs=B\ѶvN5#v\3,7Mqŕ|㍯vƮDZ9vjapYs>s]OZ杠,L#8ݔ\908ʦYGCZ*Ddž03F=2UNmE58pqPFg ;7H/uOqHbx#Ju';XG8'$vF7ac*zGU%^;#1 c\ݗ`ִrlDPU[Mڥjn&}mR-Q-8:Rj)Smm\Ԛ(^vC9.`dPRԮ S#ObsnܫmƮGiZ4;VB@NsBUZnI# ;7J2X5_' Et’lmk.tRF9l;j2*=$ֶWlb{Y.yoTgۯI4%Sc9o $Up-%@* \ X}:]>uѸ%DuEOv\킶gïIS,il2K]vF }7*+]׊pk;G.lMqݭ߂%>zXʶV*HxN\c88p"(""" """ """ ""ҿs)P9 UZW~*!V"( 9QFu'lʂޚwMyƋhp9m75]OAtG|MMWS~#?,DEDDUT5qQEKS[[# 7X0k8kFwo#=௚{3WQIIWOQU50 A`p8'h0FA߽"譬AS]TJzh4:h$ pAދl9k K} ($SO-D WNHsI `.i.3UD>[N.g \ &Oqgn3~7*&^i~r}%,/7F F~Np7DTZj-TD}Ƥ*\w0Nﵴy A6ɬ1lT:7MKNȞ-.q fz>*.5ݷo+=k}]Y][]c $ݔlϖ+TA緀iʈ2kuv.@Ȼ'6"a XY7ZKvmdXgm˷3#%';c.3xHh-u5ᄙ*x|/620u 1. Fʺ[9.4i7CG>gW^h mRWT4;6ZF2EI꺨)n1IQld{uucq#L6\UG\eF55q}ݥnYK=dpjdaq|L8Ρi; hP^2YUZ&A؇4J`8̭?+Z_APւ\bp A_8n4Xb\>(F dvjwDmڳSQK#{5Z@UkrэPIk%0ʚ[|L+Z૎s[DQl}g8l}g.Qh(n7I6^uGo Duz)Z-nj]G+pnq8gm(!JڪY䑠sƹU\U/EQ$bYh~`VU=D@DDD@DDD@DDD@UZW~*!VJA*DEg/4OhrD-YPB]^A{.?mz#G>M?.ϓigH7Vl[=ZZ‾L_8,h.Äk㢫sA}uNw-2*<ʶv.1k|{FE4yp$H7䀡Cd׳^mTVnm3VTZK;l#gvZ-mz {9iη&*ieK#D3UΧnjx|@4=êH$ DDD@DDD@DD4d G() *Jx):A4>9#9iFUM#-OQu}鶛4Oe߽5_-D}hɴTu?3Bp9m75]OA#Qe;hGSW-]@*}MbG?h =fmN'eC]%Z֜H-KYZ*nӖ5\P̈́l V@;$dDP8nM, Zg to,q'vZq,fWIO]9(Y#ڨ̱4YoZY%ن}CKEt[[ C^DuA,ppckayzR** #f}Uq!68ΣϝSR--ҝIÃ= Xʫ^UIo|N8C+7e5 s 6H-P3^MmG 5ڏ~]a_=MIԎOr-vpeQiM![m݄Xr@9sڵ>BijdA+qؾ#͂?c$.@DDD@DDD@DDD@UVw7hZwɴ_[T Uƀ6!>۵MEoҗp:7j+U'#t[V!ApWts\i$,0Z׷wo#C<דbY 6lvnz½ b8 CC4 v>ۛUCkfJۖ{ρ<aZ k`̵:51ZtR5O@%N/?t>\" zdMmuSdjp㺡݌;DwK)-\3MQVL2S0ۆ8B{yRDs}K.#M[b9l 6l{_v}݌USO Xk$kH$4vux""" ""_SiTؠ[_UTz/jJz.*nc~2o-==@,t*AKQ Q\hX9-wu*cg\QƣGmnfes;<ݽ Gmiukheʋ}Tԗha508QT1݀O; ߣ`&L? `Aڋߣ`&L? `Aڋߣ`&L? `Aڋߣ`&L? `Aڠ}ΣEI0wX(U)d201vmIs}GSݠi$n#fsr;`n۴.(y0ꚧ74dὯM\0'\0,A}˷S?TB/v[[ԪvJUj_h+*ѬF[?򲠅D]^A.G|MMWS~#?*]&}SUt 2:ER[*b dׇjHi D@DDEQE6ffM,m9:['{ tTMҪ7W85_5FV7bg5=sʲܩ6+julnִ$? hwr@i)*vk9c]5A_% -e¦&LPD6' tTULe+Eʢ9dd lq2m` U j#{A&u# ZQH؞X {nHpytQh6 Nc].RWpy9\ߙs5 a3K nW\H[TŲ:0IDЇTpvP\H;+Xm(fJ'1RHi, ݪs>pV2;]x2Pd%! Ar81WnuY(ϓn-HJS"iǾ=CKHyaU݃*.QR'" tnc)ΩnH8w3G8Jm'F}*zv:F:)X6̐[ .;Jm/];0jJm]=HnCZi*}iMgMKGN_#@7~Aݕ`iWM$s!|?٥cC[+h8g~PB  7ߤb&~'}8}M3?X I.G<`NG<`SxLO)g~A ..?~pPB  7ߤb&~'}8}M3?X I.G<`NG<`SxLO)g~A ..?~pPB ëhTӖ w I}2Z Ndx/ǏJq^g35_qL|#ǥQzPCx"=)xE7,ҜoYǥLW>zS5_qA fH'^gx"=**m 5oNUAjTxPFym=@ׇ:IZ23F~29Vσi}Rr3VܛЯ<BroB `X} Ǖl6y'*m 5oAFMNfu"nNN7:ҿs)P9 hJk5)u4͒9"tr-xQ?TB-Q@YeZ5?gVT~ן}|h˿zk>4[EވϓigK>4soj!fGR"(" ,N_AֽfZ韪햶uw?"/FiuՒѺi$`l55-3bd2[mf6y챪gH-o'鈭ކQT0IR_<_%!Y. OhdOSMGR^TӺ2 l[Ai8ܷm %[],mLPA}#5"7{cuXG:k-2 9dKC_5220kId;U1ޤ &fc-&6v֪x4m6va89x;^_ݑ'zТ %ϤRp*ʺ[pjxh'n4fx᥸]+xt$qTFG5X?]4kP[J>X?]4kAnrCk}\k\5s@@K e.mgHf ~ h8߀N$ ;H+%}$SMP$]SVh#`RIqkS)3M+4&MWjQel"I.k^C$sͻ;2Vyqhi2Up*$9s#Vv2v@!eE%-SAM0֍{yiH `ƤvDROg՛v"{4Oy`<nBhU.*+$i99Ì-*b$~:W~*!Yob$~:W~*!V"( 9QFu'lʂޚwMyƋhp9m75]OAtG|MMWS~#?,DEDD uKM}U91Na{>F9-c?>'UYSBQC.#eYTʞ%R;V662NhIP96ɮTW͖ٙ8lSQ΍ 88'42WCq|I#f kRWYcG/6Yj)elmlfHf5Y>$nQgI"e |Wf^[Omk"m.7]@{eKdkb]F ݖ|JFںIlTS>dׇjHiJy͞"s4=Ӽ= T4t~+-luM;Q Ke7Ih\r4wBFB*̼,I9jHC_ٗ6VZțK t=gG(=Q.GM ڧSɟ{#ve1k5r7T襢 \mf:88IRd15@=)RˤLd6x$l!8h h#~2Ajym)Ԝ8;[hc֖݅2OoO[U88@N{# 4bx=VT3Y݈g:c\gvrW {Uj8C$HwJ:ˣn l$Tk1v:{Xޮ,URR:j:$}u3{ ];jJbݭ=@nCZi. SCe(f"Ys[πIiDm+xR. |vpl$oQ\鬷kg.R *SP#-- ~Hþ$4A VEMlr{eG 9VӶF7]9[=-s\;H'ڭ! dW5,S6P4{~ i1n%TCmT S ߒ;Nu# ZQH؞X {nHpytQh6 Nc].RWpy9M%ϤRp*ʺ[pjxh'n4fx᥸]+xt$qTFG5Zdi0u}&JDo.|cvop.9nG M>m7Ҷ7!#C\Ӵ#w{x _M4Z&˳/ uC{Hx ʃUGtޫm:jj&p%3c_##-.oc&v>HM21ї2.sQŭVAv9]J5º略\%~"%hNH@IYZiCO{Z]C#2g\{I-=}m}jq+zU7ŒF"^ZLaه9dIU]@ji(ٴetv=[}ܪۏCn>WKP)nPi42 j+m4aXڝfK27n(t.4ҺzWꥑfH1Ϳ]|}m}jq+KM ,đyisH 7TXtb$Ԃ djecZ7vq;zq_Z}m}j ѻcids*fG#sJup{0^@nV9ކ|֧zq_Zh|L~h%R5tFS_A%n8dpf&9Ý,YmbUGV 9l EgndThQ`yI8<9I8<dH&}5\y0T;-;1K-z_\jQR>#5 txa%>9g)=z")=z"I1R zuMS34dὬrI1/=pVfWPBȷ@ 80@XpHʥ'/@^'/@^Uykѹx$UۥM-9Fq~r7g;-oOm\ dpEtҜEt҃|Od^H)Od^H(?IW1jt?TB=kE< 5[aڲL1f;&9__? Z"u'lʴk9Q!{.?=~ן}|h&}SUt D}hɴTu?3B̎DQDD@DDD@DDiI,{6I4k;" """ """ ""# h{p?DD@DDD@DDOWP;T8H_+ 2[.Z3]5T]i 2/`MP Uyszz޼YSwt&Ш9/Aמ=jr^w=Vz_ wt*K|.Z]U:wBl  y֧%>zgNЛwB^zz޼YSwt&Ш9/Aמ=jr^w=Vz_ wt*K|.Z]U:wBl  y֧%>zgNЛwB^zz޼YSwt&Ш9/Aמ=jr^w=Vz_ wt*K|.Z]U:wBl  y֧%>zgNЛwB^zz޼YSwt&Ш9/Aמ=jr^w=Vz_ wt*K|.Z]U:wBl  y֧%>zgNЛwB^zz޼YSwt&Ш9/Aמ=jr^w=Vz_ wt*K|.Z]U:wBl  y֧%>zgNЛwB^zz޼YSwt*M%sgg5sAP ̕8 {Gmu^w=Vz6iXHLm&Y0I8;88r ,F[?_h+*^˿zk>4Oe߽5_-D}hɴTu?3Bp9m75]OA#QrD-YVg/4O/e߽5_'ޚw>4soj!R菸 6ꚺkvV}O0*i` vpq2̋Y?6t8ۏECn>WKS ]/AECn>WKS ]/AECn>WKS ]/AECn>WKS ]/AECn>WKS ]/AECn>WKS ]/AECn>WKS ]/AECn>WKS ]/AECn>WKS ]/AECn>WKS ]/AECn>WKS ]/AECn>WKS ]/AECn>WKS ]/AECn>WKS ]/AECn>WKS ]/AECn>WKS ]/AECn>WKS ]/AECn>WKS ]/AECn>WKS ]/AECn>WKS ]/AECn>WKS ]/AECn>WKS ]/AECn>WKS ]/AECn>WKS ]/AECn>WKS ]/AECn>WKS ]/AECn>WKS ]/AECn>WKS ]/AECn>WKS ]/AECn>WKS ]/AECn>WKS ]/AECn>WKS ]/AECn>WKS ]/AECn>WKS ]/AECn>WKS ]/AECn>WKS ]/AECn>WKS ]/AECn>WKS ]/AECn>WKS ]/AYeN?6tD{7#M6STK,Qs7wMyƉEtߢ4N [紳R74ehpAHec~?Qrr[*''wßOD]\?tec~?Q''wßOD@.sI]?>˾~}}9DD?tec~?Q''wßOD@.sI]?>˾~}}9DD?tec~?Q''wßOD@.sI]?>˾~}}9DD?tec~?Q''wßOD@.sI]?>˾~}}9DD?tec~?Q''wßOD@.sI]?>˾~}}9DD?tec~?Q''wßOD@.sI]?>˾~}}9DD?tec~?Q''wßOD@.sI]?>˾~}}9DD?tec~?Q''wßOD@.sI]?>˾~}}9DD?tec~?Q''wßOD@.sI]?>˾~}}9DD?tec~?Q''wßOD@.sI]?>˾~}}9DD:Q2e)מw3ow1!I8A$|zzl٠Fˌw4WJFIFHHCreated with The GIMPC    $.' ",#(7),01444'9=82<.342C  2!!22222222222222222222222222222222222222222222222222"[ !1"AQ2STUaq#B34Rru%57Vs$6bcE&Cet…'(Q!1A"2a3R ?%6-~#$%| ne=ܓ'PO&)n=uYV )IX@9 5 (4K%DxT9)Ah7C{w=~S.m4RT/ip -roT*!N:wHj 8H/{ .ɍ bÜ?-=~Crj|[s-c XE[\,M;:Zu- sfmFVqQ9kE丁Q!E$/~pٙ\a6uw\9si%u!؍Pw]97~.Q ,zTsznQoYr5mEBoj=;Tj|=B%V^c P{,ץnssfi-u^v/NO'O/(7Us؎"ʰE='_C7?z-uU/-Tvno8:->n6:UDr5mzJ[Z)ǃ᪫əhu9*7(Uk"ptW?Q9w5</ /='-UM/%5U~a^dJtF/+[tyӓ}UM3:uFw̔:6QE_ ,MU6[1jt2PuZQEm wO?]U6[W&J.S5_W^omMU6[*}cSjDv= M:UM/% >EC}aSeHϊ ZPj2d..t-4UM9h"uCʺF Y+pk9#pvmrԬLncT{]Ojzcu:5:1Nq O)pskZ~X],8ܴ|>jH2JjL7{cc'1MܙWt:NB:.##b5?82l:] hD3B56ؓsuautƌΖQOlf&r[RqO?u>D{]Ojmze]-45TB&慨tMKqXQO@B:k4ڞsOmwC}aUC}cS{6sOS+piLwN6-k{O%c˨bLk- X:۵S< [_ [W+JZ:Fjq* { kHngwY`SRap#]#u JOX[{]Oj8:SS1,25)HE4Ex3 v= +KוX^'/uN_(T"Ә/yZ🏎ުݥpT:~D{UM:S%ѯ0 z 9MU6[ S[B_u?Q,,G><PKʌ5-Pictures/10000000000002A5000001E783C4177B.jpgJFIFHHCreated with The GIMPC    $.' ",#(7),01444'9=82<.342C  2!!22222222222222222222222222222222222222222222222222"[ !1"AQ2STUaq#B34Rru%57Vs$6bcE&Cet…'(Q!1A"2a3R ?%6-~#$%| ne=ܓ'PO&)n=uYV )IX@9 5 (4K%DxT9)Ah7C{w=~S.m4RT/ip -roT*!N:wHj 8H/{ .ɍ bÜ?-=~Crj|[s-c XE[\,M;:Zu- sfmFVqQ9kE丁Q!E$/~pٙ\a6uw\9si%u!؍Pw]97~.Q ,zTsznQoYr5mEBoj=;Tj|=B%V^c P{,ץnssfi-u^v/NO'O/(7Us؎"ʰE='_C7?z-uU/-Tvno8:->n6:UDr5mzJ[Z)ǃ᪫əhu9*7(Uk"ptW?Q9w5</ /='-UM/%5U~a^dJtF/+[tyӓ}UM3:uFw̔:6QE_ ,MU6[1jt2PuZQEm wO?]U6[W&J.S5_W^omMU6[*}cSjDv= M:UM/% >EC}aSeHϊ ZPj2d..t-4UM9h"uCʺF Y+pk9#pvmrԬLncT{]Ojzcu:5:1Nq O)pskZ~X],8ܴ|>jH2JjL7{cc'1MܙWt:NB:.##b5?82l:] hD3B56ؓsuautƌΖQOlf&r[RqO?u>D{]Ojmze]-45TB&慨tMKqXQO@B:k4ڞsOmwC}aUC}cS{6sOS+piLwN6-k{O%c˨bLk- X:۵S< [_ [W+JZ:Fjq* { kHngwY`SRap#]#u JOX[{]Oj8:SS1,25)HE4Ex3 v= +KוX^'/uN_(T"Ә/yZ🏎ުݥpT:~D{UM:S%ѯ0 z 9MU6[ S[B_u?Q,,G><(ә'}Ūv|Mb:SvtB/+]@9:A>?yjݥT6E۪5[Cl7(Uk"wwx<0t|x)v"*jt8*#5:9>&~5bYaU6[ ,4 /x*QTjQlFEѣ+roKSpSeJjZhnPm[9>0w9`9hH'өq~5;vPPEX@k|ʳ}nXYgZᶰnφ{⚪siF* ,a<*KsM}לj2f?S6OH"^An=`5#tcx&ڔSIЗTi9ygGx"s A.խH.A'unQ  m : ۾9@y>~M ݤpNhwB}}xa`ByS]Smp<"M4umOo?D1 3ϋZwh scАh< MTa;OM^GLQp w9ߓjN\UTj}*eltV8R67!'.5qqjSq&|?˗.vPO7d9eoIj6atOTER+LXS@'[s}ffTtdqҼ\!/:cÕAnLȪC{A<1S69\ #_>M~UVccJ.G1mK\HيZv׷cVɒWɚ9B95#b+A+z w *'loL]xN+?a'(AK$osS ^eߩ[/tީ>3`}DFNc|ZKTnJ Z:˿ip!L66auۮz-د[t.ž܋U F EфԀ5p=I|G٦N864X7ԛ@,zůe5J{CE\՝F7ݓS~] F Eޤx7Nuߍߪ{CE\T'w&?CSo>4[ nMy}mOwh Q5<~ohF1I~uc8x g٭ݐ6ޭ3imc*lkMޤTx7<}Ndo'57qnjd:?3y z8wzs#U{-G7t;I}c6h .z1מZPӧMo6'{#Ԛ>o@#U֝EM> _t1۱8!?? y9-n[b)o2&b!N}NcvORjnq|jjt| k\#|o(MNǒj=I/u|>5qu&N!S|nMMk^tޞW"5[_MN/N!Sտtz{CE\UwzS j=GƴSkoO r.!S߇&_ӈ^GƯSkoO r.!S|jÛ5?Bֿ&]ޟ*tp7~T 0cӘj|y+g6B3X/}Wl$GghP^]b.K quOHDjܶ|jrΣLߦωv:~'K#`$B9= 0 t]Gz|oV*9aɌXjGed̚ .vaKnj1WWVi1QU#_,u4 Zu/OfzTvm"B' $WH"iQwѶE)-5,T;@i S@ª.e"˸+Rw>7~L QەV;$%0YY%%G=Ƭ9SkoOQD$+.E*{ύk&ӈhzi&]ޟ|>7I45[_&O]ޟ*\BwQ~5>7s&7^ޟ*\FԚ 8Oyo'57?ᮻ>74UȸVWS7xST>7su&S~oc~7|:zS{t~o{j͍i؎yӮoO r!UݷԚ[xU0|h;vMN+oO r!AQQԚOIݠ.#TGF7wvG57@uߍ=!EjwFx7<[$3q׾7|nAۅH|rOAlo+yS='vX=!Ej#Iu%U̓c6hԚ?uԮ>74UȸObp|l{Rnޟ:I%8] #jy#߮oO r.!QN 5& 8YgvnMMl>o[z{CE\ [c#(0zSU6`MH.?CW]ޟ*;-GR b2aӰ]]oٓ@E ܬ&q_O7S/n_75M,r>ucjf5tۮnOƖ._E}Vz:XML86@Am,zW٬$t"Dܔ*#|Mk uKg S=c|w-qnf8`GI3)86 뫜KrVtvb;Q5}TL\Il3 c89NVX},Tѿtq\7pҶ;A\qTEOK$2MFfHɘ[6M)j b2ȧK'0 s羖AjޤqN>5euRXԔURR9,PL`cK󭄻'\jH)m]y /:CA: gpq׈|{ S,9ŭ6'PT0͇jYS6(+K949o{ Tܔp(1N#URMrGA#b},[Ğ#wPbFjewK[ijCa𶝢9f'MS$l 39Ļ,M "QblH4۔@$/'¨)go½'O ßOCq V4=3";$Ѻ MɅiTWbs{./g [#Ňud60b/ rξJ#pט#&fG,5C͗8]3gKSU1,gdٌ]E>1 Ppf7's\x͋Zu&aS☜8}fanS;XF2FlvkU|^ ~Gmd䆜S浭ebg(k%5m|meDLو c5'VGfeF܈~}L+Wa8jxbzf9s$/8 gPFPp 3:'AIe|yHSSz" """ """ """ """ """ """ """ """ """ ""_S(??*|!o5b=M& ˱0098ڇnH{ﵭ}4˹vsS~oTEF6w.}z6w.}zh3KߨͶ]˳^C6\AaAt / &~Yux&lPAͶ]˳^Ͷ]˳^D eܻ9Leܻ9[AͶ]˳^Ͷ]˳^D;vsb l+ 4`Km#>űXKL`o7kr@UVl2J|mv=>#g:9D%<&ZH*[:FUEQ.ac]If;'+t2!NwMCA00Z^-v6Q6kbά֜vVvwP 5SRg|F8ӾD-QO-\n/?=61/gLY_O6 Cg n2G^۷mh9K9ck״s6X%|8Lt΂1=54&`Q{1OYw?)ZwP\ז;.TSm{{?DSm{{?DSm{{?DSm{{?DSm{{?DSm{{?DSm{|#?ӽGj&ly uSL)fKhdrr[{Ϭ؟Ywhkf6cX%}1RXֹMNqQpa1ƧW%x ŽrR?1vϬ؟YwU-he 3N戎rܮ )<6w1q[Oxw' }@FB&4T8zme,CgpEj)"-\{0Mйڷ]WcmM&^jqobrǛlg>Q lg>Q ~Alg>Q lg>Q ~4˹vs˹vsAlg>Q lg>Q ~4˹vs˹vsAlg>Q lg>Q ~4˹vs˹vsAlg>Q lg>Q ~4˹vs˹vsAlg>Q lg>Q ~4˹vs˹vsAlg>Q lg>Q ~4˹vs˹vsAlg>Q lg>Q ~4˹vs˹vsAlg>Q lg>Q ~4˹vs˹vsAlg>Q lg>Q ~4˹vs˹vsAlg>Q lg>Q ~4˹vsTk,Zm89º[_B(??*!o5b=M&}7~oTE.o]m,&'SaloB=4Rֻ›aQ:W_̕ǵ~d6wǨ,=73t\ }JY*j:yidҲmge\9oqsz&OKAQWcaH%ܒbosRxw W2 93< XXsiӹƺר7n(({\:oԃm ;+$1F 3Sp3pZ^KnuN; 3MȑʼnƷ Xb IExQqzpjIؗũ7se/!MNZJgO4OR@`3c Y8 %,4,Nd3%<+s] l}jRz/'ګԞٲŮ!IS:9WKViwtr~Ւ58Ÿ/T=?k5m;6'b~MIخA15zYO͕co_O*g֜9ͤ@ܠd.؛Q,8,qJzޫcd"_<%e9M.,Qᘍp)'c.ؕ-Fڪ j`v=\ꯌl]It4T'N{l7?:nLaqkrssMXk'593QokٷY ~5CQNS,Ng &y2_1\:{w䆢)6V<8ۧNezqpHZրoklQоHţ&` y$|g1 #uW %j'I{l,7/]pN='9<} 2CEVdhJ= io GqJ<*Y6@1i{ * -l6IX$&Bn@-v;4xvaw# 3& c`ZǶ_~?[Ap:zWOُ_BtOm7Ĩki 4y$c01ᮞs3jl@_QWշϋ!5U 9PZc(xk} L Wо[TF"e)˘v㾛uq#I6fZӉT=>sy9-'Ḇ 2:jNʫM$:G 5ڢ&1M]Ph[Tm 7 " """ """ "" s-<#"&6 \z,Og"Hȝ1HXchX6ƞZG  s,LsݡsZpd,:.$ҼH͗VY]pnk4AUI)d){ZA_rSvv keä|_37.m/m vv`ê)N xy9o[pAZض ,:HHRkkq{]iv QA55tSqE3 w\G ]jms^;#Z$n+K h9"ǙoFnqS6=ٯTj_SlU{h;T&h;T uGޜuIޜ惵IbQ4S/dkG5s:N*/:Vt4ToVXLw f=q7 \<)=1L ^t}抍88=OK]OtrA{nsx:V4OL{,47>m,4Ӣ&Bx=<nE5s1fnJĨ 7pV?wup6߭,5cbu "nȈ""" """ """ F|Cwz\6U6k6twP鶋 ZT/:>4OL{4OL{uGޜuGޜ惵IbfIb`V:N?ꊏq:N?ꊏqYAڤDZ3AڤDZ0+yEGyEGRzcؙRzcؘӏNӏV|v=1Lv=1L ^t}QQ'^t}QQ+>h;T&h;T uGޜuGޜ惵IbfIb ^t}QQ'^t}QQ+>h;T&h;T uGޜuGޜ惵IbfIb ^t}QQ'^t}QQ+>h;T&h;T uGޜuGޜ惵IbfIb ^t}QQ'^t}QQ+>h;T&h;T uGޜuGޜ惵IbfIb ^t}QQ'^t}QQ+>h;T&h;T uGޜuGޜ惵IbfIb ^t}QQ'^t}QQ+>h;T&h;T uGޜuGޜ惵IbfIb ^t}QQ'^t}QQ+>h;T&h;T uGޜuGޜ惵IbfIb ^t}QQ'^t}QQ+>h;T&h;T uGޜSԳRMFp ?a[3AڤDZj'ݭcdq2H-k =Z3l4lVa7pm9ߟ}tI?=Rm.AAOU-[*%.62wF2O|QWn%KWC5QJw5tD~qwBK,n8*l Lov ]tR`pvSTrM/6AiULKgqlf6UVEJǸEfvgbvqu>UWOcIsx 7&MAk`IðǙT׼4؝H!s[Ű*j Jz)fkm&UT8.1_ՕUB<_)gc(<{X1v{[)ť8CĦF$c`,=2co+ HaƾW~-7^b;13}-P%8d]`FKz!GnqAR;MֿU3V( 2ųkom*a`0­U6[IdZܬ7sxV = M KP@3D\]QluVYT>Y$T5!JcI:\|[j9 s5g)Up42FFQlX1qJ s@j9ci햇 }< !{ɀI8 ~2i 6~%ZxIt-^ӮߙWm|Qb5#w+Llsѣ\ƥ_+J\JOb\3 ih#|Y&,IMTEg%ұs$`%[v |U+eT}T1[+K&:@g9ms-#Os i2ELhmq@Vikn7nKxw7ř7]-w<'–ן}7PWǾOT71k r1"ٯsN-~e SvbqI ji%ZI٘ h3_K.^o%*)`bk& )恱b=?f.ڒ,58}]%;[7Λ7 h,[} ߥmm94uuTvw 9o'f;~7[c?n3: 11{55h|>??+Y6VοsOӧB#lo5ADEAr*=OV5\JSh[7>Fv V#QiY3]Up w)*4QbT +h"5x~# JxwUڸw ~f֫]85W u=˯a5jF۹vnrmlQ6l7 {PB7ӳwkemzc}]ˏћZ w|N?Fmj5_pSѼf 6F\[+m㠣~bp0^PB7Ӳw[i7NͭWpk6\ڸs.L9WZF۬;7x6VAF`&"F[k۵;6\ڰVҾtO t;u=aA#~mfkem~k`rmC:潹vnG&O^E>*"kH \< ׏ћZ w|\9R<7 kӳu,mu=˯a5jF۹vnrmlײoq3kUDrHֺٝ׷s 0#~m܍;7x6Vۡ|&Ì*jhKn/aCdm~m4N!uIx4h7gؽelPqWT,5dl,48FAEu@uuN ڜHBGLevKNG*O^qz^~ѻk)TIQ4btO6.tk÷X]NKYKMH4- 8L^!ņPHk'S'Vmm/xh|HQdo e͖浹6‘Mj:5[h!$o+-o&i7 %v{{v{ŋC1 x)h) 9$Xfkk$;*ic&{X&׋N=Kv{AAmv>CZ]u8/qR}]'*O_h'*O_h'*O_h'*O_h'*O_h'*O_h%tKi)ZDNm;-F3b0a H W7TdS;} GIa.-<[n:Mpů~f֫]85SћZ w|\5o. ׽!niٻɵSѼf3^oͻf&t羮ͭWpk6\ڸk]|3 {PB7ӳwkemyu0fAߛw#N.Mz7ܸw ~f֫]85W u=˯a5jF۹vnrmlo. ׽!niٻɵ^6\ڜ~pkjᮧyu0fAߛw#N.M:0#~m܍;7x6VWrf֫]85SћZ w|\5o. ׽!niٻɵSѼf3^oͻf&={\~pkjq3kU0#~m܍;7x6Vz7_ kr4[gc}]ˏћZ w|N?Fmj5_pSѼf3^oͻf&OF`{ڂ6F\[+loq3kUͭWpkz7_ kr4[c]|3 {PB7ӳwkemw.?Fmj5_8w ~]OF`{ڂ6F\[+lu=˯a5jF۹vnrmlױͭWpk6\ڸk]|3 {PB7ӳwkemyu0fAߛw#N.Mz7ܸw ~f֫]85W u=˯a5jF۹vnrmlo. ׽!niٻɵ^6\ڜ~pkjᮧyu0fAߛw#N.M:0#~m܍;7x6VWrf֫]85SћZ w|\5o. ׽!niٻɵSѼf3^oͻf&={\~pkjq3kU0#~m܍;7x6Vz7_ kr4[gc}]ˏћZ w|N?Fmj5_pSѼf3^oͻf&OF`{ڂ6F\[+loq3kUͭWpkz7_ kr4[c]|3 {PB7ӳwkemw.?Fmj5_8w ~]OF`{ڂ6F\[+lu=˯a5jF۹vnrmlױͭWpk6\ڸk]|3 {PB7ӳwkemyu0fAߛw#N.Mz7ܸw ~f֫]85W u=˯a5jF۹vnrmlo. ׽!niٻɵ^6\ڜ~pkjᮧyu0fAߛw#N.M:0#~m܍;7x6VWrf֫]85V7E oR*En/@.Vz7_ kr4[kR4D VE`^]nK-buv#G3o 3-G-1r#4c|CE]>U(lI[TPXY` ,ZrE7.?'[S-5N'H2YȐd$6[a{eix>rMECOTqUe-h"q%gZ)&ms<56 OG8{!m5@h١&&8IH%\cо`ƫXƸXY.BuKӧ9]Yǔ'`T-2K]Xvp,𧻓1AOQ):, K"o$;v4rjV9DnLFy'Sސo{OJf`TWGtfhCuhHAS&c &ָ! F}IOƶ>FcZutF=F͖v3se;Ke;_ T`>"Ll)$뫚OZըٙk.k./p}.~ɯ踿HyO$S Z16iЂ F3Jh&18斎U7O(ˉ;%J=nO,cOOF670, 5d8b{Cr@/puS|xK㫙=R*7Nb&ZXә-yZj.u-.RbXo .)m-x>\7 <44_ރUEϐE r.7]YV0j# u sT7SSpI!yhZ1+O>΋a[[A>'šsRpfF˽ٍ3^ltd6> "mmߘSpns9 5SekN{[^, a*gi#SګX`H(&+m6aWEEbbC EC[¶=Ujk(0 DjWY[)vc`V3LLD3u."1[.E,9M3xk^U][m  ]LtT088Vٮ8`&juб4K8Hq`EݧDoɞδ6q'OR1F H^sw5U/؍SSU sDn9۳zAoɖ46Ը^U;ŝַ')&[(ZֱZᛀuZXV?ayp!t6@Z A6iD[C[]R)S;i$WX c`QDQi(%e3GI `־zW: I4Z1k36ݾQplٗsa h$h ̑\ZCW<2~ CQ]SP׹FbXJvzeEIb='(˕FrmV|[WfLX$ik7K٧B-8'YTcFS\0s K sK^_]>f*>/,8en :YVƸc -w 2\;F~~M?͗ =hk[i6NhjQ>&&bSyd8MN0V8hk$|j"FZ/ hqA-}nE푸6=sZ c% '«;-3瞦du.#/kO! SwUY-cDrH3fvpNw QC @$kiqAn9Vwmک" """ _S;!{Vv[4S'xc!7ӗ}^St?T̈́vkU/YI&[!uMU +7TpU89 \ۆ` 5*򊊾#̯z \U0AF jLE΄~NG8q)5];1MDBt\퍡E֋I[AHdKRyb} aypEqumk1iVUEJ"mZ*B34Fa0$|>ﰵ:ޖz@DDJJ nbnsxXX#om/Dm?#'T6vUuCii?:ԌG봟m?#'T6vU b?]TR1~*`P:ԌG봟PF#OWL T6vSOH~I@R1~*uCii?_0(PF#ONm?#'&OH~I b?]UDuCii?:ԌG봟m?#'T6vU b?]TR1~*`P:ԌG봟PF#OWLy=^OM,]d; 㔚\j['CSlnAYUM W6 ;D@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DD>(8$Bno|H s*߭Gʷ\fh ʋG0v ʋG0v *,\fۣ q;t~^;t~A>1n ADX?H'Gcv bdEAۣq;t~A>1n ADX?H'Gcv bdEAۣq;t~A>1n ADX?H'Gcv bdEAۣq;t~A>1n ADX?H'Gcv bdEkALLA.3n ` ;|TX h/x?H ȋGv ",|bAۣ ;t~N1n2" A8?H ȋGv ",|bAۣ ;t~N1n2" A8?H ȋGv ",|bAۣ ;t~N1n2" A8?H ȋGv ",|bAۣ ;t~N1n2" A8?H ȋGv ",|bAۣ ++l}Jv SDfHtp{YXt?5Vv?߱G07'/k^߼)2anbxbxRn%@++ib+H˿y[= P,A  nƧ nƪԲE/g8rI;̓ \,DȮw8rl5J0vjpvjXx9fId6f 3`.y@ɅCSCUrNF *h2T\ \G:,:3 E_U`DEAGQYSt{1sK~sK6{\h-|2z:54$'VEMNbm#C C>Ն"̗E #˴Nd؍-. LOJq%Kpt{ oqt72tk9|iH9%ټki)QJIY+bd wbѩ" jᯚjj1JJz)̻pk ָ6us[ *wEҋZ iSC –UORՖ@DDDAġxX㙓5ż;3MF/eQw~F)px63`aMh4I02qJ.R,r_OBmSP׾6pf$'uF.)EqOQw~cԺgkC1.fN Qw~]ye7J\tq?N)EqO]+WI8]uy/gxɐFWMMFz?9Wٚ{-lEζ+;/;_bTU0#CZ^kZK,]R_?Ϳ>{ɒٳ_Lgsw8Vw<^w{T ?)EM$Yvok-#T^YyX^YyX^YyX^YyX^YyX^YyX^YyX^YyX^YyX^YyX^YyX^YyX^YyX^YyX^YyX^YyX^YyX^YyX^YyX^YyX^YyX^YyX^YyX^YyX^YyX^YyX^YyX^YyX^YyX^YyX^դ· h }EDkXCMȶgs-U?Ɓ> 3 3*lyST~@DDD@Z͡3VP1%;'Hf;[4ApOpsjp'Elfy@8CGO|}+CUKSa>#Eh+K88^$vNn*Jj[tBMN|Yb#La 3k.LJ>?Gse^ں{QVb%4 l1i@^}Ɲ>|!D@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DD\JShW'?s/Ћ~m$ZK^T/퟽{?*Jޤ"(]M|/5 |3)5C gn!U1F lִIQ+ci76*8v%&$}|%Bڜz,ǠBzzTV6)XAh]%GUcxFꍙ Ҹn mwjl{ IGmu3 (%c9NqsAp27 I0暗fs278Ymsmߴ]S'g-D{\ZMXjA,8]x`H5ݪ)p9)꤫1nGjwo\''ı#X^' c^}m|Hl"Gʼ*_?z^T/퟽fZbDEhnڋmd} EmS:zx49Xrl5:b .7/^e=,؍%9˔IA';vа w vO #\#Y.\Kr:S᷇zϴU=3jnѤTSRMK eN '3v&MHDPqUMUT+\d{"kKYFt_M<2M3qqh䕫|*N u,{I-AAEL7NmPQMψc4[kg.whyA= 7C++`9۹m:BqZ|/;=ֶ ˿ۊ L>:l e,BCn{ jmaYK0jTdŰb|42饍ddZv M,4MO$Zi"]bFXGD_%͘\l7=mQafd:Tz^N 8| Z䆃f6NDZ=5I|xo<2Bg< Mlh9TqaQM |5du\c1 <0l2W8C'pˮ (~#KR9xH:-,p6-sHB""" """ """ """ """ """ """ ""O%P~4 ƫIT klX э"?*J޽W?KgYؑ@DDD@DDzYvKi|79y[d8M5 ?q êYQ-5O`s9UaMdv1pFFb2nt<׺\[yuC}5*l*ip7s-U6/RbT h ]Ց GFכ@<*ϼyA tRTrSIWc3.qޫXN5T|0,'asP^րu#K|sE.٧BTpI#.8YT͊lK]UϐI={1QkFPnI:Yt97{GG6La4ue. 3I+ =dٍ'@m3SqZ6|![ <ƺg{\:˩n)/)77 5UVl)WSﯤw8C?6`XMh PCSYov)USﯤg8C?.`Xt%./˩^s|}^s|}i+Sa$ش#U#ML̚.wXX9y~AHۚ*2c-#,qqmX. 6k69dVCw>i 7lWP:,7ĩAɉ#Kʧ:IN,G6lK\3&8|vg$ t:zyyI}7E-uU&%d 9sAmd2%idv34XMM%luSPe-suslQo<_ bG6y\eue.OJc!CsJbR{RGFd9CZMutoA0QC`au.h}3K.h.K\Hnpzjʈ,omtɖHlo Gs氵6dj]ENOlEUewS|!G5m0e/|5t!p; lF }tPS~Ap; ٚ91ڌ-bR>GHyKRH܂~PGJ,5R{+Y,&X Mљuԓu$bUs!5Hi1&:7溲33˪ٰ\;Fw9\AIk)'sn_07 E8x j[lt.M5#K}a15,\L<ts$jN>a"GI/,`hh"ʰtdDPW'?scU?Ɓ#EE3V9yST~Gʼ*_?z̴ROxZp > O<[_amHY]PYwًci _Rc5@C*,Gͼ4܋jX7wm}淗'{9!VGf;aw֋@72O>7" snKNoq,b 0W&$EQ>Ymcq78_t=k }8̙sihH:e7;blm%Esi-]ܛЃQqA81@j8# k HA9ikaICj'I#eahql_8v%Iq)xhs@6%P h""" """ """ """ """ """ """ """ """ """ """ """ """ """ """ *Uz@j?A F7>^Ll"NOyST~Gʼ*_?z1""*tؕ*bs<81"kr s_ɾڨLxTaOfcu3ǗT# >:~<[<3 KPǗG Ѐ ٛsq_ٜF4̣Z|^zH3=x}61hS$0b{r,u]k{IfU6(옵,ƍΩ .ܲY6.v+ӟ:?Ӣ:yCäcjYigyw2.Hԝ7. }_VE]UOW%T3_.Y En|sMAD۬2 s[NemKCˢ.V pIF b<fGSGbFl|xP@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@U?ƁXr*=OAnoEE-39yST~Gʼ*_?z̶Ĉ" -n%a3Tj { Ygs̹kfzVRk *V4_.o&=7,qcdSTt rT8nI{[zخs9.t þgokrn ^F~;u}q_ [ۃy\%DG `'#3H6:*`ts5TŪ#[+YB1sy!5ɔԝVb}M".o(p jxbDQEf0\ssISֲӉɚ1ƞFCSs&ۍ{T9sm+dqacaK-^`b8$]49"kf{-toaU4X)cOXhdcG=mW.ѽ3e*vLZZcFT^ZɄnY,aM_FwJqO}ɿ~ tff3SUOAPe´[6`.vl|xVhFgCMX_c8%IS r15{浭 A-\?)x$Yv8p@!C\*Ji&nAM$+Ct[ "UVG;ajR =$4DC\\Ɛ-;2TCYKM4 Z7iB ctϨt>6k#o i.WkXߴK0:XHbq^׍,˘AEv5B0lMZJ1y$sf5ڂ4ݡ_HD'6:Y]#^7B -H\-xd :i \s`M^ßS<ZQ0**澖V2lwy ͢dulrO$M2WJǁrFkAEv ݉fbwIY0'WSYMLfefdg!+I:5^#Ň`ie*xZ׎ly '[48tkdl%WYa.6x&359F|Cl ܑ{*[axN$suKG/h@m/.~o9beK3zO-C"'l u+gڙ}>,ɝgӑ $sZZc] !z<2: X . isk 9S sMU.)QS†I%IJ`ۖ]dJѹ0a%ۖK>6FXnE}>xPik<"n\*Jz*u; u2fe<g pjNIg62sP^[Wr(9ڙq]ik\pRJ翃lOcxQkFy@[1$m U-NM.c${'ۑ:Q>hRAxymrjx< %#e\-Hk\ ţRñfNWN細0pn[rݧlj7΃ETڊ|c3q]n 3lZX.Ӯ`uM(fRɾ[Z 0pnxg T\:7ܓ`Rz.e w sZjYmukmKTRD1 d2w]ֽqYonSs›q9=+E+Y,gD9$9xe1i*1|E͇6dh6r n"i΃IGx+O9dIY# {@%h\,ܱbX>!E+J%edRe6 Nnst/:7y@Î8 +gPmMn"^W.tw Y'(i0fi>>4OoAϜN ^\2z'Ń4ύ ֐-r>>;mB<*WDylr&qDXgD'H%`;#ø "" ?A+O%P~4- H o,Z~X>UOR׳*l2"(Om>6W ^px}DŹkl z QG]Jʈ3XٳBڱ8j9ƫIKf O^UǴsÌXm h% &Ą; *Ol5 `w@$t!Y9.uEY5>'SQȝV==,$g16tXeꓥxsesMh^ 85͵gshWSbtrp DuyT- S4BQ9d-q79F/hp[ p<}#ƺZ8eq{@ l~W@lw'=!j}u./__<ЈC p`k.ԯjg &v"f8secND/{[#kk5:;RK}\ sMU.)QS†I%IJ`ۖ]dJѹ0a%ۖK>6FXnD߻j *KO44/KO$BP74x[ *u; u2fe<C ݮڊzqJ6:FRTHZZr^ᡙZEή<,CW*z|l e}J)pCSY/!l@tU); (r9}8`7ٞk[,d͓ v1Xwꆼ_5X0F4rfsMZZ9P I|[SI0Ɛ5ʽuX#8Ji@ccsN֦J Fś_Q;]^;_#-m,{UQdGTOI4<1KN̶RSM+eV# ÚwY>?N-7>s9ϘJ/k u/<=\@1F8]%u :뮨$͏aS>;HLf5\ #c~a,Ž$qa!C!{^7.`|]W0ُ͢|:Ì`كto)&`x]8;|u0Գ9~AgᯂPilAmplu@i"gSCU-Ck]mi`lF@ZNAdTL!p 8~3i0h8.!/G41/n o!UkGTS[ץGK5s]vKqX0hWT>*噵ՖPr-栚ked. =h>bÅ+\k?|2[6k*`)QRUq[' H:/cclBqD($4Ř"4 `y1 fCRb4flЂk-#Qm6Q g5hjtĪmn'/.E=ngKNclkH{ٖZ4`W OR]&v5fchqڐtUYbuLd5fq\s@ :u5}m5TtU#g37""(ITUz@?6 |H*lyST~%$DPA*|EC׀}1y+@ݯ?z"XN RؘeqKX\̦M眯QAEt sO}ߍtD ~/ܼܽDND@ۮ>/n@DA X)D^;ͮ^׸7|};}x>>DgD'H%`;#ø" "" ?A+O%P~4-k`͘cJ*m5\48CO WC$ 9@:2 d t#E%ٽ}4(BrӸp7|YtE-3D Yt~\ Yt[+N:"D`xΈ0o;ΜYtDɃiN󠁧;Έ8`hNxΈ0iNx:"7tKޗyלYtD w8:"dyל YtDz]N.-9z]^p ItDoxΜz]DYɇIgy׼\tΈ1x8<.*\tΜ\tΈqq:qq:".tΜz]DCޗyӋyoK7tDSyӀoKXyӋy1,dyN.& kw8DHe N.,e<x:"\<[D ;$K:"H8v遣tDKg_Ɯ YtE1 58N:"Zah.w8:"Zq$A7N;Έ/t+{6@sX0p-gyӀo;Έxμ+N:"oKXDD>\i6.xg41Wϥ 7$[Qkꈋz٠FB4GVJFIFHHxwdumpC    $.' ",#(7),01444'9=82<.342C  2!!22222222222222222222222222222222222222222222222222"^  !1AV"QSU267RTaeu#3Bbqrt4$%58CEc(1Q!ARabB ?a9O#Tq͔ZޑZ=h"lTWVk&m֖ln;H\6,c[o+k;sGb9n+y]Q;-$ds3?R|'0"7#E uCr8[^`TKn+y]P܎5#r8[^`T7#E uD܎5 n+y]Q."7#E uCr8[^`TKn+y]P܎5#r8[^`T7#E uD܎5 n+y]Q."7#E uCr8[^`TKn+y]P܎5#r8[^`T7#E uD܎5 n+y]Q."7#E uCr8[^`TKn+y]P܎5#r8[^`T7#E uD܎5 n+y]Q\2Ts/ TOk۹#>񘌘Bz%FL!-IlM5w-܎5 n+y]QthS]Ùu1`EQ ,IrQV[{$7#E uCr8[^`TFi\qܶ[호pنYĿFLC%k}GpopnG q^U-*͕))R}HqOćNquD눲&V=Df6pnG q^N\& NGb׮ʣ)ifIJת܎5 I!a\]$FoزU^%qkRui;e˫PnkPeT܃4[1x?M n+y]P܎5:Bk͸u 3YTGDyn܎5 n+y]Q."7#E uCr8[^`TKn+y]P܎5#r8[^`T7#E uD܎5 n+y]Q."7#E uCr8[^`TKn+y]P܎5#r8[^`T7#E uD܎5 n+y]Q."7#E uCr8[^`TKn+y]P܎5#r8[^`T7#E uD܎5 n+y]Q."7#E uCr8[^`TKn+y]Q O:p Z3vF+T&QB\L974 P˛kڶ{Zl˲TFYg!)FyZQ\e?` x= iXiWOdpȬ^,=AuZƊsEr=#|mʥ~%4ǤFԤIZ˄nD˅hCIQw7ʞ<=AisGb-3[x}FAPh;XȏxMƔoYțpJ<-CHH{NM;_%mdAGz~;ZFiDfԕT.DfRok2#ه}c@:r23xʙmI<@[i4ڙYZi;v FEO%MH42͝*ʓɥ Dfe"#\MhTT8Ym ~6D]Dyvld^7].3{Ne9>Mկk1ʣIi5)Q2+'HDkV;ElJZ-ckqӊ-i5lD(b9)šy掅.5o߼6'jPq 6dj1*X[WVTtF(fWN#-b.Je2zGJՐs+YQiJETg8a:NQaJP0f|y;PKʌ5 layout-cachecd`d(Had`d``e2ā 7i ?^10ܓg`-US4;PM /c+050#`vcL;bhp20o@5=hj <S tih)e` e 2B1A8 cӁ PKChPKʌ5 content.xml}ےr~EEOn/ xs$oOv$BjYp8E8TF/ؗ}YG?O9~/83p#@@=bW֟e"{rkwϘ,v'gn0&gɟK\ھ^ /2,ߋ__{|lx>K^F֥^e%%у95~O6-L_~mǟBטG\lENgٴ7uzCojʶ:8X{RD\>l[/g"F xaU;mہkmڠ۷.yر&KxIy"`;͡ 4e'rf;ȿ37{o'A]+,Cu!LGD;>0;u8wv/_bNucˆ{r|i4%N`ӘsK$'ۈo3=c@q>lL;AJP'#o;b+?Up|+{(v.VNd& l#QEWƝDu{_+)O>Ӧd1^²e$^p_?9;眴X[ٵ+[nxyx_?7.Xx-4ȸb[D .4{ˈCհIܖlչIZ4{i'MJ&*F{,gPQM}[K8)I织7N_.XCPuv)9_Gȱ '<迹9%c)Wkw;Ш,+ۿș?!o9wי3C8 Pe > <9ig T&*Þm!UMJzѢsŧ'YaCf厇  ~ }F=".8 h|e;5>0]X W{]+}8w |~φC?jFI^(&nɫ(l{ @Wa2h$u1( bMXNܧ^q~D*;5Mˀx5kR(_ß Q) C0 1 sK:wQ%}&I;9|;6pŽpcB-Lm{xg(3d=G?= att{RE٦GGՋo?$ P&.>0Nr#"V9]10 0ꡝǵc;ERԗbm!?Ɋ*<Ns-`8׈1X^xk^w ٟ׎- 5ڪd43i,rX̳6{޷.Hdr8^Ό 3 ,2Hkw,Bkf{ [vp-IK u(vPCmvpx(6L- <1jj}yBAdmw`.#Xmx &e`Ro/%'XRCX8lnh)>fo+@+( (kU'j1=4˱`pic`@K {;wjzsՉjvG\faX2 7GýfӘi`.o tE]rRX ODˏ0{ WЉ"b$l!Ri.4<"3Y* 'gy=w\l># {TsF / ԳE i0lDm@V_@;5QQbr߼#8 ѩ<풯71!VAu82;®hZj{@Pqm)/YSx{<\ZlF߬O $\9*uBf8Z۷@c$`a:BG.!]ϞfG3/VD YZGmh,Y,pp9`/25WTCK Hq&C#w(];' ;V Ј:3$ IG{18{0Ԕ1[.vn EQæ@Zn͍@꽞ie~SX\0KQN7ҽ QyJDJîl~-Õ +12jj ڸd A52mبȞ~(myQ[YaOiv6 U\ 閗}ɑ9t'swd'ûe0:pʝh"ɃrߡaH xf3=!m m|WsZ#qPdhj!rH^KSVvՐPI6;yx!z+0| p 9b~fPBWc;0Κ 7]@= *PqA;S7 n+9skaϼӿg|6l;ۣM<9CO?53{!R?`Pk@fPddXxX@ój X{%{+y;hQB6ЦQa&adDuvaߨ o艤@&ڍJ3Tkec{{c21ɴ1 '"cn7tp%3xWm=b%j4AO)\;5*#|z!wJf_A$!5Cş@%F'}-]l)~,ꠢv5kUy]*$>*£ e&dVYܯ/Y%ipM+A%>ȈFL~l wì;/ 9=ؖxp`}leЃēsYk0L]l5LStKRvK`R1(| g=e,U.\ ^6G ~d' :ZuЧ{ԙܦTSŇߋ쬚k#ePdbv0yHئƼyKz;m"]qa~}Q؊ l|@Ѻ1Ȯ~? BMHYơҥpwGjS[$1)w| > Ǟ٘sHWrNLk q)+Ĭ0Ҡ̳iuĵ%ȝG#ܛbonI7GM03`3k!`Gњڿ#Jh**NU#c2 ڃ?mw `/erCXY0 (ݣbH+)[]6]i-d6$n*bzei`5 0hPS.ɾOtG]ՃbQic,c)|;MfMCL'۸=~{'#ԤV-&3q#keTNIB@&ⴥ7NYF @a -5@9dB?R:LOG3ѻUC_Lf3zcQ=l:նi(G&cGYs 2U%G7*I3(%v9JBjkQ8yl!A К뤦"ZxX /4ʝb&;F:M/S.yӓq.{mv ,T'ZR,^DY6AQK%aCcGaEi4p }.^dщp#^>{I &Ty@I4{rg๎׸>0w:2D$KYf͕&X;FNE'@C`lbQ6!.5GsQ))25S,"Qѯ "gW_N@-i:Y95߽vmz54JzL]uI+B!E^vN,yΎx>/yqZ:*b7hVJ扽W$ڊF`)  eө+ X8 #u̫_ɕ,ZNr͟b%5AQTXl5rWpW 3f㚰a/H,Оz3}f 6ʩl,@^fCՅ~!.HJؓ 4>O鈝Ϝ(|3/soGn'u ƃ%PFa8)>`Cs`NƦ׺DcU#Pa`tߗ(@s 'p0shj% AEzX|Nz 8a]8ڤT:8n|46r>ϰpMkKCq"YpWԡ#{r O,"( {`9GmF puqp|a_Xѩ[xOnmPomYGm:4[29QR'xq|Ht|37r{j9FY]9am;X*b#q;[4NL#fڇ xg2"OJ@@!5qOc M˳NfmXu"e )^yWMf``8 W0wIEk:kԺ:n`]GIήs >1wX{;ttRۋVr*#X <ɔ:~66ΖAN:uϞ9y_RemUevVj&H{WR2cл6Vkxܺ&n'C4 G"K=˲gJ RNv:Wc1 +(m ØVyne8"W]Pl@?|,摙ݚT24SyDLpK>+ld`ڵft ? `*@#q+OAb]̢s髙"Y/aUYujGR-x"zsTBkJB.‰R}lN%9va2)Zt hlzCtIK['"H6ZN~:,y8o,v.#ޚ ub }oX&+ӼĜ&7񃏔l~"蝉d/9(%G\&*L@((4vo%!x@QEסdTn=@Vy+9CI,4/HhCJVA"Ga sEa\m'gf^yi^~; pWV] OebQ }-So?,v,/8v$OPd4Kza1Q%Q7hMfQw'~3GovRwYD̍'h_TН >4J@k(7w)?$TM$xW;wqʯHp䰀)*^+PdA usՊ9NqxDq4_Lo>G̱uW힫ɋYF J*}kBr_d21qrR/SrF$|xqT9c'/9z..bg?d5M Ӊҗ|ZbE]4lT ]h|*i4:B] AJ#I[?|yM_IqNbx|0I:HNKedмvVÚzI;w%5oon2teޥՍ5"vWn.?+6qC!wklK<hD7:jDNz+Dh/> $>5R0o#i]fT(f[{#2(.9ITE~ %u]+F :w8j\#"~ QN3!_@I]?T9Yk.m1nifޜ l$ؤĔM̶hCfڬǏ"J"@J>;by\HMۍx .,g>HQV?y"(lIN=.x l*(-4#{'e|t OG8^_c6݊/kU1,4'vrp{\GYˑ@XG-h-d #^T ;^:^s $)m mH kC:yا쭳t-neUȫ˶ձJ%v>l0C,yGYpKH&b6 l2#!s ӊp&>Sɨ?h< .Z ¢Gh S Җҁ(hBU7<3iK.yw%;QtǝK|=?J '*5NN0wI(HY\uyU@IGl5'Lcl^  uȧ'5,O6Pf c\ECK~lF雗~ֺWp _ 6er%]vKOXw9g= DcHi+݉׮?_u*EVE FG]a|1s7V $NoIVfgHO]Rc/lJt6-|sgJXμx@MQw~s\M-U4TW Qb+mksijKdWB5x;^T:IQ/7A8pTGz{҈KѭEırWR]sTy۱a40&3[%U:P9ϖ/C C5M [P*+c(#8O\e$_<65zgվװҧK'bfAoڳy˖{6ש[x~`1[!nonpVB_ z3aܞ54M)[5 mkbA+ܕad .D `0 ebϺ3kjQR ]݅l<7cc0xڃu >MѸP s-ralܜsnQw6 5mqs sЃE莄1 m{4 MY o UQt01-`bdޝ@<,`"L{j=C@s ,k " {i;nA9N@!e# o_nQ7:OotD-=OՈS94XFhxz 4g܌e|ӝGn@Y]29s[Gu4Tyx|M n|0Hx>lQavB)iReڍX̽.`89wI1Ҽ1)ˉ Xɰ򐬜Ay: 0CKO+[BorIxЭ%.hnρViǓKBkkQgVҪ1]uCacN+LY^iӿmN ݓ~KQ'Cz#y*?'L$ONE,  ]tO\OXoj{Iuu;P90h=$t?7Zj?HQI}~296_+џ ^#KoɫrpiD!JgP#•LV/Y56KYc{o>U&9? }?EjP, xƮmY(DTo%Z&Vyί\ԥ9Z>$IOjMzT= e2sJ]$XqB%.u̷ҿlZcnPKS) PKʌ5 styles.xml]͒)Xr%'s$JκǛjvYg.$hysH9C.>a|W~%+ʎFFz޸$ag zl ous/^voRBjxwPD<ηzѝ.}'%BYBXogjc.h {zk]\a7_⪍k.i(eų{kf7dշy_Fۑ6 r>t!d7U㲺I^Y@R̬ҧUeF< ׀TNNȩ>#Go`NfRCM@e$]*n׎Ǜ<Иg&|4aI*F-1OK`CӁK_܊ ct{yoGJj}2V‰B!٤_/_- yah}iz]b|q 0k_ A!Y|e$ 3rk296xæxͣDXD32b9?1`|=]X=oȳDI{>?oϜ"[dKv ~aƂ@6LgSrx > ρ<_}0,h*&M9RNb#qnwf4 ϭiI">1Ke.VX"O){L36xuM@y؋2PW^U\\5ӛK8Qϊoc{F.pe k l7(BEnmpǪNQ0iL1'x'qIrnS m⊗si5+$f(TqmtUZӎ9&,$xT1ys}阓൷eBP&٭3d3A('TS1RPn+plN_`ge{30Bc33gq ,l0dAX7و/{oAX*RiO݁==7hmRx% E'6^Wqr|ܗ].s8Ev_pxAu niX'(Ğ'$뒙ٴ 7)M/vPnW>[N2`$BQUzsH=NH@{$ a9c6,Yip*;?*cչǜnC6KQQ@4zO n*p.}/A']joƭ%Eޱ|֗AI&?!, @nd2 1Ǐ{{'PI?yN&M׃}c|DQ'ꌇc/x( R(4RbUM`?RyGO"y|:P Æ?x,k$zM<蔔$+v&捿#r? 3*]#/2q612EN9u,q/TUψpQ|(D3l`D>ԎjPg ٠~9UT(vbqƂ`P)eO-rs9۫jF:Nf5i9=_=5?Iq/u*MvRa##jaWZ@>uOt|D:-/~.w ?T2'oOQWX~ ݳ:e8Ĺ8B?O̷1#m40={tH,[l@T~ p509_<ᅮf< gLLJo1uW&}DE2*SJgZOj==Z[B&3K$S=]J֔Q4X.ћaK:מ]S Rx{'E`4/gم,dxaƗE`>N/g:.Ʌ]a`~YYᵥc/1/1/1/1/1/1Wss{1 >JΧ!>?^,/緽ͥMligy˞rɏm71l ^tش{97;V5;si,6휣+Z;ͻgiۺsf*?Dڍ o\NwW%1P\Ҿ?x++hm6LOT t %/ ?ZggR;ICɳY htك%6%Zb=˓y4ɫ\§Vv]&8 5,]]H_RܒP>nnsl)3RQ&p@Vn0jس|Eђ7B#մXr͕'H$/U~@8tj\knfva-zؗ{}tN]&r;UuN!X /aE,%V% Z9CjR _lTMt(}?PK;iPKʌ5BmoHmeta.xml OpenOffice.org/2.0$Linux OpenOffice.org_project/680m5$Build-9011Oracle Cluster File System (OCFS2) User’s GuideAdministrator1995-11-21T17:41:002006-12-29T09:38:20en-US14PT8H48M13SPKʌ5Thumbnails/thumbnail.pngU{4 1S36QHRtې(K!4C%ZJ5(v14#52i56FEJDdiC41a< C9sv}ܿ=QCCۼ,O~s]fov.-dv̊J]ʸȪڰӍ(Dž d&ܸ$1vV^[9G7ߓǶzW!#[kŒek[V0xq :+w=CC_/o9twAWGȅ%ޠ> .~>󃍨D[ZSC D;,7ԘʘK*SՙR[_R@L,*~EN~u>I뇎E}cx|TL$GTA:RKNPe~1ÏLTUޫ.漗 Ԍ:%k@qdZVydI},2SH]O 5LrW5M>ƙ\ 7C޳k9*Xf-5nN)HYx;P5'Ǡ>>|xcEYqz%Q?Κ/ 1!] 5D+Kd:[t+/6_V\wR*Mg =3)92z=w6|/?$"[F.* s_$n֚Tm̉48{ * .AEzL[_q +GaG˿[¯RF(qp]&ЪmX {X t."` X̉tp5٬xuz#x-2N<5?r!5 jz'և "cATzY_IzR`UG#[k!hi0/wM>ށ!.k%Es\$=Ȣr%ҁM 3PoGT)$Tr{ԠvxR+7~9<n-fS2Î0 8@D!>?|픡yހyTqK5qZyjwU_pEJ)6^ e*6ͮST-|g4Ӿii{~D! k.wkohsn0ߦ:mڰk(C(O\n+ܽ93"#g9K~g<}4q_ PKjdwmZPKʌ5 settings.xmlYnE)o7&qvb$dvf5; mJ-H@O.kR% ̮퐃5W"RnT4s3KDVp}9P'gnY""L$C1EDZ-/ @zIQA<8=;2i!,i`l5#H]l)QYvgE9c 2 B8 y4ǖ z ckGg=T`ja.)" O`5`CHh)xӰƆ&#[RdF7b(<5C aLif̝ &ס~A5x/ /.L@+R"Bdlӕ*S/Rz H cD*wZh\;WQewk:I 1B$ rcck72z60(`<$rPr8:Xf8ʰ,tR/ AE;s>9ojR_Iڥɀ +NIB,CS^m7qHdhY`g1T72c aO5qXW0,/m}Ï碟g'NMשc˼iGy2Z淖e~o?Zu:zl+sܶ;w=sfw؝'vkwݧv_=_=_>aVϿ:y~|hw,˼g-ܲ;wK>_?_F…UUc JcBaR:sLl&pMUPwBp9i !\<U gBh:|r ih3wj5}p2W ɉLƋN2Vq#`+}׉&|''!OHy;*j J[چVY$_Ay߾$[" 'dS#J69q5ɆFeoC+㻒 9Ul'_/J#SJ8.PBҗ_.P&5VAK YV<29N"JؔSW*"4ϭ@i7 n(vuv%Bh׶*{YG#bL[{cb9 L0ӁK!yD"Hj(U'#"4~]boj8]eB J9~/\yq1 7Lw1w#ZʛL Jr#7"80= (uzRm2d#Eyi4pV6aYwG31sK_.יPk~H>xi{c? #Ԗ&< aul5X 'g7Ll,SV1ׂ`ZyGp@yGl9 ӐTk>N&*Bwk'D)r"yJdgA)H 5)c *C3fXb]j(Hpp#j hpTJ-mG-vTDž_ To1\(߇L^54ϜD'ϴߓfq%QvIϪJ4M&d}?*{=-^e2? tva(a-^tNI!++\֟)"m-^tKm-^tKm-^tKm-^tKm-^tKm-^tKm-^tKm-^tKm-^tKm-^tKm-^tKm-^tKm-^tKm-^tKm-^tKm-^t| *4qFvHϘFLZ=Sj&Sw&ךڹ-z/Pk/E wGbQѠz6Nxgv׬vKJD2̳=%GmYmqz/Pk/E wDlV͌O^mً mő\Ktm4t8V~)lG l5mKDz,yʴ3eJJT%n_$8'C8f:uYY r+38[eApG l5'TLo:d#']9s*S䥧ҷٚs%*R ^o8[eAVHq В'5[ŗXb-ns$Z󭠔kI/]\zs^"- ٌmHpG l5mZQMy2SFw6*H5,"8#^pz]."8#^pz]."8#^pz]."8#^pz]."8#^pz]."8#^pz]."8#^pz]."8#^pz]."8#^pz]."8#^pz]."8#^pz]."8#^pz]."8#^pz]."8#^pz]."8#^V}Ӈ(HM̈ў1 QnJnBm.H%D/}|pÛ?A-cM>lq^ؕȬ%Ierdg%_'_ 'pvYqtH a [Uh4W#2;ܪ\w^LzAdhZJK.DH[[4H̸YV41!Ӄ0SLJ-6NhEfv+qhh* 1{?Iҟm+9nG~:TdӹA%QK![|oHjcLhVJ6mIQ%MHVe&s"0}+4>^Ɍ9ԓM̕%Fsgy_Ўה?BɱiW*lcGE&bd_qiڔ#Ywhd{+J`J$ِƬcێcq JͺMТQvRNܤdded}d{ r rSң|l4]q-$Ɣz"[ ~5V ̡IBnBGd32".S1j72=zTvGOJ UGOJ)Qd|GqQGOJ)Q= rSңFLĴ|ś[ƐgihBr\Zؓ""T),Ɏu hUc-Gp2=zTvGOJ܆]u֛yʉ.*#4dz C#ܧGhhd{ySң42=zTvFQ^rtA+1ⱋF} rSңDFFFGHd1<پhY&fD.; >2=zTvGOJ G=*;CC#ܧGh42=zTvGOJhd{ r <)Q=*;Gcr#6mKAN u0N:20-#ܡmk7JmtÌ=M?+Pd, (qE@ 56ÚFmtlC,N+nѣ#/qF'*B2<.ܭmeKl6 bdg.6@(øR'(I+:T6ƴIJl\jժ#1'm{"`4=(EvNY4Ù"GZ\^<yC*/ }թ 1[KiffBӨwZENXCfLήUOB6- ez*]f"N2MBt+ҙ#1H1_h5%L˩gKj=_tIj.>%]: ]Q)n2hZyfV>SFEO%MH42͝*ʓɥ Dfe"#\MhTT8Ym ~{Xl+XGn#uȽnL]-,gZ=Pse|_UZcFjR7eeVNRwؔn[5P!Zj&I'eߌQZs`S5-;M˸%? -F^5j0؟QA&ْS,ܩam]YSQY]:-*{yRma+VCM̯mgE*US!RM9u UB3ϕ)u l,nx3WMϐeUq4*MC2d$œ91 Jʍ&XG4j$Ȓ|D%9_tI)Q/1ǡQ0#RˋJqДI;̈\laPҝowJIJ߉$E '԰?UhȋJK^iEqJ{e.-w13P=P9qf2҉9s$ʔGcVQj uMT$䲣SRgGdlx4!$Er||BԚY\dE.̷XZZԳq.cMk٩oV`4.{&sUQ)\#"#>+~D23i)$Hô/"BQ)[I2tȈի2$k.` Df̔)Rf>K jʜ%DeŬ[iJ@鐎qqI6.O]ks,rٝL̮ᖕۑ,"#=v<ӅsW!wf[G ffŹw3;ߎ(U*&OL!{rijqJ5Q]%'_U$TL9\:F7|H+W/- VZ74:;KV$KqCP 1 PhTI.KsomgpzנzU&gS1GBt JDiE+95O$88崮4PnrgF9^$ڿL_׿&haj<PQN7w)עFDZ,w3}Z_׉ȧ=(E T})7L/O|@o^'ژ_"z>91zj̚sԲ$ )!&$׉ȧb5T\8s )#IXZ\Z̍*U\@o^'ژ_"z>'T@o^'ژ_"z>'})7L/O|U$$֬Q&NO',9g]$,YK)_ Msz>׉ȧNqRK(5S4eǟ6qW%(h(ՑW#JJi2# Tf})7L/O|T})7L/O|@o^'ژ_"z>'})7L/O|@o^'ژ_"z>&*+K";:BDo^'ژ_"z>%|?7|?F})7L/O_w펐w펐Do^'ژ_"z>%|?7|?F})7L/O_w펐w펐Do^'ژ_"z>%|?7|?F})7L/O_w펐w펐Do^'ژ_"z>%|?7|?F})7L/O_w펐w펐Do^'ژ_"z>%|?7|?F})7L/Oy*%#IE׉ȧ޼O0E? xja|x S S׉ȧ޼O0E? xja|x S S׉ȧ޼O0E? xja|x S S׉ȧ޼O0E? xja|x S S׉ȧ޼O0E? xja|x S S׉ȧ޼O0E? xja|x S S׉ȧ޼O0E? xja|x S S׉ȧ޼O0E? ʱ>U )7Q=ynv;sq'm{"`4=(E =(E PkΘ>a8"+Tffz,%lBҢXAq>5jM; SB$)d;)"Vk2')INKuzc+lHl͊ddzV2 *St 4ܒi2."(\I%Aȹl$DEb!= IYVf3+jW.#RjPOԙ([m(ӛE]߆M!V228QR$;fU{}wLmY.OIƑԇ ܎)I4=|{kUʉ5\g\IT\Ɍ׫hJ'td5Pe-]DJ_Q"G΍,7+jc417"1AMו*3+JQUX]KmMTm*+Ue{Q߯oZqYm[d_9 R$""!37gw6[cYJh4u_Q!JFF"Dpd)#$w%!.\dYLo7\__,U)-TՌq*ˈRƕbůY8Nt>!EVKK`g q7̂׭EW.2|9QNtG%9[xS-*K*4lec#I-I.ydwI$WMQ\Z=M6.z>$G)V3+,)C9腃 I_o^+)C9p{0~PNg)C9 0~PNg)C9 0~PNg)C9 0~PNg)C9 0~PNg)C9 0~PNg)C9 0~PNg)C9 0~PNg1pGt c*{\ߪ,I?sQe)?_׿&?kt<yC*/ HyC*/ HXҿ?"FfmN82ޮ2 cZM& #ǑՙM8d܏RKVioTeQmM!NdEVˎڮ"c`iR;!}wYQR"I<Z%T&$iFWFf҇r̉+.S16KיCȢxR]/UX(sʔnȳjii"G3\+ VF}p7ő-n>emHeW;;걄,aC$]'S̙EtԤA|uq7W2aB%F";-ƐKJHWT,܄u3 ̅NQs]ңMZe붫+NGFNDkCm,ZT[2i,Ũ_41Bdidh3ŝI#_5{Fg6h՗9\ֵq\bbf$W\`[gZE #Ie07gnqؓa@wL+6ԳmYIDfFFZGOlЖ!H4j2#r8DxJIg>^JxFuíz3?3 >9J5-9T)+mIQ>) tedSs~3#!Iy)NBPjRSQfDgs>mD !$v*(RMJUW|*̴:%R 1;|D7f8ȜA#"2ra:B㩲QmjQV;rϔsdڍD_ȏR긒 A%S!(azWM6"pbQ̈ |JmzcLo6 O) k\؎_JMuH724RP\jQ *}'_ iTzۖ\R3 I4X9ҝW!P59U&vSڏ)rb!+A%mWZ)Y ?N7i9f:Q6(Yiԣ22/FFd>ӆ$".9/t,RYH4v+j"ըXJ%91%üWM$f\U T52wJYQ# )  4lKm5J-*RJ2)Av3|$1E#r>Խ!۶feǖxȵE|!Jғ2Id\EY\y>[NlQ)%$F""#Q򝈋_! rNL,)Y\U,jQUό8ƚE5dIM>"_Ls(u>= \ZVێJI&dE|7;'%2MKI3#4̈"iWTn,vɩ- *:E Q)Eb24궻9zFD=7_hn9zBps篴7k=}D=7_hn9zB{oݮs| k=}\ 'A9zCvNݮs|s篴(\ 7_hQ81I 7_hřH־УN>q,u|9^$ڿL_׿&h'H΀WMi/I+jW=mG{ß$ũ $A;uU]mGݍZ4 j *<@U҆v6Qhң74 jWz_J]ڏ[EJ Ʈң7_Qq}(owcj=n/*<CJ Ʈ}mGݍHናr!\I3Q%$IJKVң7_Qq}(owcj=n/*<CJ Ʈ}mGݍZ4 j *<@U҆v6Qhң74 jWz_J]ڏ[EJ Ʈң7_Qq}(owcj=n/*<CJ Ʈ}mGݍZ4 j *<@U҆v6Qhң74 jWz_J]ڏ[EJ Ʈң7_Qq}(owcj=n/*<CJ Ʈ}mGݍZ4 j *<@U҆v6Qhң74 jWz_J]ڏ[EJ Ʈң7_Qq}(owcj=n/*<CJ Ʈ}mGݍZ4 j *<@U҆v6Qhң74 jWz_J]ڏ[EJ Ʈң7_Qq}(owcj=n/*<CJ Ʈ}mGݍZ4 j *<@U҆v6Qhң74 jWz_J]ڏ[EJ Ʈң7_Qq}(owcj=n/*<CJ Ʈ}mGݍZ4 j *<@U҆v6Qhң74 jWz_J]ڏ[EJ Ʈң7_Qq}(owcj=n/*<CJ Ʈ}mGݍZ4 j *<@U҆v6Qhң74 jWz_J]ڏ[EJ Ʈң7_Qq}(owcj=n/*<CJ Ʈ}mGݍZ4 j *<@U҆v6Qhң74 jWz_J]ڏ[EJ Ʈң7_Qq}(owcj=n/*<CJ Ʈ}mGݍZ4 j *<@U҆v6Qhң74 jWz_J]ڏ[EJ ƮZ06n)ak3zpRx36:9 tRI.vtfWjj>ݰD> IbOR.3oNTP|RnwA(ß$1U:|שӑǠDnCi+NhdV;gFJȵw1N[r#m6$2TgfV>Km)N%A^Q$?&4%az ;*Z-,zԣR&3$Y>]faSq(ԕDu )\TNR!,Pmr#2r3o{ɘO[-HyžzD;(%'=BчkKPN ʲXKjKy%V#QUV dTfJBڮV."n^'B)IYT&V4JEM-!.),*MJ6ܨ3JiuI1ѥ ,.$%ckֻ%$SEb(ꓨ e5Si#4Qqd>F\8tQaS 36ȬeeY^Z2_x\']dVn]v6d[[S2zEJ4 CoTXή2J𖑾[uK͢^KɟwNY+M[[-@pfެ|~έ=|2f>U&pjd^Ӻ #.[ǘb+A2,4"ZШ8H'Ik˔r;Vi@f&D&b[ q,OhyW3ƛ|pffM$}!7=զ˫"kʝ7 ⨡UqJVgfyVR+n2QD&;\m7۠im-ۡDJɫ_%%bZ{^~<`y)1]% ZR6*6kk#!/ *O-q97;M-N4[u9ғc#ec.> I:;?uqn*wNA#Tӊm)$줨d|@n2u C;)*+1^8vӐp;!EHmnRFE|{\_Qx*wNA#Tej;e :Q$>"'c{EׇGh9Sr LzmQ'ص pŅq&Omґ-F*SI򑑗d|cGh9 (t?;ENQ@J/\?;EN8vӐQz*wNA#TSrt(^vӐp;!E#TGh9 (t?;ENQ@(p; *wNBEGh9SrP /\?;EN8vӐQz*wNA#TSrt(^vӐp;!E#TGh9 (t?;ENQ@(p; *wNBEGh9SrP /\?;EN8vӐQz*wNA#TSrt(^vӐp;!E#TGh9 (t?;ENQ@(p; *wNBEGh9SrP /\?;EN8vӐQz*wNA#TSrt(^vӐp;!E#TGh9 (t?;ENQ@(p; *wNBEGh9SrP /\?;EN8vӐQz*wNA#TSrr%pTIn7-- J2-s ȗȢ o ՟}`}?vmxG67$TŒ4v[Η:f/Eo-G67$Q/8^MN:nR{"qv'H1mQdԓcj;^嫈J!@) :>n$ǫFG|zqTb\ZXIpmfQ{mTg256CnwqiuDw%Ts[q]tFqY,U}ECU4*KD:6Ffii1=fJ:>M4d,|d|cži,  s`,+A&1Dm*[l-S W,yob=iըl@b<©ۙc2 k##3;׮-D+R'̖L$zqV_sKpk%FJjuGcO2W;s\*7iь\[f5\G: Y7\ gR+2ǪT" tPf4F|k8qN8-k3R@EP__@%.SVx]є> I%2=3Veu=JG67$X3Y5';ƻHnY 2/'5|wwNxi"R)? "%fl,iVwmyK+#*if=PUUIk3JU̮ÀwNx8|wܘ*fWa+[kΜԤB}BuF@܆":3n<4f,YX͠68|wwNxE1ejYNj ":SW&U14gb7×\|6(Ӧ8yf\$"2ʒ$\FERwNx8|wlAT[CʞOICMқdBr²Is"X}_8!GwBrlhV6\ҝ­Gu(Ŭ& 7};>wӹ |;sþnw;0W|wwNxX& p _>wӹ7};`+þnw;|;s,wNx8|w`L7};>wӹ |;sþnw;0W|wwNxX& p _>wӹ7};`+þnw;|;s,wNx8|w`L7};>wӹ |;sþnw;0W|wwNxX& p _>wӹ7};`+þnw;|;s,wNx8|w`L7};>wӹ |;sþnw;0W|wwNxX& p _>wӹ7};`+þnw;|;s,wNx8|w`L7};>wӹ |;sþnw;0W|wwNxX& p _>wӹ*´ZjN\HZ7Ҭ}GFb5\A3MY۶wښlxG67$YVNmM 02V+4~2R|9%_> IXZBA?S,TjG=ӡ>󈴤h̒;Xԫ)6Qf=cN6 OeRؐۅeM)o4%Фc$,(RszҢZVDȲZңBLȭ{ޏIK5zEO)nCMhAGZ X.z>uUNCr l,l-{-gs1#ArA=PԪNKqs'Buq굤SX³`d*cNRe8٩R6DI+dfv JUt%ZMݮ7),e729FL*V_XPR#Z]MY۶& 9loH  V> IgFC NX$G0|>W`dE4|"_}6@ko_+O7/dE4|"_}6@ko_+O7/dE4|"_}6@ko_+O7/dE4|"_}6@ko_+O7/dE4|"_}6@ko_+O7/dE4|"_}6@ko_+O7/dE4|"_}6@ko_+O7/dE4|"_}6@ko_+O7/dE4|"_}6@ko_+O7/dNi5ȸ)CɨJUW"ȭe_wG˛9彯{%MJJY kQȏ<W`  ~i|>W`  ~i|>W`  ~i|>W`  ~i|>W`  ~i|>W`  ~i|>W`  ~i|>W`  ~i|>W`  ~i|>W` PR ~i &0U]GkAμ.SVx]D> IjKM*JyHJ"+L#|&a~?t31 31 W[xJUJT$ӻmfj+ZRd|ntnt1.f=cpGCRiĢO/+~&Y^<+W>sat58n3TuN-gL$e56JK)ֶ&5UO*jK) 7U-D"Hқv-z]MY۶h>0"bRs[]˓!}aσc}D/GΎpo))$&+# hD2IBIULF"O*ʔjAhH=1HI~ D҉dE{],\굇!i8QJr4^tjgFWO|C!ϓ*{"I!%'YJ%Ys3#tFv& iIRI]+QDm>%tkƩ!TEĕd11P>)%)BA_1\ljFut##D)ioAmGZEgZ$F,x>FuqѢ*lШ4Me5 )f2-_̹QF 8FrJ2BLQz}+ C6&S2y^eq*Fcc"]*0(jfڐ*/'&$?P74nzjƨSƴÍ F2u3Q6VvcQI;(o+X.+XrtnéCi n)E-CAD)&;MyרK Z^ƅFSQJYI3/V0-d[︧rJZw5Y9*/8He*m"I6"H  ՟}`}?vmxG67$M?DI\#|&a~?tbDv%r<[yi[n$12,vӫTҤ_mqhI.3q@7,v?@2L2MX$j=~&foY 7~1ŦCm) J#NTZw;vj;U%?@f7#,vi ˨%M IX} .T4TXuP* גv|GR-+~NbK\S&sզ %MmeqX:P 9<1 6n10䒩n(I,If;"\a緤!u6wqfbIzYr xzS(\f [-TZ58egs8iRlgb.@X}T&KTXh6hKqy]>;D#FѣzgO#Vm"w̹,emVD]ۆVuR4*h7IHϤ[{ߒAZ=5QKVtJВ#5\vQ 5+54Vlq(iTIifj"2YwFO*FUŏʚ[8+*EB+k+eOtXr MY۶wښl7}aσc}D/G>0"i#HgF @Sl߀өqJ#s6h"2%dZza+Ki%Twݰ ՟}`6#|&a~?.> IO0QC:1N&7M?e4hYJ7+Q}"T:_F秪B]NR̭ +Ys.N  ebq^ҝ8/mfZ&n/\bye>[5ԢGV͕HRfW+﯈c4#]Pf2󭤛q(RRyO5Z` )f㛹S/qГٲew|(N* /sSr'Bo=vo]-'@A'CT旹Q)7l7oqx!K6ʙ{dN͓/[@OҪT٤Lo4͛"3Bx̵]Ex*6i0)3fȌ8P3-WQ_4+5VuJ\mmԥ(Ic"Qq[SQ*܅6j2h֢ᆴ!+A2liV:IjZHWA)Z%iS9 Sp6IuE+;5X!)E@kq$DJ%'Y&J##p" ~;RV8e$fd3."#3FbM 'JQ}UI1AnF&.rRf~7\RS mD݈TyԞ;zP  b<ثCiuZJz~-P 5I)$dF5ғ2Er  Z:*dJ io)&+#2-d\\fBFuM`0\5N4Xk]nW)DdjDg*bR̎ȸoCa.1O9Fu6J""Vn;1H:t;On)f\&,VVQc)b{ǕZ$(#Btc"Q t*mw( !D =v"+N\WQ ;ܬz.:b,Ÿ'Ml/q ]QwmYqT̼DKiekf\FGr3###jt"TNXb>\Q%J=jRRDIIˈ3##2>AxT.畠)% dNkž[_Upz~.s4[Quo)Il,3;XەҋIzP̂H&]ѭ ̉$mj#I"kK!"$Ɲ,y%ҫJI r Wa?_^}?v<.SV& 9loHG67$M?DI (fZ >jg~*ZJu+Rd22F#3ZܴKcb,DUdq)B[vJg{%$gs-v#NxVw~F64Cͣ_t' DNzm%QJTXM,e{jO=Fw~ysjJ'"Y/q3jPM!8d!y)$\J:*õHCF?QC/fV|ڐ(y,FVр:Jo?<)ekKmzLsQ[`ۗ6䗋kP 6R6%d%d";j|VL9NyI[2N QX%!Y9W^RNL:@&f-Uim&;IAbwW#Q6LI*%AH-z JRg.TFdW>(hM[։h!e $BI=o6_Y׿ OPJPv<ܱXr#IgZ踛6 6O(pLjI,O ]J\pգAH&fec;R#_mF%4UO#-eHhÕ=>EAjFY:ѥY]]l̵$ܙ}3p^) 9BkA*M=r!E&sU.dkљ.SNõ֊h -+QEFIr'W/9ոw=4SRId)8ؚ W}zgzNn5d%]gA\44Tj]ݺESd'z{9Zƕjw?_kտl Qq)VOVfߠ_Aqo5+1gQ{/'a#eESȟ|zȫrY{{\}t=ۋOgY/ /yWgWc3[_2K\Ǚbի_ku͠:h\-PiqT+Mv@z ]1JbLq *Q}Ť7M՗s;IZ>so9?n6z]`g W}ASl~\6JIReM)p̊">,lHR SN2*ӫGŮD/==: _]A ףR跽@5AN3Ȝ9uV9JV3""ˬ߼. 3$Til2wH%DI=DI+cE/\zdљ]KRSS's?*BSqM9D!ԙTn;.q=//|Z/?gUBU;$QUӔjr^c>>73cnzgҳqL_ Y*5. [nENj"źO֧{kҿ_b>BuɠRr׫p3n{DwNEG Il=D?Q!E8|VɫOkլ WQf"=fWy̽n1q]"edIXĕI}e-c!ܙc2-Mn^M_\bONc)r+ ׏eζ l 擊nFi-!63b?/G8$ ΋R<լme'm`OFyEpU~ Q?b# Ժ21h[*igFys;7k_2cԊ@cDjQ(2XF_g_)9e\M)EUF%%fL7@Wa?_c'kںK5gnjj>ݰD> IO0QBß$_!P$)ABD)J֥*<{iȽqĐ4YR`J,IJId23³PÕZf&(->TI[Di2Ȍ36D|Ne10NiK!+R`71JV*%doV׫P+ n%%*6V3w2ըUZs.oO<32E !ɣN% ܺlW3!# CÍ̏YR)h%=4(ԛ\{ՆIT%okP^#YBҎfJlQUTWcS)"_f; Y 4:"j \gnS@s+kγNyICj\wSJ7 $dJ,w>#"co8*b& [VѓD6\ZOesTJL-FJRj2+kTLnH%hGkƕAJeƟr{UM Sɑ_Txo9L/w-hAc:QCrJ5 @UJ4i\R 晳dFyq(O 6=mG]+&fu;Z}0N!4竻˘RUtI=!R}FW9T%.b;θtJJI9-vIȬWg)pSU)OJ}o˓MqGBW{$\\/&v"O*q#F4dDk;E[OlRR`Z4woL|JͤVc3%™*دӥ9O%nV7)ód˨BTȸ?A@@ bd؄^~B+3Q'jmlwQ/}WD|ǥF;'>KqM\/Qy0;1F%*4ȏe~u)<˖o%v|OY%kj-/-\ooQpjqߍrjL_=\y+s<9;2է{_ nu֖6||c|){\7;_gyrpnu†5zQ>gxsSCMLҫl4lTZm'I/TF˜STZj!DKFJ֬LL#RSHrnvI\]KrM3,Tm2hnRڈի|.$赃'#Udz+; *wNAr>K$B NUeġ[>FRgn;G).z-\u2WhvZGh9Sr?QV#}r+K(4YeHAq%*'U.Q\ IQdayM%(htFf3; _Srt'WWx)!0q $Iꭡ$DV$""K4m.\6Ͳhu&O oSrt/?b&f̤VdVt&EAs)"1tP *F5 .#$F^ _Srt'*jh5>Թ1ffw337ffg}ooQpnv^c~6 Fp*wNA#T~TyS'\OCy+d|7Srt3{<\n αWk_uws2ssZ4k$n-/-\p; *wNAʝUEK]Ϝ &5st Jdַ㝒G|#TGh9)T _Ze9Z?h*wNBjB%V&A}#9|*|^7Wqb^<rŬ3<36;+b"w GlPR@'kں,b_QmW@ix]MY۶h>0"i#HB`sd4 $3ڃ-ME6k7 GժZaN4taFVdgb̊=QzSHRScѭh|ex3;o@%ʜfHѤ-*QVS,J}Qs[y+3#si"Ukz>~AJG6B7!DDhsI؈.@&2 > Jb{% ,/OϋdX;fK1b;\xcjMq3Iq֤܌7%|UTNKI6I=H32#3QgNN%k:͔Z -\QdJ# Uq4eLhzFķ%y3f5gmu6iޟFDb} |N)qFf6fW;,;ĸT3'XhK%8,d3Oy\\ķM1h\47%Im(,B;(Yq깙(ju(S%>+[nCz$$iXȑY"jC 5&dfӨ gFUIz\Ko)BIIߋY_l)dM ǎҝuv3ʄZQr~FSE BUE^uuOR.,V/T0|D~9ХWvkLi[iq:Yj+Q KJwJXܠm[> g18JRbI.+qkͩGnt@{b9UIΥ45!nW-%ԷY~FvUvA=Sb&BH^m_#_*ȮfcHT*1iq7TtLk6SWV"3֥$x"B#5q.s%6m8wQzψv"3ʰ^ y|C.|ɧrzq|cBɥ^)qqjj̢ʯST Gyz[-C%YTH/|`4=ϩC22YrqY֔HWrFutbJubS^h*R]4be-2.Q(ZŨ:tjYaBq-HNS,R+XU$_mm>L臍݌㬯}`Km2lF'W wC6]Úrd.J锝5\g`H(]EfZ)mCɞAiOIk#3IDV QfJLC1Ӿ*O3O/R52Q9/BpAqjD. J+{% Q5@r Wa?_^}?v<.SV& 9loHG67$M?DI (+rTv,K̩2В5YDN2:G8=RڎP<ɠf2܏)؎Ǩ 0 9 QmZDŪ2̣҆:̬32k-w2!=JFIvJdZ⾧FO(f+X~Q7Sv5TZX4JLWh|Tȥj%k(2m> 1皷D5hmMdDW$#Ah7>,CSk DvYhb-D|b=Y):Rɉkƌ(X:Dzd/CJRE l$Z:2I#U+VNbK\S&sզ %MmeqX:PѤhޙ(Hgs.K[U2U/_Jr Jܬ)nSfɗP:/)Iq\c-"ЖP+&tcs";;۔JspjN,vmJi&MU|V24!!1lAlP6 6O(Qax3??a hT}$?ݯhn_fO=ctI+$"? mmĸq$%Ǫz˘ȏZ26kkZ=:u7ѧj_3}{Nf4:u7ѧ( 2T2ݎ&Y|r2#u7ѧ4oO`QH5I.bK6^BzLFsJ/$HJRDYv"-fbXWoO`iF_3}{Nf4:u7ѧ(Fӯ=ENf4|i ,oPǧ_3}{SFj+TF. J+{% PW+0j豊F]wښl5gn oß$_! }aσc}D/GΌ@"#+5Qv\Qf5dR֤|LUr}58#S'8)xB4/oW4˜QX!Sz,WSQSmQ;NtUj~P4,lQ?i$'us+ s̯s$՟44RhI)J@tALUaXKKRYec=WIsSq掟%0mHIY&2hd3<ƕ :-q갤u9jiVpŢQ(ğz.5j4S<&XaՙخdE&w3"bT^T7tkC;9fF"Le@Oe5ӝm/r S,W">1\)Tdď]5vcFO}fpFEFDj"=e~2~AåJŋ"6SfpTwY-dyq$E{GO R'1Ua9jHy f&fȸȬI҄LE vGq/.T2F$inRw|0ؤhޙ(Hgs.K[U2U/_Jr Jܬ)nSfɗP:/)Iq\>9-͑4Y*ȵW=Ed? V#Pt!y"C4kUD|X!Ht%? Ř2Ȏ~\\.RlɇSom' JX$*$ЅҴq}8]eh37q:;>777S1TgQ{:;#?_({:;kQ{:;kQ{:;kQ{:;kQ{:; 2'ЄMv%*3$c4r0+l C|i5lhBnv+Y~¼9G /i2'7KY o0M8W|:hɺ_t1tÅx7s,OTf}f-ַT$W33>""iQ,Z߃%$ԥ $\fD\0tÅx7s,,7KY o0M8W|:hɺ_t1t>B7Ds%k彯cKn1ϰ{wf>[RեWQMb")$SdؒV"K]'%C@\(+eOtjj>ݰ ՟}`6#|&a~?.> IO0QC:1Tz=@%ʖ0MQGm&{]' &B.bډLHsIY+*K2#L  iF*eeo![I6Pk%A'CT旹Q)7l7oq%SGܛ'>K*JRr:Xn4tsWJ)&GcF|G0̯AeBKI%<4GřKRSsK˰jR&knZ I+6i"ֲ$)UF+.:\Bs6r B*JnUib,*ZZYAj"P)Lqtxx}ʄۋ3I$IQ*H&w#4̯jܪԟDJukm23<.lV5<=Ə%ŧ.Ck#dTewNSHJr%f9ZQE"RilOE"&f)i)umғWSHA)?Zq~I""]%1"URpifᒓFF|ZȮ3S\j|cSE,Puh F]I#QYZQk\3 $J$d8GI3_ߏ^lQ!+41F(ʔۻ3l2LbZ>2sqj4*f]¤IÔdyQģIdjI\v=B Fy̘-m)HBH$j+=<5LʬTFqM)Ec22Aܪ5fl)FffLu*o!,-M4{!$+l C|AV"\Db/0xȗ5}4i5ŨfgDCG֩{NTt\wRqRY+N(#%(b\S6L =4bN1 4HJ$+҈U0A8d6YAKQT);OַsdRaJA3c;%IZ$iVrQZ;Xpz#Yer=w"$%<-Fwݭ-鸊ELFy侦66mK-dWrc#K_,V)͒USG*r-wǓ,XSH|Xĸ[RzuDIи$w#F#aʓv5N,|_jD֦YN:"e"#3-cm0UbvLyɉc4hbyNYQQGEFWPZ9DZveZrԻj3#Fv1"  ǿ?b(83+-8#6O(J]'%C@\(+eOtjj>ݰ ՟}`6#|&a~?.> IO0QC:1Zcl=S/%J^Uhd24]zO8{sӉqڬ%%n%+4EdV<*P7iz)i3TS7ʣIk3Vd5CJ"uFÒMm3CC%7JqFM|s;sWRjkҩ1˅ pE+1gmB9m>#o¦JߒodC%)ZKIKI3;]e3ګ"ԡsev_)܈L5=ލ<,wZjQ33E.35i;J}uLymO\5F٥#JHYnZ)HV*LݫM!*AJ'$ۅ#""'tIK ha3Q+6Y;\23^!/r O&8Y4rF[Y*b!lu"cQ%"&sOZݝ'WsT;)#;kWZ=5QKVtJВ#5\vQ S*jθi5Tl365$rs<ܖ)-Zۨ!QNBz$6i4Wc,+s;+ZpA.=oCsLIjerlñRdžNn$K=w52a }A9̗;f(݈V|G2af-B7w;Q{#t i A)II_fg-$$$"RP“ IKhS2-mгCRM$i#W3s.[C$StPUHiq+K1kk S]iuyPMTo>i4=weHU'C>+v;S+Imq 5%%rs3;D:\x;㰜]BLB7RRGhh%3Pt!Վ"rK/S]D hI3VQu:S֧3T%ry.8Yv%dqFjD&#gM?M>D"Q QCW֦\fƍ PLR|W2ˈDNUGݢhrm3լ֔nb2n$mV22(K6-=4=Č;L"sH#+/˔Z0Wa\I7ӱܿJ[hu9\BVEr bd؋ߨaJ($HM"Y\4#Kf4nlmeʓ+/0DsDs|/0/0>K?4K? DѝZXenD2z JzLLSLt ϽRl&)&S6DsDssT z^lYi5 s3Llu4ʕ~;79_a_`!w GlPXV զqI;FkRh+eOtXr MY۶wښl7}aσc}D/G>0"i#HgF @ ] %.I*9h79IkY!)JsO%~0ɂVRnB5X j+kqa8EsvDVVj;&Kfgb"#3Ɩ(՚c(O! S䗏&E)g'fܴ hz҃I(VΟlkS0Dҗ!BJ%dD,Dgnej[ȃ=+HKMJL]1':Y)geȈGT٪%ĥQ, 2 &1e[Q)  .bމ+%eWfDw#2IR;ѩ.4U&ZM&iS4ddd|bLm]K}YOJS.7n5F +~KN&U i.'!-&mw/Ŗ$jr jcRLIΧyJoY|r"#R3Vt]Tǖ[њm^"4$嬏P”Uikjb}ڴBDrImY2.2+xt "DybM6|vQSvIjQQZJXL F QvLNAINgeRܻV3:^m}WT#ҩN-)\DEffDEb7+{pӮ FZ-){xo{[Xĉ~BH9l8Mt't_ ]e8"JiǐIΒQ]*T )1=1`LIi$f)jJn|v#3 ԣi'j`Hb*NJeM-BRDJ"!̵ƪ\KIHbl(!H=4I+Kee{7bdZTIۮ!%ւlJͤZHc; UQˎmMBУJ##!Eqò u TZy.)91ʹjW!f;!xiK4l#sYZc+GYRU Ĵ3ZDySs+ؔ؊zXM:t$*M9Ti2;LqjOkq"m؈iD|&}b*ws91GN!f|`&v ZA-*Bԅ̸|JcV"T$S!L&2$IrJH5,iQ\u)-T*8]./Ƙo5yVͳ"<1A%M9mm&;pٲ6ә6M\+j,Js\2Tjri2qMK5Ȓip{XV#!>!))StLhJD&C-J>]CG63 PƒR纩i)VNTB2V0 %N*4jSm!sqĒi?i"bW)m#&ꊗN TimflNj#Q:ُNNi4gKk\%fd$ U~W-W=^8'PS%ĥn$ԔZ""#Ir0bU+bŨ5m2irsn8^b-/,#5[x6J˧φqP6ZNj#Q:zӊ!Ǖ $~3IDYlQ+1+;TS&< q]%mB+2*HIZ TctQ%Qf;ߒ.u7$qD.U6Mt0z*[Xaފ:~LQMt0oESkk ?@&(ފ:~7CESkk ?@TC ! mmcTCU6`}mmcz*[Xa0D>U6Mt0z*[Xaފ:~LQMt0oESkk ?@&(ފ:~7CESkk ?@TC ! mmchj[s&͗Rmԟ*,sYFWI+eOtXr MY۶wښl7}aσc}D/G>0"i#HgF @Ƨq[ѡǜ}eZJ=|fc`|hKpZJ"d\W?c|:!+M좹\Wa?_c'kںK5gnjj>ݰD> IO0QBß$_!EW+0j豊F]wښl5gn oß$_! }aσc}F&NHI]وˑ^n2.!`+]ڏ[CQq}(owcj=n/ mG,`+]ڏ[CQq}(  v6Pz_Jݍ7Ҁowcj=n/ mG,`+]ڏ[CQq}(  v6Pz_Jݍ7Ҁowcj=n/ mG,`+]ڏ[CQq}(  v6Pz_Jݍ7Ҁowcj=n/ mG,`+]ڏ[CQq}(  v6Pz_Jݍ7Ҁowcj=n/ mG,`+]ڏ[CQq}(  v6Pz_Jݍ7Ҁowcj=n/ mG,`+]ڏ[CQq}(  v6Pz_Jݍ7Ҁowcj=n/ mG,`+]ڏ[CQq}(  v6Pz_Jݍ7Ҁowcj=n/ mG,`+]ڏ[CQq}(  v6Pz_Jݍ7Ҁowcj=n/ mG,`+]ڏ[CQq}(  v6Pz_Jݍ7Ҁowcj=n/ mG,`+]ڏ[CQq}(  v6Pz_J+eOt7ҍEXt'òG!]u ,AjBu'υjj>ݰ ՟}`64]ObTj|u0*ZM<%uddDw#3"ϟT9xz޸9aϟTGϟTGGϟTϟTGGϟTϟTGGϟTϟTGGϟTϟTGGϟTϟTGGϟTϟTGGϟTϟTGGϟTϟTGGϟTϟTGGϟTϟTGGϟTϟTGGϟTϟTGGϟTϟTGGϟTϟTGGϟTϟTGGϟTϟTGGϟTϟTGGϟTϟTGGϟTϟTGGϟTbf2T-+JF)BJ"Qi|/^q͏PK1 3X-Pictures/10000000000002A7000001E7A4935D3C.jpgJFIFHHxwdumpC    $.' ",#(7),01444'9=82<.342C  2!!22222222222222222222222222222222222222222222222222"^  !1AV"QSU267RTaeu#3Bbqrt4$%58CEc(1Q!ARabB ?a9O#Tq͔ZޑZ=h"lTWVk&m֖ln;H\6,c[o+k;sGb9n+y]Q;-$ds3?R|'0"7#E uCr8[^`TKn+y]P܎5#r8[^`T7#E uD܎5 n+y]Q."7#E uCr8[^`TKn+y]P܎5#r8[^`T7#E uD܎5 n+y]Q."7#E uCr8[^`TKn+y]P܎5#r8[^`T7#E uD܎5 n+y]Q."7#E uCr8[^`TKn+y]P܎5#r8[^`T7#E uD܎5 n+y]Q\2Ts/ TOk۹#>񘌘Bz%FL!-IlM5w-܎5 n+y]QthS]Ùu1`EQ ,IrQV[{$7#E uCr8[^`TFi\qܶ[호pنYĿFLC%k}GpopnG q^U-*͕))R}HqOćNquD눲&V=Df6pnG q^N\& NGb׮ʣ)ifIJת܎5 I!a\]$FoزU^%qkRui;e˫PnkPeT܃4[1x?M n+y]P܎5:Bk͸u 3YTGDyn܎5 n+y]Q."7#E uCr8[^`TKn+y]P܎5#r8[^`T7#E uD܎5 n+y]Q."7#E uCr8[^`TKn+y]P܎5#r8[^`T7#E uD܎5 n+y]Q."7#E uCr8[^`TKn+y]P܎5#r8[^`T7#E uD܎5 n+y]Q."7#E uCr8[^`TKn+y]Q O:p Z3vF+T&QB\L974 P˛kڶ{Zl˲TFYg!)FyZQ\e?` x= iXiWOdpȬ^,=AuZƊsEr=#|mʥ~%4ǤFԤIZ˄nD˅hCIQw7ʞ<=AisGb-3[x}FAPh;XȏxMƔoYțpJ<-CHH{NM;_%mdAGz~;ZFiDfԕT.DfRok2#ه}c@:r23xʙmI<@[i4ڙYZi;v FEO%MH42͝*ʓɥ Dfe"#\MhTT8Ym ~6D]Dyvld^7].3{Ne9>Mկk1ʣIi5)Q2+'HDkV;ElJZ-ckqӊ-i5lD(b9)šy掅.5o߼6'jPq 6dj1*X[WVTtF(fWN#-b.Je2zGJՐs+YQiJETg8a:NQaJP0f|y;#n RjI̪&IfTLg&2#qiRYQc+x\HL)KSI.Q~= \ZVێJI&dE|f#NJ{RJU"-['gWq?K l_۾#EZPa uk*D^PwRs5  sKk'ic)-(2MlDv5k0T۵BM^K*55&tvMWrBDW+^翼(IU=&DZR|%K74&񍚜fOlg9I%]E2)E®2"3޸9议:ܤ@Z!(aJ6N䋀xl;N$!qL%'LZ"IwVjP12STJe,-*rJ#pk3+Qn'E*UB9Ž$N=U7dXVftB32kSZWnDtN:q^x5lhY5DZ}.VU=3J1(]֝EtRԝ}ֳ-WԑR0rQe$V#[dJ/|HT&#UYjl-ZϼH$j"!RGAec9.i]4\$3$z"(ATLΧ*bF ӔVsks-cJIL%Hqqi\i2UrIo^Ș$ڿLxì$v49ndSSE7.:$Xmfgܾ6O{{P*#5PW ȧڼOƘ^E? x0h iS@O[2iR;Hh, Z|o\UDØ=Rl4-MHYej?ayW ȧ> 6Ox0hj?ayW ȧ> 6Ox0hziqIyt,q_Us;s]Iԥ&5*ɶk7yz,{W ȧڼOƘ^E? XsØIL1JIJ}W>pk24VAel@m^'L/"j?ay'T@m^'L/"j?ay'~4)6O|U$$֬Q&NO',9g]$,YK{)^ llͫiS@m^'L/"7q9ǥK},kHyL8Dі|D[從]ЗTՠ\cVE\*+)*#˾Fj3j?ayW ȧ>j?ayW ȧ> 6Ox0hj?ayW ȧ> 6Ox0hNIaeuQ쥑χ9BDm^'L/"j?ay%|?ul~c W ȧڼOƘ^E? }lr>F~4)6O_g[χ9Bx0h iS@(APDm^'L/"j?ay%|?ul~c W ȧڼOƘ^E? }lr>F~4)6O_g[χ9Bx0h iS@(APDm^'L/"j?ay%|?ul~c W ȧڼOƘ^E? 䨖RL&W##d=@m^'L/"j?ay'~4)6O|@m^'L/"j?ay'~4)6O|@m^'L/"j?ay'~4)6O|@m^'L/"j?ay'~4)6O|@m^'L/"j?ay'~4)6O|@m^'L/"j?ay'~4)6O|@m^'L/"j?ay'~4)6O|@m^'L/"j?ay'*Di8NLxd'-ߢn_׿&Hx3T_@3T_@ y1L6dWEq0ʌ؏EhZTW##+9.'ƻ`mIajy(D,[{2*A->Nulԉ,YcǾ;]6]ǔ~qu!w#qz9O_^tEĢ2fynZ[08]Ӊdj,oS d{qE9jmyL YLw)U Ϗ>TvDrm3TAQX]҉7>*Rj%'&,ɌH}(ZTTi4b=g#I-I.ydwI$WMQ\Z=M6.z>$G)V3+,ۄT9 I__^RzLJ +T9nRz+T9nRz+T9nRz+T9nRz+T9nRz+T9nRz+T9nRz+T9,+C#$wTY)?]C@;'S1 (9^$ڿL_׿&hx3T_@3T_@cJ; |v[nM8˺z$bՍi4(>B23Ve4㍒nr=J--YQGq6H;d:K3q-Z-jY=JLTlF쇍fG[)JLy$h"#$ hXP^mKJk2$9LV+,S^fNf"IKWr=vT9Uch\"D;*Q"ͩfIʌƫp T5Z9TGDY i"Ib\б8tiO2e'RiE^Ɇ1 xﰷBۍ-)#Q^RDr32; G[t{J5kGڮ7:}9 Ӭ:iRElI5~` Lǎ UV;u$SQiqFzCk6Ysek_V& H&bEu ,eyդZP4[X> ͏qvV:l UFdIYjJ#225Zս>|ceMfv^ Aȏ]+(7gloؓ>^JxFuíz3>3 >9J5-9T)+mIQ=ɕtedSs~S#!Iy)NBPjRSQfDgs>@\yo;D)FI&֥*i+>-0n!IuԃiddDv^ 䙩$&r'h?ȌNP:lgZwը\#p#sG6Q2#@=$IeTHy2^SMȜ%ؔf",.;R^q6[ͻjhC-'+-v#=Ri.o,R ̍2TH#ʟ|B*׺ZU&6rW <$M"#fk34Uj0NUI蝔6eJj<[Eu֭JOZ1#OVe,-Z-J4Jh)Y+x|cC3-V\gly({j;72" % x㥴pґ4DFvAxCӴNEYTM "qZu(̌%KNK '*T2A R &]ڈj73Inl9Ip;jl)nٿ-W,}Br8ML;ҺDe~zr eB.|.pjRJԄJPi5 ,;LzH؏/dv홙eq帝-vQ_&HRG4̒k5hW?eӮ-ǔJuIIȈ|'b"D2\,F #}l|-Zys<HL)KSI.Q~#Ш[PYeťkmJTfDŽGcprS$lJ i,ē23IȎ */%}|qzFn£yYPV#-Jwks5(\|s{5_Hl;=}!ps5_Hh\|s'A;=}!\|{5_Hl;=}!ps5_Hh\|s')?]CA\|Y|k wF!)gke@'m{"`j0Eց:DtŦȨ:lTL-~ 'jI[WẔwwҍ#|['6deܤf#Uvڏ;CoM󸾔Z4 *<*@Uڏ;CoM󸾔Z4 *<*@Uڏ;CoM󸾔Z4 *<*@Uڏ;CoM󸾔Z4 *<*@Uڏ;CoM󸾔Hናr!\I3Q%$ǼIJK]KQU&y_J]mGѥGoWHiQU&y_J]mGѥGoWHiQU&y_J]mGѥGoWHiQU&y_J]mGѥGoWHiQU&y_J]mGѥGoWHiQU&y_J]mGѥGoWHiQU&y_J]mGѥGoWHiQU&y_J]mGѥGoWHiQU&y_J]mGѥGoWHiQU&y_J]mGѥGoWHiQU&y_J]mGѥGoWHiQU&y_J]mGѥGoWHiQU&y_J]mGѥGoWHiQU&y_J]mGѥGoWHiQU&y_J]mGѥGoWHiQU&y_J]mGѥGoWHiQU&y_J]mGѥGoWHiQU&y_J]mGѥGoWHiQU&y_J]mGѥGoWHiQU&y_J]mGѥGoWHiQU&y_J]mGѥGoWHiQU&y_J]mGѥGoWHiQU&y_J]mGѥGoWHiQU&y_J]mGѥGoWHiQU&y_J]mGѥGoWHiQU&y_J]mGѥGoWHiQU&y_J]mGѥGoWHiQU&y_J]mGѥGoWHiQU&y_J]mGѥGoWHiQU&y_J]mGѥGoWHiQU&y_J]mGѥGoWHiQU&y_J]mGѥGoWHiQU&y_J]mGѥGoWHiQU&y_J]mGѥGoWHiQU&y_J]mGѥGoWHiQU&y_J]mGѥGoWHTߏ[JjdPԔꈵۺJOV])8͎B6K743;wؚl7}`aϋc}RG>Ԇ)ˌӥ"1!:'@*TE=>0ű#UN5CFq2$ېJӠZ-)U#^%^dZ̘-LڛIW*3i3I+xyvvBͯRHԟMhJSkb]vU= JZYBFMFfI"} v2㓛k*Q+˘AR-`j!seCvY)M)ԡ]j$Feg1:Z}n|!ǜ48lwM3JL)FGQ[|&aUSK8LKIC-:Jfk4rV^ iX lR]6qIdDQ'*HE̋c曉YWt%B|K܋Vfj4N"#+ѩa br9.mjQ?C}ޭ]L_P߁NAĕI$A8fԖݲe2QB;-` F]1\4.Vv]&MXsTIF_VC7tGˀ\#|[.µy#*w.A#*w.C!Q";ͼÉ%!JJFZ<&FnLg}RKm֔JJ{FZs1Sr 1Sr cvC,)m%֫+0xN7xNt& K&񓮥?EYܮ{r`nːnːMQ鮲xG7$MbYCUe%D}]2mDBXsTnQ R':WLC1?")1k.aUOR}Ѷ1]idA6}jl?ZC8~, bZIBNB#_uUM6OĔ>Vm}&e@'&DUhIabHhY;]X0̊h'* *Jgȍ7K7mgUcl7*N(ř$Ei>i$FUb""P-吱L"1DE[I2eI+%Hw&Ee>_m$_Ȁs%rN"dt5HvSE)>6IKc T0k&J"JHi#I(xf:M>Gi0aN*4t6k9H1h1SZK[n"6ZWl;Ub\CRBKMZ jӎ6Q3k1b@*U 8S;lKʒM; jUC*6cA6ɈۊJ I#QYċ 3;Q,4CmJP+"-VkT?Ԇ֨s}}?;.V #|[+z-BLvgiT'BḦX0 9loH;bY-^93V77qc Cw?@Tj$iDFHZmsZwVIfIjJ^95:Ͷ#mbl|$FYRW/>_w?@T7qc FYBoÈ%_w#S;j%Iy&Fa&טEڒE'nx1~+2eלuIldiwJb31J&0_)95R4ܗo b/}P"7nf48\%QOM},2ʝJY2!&zE^'mx1~mUw?@T7qc Exhx1~mb/}P"7 ûEo^1Xwqc Cw?@TW61~nxa"7 b/}Q^,;^1Eo+Ew?@T7qc Exhx1~mb/}P"7 ûEo^1Xwqc Cw?@TW61~nxa"7 b/}Q^,;^1Eo+Ew?@T7qc Exhx1~mb/}P"7 ûEo^1Xwqc Cw?@TW61~nxa"7 b/}Q^,;^1Eo+Ew?@T7qc Exhx1~mb/}P"7 ûEo^1Xwqc Cw?@TW61~nxa"7 b/}Q^,;^1Eo+Ew?@T7qc Exhx1~mb/}P"7 ûEo7踪R1\#f̝ Ik"|//(5ݗ}?;.V艼#|[)rJMIAַ#fF%>-Iv:Ϲ/mrin˘k׾3b"-MѶ:a摽*LjX˼ZxfrT!הd$-[%jc;vc;E:bDl!) 24N=v4dFd[pF$!R_Rg%㵇mõm(i6EBK${t޹+>G7#3)U=f}gk.]>o>oG>{X|1|;X|1|2:ko>oMsmõm)|c;vc;e5πt y y 66>{X|1|;X|1|2:ko>oMsmõm)|c;vc;e5πt y y 66>{X|1|;X|1|2:ko>oMsmõm)|c;vc;e5πt y y 66>{X|1|;X|1|2:ko>oMsmõm)|c;vc;e5πt y y 66>{X|1|;X|1|2:ko>oMs/qE Wk<&JECl+ e}!;.Vv]&MXsTu/#|[-ΌJH>👺[l"_}|/>F m~iE4l@m_ O m~iE4l@m_ O m~iE4l@m_ O m~iE4l@m_ O m~iE4l@m_ O m~iE4l@m_ O m~iE4l@m_ O m~iE4l@m_ O m~iE4l@m_ O rj%oS1Q$EZΏ6s{_) f2J4t̲+Q|39$Y=y|/> 5/a[l"_}|/> 5/a[l"_}|/> 5/a[l"_}|/> 5/a[l"_}|/> 5/a[l"_}|/> 5/a[l"_}|/> 5/a[l"_}|/> 5/a[l"_}|/> 5/a[l"_}|/>#Za&LaثB+|Aλ.Vv]&D>-IjKM*JyHJ"+&>-IO0QC431 31 W[xJUJT$ӳmfj+ZRd|nrnr1.f<tf<t CV=U*eELao("5RQ\]Q_GP8anzR,Er+3lOU 1ۄcۉj+Z[lR-3&+uJUV$g% *gDBʮWPfY6mJt-,g[^V{_]cp1PȮiyc,=W] *gDBʮ5XFLEF:eBڍJM6gZ-:'UtΉ]FW+ *&A7Pԓ˚J{w ] *gDBʮGr#+%ViO̕-- Gd\kÞOYU :-KC/!)KA<3$f"=v;ȵj;WeΖZr92!<%I=ql6f{nOi46ӉE)9a^Vy.WU̮}at58n3TuN-gL$e56JK)ֶ&5UO*jK) 7U-D"Hқv-z]&MY׶h>0ű"bRs[]˃!}`aϋc}RD/G߬SpSHMbW G&Љd))ʓ7 4plEH'U(ԃxѵHzc$GغXkCXqz6=iFՔϿXCI'UU+2 FE!0BKN6&K%+fGb`MDғ.I7ںV{tkƩ!TEĕd11P=%)BA_1\ljFut##D)ioAmGZEgZ$F,x>Fj L\KFBSpZC%5H$ȵUٗ*>ҡzIFBSIj5U"9CaRe3)y]7f<ّf2+NOc-Qim ҡIhQ܌z)8rbJjCq)L;ƫIjQe>5hR1zsJ*LԔfDv;w6Z/.PIa[iK H[e5&Z|7 *)TDK66Fnj/V,k1{jIMFdTM+LsAg#f_a-d[︧rJZw5Y9*.8He*mI6"H@S.Vv]&MXsT4 $!p 9loH r#.;8Jq$Gd~#Z&ndDFIoxfG@Ys4&Uzɒr{&I䚍Gԑ$ 7x:? !ɇ%Fn* b;w;X? ~#? ~#eRػV3:^~m}W Œ~GZYe[nFBk:u#u({dѣ)eBs)JQR.32""98!z\yP݂I7>ק,bߺbԹt$D]3zB2q=&Tf]EZ_C̚B ?o**Dlx̮hQp_|O>k 4K3ZnK.rX>Vq{.mI_ek?{#_{);:t;QZj2%kW z"\=%Ғ$~"IK湖6KN~L~q5QE{Lhtsد3 (Qzp j=Nϥy=E757m^u&׷ZS4ՔgktumgU^ϥy=eoN2)MȄԧ{JzgFW{e}BB̉)r"ej4"fdG}Dz2KSYdÐ'm}{6G+jp;yDk<ݵ{Wu'^_ha .$>WZRP]ҖjIWޠP#1Ĺ9\m\Q%JzVQ&3ԋ=ݭ~ԛe}@ ϗGby v:y=ReI.59z%󞫞־Τ+_wk_Τ\L*յK<\fGSȵ{'s,m^g; iJIZ-iA$| T^ǝsݭ1'fIFWѕ]:z\:e{~FzNN] rNAk[國YlV UݧK󹮑$R |,jU!VI|m)XFdԗ,v3jȏfOD|IKdG^èˍ}-FZ KΦIWm_ȷz9d>vͨ}OlSU-RYZJQMْR{QU\vIu6Rc#MbLw+?;{}Q30?`pF%N|zN|z wF]:i :i]:i :i]:i :i]:i^Q?5) $xȎ{ЮWta?_]}?;.V& 9loHG7$M?DI шP|$-+B? R'1Ua9jHy f&fȷȬI(жLHirIT7HQd}u$Io JDyTRP:yaHmC~1$I=es=Nq .dj*J^-Y2463a>\L%kL4u4J%8#qcbT78H-G_<LDl ꏅGc? qr[)žw;l_xVl ^ !w8H-CqcbT;osmw;W`~iw:N)žn? qr[ B-=M& DZe,f7"դݧM8Q6qfFʛ$̬W.R9}P~#+SgS)Z#΁EC(`G4՜ԃI+q=FMnif+@22V[1G1o\s__*)'#QS9 *zMȍGc*]EZ%}J˪gGao~# .R9}P+_u-1M)n)WQ&kǤBLW7u*n|K-w%Uoo )žn? qr[u1gqV6JuB +34$w}p4:y%iec+j/Ŗ$jr jcRLIΧyJoY|'r"#44\R͚Y )%]Njv2I"Cj;#2";2J&.xI%rZGIxSғ3+.%%*6V3w2ըUZs,"%N,zV ͪE@1FCm JE\S:[%$Ȗͻ$dV+3}0"f.%M%%"Q3+9*j֩R#Ӕ܈no m-㱟sͱE:2\C{/,E0 g3q(+yyLрrpXifT iOh!V%,%꺷JQ ܙ<_ۋ[L8P$F8V#lfvmdGr>  w1qF]JKh 'k\^uuD*JR8Ҝ$JQi#"Qe#hsbCCThԶFMӒ$j4%7"23լ%0d&J&8ȣ7IvHYfw3+X p*l/%DU;__[(J@1_^ioHv.R)d󆬏&JѦ"SJo(Ja:m)U̔i2]wւ" n)MFm:)CSKZRR͗#֍EwJ=w-VX\GICT"rVD#cMGʽZ-`)MbjJC㥉1l4]Cjzlَ_i_)Fi5ːRnQ=Je&W=FEp=eim>;+OLMm[dw7~ Eb#ݕYOKnK3O>e?%HW"n'<5p3)@E0z3D>-IO0QBß_!1N&7M?e4hYJ7+Q>C*Jc!.)fVJJv p2\8j/liN؎J3-ofQu7؇^nEA0%AO m5(DeR{,."CgTrPKdYTfDZǬ dKjLw&HdY#F}} mDVJgN{V,TЕ$QܐM4Ȳ-FG-تJTf[L%9.$ܒI\YFe1ȌѧPF6䜂$ؒ*oDvb(JB^TaOdѦn;5n5pXK•1jY Iͼgs5)$E|̸p`Ʀj6RvBwƜ?luV\iڒ %^,Uk$'+a3Bt}G{*mwF&c̔8Q oڬ݈̌w#F6ӍBJźZb+k3QY uY[&QSL3Jspeg{|yC\%C\eFKMXХ1"@8IoH̳(ed$gӈjU65"|~B m%w%VWO4 :c9S$ !&bV{Gbv8 LA.l0ڜqgg24~)ʹ$l*v2jt575Pn@ X9 RPZVȌ*kfMzPOPu*2^7`M %VcQY'{ #վFC%R5ѓLrsQsSlʺ|H;__[(JǪ⚔ئid;e$di22222 EF* 6+L6kRq)J4ꌋ&2S ;SgFzB~yY^i3[IIl;Z䡶ė2#ЍD8!Ң<ǬofJr zJd*YvV'0fvΛHZl5>ę)YokjX>]9Q~Z[:N YԶv#VUeErW yT͉fΝ6|ާ7}v0]EQkq&b=^o-}' }O8r}PK344Mw$[)o֔b"ʗ!4#M""5!j?xX Ds *%\Bn!fI)"GieNuDzͤ54Vfwmw+w}ᨈ`*UM>s6YfCA(FB)$wVH{n(-Z*ܟ4NTI+$d"+/w}?/*Ň*8b+uIY]eRDԸ#mt뽵Z"\en0FhVSMj+(2$T+]OtXr MY׶wؚl7}`aϋc}RD/G>0ű"i#Hf@"9Ve|W:ɔR,C#$j"3=e}D0"N(Du)IQE$Y2=G_Q%DNNu6q("3QE^'*y$H[Q)u_lfRSiBR%33,FѣzgO#Vm"tw̸,emV<8դ)U1=ptff(I#)ek#.zEA5rTqꌒbGy*B&沈ȷȬIXR9-s LTϳVCT4(NI7 k(|ĝҀ.PU7PĩE6 [##%:ㆂFu^GmfcԒ' eBN%nlÞiͳ.RVBk0K$7Q!Id aifٗpN$VBfN]V$NSqMz-B~4"4s2 s]+S=#dlig&cr\q*qŴjRlwOr2e=VhT%S.ߊt,R_"4>s%Z̻ԥʞqh"G)TEK);ԓXD7ltʛnN&ۺ,hkyRm1Pi,<"E%L&(^ $Djd뱝tE+G-Se4#LfJ;uEr26 yWM95[h]gK- 4M%FXZ|ajǔTI&Y.ًI&bȎگeANW@a5ZIf[#<-2nJ6iUJ"lo_eF6hήCSۜM;),QI؈dgk \Wq:eqYܖqh^ni$$yw2IbZ{Tztl)FW5%̲+k{p +M^7 jdˎA*JLYkvx$&K&ZT;-d"QBMEc2#j=F F# !ԓ%di#;j:^!UYPym'e:М34G-6<bz%1*zN<Ϗ Yp̷"$*Q%\ -=< =<TRmvVMh\K&~ z^1r+.Nl*Tu[3Vwblj2"V{yA@{yA@, :(:u]Ci-#mI%)J"#CKpۓ=i*nDhcdW. 8s8s'@r])[r\4h&┥ )c"#>舌FB^ZXKRDGXeJsW q0q0Qv<6<^2 .ǞÞPǞÞP AE8s8sz({yA@{yA@/@`pa(?`pa(? m*p34̈Y^ױ_~ż#q_@4 $!q_@"@@\+]Otebj>˾՟}{`6#|[&a~?.>-IO0QC4ACĐbZ}+Z$8Fv%"DwBeIy*fH%*%&wI CUj5zQ%l9vc"29 ¨A:sɥ.ITtm)Z4YIU[^@1SDp4lX1V-W;j̹>8 C8ѡDPIB˦{-s20ID<8ڠIL.jS٭cBI:`XdNrV־U5((dk&͵XEzx52!%|cՐC[2-F Ev081"&4GT6q8HpFDGs2-{1"`J .Ei-4O:KSe[ZW|ʧ=U*eELao("5RQ\]G&BJe/uGMDm,F;__4 MغS.4ݢ*lf6LR6yd{9kB Ҏ%{RQWiU*laSqH7f͑qġ;Z(\ͶKiv@M˓C{piQ8Ӟo.b#-IT}$V4yK%^ڮmR*6jhnR(:))$Dm&"_QLO#Abԛ8{~K$v3S5%K{49:Q&+tmi)g$IQ*QZ ? թpN uHmVC5%d226u@MaL+JDUERVZNK8l"re~Cr+zYwڧ hOin%eN *Ѓ3+n\EGSf yo)-"A.8e"2+[)f|7$@ PmVQ.t⫦qddlH2YXr2$VYه%_Vd"N;#e~i+1p٭z:F) `9kBDz'8b[Y c(ҭ^fW*8R+1JQ!N-(qSNJ)'kW!k٭z:CfsLזJ%("ĂGmΩ, ͘DWzZVUZdSXri4m2Mih+܏Qصֹk}٭z:@WCVFٔŜ4yi2Q0)%$k0ű"i#HBXsT4 $3F @Q[P&lnxĜĂ;fQIJJ_1ӡr$%6C"M Dv3GX Pl)$Δ4U$fgbzp(Z\m+I*"2?x}U٤œiݐ2 $ns"-I3{ҦQZ24rfw=  #ؕLQ6m)j;$,Dgb#4k2DĚl9 7-#D ԣY3IܵSYI]sVAnV#Pt!y"C4kUD|*l/$޿qKOGm;__[(JG{|]pr/k>Ay_,\/#܋돰y_,\/#܋돰y_,\/#܋돱I) ar/kEuV<#tV<#t5#܋{|]q&]!&] 5#܋{|]q&]!&] 5#܋{|]q&]!&] 5#܋{|]q&]!&] 5#܋{|]qN.m /bQYY5ֹ_$I) o#܋{|]q&]!&]"{"fy_,\lec9ɗHec9ɗH {"fy_,\lec9ɗHec9ɗH {"fy_,\lec9ɗHec9ɗH {"fy_,\lec9ɗHec9ɗH {"f7ߏTTm!0%\=k=k3=ذ[2!+]OtXr MY׶wؚl7}`aϋc}RD/G>0ű"i#Hf@"-NkGb~m0kʞS+mn #UNo!zhsۅ-5 S̚ &jc-MzPё-,`jݥLZ-(l3+,#3+s"L3ѫ4Tmi/H+xiqK]b7Lsz9cQO3Itv'̑JVYf,pJٞ˳ydC[HI)ƍFDErI(>@&N~sq<6padGe.G+՞r*릸h\,ȳȍDGB(48tXu$PlnJ%3;$b5XiJD*51Rg>Zi!R ТQ9$6,Q;å JXL F xJM-ǑXuRyJ%V34r$E-ķjĕSKQܔeҏHA(nxG{ZJ[MuzWnm+yo!j\v\PilĵfZwSOHBm%ԣp$w=%̍EΪ%@i$y[B h53%oL7]<'5aJL8DOWrdHUkZ4::[9uII%k cUK)!5{)xȔ+Ykܷ]خvIɧٙK|GIV%p*t-Q W~?P?dho(`ob[2"* F]1\4.Vv]&MXsT4 $!p 9loHetü92ݗ |Y$EdlmE N39T ^7 ś=%r7 D*|OEJj#Jt␔4~'r3 ΗRS`O*cdœ=' $ 82MYNKi),$Y\DTʛ5Vu!%YFV3tXq'9_?9hS odi#&K33{iPӢ~ KSi&h) Z-u+VIi8ϻ!eIYDZgs2+%EM!KMwF= -di.{Zx4 TSP9- :Ȏ-t\%r#q9IҕFLH\gf4dGخg dYdF#W :TX(NSe6n %GuGђLWD~Z"sZZ3f4hQ(nnk(|ĝ(DPWiwN3$hF(g}Wȫo)%,&G}ΞGDEf>c;pXڬ)x:SRVaOCr;6L%Iw JL|]'=96;f+%Y稬Ub=e=N2 "$?mFH^Gq(nbzvB[]C𮰛Imr?RDzH2Ӽ~6q,`+[NA`]Zwao(`ob[2#=S&.DG%kme$z4"ēRG+(2, Wta?_c'kںK5g^ebj>D>-IO0QBß_!1Tz=@%ʖ0MQGm&{]' &B.bډLHsIY+*K2#L  iF*eeo![I6Pk%A'CTƗQ)7l7oq%SGܛ'>K*JRp:Xn4tsWJ)&GcF{fW Ł2%DRE#̥)،%AHe HH5\CKm٤HkYZw .!9ڛu9VF%E## #Zh*J8PFkZ*ne{s;_Q ښGNܸDv;e_)*&GcW2&^ENUXfF4̇ ˾RfT͆HSM |%  q e&keLd9 dސwi2Y;gq|x&"=1oJze22^C#3=yX KjDF z{Fx)6Wƶ kˊ3lnݲyV=ON+'lYp^8wғYfdNE(ȈVbh5qzNu 7L;7*Jzǿr;XQ:KNv[2  HJMiJ2j%fXYoEis!/2!lԣqiBLQ^F] ZCieuK|bᲕ%hJТRTW##CSUIae(7E(Y!%w%ZWAģ/Dȏt}}t`DV밗n q P [\g%z??k%@4En q밗%z??k\gTDV밗n q P [\g%z??k%@4En q밗%z??k\gTDV밗n q P [\g%z??k%@4En q밗%z??k\gTDV밗n q P [\g%z??k%@4En q밗%z??k5ΏW1G"ʑ9藝mv2p&nD7r Wta?_]}?;.V& 9loHG7$M?DI шPW sg>H:եy-TE!~^~&MUP5<&d)+q)Y-"vP,:EQOM+NM2%&UHYȬ}0[(I5׹ T(m7BLm]K}YOJS.7n5F +nKN&U i.'!-&mw/Ŗ$jr jcRLIΧyJoY|'r"#2n+z48󏬳kQGff6E.35i;gJ}uLymO\5F٥#JHYnZ)HV*L٫M!*AJ'$ۅ#""'xtIK ha3Q+6Y;\23]!/b O&8Y4rF[Y*b!lu"cQ%"&sOZݝ'WsT;)#;kWZ=5QKVtJВ#5Q S*jθi5Tl365$Gs<)-Zۨ!QNBz$6i4Wc,+s;+ZpA.=oCcLIjerlñRdžNl$K=w52a }A9̗;f(݈V|G0TɊ:tȈ Fw33m qNKQ)DZ̋z}aI鍅U4)R Y)&4Q幗 c)sYU:Sc*48t%Dh5S(dtaNʩ i`Dæ65'~Řg~ i(aFI u8Rn~w=DdfZJhiOF%FV o9R[QNte-~+а.$؈n_%Go:!+M좹\+]OtXr MY׶wؚl7}`aϋc}RD/G>0ű"i#Hf@"BJ]r>Ts6oG6rֲBSe^ k߶ oLaN?OD܅6j2kVm;p;.:̈vLDFg,Q4ǪPFCO%/LSi#5#,.OF<i2qєQx?(5a镚+#.C9.00wS%\iJMq$LҧiQ_HpUȳ\I\(n -+j-]9's;j|i}2Vܖ#}hL")J]NBZM>_-HAY%Ǧ &.O,޳NDFf'lO-놷5(4DiBIK-Y)0I5i!NAeub"6֢gHjGƴTʍAds=]+.Clv6|BU9RhҗYIQD/cU:tW#-4Z[a+I*IVZ!\;iY ӑJ%f22"#ըf_Liӑ2&SrS%z+,dv=y). 2p}]δݚr$DY'"I7+UzT QcBv+yJ5(.ť"J>!))StLhJD&C-J>CLPRSl6hTIȵgs+,JfTh8JB!Ӑ%g"~EĮ; 2۴6Fȗ5ΔZRI#"HfM܀܃aG5Dk[{"tْebV] k,U&uPn'LCjTi4(IN.&TcHE/!ni$8e%$jQK|EMCUTzʼPܖ~?rdZD(I+Y{;\@NnN98A6WQ$6+)RV %%Er2;Ljz1,ش2u0Kx"ڈFw.h^q$NFr("?@$@@\+]Otebj>˾՟}{`6#|[&a~?.>-IO0QC4b j{N7yYԣ33B\SDJQ"޹@mq ZnGew/>r Wta?_]}?;.V& 9loHG7$M?DI шPr Wta?_]}?;.V& 9loHG7$a^k!.%حܹUv"lsoM󸾔6ڏ;DUsoM󸾔6ڏ;@XW6ڏ;CoM󸾔soM󸾔6ڏ;@XW6ڏ;CoM󸾔soM󸾔6ڏ;@XW6ڏ;CoM󸾔soM󸾔6ڏ;@XW6ڏ;CoM󸾔soM󸾔6ڏ;@XW6ڏ;CoM󸾔soM󸾔6ڏ;@XW6ڏ;CoM󸾔soM󸾔6ڏ;@XW6ڏ;CoM󸾔soM󸾔6ڏ;@XW6ڏ;CoM󸾔soM󸾔6ڏ;@XW6ڏ;CoM󸾔soM󸾔6ڏ;@XW6ڏ;CoM󸾔soM󸾔6ڏ;@XW6ڏ;CoM󸾔soM󸾔6ڏ;@XW6ڏ;CoM󸾔soM󸾔6ڏ;@XW6ڏ;CoM󸾔soM󸾔6ڏ;@XW6ڏ;CoM󸾔soM󸾔6ڏ;@XW6ڏ;CoM󸾔soM󸾔6ڏ;@XW6ڏ;CoM󸾔soM󸾔6ڏ;@XW6ڏ;CoM󸾔soM󸾔6ڏ;@XW6ڏ;CoM󸾔soM󸾔6ڏ;@XW6ڏ;CoM󸾔soM󸾔6ڏ;@Xr xQq}(Z5GBy;.xRu^a,Rx|]&MY׶hN!Ij|ɘrЕ-&FԒIIZ22";gǞhPrmuqyd;n??۽yW#cy@ǞhPcy@ǞhPcy@ǞhPcy@ǞhPcy@ǞhPcy@ǞhPcy@ǞhPcy@ǞhPcy@ǞhPcy@ǞhPcy@ǞhPcy@ǞhPcy@ǞhPcy@ǞhPcy@ǞhPcy@ǞhPcy@ǞhPcy@ǞhPcy@ǞhPcy@ǞhPcy@ǞhPcy@ǞhPcy@ǞhPcy@ǞhPcy@ǞhPcy@ǞhPcy@ǞhPcy@ǞhPcy@ǞhPcy@ǞhPcy@ǞhPcy@ǞhPcy@ǞhPcy@ǞhPcy@ǞhPcy@ǞhPcy@ǞhPcy@ǞhPcy@ǞhPcy@ǞhPkȸҊT1-TTK~R.)ܐDJ"33"$DFGr=I}_zDz٠F xNPFK gWJFIFHHxwdumpC    $.' ",#(7),01444'9=82<.342C  2!!22222222222222222222222222222222222222222222222222"]  !1AQV"SU267RTaqu#3Brt4b8$%5Cs&Ec&AQ!1a"bB ?a9O#Tq͔ZޑZ=h"lTWVk&mܖ%6ؘ"vpژ['gm ,eφk%kG r^i$5{%v+S=AQ-z/@kܗ"TnG r^p%ȷܗ!-z/@k-Fp%nG r^pQ-z/@kܗ"TnG r^p%ȷܗ!-z/@k-Fp%nG r^pQ-z/@kܗ"TnG r^p%ȷܗ!-z/@k-Fp%nG r^pQ-z/@kܗ"TnG r^p%ȷܗ!-z/@k-Fp%nG r^pR̾L7Q=gndŪ6*2e1 jKgrmyp ]nKz]܎5`ģ@m 2u(ïY3 ,BegzKڲa-z/@kܗ"+MR'ۏ/LV嶿lmő\Ktm4t8V' I܎5 nKz]ʥvJZ}+}g9R 꾬%G r^°CmXn|8,,F*Lf8Io: Feտ~[nU7 $Vdp%nG r^:Bk͸u 3YTGDyn1Q-z/@kܗ"TnG r^p%ȷܗ!-z/@k-Fp%nG r^pQ-z/@kܗ"TnG r^p%ȷܗ!-z/@k-Fp%nG r^pQ-z/@kܗ"TnG r^p%ȷܗ!-z/@k-Fp%nG r^pQ-z/@kܗ"TnG r^p%ȷܗ# O:p Z3v!j?F+T&QB\YL974 P˛kڶx?d5C2R \{c?0n<Æ۴4}+F_tV e/ڭcE9%ʥ~%4ǤFԤIZ˄MKkf *І24:pf[*x{m, f3vC)+!R6ؔyZ*E2Cri͠(o[%p||oHjcLhVJ6mIQ%MHVe&s"0{WY8{yqe\ඤ-mLd-Z4;v %Lrf4t[;8HBnv+-fEZv6,do]}F<:DPd%NhlBcVR،b11 JͺMТQvRN$dded?z<:C#ঢ়GXjhd| uc뭰ҝuĶ ZĒ3i[^e JrJ;$pSΣ42> yu&GO:)Qd{q4|)Q<: 42> yuGO:Y3imnB- Mr3j2?&ęIfLu#KBv;j=dev)Q<:͹ 6kq]JTFh3"2%SΣ42> yu&<:C#ঢ়GX#e'-wHY4X};d| u`FJ"222=dd?!fPN)Qf$fFW 4~2> yuGO:C#ঢ়GXhd|@4|)Q<: 42> yuGO:G uaSΣ}Ȏ<ٶM-8#`tdaZGBIC4FnN[|4d]VɶYiP#5Jm4C۟ qC,N+nѣ#/q(G'*B2<.meKl6 bdg\I`wc&[Z..$e!kΤdj={(2mq5mWmd!Yt~#7:]VҦG;d|@4|)Q<: 42> yuGO:G uaSΣ}C#ঢ়GXhd|@4|)Q<: 42> yuGO:G uaSΣ}C#ঢ়GXhd|@4|)Q<: 42> yuGO:G uaSΣ}C#ঢ়GXhd|@4|)Q<: 42> yuGO:G u`堬J8ɏgx_$_7{ͥEǾ?@gx_嬬>%h+!DN6cY<|OQZzVtmNh+粒erիVğWDi=(EE PK1 3 -Pictures/10000000000002A7000001E7D4D9994D.jpgJFIFHHxwdumpC    $.' ",#(7),01444'9=82<.342C  2!!22222222222222222222222222222222222222222222222222"d  !1Q"ATV23RUau#46Sqr$578BstbdecC%DHEv&Q!1A2a ?i9o#\q͉,-LxSq9*Ou;߰->,~ͺv;|mLX6\OJ=g+mvMgt^cgoMp1nh`=ײ^NHCQ3$Ol#-޽i֎^ z5/Jtz_kѧZ:[{/5Խ(":|AFhn ףRGKweuײ^K҂#-޽i֎^ z5/Jtz_kѧZ:[{/5Խ(":|AFhn ףRGKweuײ^K҂#-޽i֎^ z5/Jtz_kѧZ:[{/5Խ(":|AFhn ףRGKweuײ^K҂#-޽i֎^ z5/Jtz_kѧZ:[{/5Խ(":|AFhn ףRGKweuײ^yV_QL7P{8A=dŪ6*! R[9 k:OA+֎^ z4GKwe%nTyR;|x0BEQYB8l栐GKweuײ^Fi\=ܶܜhvjنYĿvLq}GϚ tz_kѧZ:[{/5+Xe))Rc¾HqOćnquAlA-޽i֎^ z5n %lG8tW]KOo5L퐔HxgZ:[{/5ׅi}$ڴ݉./; 0YXZ]^%q֥h +I6~幯KaMSr H!=GKweuײ^kX7 6)Pr[*HkGZ:[{/5ӭ-޽j^hn ףNtz_kѩzPDuײ^:|AFA֎^ z4GKweGZ:[{/5ӭ-޽j^hn ףNtz_kѩzPDuײ^:|AFA֎^ z4GKweGZ:[{/5ӭ-޽j^hn ףNtz_kѩzPDuײ^:|AFA֎^ z4GKweGZ:[{/5ӭ-޽j^hn ףNtz_kѩzPDuײ^zkEiѶ9bBr@<:Jע+T&䀣ϏlvA&~Yc}bǷ͜^֥m/.Rn1eBA;Y=fwI-jMsvA5dVe/ =aucA 瞶Zpc Rm=KkfІANh!ӣ4SOXZm9]98cCiam OS&J~Haԭp(zձڤ[$= ;_ ;\ |]-lv 4UNRT@JF@Bi[?Tž2t[RN-m$pݧ;U?QQ2l[|Uʛ%э^p! <\mi)dse}M̏z =hD'*u!*st̄,d`|[fJTmԥjmE I8RN;`M2=|*zJhSQGOG&j[ RͽcHF6zZddInQ*d^ve+pzZ-NXul0AyjϹSQGOGA $2d0yPSACh$ F=d{Ty鹑SQ篴GOG>zJhSQGOGҚ>nd{Ty鹑SQ篴>znd{Ty)yۭ4t~lז`aZGBH#YMڛip!~?5ka,2P05Ѭթ53n{ZePbZ7`5Jh%HF2=ڇ-m `9-归~kfчv ڂ9ׅČ-{jI-UxuJhU2GhfJ+meJ![hsώ<+-)]Զycs*l(>zJhSQGOGҚ>nd{Ty鹑SQ篴>znd{Ty)GOG>zJhSQGOGҚ>nd{Ty鹑SQ篴>znd{Ty)GOG>zJhSQGOGҚ>nd{Ty鹑SQ篴>znd{Ty)GOG>zJhSQGOGҚ>nd{Ty鹑SQ篴>z%hBFsgJhl/^w3;[g=<ٝ_j)kZscJha k;T4sdHQz{wmN0V)B28pI_n^&t=XJv:Ua%.yKX$dd|Yngyc*/"ݭU[q.4K$ 82 GQS-Xٓ63sܺKIS[U-g;H בulݹlu8om emI-q{8#|9X$jխm6(B>A*SN8A9<9Zkz*jl"PY=wN#|pD,&,F쇋Ul) s$0OlKBԶč(niiC>vW5KיC cbx㚠zrP\!rˬEmM#i% #Q'Y#XZj zr3Ȭ,ˏvJ@IC#'Y~ݾov:PmHP# OlH-+ݬͲ􋻎95eG~~ZSF-~~ZSF-~~ZSF-~~ZSF-~~ZSF-~~ZSF-~~ZSF-~~ZSF-~~ZSF-~~ZSF-~~ZSF-~~ZSF-*`aJIֳҚ5(o?ֲ5BVY+3(_wRwR`"qm.ZN+RF8vrGr1~m?Xs߲MN-HlP{UUۏۏhޣ yGU}6q}m:}6q}mZ7}^zoQ~{͸_[N{͸_[V>W}=_n>7ӫn>7գz7`oAWۏۏ#.Nl ˔yre$Q%$ `tR}=_n>7ӫn>7գz7`oAWۏۏhޣ yGU}6q}m:}6q}mZ7}^zoQ~{͸_[N{͸_[V>W}=_n>7ӫn>7գz7`oAWۏ7RX%PuX'HpWoKJz`3_OrI4at5CۦH^dY.3$4bBiO¸8"vlh)V}?9UQj`MaRD#$N8$vyV:M"}7p޾ĿXqͤ'`8ճp"|/=^.$P옓%zKK9q;}ݑvѲUtշp"|/=M"zލ.Am-Ս,=\1k˸_K~>zS&ݼ*qDaiyØ=Ҳ߄O1R߄OJ˸_K~>zn߄O+.}-D_K~>z TjEu8^ R󀤨gEln߄OJ˸_K~>zn߄OJ˸_K~>z}I[KSl ,)gG'*t}-D T}-D1R߄OJ˸_K~>zn߄O+.}-D_K~>z T}-D1R߄OJMŻyuT+}-!IJ<Ê81}-DT}-D T}-D1R d|njm.6š`)*L7H vh6iZOe$݌dEl)JP)JP)JP)JP)JP)JP+3 fc_5X8̸o𶖤74۠drxt@MW[c٥i}#>M$Uq Cmґ!:'pe*NNP?n 9lo&jtZ"3A%);IJryU5]ElIe)[mMi*'dlFk[er<ħvyBˑA-}!II9+MMuE0{j*RTI uͦ]fav2:*QRWvF@$h!seCvXRSCh+PN2#A\+ղ, #j( W˧=dC;t'/UvJڦreA\:M5=.)^#MޕDv_mLoiv YmNp+I{֛Nif ޢ"4; .;֙γ)Q&)w#uB,F*P)JPVȰ4K2fIf4tJBs12OāՅRL%KCeCiI:ke>ͰvPe$TyF,e`i*<2Till7d2C6ڞ^iR+V 8oENOv&ugo{6R]HA'VSsE]D'JT 6QdsnjP)*OT;֙γ)Q&)w#uB,F*z٥?I IK|F;&|V[R*Dĝ=#A2C@&!jb T~#X)J)JHѾDzzCe\1zY޻!)Nddm pz*9_4o$RQ֨4쁫'l!Ki?Us%D}Tt8N$5!K,-*Dp5"z1u)ITtHBI`浴MK,%)MG$R@' xUbm];mAd)Q)j9#A+ uvTSFuύgkbuSJCM_viBR'% *%dd9UmIf' ]eF,G[IݧppH7:lVVd4d#BB^0h'h7YNP  p 6O=A:v2Ke- 6СvU%7k6TGxj7^Bvv~thL^@K 8q8ώ~jn|PX3 fc_5 庯6Jj+H 9lo&\59/kq*AYt67WXRT#Êu{xq] JmHRmAIPp"23rc<:iAIZO1p#+u{xqN|V+V7d2šK6ڞ^V3p @4=[8_G+w+ZM"D!C?KF6Td #Êu{xqW7\'1* (&;nNH'*$`WY:j;o%N@T{u1tS=[8_G+w)LW7HnWH0s~|V)tS7HnWH0s~|V)tS7Hn+֥QXLSlBDI?k$}uY՟Z[U&1![wwm+aAJ†yn5ff3)JP!j* '8cUquqAAT8=M6͢h0Q:?v_=sw–'YNw–'YSGHmhl47-%@$G$׸ТKM㜞ٮi6R)6R)CZϸl89n0Pv8rh"I=S: [}ekcSuU7#r)`!KB >[1?:{݆EyEnPmqR얩2C{)H\m]Sm]SGJf >71k NNvQ{Q2h1";1[r1{ю;Uz [}e: [}e4tFn 5ldBXHp*kzo\ڻKwϬ\ڻKwϬJsj-O>sj-O>-F;2ttv2ۭjq!C)|`TLfL?gk/qV $[cm]SGFiD{mkj'-mL2ڶBDy,$,mmc9ˆzkͫyUuJLW|)nySHc+z +mm7E. FZ\wiv֜l\8Ͳ:+bo\ڻKwϬ\ڻKwϬf#o7 e$'iGG֪vp,%QCG9F;\ڻKwϬ\ڻKwϬqr hN06c94xda8m9@'d5W|)nyW|)nyښ!"b!,$8XfQVuͫy6;vvMrެjnnV%1@)hYc'\ڻKwϬXَd,2IR rv$&\ڻKwϬ\ڻKwϬԛwTz;1{lg q]qw:i; *9QP $skuͫyc bS|uW|mKO V (E1f?U)l`~4"~4ɱdԳU2Kh: @jH`iϓc~5nQ RKg+ MOT옷l0oZ}m[ᕧ+h ;;'*WY4؛hgOŒALB\c(NGˉ3&m'r}$T)Yl'N&DUh%]!̈́'TTfʼnd]@zhKbIL IRs\F8jaRuE,"K[vC!!r];=el"1DE[I!$a*FGb@#532$)!O+lI|ۮ*H"ݥFR#+eL H?Gf-7Q5E)lik-4 k$(R B9&Mgm0a*4t6W@cgLc1dPBҼmq fX{$mRm⁵%Fz8Uʣ.:vwSJ٭Tv[2b́6f,viXi BR(2RRRRRRUgV9ioWUgV9ioW m>DIw smQ+g&V"+^|}-{C'b2CD񅲠#eĝF設7)%e%2dhEKRT2̓mv9lqJ\KjJ#꜆%ѧ7EFܦ[PRad8){7aR$#fK.=ٕa͝q# T*+}-DvmPTAX q13G2RVe$XȗvdqHt)}9^a7G(6s +NNr'Ad~* c>APq)QN@QHp:v Jǵ<8֔9*:<Vˉp fM eR!)'>AJRJRN!PCB A'8PRo|ʀI*9>p_,aD[JrBT\e!R٠$lV[mň;#[X9Q;|c81#ܜKnS JG>Qf,8qHjb-\!TRLϳ 9#=Pnl-8 8'A>$&퐥yn!a$HGsB%A&J4i lv̩#EiE;6`F6OkY^X}fBBR((sp8 ۠qG KLܗ Rr@: <㊋jJ8Xb;%Մ! yT8Ԅ۴'pu 5)l@ 08 Zq%Ċ ,m*“tPcj?&z9YbK[TȤF3Zb{SpdR21'Z#Eio: BA$q<l5ɺ_~]Jc/.ߥV ~o"H }´P;Mv[g*ž|쑮w;TQ|v*4l5Ǻ_~]Jc/.ߥ]톸X˷S}Ew]llR؆8*)BJ gVk6@v\}Ew>;U6Ҙ\YMZVyPQA;!X+RSB;o1JCRimNVsv\}Ew>;UiNku~:_~]JnXq݅a%[(R9횊Bշ d5Òc:̴%+ BT톹X˷S}EwҝQ|v*u~vSc/.ߥNߗoҮzԬ{ȖL 6JqvP j];ʆ3>~T"!*ܵ%*NTvN;au>;TQ|v*5:#Ԙ#+Ce ZJpNƥb<خ#m.̤(dv\}Ew>;UѬڕo߂-m.8Q8ZWvEan-r- 0Aep{DӶc/.ߥNߗoҫZ¹ɂAkR`y I)IWc *#i)*VxbmaH.:⹒;DNku~:_~]J38aeR99 mXΔuElRʂ Su~:_~]J4JMM ~) KݧqS;EY0;TQ|v*j X:WPR£RPOV>;aitprD軦@V*8k ?cv]*~~\1O򘱽'@?5=HVW}?TV*GU RS]r[s-H*NvcҳgdB l'd)J%8_fLЄxJvKMӅCז-6%IJBP2xvHO:ܵ %6eIFqjggw)&Ce[ 8<OVi e[Ii#gg<>8W2k[alm!Jjh=Ҕ\ϖ sY7[>X(,u|Ն1UV&viMW[c٥i}#>M$.\sRP ַ>hO~4ɱd.}}UߨsqرHsr4nN}ozwj8ՙ۲}RƐ^SO0ot?6ΦQNx,!$$8)HepF (Q' 5^>Ϳt*&B"b K!G+Np{%lߟK{FHe*sĞnnqgt?6ΙE]˫٤ceiIP ^$q#Y19Q'a h(uaX:} ?LRt?6ΙMsWA?6ΟC)}J?CaX:e5ϩ]aX:} ?L+ ?Oco>tco0~m2շc} ?Z24w[ڏMKn[;f$IEt53ow Lo&>ѳP:,zB䈒$+S ,Gl$zGZSVڅ(m+ (AJ1"4Y h(?&C:O%@GoʎQ$[\ݹ5rrUe; S).6+榵IN{Q)Z 'e0ˡjVJro(965z.IInH(B0I$c +r{Mh?5 $}) 9$Jg7MLZpČNSvpMKjiȉty%[#dѝ^5 ]nuL(*9 jYYIrjHR=Lqm%#!'e)h'gJ'SNDMK.%d9! B'iI5v((Yd]5߸t2;M4l/ufgPEv~;w`76PEv~;w`76՚AESXmL)݃THڂVjRF*~~\1O讇u?v?AyݧV9}oC?5=P0MOTPs!8{ImZR9Ճ[w[Ԕ-a<^7oZE_[wSuw|$]ViS~|~mH>Mۼ}uZ7oMiPUy u7WwnAh߯57oAWn]-U~|߯5_[w4.*nؔ̒텵(VV;8M$7ԭ}J?Xs߲M[Y 69OEOѻJ_)S~Tlҵ쿊|_)Plҵ쿊|_)Plҵ쿊|_)Plҵ쿊|_)Plҵ쿊|_)Plҵ쿊|_)Plҵ쿊|_)Plҵ쿊|_)Plҵ쿊|_)PlgV9ioW_)UsSeKk"GABMC;mPR}Hp95&"l< (VYxzu֘Y/1^lIxx2{S3.;r#<:mAIZO1p">8Z##>HBdFPJe]7؉Ra0Q"P%+_d+x\v86SRQz)T)\y:𦘱8D)p:E%DFO0K3Ua @bi&OҐ 5(;$Ս=Z#.B̈́kX+}W6$݃NFl ! lvӴrWP|( 46lofpxAw)Ajqť )J8 ZͮlC8Y\=dVēDxmow3WO_u+950(9N (Gi })J)J 9w{l 1LD0% uDrI<0)>lE!y[- М0v%[(@rgؑs@@>ØϾZ-o4#$HCj_s^gCX켉ziiC{)8BI9G+VewFVPAry`"Jv90V6<3k/lE JItȚ|m#;R{B4mmk8n6j|!kqŨ+NP4׻LtsԄ)c)#筙RrI+T-rLw-:S$K./'ya)Šsט}28ېzQۡ:> ,IqnT9 HeahPp5E{0$b31qK}IK<7-P8 *%!a?mҡYc[Ÿ[H_UwK-/6 + lvӴrWh:D{-`9dF\%g ''TR5@E{03RRm~](;Vid"O[/m AbzuR]M'nZ? IMe$Aj%mд2r2?=&7m,[BӐI U%tx(?(1JݣqTeI⛄,~%+vƂRnvk?PZol5-9S"zuR]M'nZ?Ph~OݏkC?iR5!C/WG?Ok+>*:sbږ)Ä(| }?TTmZRrA4qG=8qH[q=VsA1.y>^Yyl[mqL%A*9%J*$xyɥiGd.+!-HIV2^;V:6[x´1`Z_\/]ʇ |vƳcO K p;XEdzU'j;帪 y25 t>ӱMo(N>*1dc <}{Ul6ʔV)8J90KRY/('iH`Le%ce(2~k{I'<[-$ '$쎁V-M5nǖ((gee Np ffYqӑajF!;$ 'PS0!R F`s@ ,A:jYH9nMҚ6J.GN|I%%j; (q?Xs߲MM?CiL}<\NmৢZR%E<[88%)* Px$jz˴M>jJ.cs56yjjL-%D GqH @ d ܺ't'U vwsZ~4֥+ii+X @'$vQj;?59tNOUxehQ7g;Ig3^Lb \0^I8R_*bϚc%"n-3jf ?5g6kˌi !Am2GlY)TQUG _Dy(eCӧ k'++|1s --rq$$I'$[tRr(DϪRCOf1v =cfLVoca*qb2>jޥ| Hi)h8t-[S3㯓lk=>S6!j@#J ,֧nhm)L$ZtLrd?j)m6ֵ`22x>j7fTwciu6B<#Q7bT] NESpJ@8>!RRRi-ܺBFC#;= !J 98v `p%J$&EmPeEC N2O6Q˜GgPR[k` SpGEl҂&.l4(Ij#hp 5(4#X0>-CQЗ| ȫ]p1 ¢唖T`DNkn[6EDVYHCjJR`FAǍxrjv招k a2KxJyON\92~J6~kZQ<5LR R cbYaR-A9)@8-(1rXXXkA큶NJv>*rXXXkA큶NJv>*J \9%!{`m|dҔ7_ScEt;ԩ sP?czOꁇtjzZǚOZ ڵHR `q+[8_#Ы*ל?GS8_#Ы(+y*y:U= ҂ל?GS8_#Ы(+y*y:U= ҂ל?GT]-"ZAs/[m!)}D WzP+3 fc_5X9nMҚ6J.GN|II;9#-dvPGN|I}?TVhŷE1d%p<u JvRKBĤkj+D:EH/u>R-Lq$})J <@rIҊW> kI-WaL{@<ՇPgUJAxhzc$!#N6PXQsH8.gln&ԙw$Dm=zUsUƭ!VEĕabbr{m P' J >wtE@j T߰܇#W!Z *O}jǯ'?EyՈ1u-"} )N)‘!Ml#h.T}1C695qPG㟆FMZmr2˥ĩGg m0 fnū:[jBwRZ' g_iAISR)x$ 'Tx ִdf?Y[3J%IF;`c./XrtnéC$q>#h1t2DDibӥ{u9y^Bm]2* JVl>Էݓ>qN<)kYQ(I\UFf,t26Gi!8WH Rw|)/I1P1tV }Ϳjt53o5Ťn32O qA/J0@9ָCꅲT"rVlFOh+SG<]S ܑ(c+B_VWC Fq=^[\LR:@ (VRIm~::#ֶpIX  e g`:bޛhnJ)80NI8H%JUۦnR,z R ) ilG0ずdBrkHm'2v\V'oe I9Rzk~n:&B/T9GҒaÇbx~`hW)SVpq![) 첒4U\WRlЊDqgYi,Bx셠ђ+: T0rBE Y(J ps S4{{#q C#`+A![!#쌂+a,5Pn+qd*yIHHQBUg8[MK:E\K*Z}VUQ 穝wb[w/-Iݥ-LX@c*u d6U秓wy]q%˘co6{crx@KSacx3A:PA_h<:yTi †{`R+]=n$5AZad-.$oZQZR@iF*!"3\hg FF2HTMN"ӪrSSRae %- R 8MRRkviV{-HLX4BmH'֥2 6Թo1-F`;GӇP] m(g,- ;mRGp{`׹' 2ײm *;sVLn:٩nMngrn5! )GVF6NXRn;|.썼c-F12pEf2XSM'R^Fti.7";˟-rʒI); ǵ+6zPM|L waKTx$s nW$v SQI^ rsL.z7ۛB[ySv#oa=ACd0A<2M|aƸE:5pZw1ԠRJ@HQ<1AKҳ]tϿsĉe"\BC O}%GE: 8 Pw`d`WnmIp~-JTo8bRS!JT6·egZۆ٪qKӾiͲRvqRl9ҏ>7Sݶxe^pmЕ(QIpNT5V:\w+xm!IQ;p+%Dkp0 ΚrZfHV셅vN6HHIǀ|Wr`gyoIl2=퍬s] RwipKjrʝgoH' <{( TL 7*s$6KN) ,g'@ iJP*uMBuuMj;jt[@N,6B\޳Ǒ~wun8-6RTc;XH6xqkbr0c.Zڣ$Ӌ ]r imqL›utj3jt[@N,686v'Kx/n?eFvJgk 94zE-պ,HNIRa g<ƂmqL›utj3jt[@N,686v'0_[~+2d픕+s͞jBF*~~\1O讇u?v?AyݧV9}oC?5=P0MOTJRP)@)@)@)@)@k5|`庯6Jj+H 9lo&AQPGN|I}?TVhJR((1ȎĸGo0JVۉ JA <5+Nrjjq}\f2 * a 7<}#Z.odLTTxPI?joy?GN-2l0␤ANNN0s oy?GaxjT0˒y[$C36k9sh&K aQicem JAqiT}orl6F)D%)I Q{ʆg R¨߃O7樋]廛1D{@NBRJIT]iT-.s_2zJ=L|$R }Ϳjt53o4G_g\,Nʖe ^;v40]Շ@(܅"''ݡKA q*M]F:zJĹ#&R p-(X=RJvUxhAa)@@v“5hz+nN#0V;k )H!C$qY'%zKP{qMwJJ[kh UWVh:zwܼr o%0B m.-#e;D Nxs[fc̖PBC.-viIKymA9Jue[-!2Zy /HZYBFY V}Y$je[-NR0GfAo)@ NKMjwKsXeFfL;[ZFڸ -m'ڑAm)@)@VMg~.@`=64Y } Sz[; g'<{UzeAl‚.bږ;>欨c#Ro_.}Ejkn]3T-j$+H@<*hnX70 Z_;{I ?(7)U n}DBK7c־)y8m͝hhE)d[i\Okn1N6XmJ/rqPjͨҭ@ uZb^eKR("j nQpK"uQIJVI)Vxˤ'/b[l Bd^:%cJA栻RWII*Zy- `EGg+ R R:lR Ɖ -kqj p۫lB%H!@' JҨ(EwzFҠSNp:NiGC儺\$FPOcq­4)J uIΝmv0rLTa] U說29}ְlؠ'J I֕ە%n,8)h)$W5a9o\nkCm͉s/\DڒPRH@hyc-ҧ^h-V;(H$'5ޏ}*ZL7+c/1gN8PYm!D$@\F9~o"H }´P;Mv[g*ž|쑮w_Gc>:ϥAY̓|-m%ぴ풡#|v-S gN}DBUk$+iIJU kdmv89*T_ mR( $MI.UkJC;Rĩ!\\3pRbZ7(FLS $i*8A8#-b!2~pkL׶ &賩bط6 F@BvTE8'ڧE^-Z[Fal%Cme)B)V0ثKٵ+7---b[iF\+ RpqsگvB[tC*Zp)@ae$ol%3F;zXCREe m- m'F3PWTجt>ImqQv{;8nu+(DƸ& ǐԒv8QPR6¥o,yyna+)NII5qIFjÏd$p/kqr:IէP+5%MnuS{K%YWdxbZia0w2)V Aj6`gJaqf:"6[HIj)XmAG`N*4goWَ)l8n`8e(*9 jYj=;%ͬ])߳Pč{h:bu2LE\7bDZAZNN{xOW T4u([s"R%K%[Iqƺ/_z?Y,_z?Yb w8ḿ-q\8BH8PpA5W5^CWPUQVT@)Sd('\jUAESXmL)݃THڂAPj~N eD<N҂}?6Gj X:WPR£RPO^5YmMa0#vSx #k;j 5YmMa0#vSx #k;j A[}:~6XTbJTY;J jڪΠ"6æwUq*omsgmAxӫ5TP,l5*R\v׀8A[nR5&t&wQS1!mGv's~7{"a q9:4t$eRO7c=M&uv-5eeyeKyK[2D짡#9&P)JP)JP)JP)JP)JP)JP+3 fc_5X9nMҚ6J.GN|I}?TT.ӟ&jitp1R)J)J)J)J*:;iK/qFե 8GQC;v'B]NVJJFqĤ$v%P2\8n/riN/mm-o6r?H;r. * |VR>!JI###9A-JRJRRu Aˎ2O46|S6@]6K0f8B,)Hubgʞo鄄D~: nU}}S&ԇVe"K}wVe+{[T~|IW+>M˙0{C?6z3?T o_:/gGU?FmHubgu=κICaSKIp[ V9Q;Yfbc"[- K()%d8\Ǵ{U8Z6cܮ5S! 8.m׌cvl8Vv yV`5rRg}ZЀH;YsF躳*#w}P}!jh $!)9sW ;>꨹&vAK%8Tv)Ȏ(suqUW6of,)@mR~Эs2\xm*VeKS.oB1;rG G:nU~uNmHuf_ʟ)g<sV&ݮrw`MG\8@ɦDT3.ԇVfp"C# N\9txs1eШ"Vs3g+aC' tQ;a ov߼z?lXTH8ˇ]ښL#Q9V)eA?Js=,j&;cdI$v@O4)Ԗ̝9Rqq|5xme*3Wy-Ҕ-؞c>"]bWӫ{Ѭ63y/z4+^k):+(`)Յ(RI 8 ;D+^j}0?W Rf@oK݀[p z6x*KEW="PA-`쫏&#nyQJ``88^-_"4 h2|G@r%ąAI%*p;U?JT R R R R R R jYV[l`~4MҴӟ&jitp ~4ɱd#EfT*)JRJRJRJRfZK|ϊT>GXe(eԭKc`O½D45ߘ#)֦ԥ%E6¯9bjR"Jq&nu") T³SS*yH[Q-u_lm)M)X >ؒqq=(4m)a01;7t>VpNH`pVl3v;S뾪cjzʔ[)x$#dlq])Aڹ*1ŦU!E ;$Pv@|5)[Su| Z:9䶏skZ:䶏s< /lxAOuz\<(=hN?ɭ>穯#GjJL;0RȜOkr}DŽ)OK;:NMNycۧ9Ee ue)xIº'/lxAN_=.@Cp@Jfx:"Y }tB9c6/lxAN_=.h^.@X~|+uh(9߸sD)[Su|GÞu}/oɽ/9hc r}DŽ]_'ƒ֎6y/w?uG&q9?}DŽ)OK-IKh0?9|_IZd'H3~?nn)[Su| ]Kߣ^b̒ųagbcR'Z?5ZJUYI<\F~8/lxAN_˯ϗYӓȤ>QwѨREұ>jJ+Mchg'koƦYM7ϸtj{fO_̟O W\@up{OxdxUltb۞hq-eo{Čp`j侷ųMN m))$NJMXdxUltv;uٛvL-!-7eܒTO1'S¯fO_J'S¯fO_J'S¯fO_J'S¯fO_J'S¯fO_J'S¯fO_J'S¯fO_J'S¯fO_YZ2<*]lF[{]HV]l`~4MҴӟ&jitp ~4ɱd#Ef(zKp\kR)Q9Zs=Ԑ4YR`J@!)PRpNRBUYi˭~oP(JZ}%l98=әlr +ny6ސ0yJ+Ce* I ʱLJ +V7\[jh% ^ʊ p;G$p #󎙙w! ]F:whPt%g%"a@zq@*] mOmc *N0O bJ!V%u5p^Y/% BGmDp'++1 /:V29ltW81"&6눏 ZPڗƔD ACdblIUQ0-,CA Scyطq:h.W9ZS./jf# }i@E)IQ$qVL@3ځxҸ56Wt\i'eZTA.)ck=|3֩5}r"_.ZЂS8BFA+Jsf 2KAyp8Pr8eC?Q-S%ՍTw v9vZ}8Ӟm1?)Jw`763h:}+hDQfB%")R$9xII< d-pYƶRߗ&OG <$+qBGh DP_mwN$#vRV9z+bҖ{NA[[m U2UQJr Jܼ)nS ]B$sҔ!Ϛc%"CP1tSnGAsMQIiIVސPT0mcx1Y$+h.!y]m l )olO W5 ʁl!a8ۉ Jd AA1^l/Ny mNBvXaN<`JQ@Yʒ;UU(uԱ9Fbh,~NgѪu5M1bqƈ#RuIZJ%G $` AvgtjL!$jQBv x٭"zF1#;"NJu,(H$n VAjw ;˷SطF1qҢ6JvHOozwbXeFw-!y|I*)0Msnػ.LA-TXՀ2pds H^ ML1.GH[r2~ju#܇'<XH?HW{$[%Ϸ'FgxAb^=nݱv]0[ëd &R}оmFS-&SbV X?Ki&R}R&R}uԱ9FԱ9Fbh,~Ngѫ(+bh,~NgѧX?KjJ X?Ki&R}R&R}uԱ9FԱ9Fbh,~Ngѫ(+bh,~NgѧX?KjJ X?Ki&R}RԫuF@ ZZIQJ8H<_~X^~jj~maICHXG}?TV*.cs56yB[4ujX>*P+BO>TfkiL}<Ո8AvWk$&>K8;)K./㔧FxqȒIWjAn;L}<ԓSvMb217< SiCbܩ,6I쒓P$t5,Zϩecd@;H1 4 >jm17< TUůpښzD )b; [ dONm+ܫtJPGnr'cs56y* L}S6!j@\u\u5K*bL@IvLF^0ʁ5"m5m_umʈ?ӖFkiFUl 옍ajZk=9k=4NRk=9k=4NRk=9k=4NRk=9k=4NRk=9k=4NRk=9k=4NRk=9k=4NRk=9k=4NRk=9k=4NRk=9k=4e{ߏ_S0I'<']W(3EM?VW}?TV*ȥjŘLe,V2ҒvQ$a ooF]%j`ӏ*#jG!69"9S{WA 8`-Fai 2B$ vK/q.:Y)@m=@gsvBkRS{r C};)I;n)!D'#?^D8yLv^XFk m6ԭ LR9Fj:ld\jD[ђ;ZJN1dgk9_\hn1r"ܔm(;$98T#HPD"0$',8Rt$}jYDa+my(s+*' p8׺ IWjUFՎ'~YdTݟX9(֗XV .T%0(hq9>F3K=d6륿*2sq38T+פ?P/I0g pC-ssֵKcB-[*A9PPMLϰYǒqA9'P<2O,kS~h '`2QNAR ⠥u9[K돫ިɷ^퍝csͺ#Pʒ_S[iAG"A\Fp#R9s 3VCTPm8 殕JPW|'U|VJ<1V?R[l`~4MҴӟ&jitp ~4ɱd#EfT*)JR-NkGb~m0W;'<*Jّ-,hjݵLZ-(lΠada@0A#i3d۳^mPܩ._H"ָ.)|QWe1҃ܭͻ̫},YKLWlRaJ6*nf{.ςy\BBhڛYQ p{UiA۟i%M.7}v{Zw&${\gf4C?%pm80j xRcjrEl˭HӮ; %C8XpҖ]mkTɝZB$ n (9O5tPhR`b4wo||(+kxክ_.WJ꫔ipy@W}(D)=vqW:PR*|K{}P{28PIGqo@HZAT7nu_Y[kӞPSӞPTz, 6`K9$ j:.͊!m) QzG0e*Dc:4:4!W]9lS+>slgMZQy 9EeV[}Rdj!\inIJHF9gN:\jm?\ <)i^Q}0%)9WB@c82MKi),Bv\ ld۠蕥luaXKKR*me #N>*r8ڟ4mS oa IH!I;99+t[aI~r !b3}Z16\OŝݛKL)ƞ}XaVA8  '$_b\^T.ևy@)''=O{)Fix- :8XC28gELHҸh~Jp`6@*U+V.M[gu\wvJp ᧥-0;|iR_*mt5 w|)/I6~ b{D5Gn"N7)N <5Zč:#ĎvZi OԨ(hKE{|yJh6XK@HCcƳtD8f۱zoiża[P$$ A<5])TRf zbT䅬)RVȋyn- rt4r҂KmO.l;܌=u).$BvFqx O]D)f# n-yTIPyHI=IH"@۠^aչ-+CNqRCKVH~gn*~5)3"3[5tNZIR~0h$PLyv#_[qTˍ,'yNq Z-zt6Lm Ȗ&@mj;*Rm~:<~:dIj7-ه%_VSČKHGT_Ӌv\r:RBvpIP)'z/9.WxN]T;xn ?zr?zf[ty[ty06 ZQN=N{$슻jg!&"*ͭ RKo;] tV-o-o窍V-o-o٨k!>`U+a;!]6T-o)Υԕ91 OHEKH"MG~&OuF#M/m#iH ?zAJ~:<~:j Az "xRSkr=X{Wnb<;sse-Fq!# 8<樴R[-)en(JvR3³Ry)n4gRQ ,r2tbu U ueȖV$@@հw;v($\JwIc$ }~gn*C;NyAQNC;NyAQ[lR Ɖ -kqj pM$>+4b)QJR [cl=5imr^Ke*y{*mlRAgժ4M=6UuC 6%’1#ӨDUmT޽i3c)7QH#8 J+z2ۥI+i (m'2NCɍɯwO|5Te T7c) p(9m#olՑZ'UDo20NZJ3Nq5ԍND]zB]L{jPbviaM8_l tpc[q>NV($ئ]fnzj}wLymO\5Re/Rl )hֹL\M*A(PPrIp WJ,&#G}Ν‚= 3=!/ Mz*LaźH8E[)AgZ&?pE"_"dq-B[á<*I9R p YjfÈ&y Km%hH 㛟 9Jwnpw\oSqi5 4WQ"K$J;1Ib;j>R_*mt4)JT 컔E-m2`{_''VjPGڬ4p"$o93)olpj>Npծݮ0c2)}ZVFF!ijOXIZ qkRee*NU>rÍxi,wCٴƀ4 $svo+zzivJe.wFV0׽W#PB9b#5SWHm 8%'&qȸDSuNҤ(%X089PKETZ&nKv!@!*19ʇ ~:<~:M$>*H`iϓc~54GJTR5P*Ĕ|䶂rZn%YOky[SPVnB*#֡8jޓQq9dEll~ kKTEmͱِu”A*ppOTX\x sd6㍣dJ By6fk+mL/6WdGm[J\dU+(mO+UԍND]zB]L{jPbviaM8_l G\ٺ%ĥPX6xe'i1.bډlH2!-s,r #R;ѭ.4]&> $ԻھELu2 z*RrN8P_)\|¶Jo*C"Rw'`-%'8YnjF" Ƚ!.=(i1w;jw/rRvxqIꝩU1=pweJ-J6r8¢ts[Z1rgo6AA%ۃk p0AҪDěl9 Fn[FN +r8JXL F ;mo{#;X#1U jGovLuNx(&N.\ "g;YcgsRW+{UҔC'ddNI j9+{߮ʷ[R$m1v;9{XSz/,SB-jFtn`@ Gj]wz"$3 'm!P䔫RA=;Uk}+Se U ;V!VC*inE+e@(8t4wNZ{*}e{ӽ֏>{*}e{ӽ֏>{*}e{ӽ֏>{*}e{ӽ֏>{*}e{ӽ֏>{*}e{)![)= vGYMӽ֏>7}^zoQFGYMӽ֏>7}^zoQ{*}e7NZW}=GYMӽ֏> Se)@ecJQсKڥ.]i-]a-&;`oɴ%wNZ?I]>?%7NZ?I]>?%7NZ?I]>?%7NZ?I]>?%7NZ?I]>?%7NZNV($إ((((!Ϛc%"CP1tSnOjzxjw7+\x lcP|C=f}LX/%OԄ)-2J8UQrnG[q3O% BU{`}QyjŦ$X%B}>0\RR=*: K{7|Y2pYy+-<>zj8OqRU-d@ޤ{8/_np.7.嵫3Y C%ET0Bp6K԰YKIP߶6󏋵SUA^ʇ=Ot3$?hOr C2@c 'W q,ح˯RkOK8ꏖVM{)4*Vרڥn7̍FxR 󌂴b I][2\Puy< 4H S;$`3LV@/- ;Kqq[_26ge<(e')Q9 Ėa"\A%)%A*9$+O5U4ŕ[SKVw')qJҐT(93gkf2Fg 2*9()f=wa|q)_Vⰷ1HAURT|Ǧ]|60e)W$gWl fz!PKSRRvRR̡gRԥ* Ov~WEZu_~U'B*Ӫj+JRRRRRRRRRRRUrѤƿcIWh4k۷J{.DןnM 9lo&AQPGN|I}?TVhJR((((((((!Ϛc%"CP1tSnc /E~N[Ur6ѱ|·>_PPLl+SNN9@h_;ӧЋB< /lxAU:}/[|>c r}DŽQh_;ӧЋB< /lxAU:}/k7jO~UUoJ+gɣꑼZ'*$$}i)JJRJRJRJRJRJRJRJRJRJRUFՎ'5]]??n)^~ݺV7~4ɱd#EB 9lo&ο]5GwFD ̀q88D*yiyj*Ju~mu~m *yiyh,tW|n/W|n/ҫ_q񸾶_q񸾶Ju~mu~m *yiyh,tW|n/W|n/ҫ_q񸾶_q񸾶Zo7Kn$^YiJ C#9a|2rǞm9 iG.8\R9|yiyjgz7 Ӯ <$?_Z=_q񸾶_q񸾶7ލCgz7 ֏W|n/W|n/&wx}:ލCyiyi{ <$?_N&wx}h~mu~mhgz7 Ӯ <$?_Z=_q񸾶_q񸾶7ލCgz7 ֏W|n/W|n/&wx}:ލCyiyi{ <$?_N&wx}h~mu~mhgz7 Ӯ <$?_Z=_q񸾶_q񸾶7ލCgz7 ֏W|n/W|n/&wx}:ލCyiyi{ <$?_N&wx}h~mu~mhgz7 Ӯ <$?_Z=_q񸾶_q񸾶7ލCgz7 ֏W|n/W|n/&wx}:ލCyiyi{ <$?_N&wx}h~mu~mhgz7 Ӯ <$?_Z=_q񸾶_q񸾶7ލCgz7 ֏W|n/W|n/&wx}cϽˊ 2Ȉ㮺2\@%J%[')ۏۏcW:}6q}m:}6q}mAcW:}6q}m:}6q}m\ۏۏ:Usn>7ӫn>7XUί{͸_[N{͸_[AcW:}6q}m:}6q}m\ۏۏ:Usn>7ӫn>7XUί{͸_[N{͸_[AcW:}6q}m:}6q}m\ۏۏ:_4uv_q񸾶Q؞{Nˁ]uפ00,IPy]??n)^~ݺVц߫B @UArЕ-%; #+ JF3@]?sG Rty^\6<Ec?4Чw)J}}BEc?4Х(Ec?4Чw)Jw)]?sG R]?sG }}B}}BEc?4Х(Ec?4Чw)Jw)]?sG R]?sG }}B}}BEc?4Х(Ec?4Чw)Jw)]?sG R]?sG }}B}}BEc?4Х(Ec?4Чw)Jw)]?sG R]?sG }}B}}BEc?4Х(Ec?4Чw)Jw)]?sG R]?sG }}B}}BEc?4Х(Ec?4Чw)Jw)]?sG R]?sG }}B}}BEc?4Х(Ec?4Чw)Jw)]?sG R]?sG }}B}}BEc?4Х(Ec?4Чw)Jw)]?sG R]?sG }}B}}BEc?4Х(Ec?4Чw)Jw)]?sG R]?sG }}B}}BEc?4Х(Ec?4Чw)Jw)]?sG R]?sG }}B٬" z}#x!#!@I8A$)J^.}>q٠FֶOcv1VJFIFHHxwdumpC    $.' ",#(7),01444'9=82<.342C  2!!22222222222222222222222222222222222222222222222222"^ !1V"AQSU26RTau#37qrt$5Bb4s8%CdEc"!Q1A2 ?hƎ[vkΚWF*a=Pw[o9.&t~F{ߒ`mgXmk=gk39M.7cxqź!q|GE/eSg ~I'ڞ]* HEAU9#]Un*9#]T䎋t^uU 䎋t^uS:-{/EV肣:-{/ENHEAU[ HEAU9#]Un*9#]T䎋t^uU 䎋t^uS:-{/EV肣:-{/ENHEAU[ HEAU9#]Un*9#]T䎋t^uU 䎋t^uS:-{/EV肣:-{/ENHEAU[ HEAU9#]Un*9#]T䎋t^uU 䎋t^uS:-{/EV肣:-{/ENHEAU|uU[G$o''J{iq=͌Fln\̂ג:-{/ENHEAUsCIgfjm#_.qo~f>Tkj$fwuI--vxk] kvwk 轗"ꯇh2FF6GQ]| ]$Y*cH#{48ڻ/f]A-7q,9#]T䎋t^uTk[4rLC'&1wu 䎋t^uS:-{/EV肣:-{/ENHEAU[ HEAU9#]Un*9#]T䎋t^uU 䎋t^uS:-{/EV肣:-{/ENHEAU[ HEAU9#]Un*9#]T䎋t^uU 䎋t^uS:-{/EV肣:-{/ENHEAU[ HEAU9#]Un*9#]T䎋t^uU 䎋t^uS:-{/EV肣:-{/ENHEAU[ HEAU}EZ5;5ۣ&7$m2UGi{lQ=eg!4s5ƺ9m&,zں_9kVڋ̵.mƒa$9WoKQˢz9mZ5;_oc3F f=a7cA Kp5A{c+Z5u1q7dA2&w- vf>LtzmKI2 E`, cVrDD#9#Eڪ-mRFÎ650{Ck齢ߢoTP jjWTnk tl v`] )?{A"뎮XC3JB:`|m-Ųk4nٷUw}v},%R[}+jaf5@7'$qiby?ْ=1MGog@+Qhj6M01ڣs2jZA4rtn,ppiÚqGhƣ޷ƳҹE-\lj=|k=)e,6824wIPﶊ=5ֆhXGP5qA $풭~ƣ޷ƳқzJqƣ޷ƳҀrKlj=|k=)Q,qƣ޷Ƴҫ+4o}-mMPk5[ddJ[+*a~u%@;n;[Gvƣ޷ƳқzJuQ sF!plk,$hBZ55{zW(655{zW*?T8<YvR2C09V~ƣ޷ƳқzJwԺM: IsF~">55{zW(655{zW(8{zScQ[Y\XcQ[YMGogrcGog655%)$3DvF6W4&A L|mWr%ڣ]tt̓ߴϕG 1FHv"X8$EGM0~|"C Xics=0Gʔcj'T3Rh!JGL!'&<ǪOʒcvh9߸#S9~LdwNIDָbu3fev'%тve˵v[[fRӶWs8|GogrcGog655%655{zW(8{zScQ[Y\XcQ[YMGogrcGog655%655{zW(8{zScQ[Y\XcQ[YMGogrcGog655%655{zW(8{zScQ[Y\XcQ[YMGogrcGog655%655{zW(8{zQհa +K Zq/Fu|[jξ,5kĸtuo{s$c php$\wۉ,y}S+/U4õdnT8sHݻv"O>-(CϽ_bӱ!Wk(xLX$dd|YYgU/1}ݭkFcW-06B裉䖜89Ahޤo Mm4eu=)a+pY<˭u˲lE[N!뱻iHf-ko'lERPK1 3 -Pictures/10000000000002A7000001E7A08E42E5.jpgJFIFHHxwdumpC    $.' ",#(7),01444'9=82<.342C  2!!22222222222222222222222222222222222222222222222222"] !1AV"QSU26aeq#37Trtu$BR4b&58s%CE'Q!1A"aB ?*:zۄQUȷQGE ;5:xvjsb?e!"Ə![hIo٪\eҳ'!>.:Az,i-٪@ϻw%Yij` b:ݾM16B;+V5]ڇY>vvtw݃oJַsiO,ֲr\,tkp ig"wr濷%?Ƿ? (sp׏`ðF?}ݿ'g\8zmAhijRg'Ŵj\k,Űj~Ӓ1jSy@w8&πm% q`cg6dpʽ{c7 xJn6Yxwۼ!ϱNNjQŭ2ӵ;u8hZܗI3O MpY7 Pm6V7o{<&p5\ 4QwkNI30wOy\v|v)fvY''&πmMPzɸZ[×-Wѷnxӭ!!RMn@m l`/&πl?5Z->ŇMnpʽe-ghZ`OxӰq Pm7;xCv~>X_ߔԐط}߾v5CR&qp5XA&j{Yo_\x ^GQ-rWI550w2ST8e.o{gڵߛ2k^wNjQ->ſOq,lKMt&R&jvV;|[8խ4kv,z;\[tGcW?J)upq=_|;O{՞_|ݿcԓ|dّ~5CR.kk^j;G-G$> 0y}߿p]e-|\ jsxCWqCұj?n+ 3Y7 _ڳ57 _{xXg MGuCr.j=g)ZjsxCZw655Cr.j=Ck=!V-I?&p]e-~!Sp*wGYT\r'j`eLS}Ptܘif 2v3u?4x} M{_C諅J,7+[)iwY `8F-ҏFoC3P6jMJV/bU{_Ck=!U4> dsKPa]H_)k(nWKuW8#sw@5KqkrIU•)ڿZk}p*=q3QCa#KN;Mn>Cǟd6/Y!Ro7KQgraVG3ˇlGnYmڦ~?Ede K;,6C7YnٟuY,L[5y*a:zi]q<}MCo e<᭷\ 5ʴ[ M{^?PoV]+Zml7 5xvpF It󧟥/JeYFYw _YB57 _{xXu5T8r.j=}-|\ jsxCV=I26mϿjrST8g.>-|\j/v|߿X$ϻ|~̎AO)3Yw _Z!Spr-X}X'aMI26`!tj M֬7;>Mq>kX]\9+%5Cr&qp=Jkb{֨qrYw Y,ּ;7xC,tm6EU|Zؗ~~&7ż2Y7Zhijv}Npq=rŰjz6-Gjgou狓 +܁@Ł^O6dpʽdc7 < Mv5 c`Ǩ֍\~E?&K hVKME 756g vۜ``9 [=V}i7?)_YGHҩa|4ulsm9"n#aPu._IyU/1mVV_U[S 5;1,18'`@qk5os]<tިu:7~JdkWKypca}{+Qhj7&j_QǀN8 ܆Mkpr#DJҿ5;ʞ؍I}{GwP޷jvt)L\í #GjujF1nq[wݍrm{غdNYlsp;*{Mu9t1kpH; $9IOiZ=&q/} p#G*@og?ND~[wP:ݪt;S =nc Iw7oN#>g.a`rTm}mlN4wtǎ]=zb=n_ʜ7؍IvoIW%K[{T3MV=l IYKpeUL54Τ`h#'4ܿ=nک0N[Zm>8!Ybh$. p%@p8 !>g.iiz'N/~Õ[8rȟ30nuUkruzݩI־T7YwAa!Lj``=M1s]nUc6?N/VOttC B*!5.Fgk8k@qx$p4:ܿdʭ_6n_jt]=zbn_ݩ'M'[.*']=zbn_ڬ3=ru~V/ttOiu~W'[ny]']=zbn_U=ruWz>ggHx|V=㭥kD Ě;STowgJw21sISS=,;?=]^E,-Yev䖾qf~.SOՍQsJ&0WkDNw)=&zim[ ub pr;SQ06atO>SU+@x,uұ"pWeґRjx.jKuww]`twFG+Jɪ.`4sHjG\TT!AJ6s;-WVM6#.tscn֑?j6tSOq+M͍ٓf`?~?S#Ē0\)~۷+"|cT\[O"u|, \mqXܿsrzZ~_?F[ny]'N/ܪ.SLí={=ruNy?l9tOiu~rI듭;Uw']=zb=n_rgM/n_jt]=zbn_ݩInV;oe>g.ao;/n_?V!6r'4:ۿcn{6iiv8IOiu~:ܪi 6'[VOtǎ]=zb=n_ʝoM}1VݮIõtOiuzݩ0?R` `rm}mb30sHO[Zv3?}9 7؍IwߏtOiuڬI~SsH2Hڝlaک0ьll秢|CL\íQ`ۃWIOɱ:NU8tx>_.b4rʐ}qoh4jwN)/<+MExasce<7*l:ǨSoĿ"R?M[|_I|ӎQ snkws~6c,wK!L 㭝2ICom Zk6Al d)ܾ;RKymxbR*Wy[粥vp٤d lلZIۿ%ZR·_bӱ!Wk(wX$dd{ U/1{[xVƮZ`$lG-91psѵf"pJ+LMt[ÞfGs..Bu4B#cwi3X56mY"d՗ZDʸYHv }JYi_ENy\ icF0I;I*5-S.LEPY 0U88Fdp2ֵO(aζawal+Z]Y7ޯ7޿ƣ=nM]sŦ{ρݹ}Pj_S}E*jlbmʪ<8HcPqXskK[cZbҺHᵥ8ZrӇ0#iKmzoᰎ/.qlWnT:BeaޭeڭՔ8Cnڬv[U|emC4 {rFqGYmNfm&eU\,'y)rX˪ ŝ wlܞAųUuj+MY05ijkLAvsaqin;Opsmʚ=KugBGqpV:{Mj׾8ֹ9i y H%dSUKU#skC&6qqrr:"QQvQKjΡuhU{(ds5Gܕ3pMpmG @$(sbpnKsvxl_Yt29nz\]Mu<&Hեh# 8/IE)p^S> 8/IE)p^S> 8/IE)p^S5%LVYgx a8/IE)—o?G?:hE?XN ~t|~px<`AzOΚ/O4_")}G0&Dp^S'Mȧ _w ;a8/IE)—o?G?:hE?XN ~t|~px<`AzOΚ/O4_")}G0&Dp^S'Mȧ _w ;a8/IE)—o?G?:hE?XN ~t|~42;_Ua8/IE)ŸDa8/IE)ŸDa8/IE)ŸDa8/IE)ŸDa8/IE)ŸDa8/IE)ŸDa8/IE)ŸDa8/IE)ŸDa8/IE)ŸDa8/IE)ŸDa8/IE)ŸDa8/IE)ŸDa8/IE)ŸDa8/IE)ŸDa8/IE)ŸDHo˳] Ɂxf;urqdQ}O>](_bQ<ʥ)"TVNqG3 Ij3   KkGMfxT7*&c nl.Yqv;_hQpduݚ|Aٍ6)t+GU!-8iocTLPY( | yVh )ls;25ٷdqs"rm*>w[gۜ͝k~[E}+!StY'q Ȓ-g`;s0s̛kJt5ED:iNH%ԑI#NSyru7LQV[ %r$q$rI&UZRfrʷVDD@\zie~WQO cŀQ>6qvsfjY+KɦCkݍJi"a-1q#qjŋAďTjd6`,he{ KØpxVhk@r)J,5oZ"u6wIwѨ+%%q[4Tɓ9ۑo_=U-1\ݦ:JZHEtrҺG49x;=ݼJ0v*::bAH0m1{8 )KdM=T :amSZ6wiQU\,'y)rX˪ ŝ wlܞA!Va"j&6҃$cv*KM5\1MwuH]ѰI!n2jxdm-6T {^lݠt7XK*EZ~_Cc:E:jXNVTWjXNN4??UWjXNN4??UWjXNN4??UWjXNN4??UWjXNN4??UWjXNN4??UWjXNN4??UWjXNX]򑢶0CdXe!*3S/XDDD@DDD@DDD@DDD@DDD@DDD@DDD@DDI?wvoDi'nȔ[GCU/1H XeRCUZK~P[r $RH\b=cX*4wiBym 9YI$a8' pRכ\<0*(y=w)i6eDhL6ɭM jj&3/-u;kZ@h, RZIm SUm̘K7r꒲}mhQt Fդ},.q&k4v$$r,VQG)}H2N i-k 9قZacFYD:hH ZїqwhƐǤJk)DǾ9"{Z p9<{Qh;/|߀|}ͽۍ[+nةU̦m)`Z5Ѫx"(4Y1QUaF0p5p32ۍ+# Zۛu#W[,PPTi)eKLΰ.8\`qRozxx ݵxT ֌fR^csn ^}1YKF9̈́#/#k˸k8WeJ|KvN=Neqs۪|nkqH_fwY5w-M\ߌKCQ3*fHT.s[d =́AQ,2M%03KJ94F9%;q P\naX1#o[%ԑFBөik%c֕87;69VM\"7 w]$O0hE|g7$q/ CȮ]\oՓLڹ53 miFpPO\Lg\&PW5 UQW '\MW }Av9 ,^Ikvg-[Q]N-TqSCΦ{ _ˆ^o 2TGAvBbEqk.Zdd O6:] \v\J]v#=P|X;cm<]K7 }lqj:VtCG۴{ӨGQ,,71H=H | Be7IW$Թ lfY %$uTsK)^7F[5\9A]Msa-<,xg;AFX.\,u> %sƇ{RqқNJ\N"߲w>{S~KP{NJo;=)q8~M's翥.'A;=)d|(='s翥7ϞXe#~CX-0^S/X#qN+O>](Iۿ%uWTPu3>QpV|l#m:Ľɓ6q͸]/[#G?f}Thi.Vjyq_Z?}m}jnnM՝?(*?}m}jp+ѺG7Vw7t+f|֫FY#҂f|֧yq_Z;SugxwJ yq_Z?}m}jыuuLISTp@ eLh4ZDވ@ٴDJ5SZ >9Ldҵ_tG~ͦ&Un=MCTʓDΉncYv^"F%޲5iuJj㪀tm!k-%Ʌ=-OQ8þd\*j3] jPC+s\I  W6 aILq' U52]TdN1sQEPG ̣9:V67 >VibG,{cW7;vCle]C4٩иc|["j5dwdsc06K; ^m%7ݷ][r~>öwwMWrT[7M˂+vwukkqr/'omJ3A@LǥYkE#*d `-1dvr[j->I ri,$-nNxgMҟt7α羷lq{-]j[.vҸQ&ESO8v~[8A-&KIWUQ| |O>,pɳo&V'i-jf)EM,2=h1W mlŷ;@!EU%}}\toMOt )䵤98ҳFWWVU(*DC|l RKA.9v;QA-UZ*ڊIi97U +ͷIa_vR(6CO.ג\[ݮ v!hܴfKJp7sG4ط- $TSICp\%͉STu DPrD]ƫm'eAwMyƉwMyƋhh96蚱\4E+,2Fۖ=9 ˢ>4sm75]ibyu9bps^AR8<'_#+w ,rClG~{/v 8 XsGVDžMŠWYO>b%1ط#'de,(8<'_#+w SxOMUʎ O ˉ;Xq=Ѫx*on,5 <|=*DyntK#*NtKYR*f۷zCCe6;VM'cA;)Y5 4{dki s2;RKT4T-ǮCCG9ZI|ۮ.TVT^c]3NYLxˡtC_T}}Zn="*4ZY(րXKAhp4sm75sy~ҸJJJVkJRzSO7NzSO7Zn\'PUU>:xX `V捙>▮SWwuҊ`tfm\ 5#l]8}jCҟ y^vCҟ y^ғFY+`c7d`%m%d=)d=)+x˫?d=)d=)&᜺Cҟ y^vCҟ y^"a˫?d=)d=)&᜺Cҟ y^vCҟ y^"a˫?d=)d=)&᜺Cҟ y^vCҟ y^"a˫?d=)d=)&᜺Cҟ y^vCҟ y^"a˫?d=)d=)&᜺Cҟ y^vCҟ y^"a˫?d=)d=)&᜺Cҟ y^vCҟ y^"a˫?d=)d=)&᜺Cҟ y^vCҟ y^"a˫?d=)d=)&᜺Cҟ y^vCҟ y^"a˫?d=)d=)&᜺Cҟ y^vCҟ y^"a˫?d=)d=)&᜺Cҟ y^vCҟ y^"a˫?d=)d=)&᜺Cҟ y^vCҟ y^"a˫?d=)d=)&᜺Cҟ y^vCҟ y^"a˫?d=)d=)&᜺Cҟ y^vCҟ y^"a˫?d=)d=)&᜺Cҟ y^vCҟ y^"a˫?d=)d=)&᜺Cҟ y^vCҟ y^"a˫?d=)d=)&᜺Cҟ y^vCҟ y^"a˫?d=)d=)&᜺Cҟ y^vCҟ y^"a˫?d=)d=)&᜺Cҟ y^2oU֞k>4Nk>4^77>4sm75sjE7ϒIk5A?~tG~ͦ& !W5o {SzWXƺyJn֔]䥤ΦnNHxFgHfՔt,wTnr{"3۱iӱi}r5Smua*Aiignͻ qikL40@λFIhˢv!>v!>lktx::jj QS9NI*[KKII:1;;n..suC}C}MOOQa4PI&$6qkNwzxx>{s2^c.'>v!>v!>lL'1Eӻm;m]jNC}C}1Eӻm;m6æQt>yN>yM|s];iӱil:jNC}C}1Eӻm;m6æQt>yN>yM|s];iӱil:jNC}C}1Eӻm;m6æQt>yN>yM|s];iӱil:jNC}C}1Eӻm;m6æQt>yN>yM|s];iӱil:jNC}C}1Eӻm;m6æQt>yN>yM|s];iӱil:jNC}C}1Eӻm;m6æQt>yN>yM|s];iӱil:jNC}C}1Eӻm;m6æQt>yN>yM|s];iӱil:jNC}C}1Eӻm;m6æQt>yN>yM|s];iӱil:jNC}C}1Eӻm;m6æQt>yN>yM|s=(nW>y[Vާ_υw}?-=#X֗Xʕl&D&Ezz#G?f}U֛/Z#G?f}UfA]&h=?[){= 枅F-n4'Rߚzl ^OBp/}-n4'Rߚzl ^OBp/}-n4'Rߚzl ^OBp/}-n4'Rߚzl ^OBp/}-n4'Rߚzl ^OBp/}-n4'Rߚzl ^OBp/}-n4'Rߚzl ^OBp/}-n4'Rߚzl ^OBp/}-n4'Rߚzl ^OBp/}-q]NZ\HgTK49p>$k}]msgT?!-.vZ2FtZ溜48x4 E构 ^OB Z!K~iN4 E构 ^OB Z!K~iN4 E构 ^OB Z!K~iN4 E构 ^OB Z!K~iN4 E构 ^OB Z!K~iN4 E构 ^OB Z!K~iN4 E构 ^OB Z!K~iN4 E构 ^OB Z!K~iN4 E构 ^OB Z!K~iN4 Z?w ^OBYYc$ˎ00{uwMyƉwMyƊim͊<5{9g4G~ͦ&?Ajw<`Mh;ܞ0t*t9\l1֗g;Z&T+.O:Z' (2A5rxб-[|VUev@\Z֗2FGڞǣ ]k2uv,r z^npgjԸ)mqH7i7(/v OIRտ?8o.MIwB\!R8֚HY{3pW*.PRɺA3~i8;Teԟ7t&@X.V\ii279XsKZ7' ?8o.P30ڂ$4ߍd`:9 d%,ԟ7t(r_Rmzʪ 2HG4 m[˞駴EЕ5k(Ꭹ9CZ\daa!kvd  ucw2۴Ƴ6xAmL)Suh&~}g:vHmTPd+]iI kkd7 h c uV\ Tp:2U'8v-DD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDDAO&D&EM菴 MMSM:#1drvC菴 MMSS0~?,s=ۡPY(_()j!,5kuZLcUV :bej hcGऍD $p:Z"GQW|\Z"q~iáuo0x!Z3p}S-VIC?$o'g9$[< mM4^I{L]qث[&d74GUh[^$6CZ269gX'uAVP[8y;U1>y1Q%;r n-`G 輬hy[5#j'cj#-T05 UxjcE{s4V$l.88`dR+FQ%TCTg FW k0 eU0Zaqn6"vr}+ si*dki[A{.% k{֬4Ue+k'_=4sm75MO QQ:x*䧩9F5øABɈ7 EvEgTgY!aA6#ޟ31! BOG5,nP\\vI?@t&#ޟ37HGG1q2V`21! 7 D@t&#ޟ3Қ1r޻ޫp"j7q9{\;3''W%3m[;(ʞo_sfcu8rHQҊ6QWOUG-if5c`TXww#mM[zEZ 5E%U1nOPᖻsA4Nk>4[Dވ@ٴD5?3Bh96蚦`HYĈ(Mu F=0B()o:YqXcuYq9.?ѹ!rKEt[[ C^DuA,ppckaxJEG4ъ*mo !TG;'$Խt!kpP[kR\)JZ qTV;vQܪe5\P:XHhÉ#X Hʢui-HI S$.qZHo:@ѹi9ζӌq[w #{" '$l$r(=[h07}NGjQ,&ȆQ6 lk$ \ZJ9&Nڭhn3csMFdnG>u!`3 b<A' `8ڨݶڨ1I7Y7I,v˞\NO uFV \].iu4c:28\kVQҚz=n5X QMHM﫝FZ  9# ;tު֊3! 綐8׌᧗A4SGn )ꤎ0DƘ郸AuApq(h竩GIx֌OToeC))S ow̃26mA:ۤQ\+G%mj!mSX7Xc8;F2&ߤTW;Lٌ!I Fbᆜ,Kv驛KSYWSb -n59dqPIW\4ot4]OgWskŅOK5p&5ԬkwWmph%vhṭaI[V#@f5% ܣ [! 'WSǭ㻱(Xv}΂_Y l"f'BF4d琞#g^KjVz dzF=nupη(,Hߥ|AWMOUTL0S։ \Ӯ憖;9<4VsSkw8ZsPO"2YA-CHjD$k=8x;ue Xl9 R׹mrAm'eV5\h?gk* .k>4Nk>4[Dވ@ٴD5?3Bh96蚦`HYĈ(V豷ֶzVNSBC+^1A 'hؽղ -+e;;@۬Av+*9҂M%"FVnp=F0R9\S.KZ*)h⤷Yk9Ѻ6x>ȒqmVtAil 5";17-Yb=vlV'M$MW0C(dyF;T`GV$ADAk)Ypj-M3'487nHXmuU)2reACGn 9v-" A׺SjbG S4v2kc[n8r*j)0˞@kق\IJD )ij"ltM-IL7YveMl{:E;ḊZ\HhzDLj7Jpl4VehcNvkv2JVD4T7G@N5Kub6WA [l[\q͜C30m# WU^$4zJqUou+eC{i]sZ:97;egGe)u] Sl6giX6.#GY*-28P|z"iHvvl[[m7i*rrKp# lSRr)euK,XˌK=&)+!ؓ`;NyVMyg;GmUT4Q5u#ll35B;RrZNO'" PZ8EM#c;L1[.kdq#+r" ѢUcUv&D&EM菴 MMSS0~?(]ijg􅚈*=$[t-kH Zs { ;ZMKUSB=Kɕkָ9'-!p}YhyT(k[5JHTnrkFZu(h&3mO1shwˣm$mkkCsmٱҖ9d|n6 $~ q:$l̜#Fk( wS;w68JCXdp3dh*YG>#]&0tc2A{Eݣ!*6qp~ E;v#1$=5zCMl&oǖJ˹ 㔠( MXmOP-,ϧ'HT!h 8j@ł:]4hP%,MJǴm;.W:Vˌ{. d >J(+(PcS @3;~5puT2E<^FXLA'T4֪ /OG4ʊQO#$qs:Һ\ѶvN5#f\3*7Mq}q;7jn=瓕kW ]5{}4`G53fPt\+EX Å eidt;ZR$lf<426}5NwWSm)Ãʇv38hh#koٟU\Hf[v8{bAjyvN;X8'$r`l¦UZ K}K:wGU!bXƹhִr6@tDPW/4OʬjѢT]W$ן}4sm75MO QQF\oqPE-Mml2 z``q5ٴgyC5utQ#Y3xA2ݨ%Q -5OԧJւIvyc$m{NZ>>w)Q @_+'$0KQ1ϵVӸ˹ny`ι-''DDE Q- et0K;ipc'6E .2TkSGW#:BpѱFݫvjkɭ񖁆Q˲v 0yn^),jH4~u2|qmh;3&T" """ """ """ """ """ """ """ """ """ """ """ """ """ """ ""_hXrD]4I?x:I?xmz#G?f}T D}hoj!f"(" պ[>tz*8/:  'kvt㥶:e%k,-%jcSpv̊sfe[`лk嵾FV=k#"V <8F$mr@ZP5כU}6[LdsLXc]Ms{9iη&*ieK#D3UΧn4sm75MO QQWElm}mt^S3c-tv孨Ai]HãSho{poh40F6HÚז$2[ nRIk*nSɽ/MN5^ݼɣ3 -[ͩ%c0289xդFO+ WE[=͎ǖKQ#Z AwjNHXal4wSjj)U$_b 1#sDe[ ڥdgI'lgai[Fj{],u1ČQ= `2庭h5߫ګRzHi&2C#@s18WA$._nRMP'm`-4pS##y.o9u!T̔ۻ 2n 9;" 5iZ[|6=X%t&1#Z4~j-Tw( }Vƽ#H""" """ """ """ """ """ """ """ """ """ ""_hXrD]4I?x:I?xmz#G?f}T D}hoj!f"(" MlZkɍjs{5o&6~j$jq mvq(0G*xdMHX\pژ8O ҊX&PM_fdS6X#tMG:6\0$ި_M ƞb9$kXZs1ߍlkMe*eQ ckfjKW&E\̽1D]7]ziN@˜7X&Q-378-8FںIlTS>rkõ]$4xJy͞"s4=Ӽ<A@TokeW4AG# u e/m9[*diepڌU;t̼A,I9ܯ3ɤW2+/Ll6v tãpS(:*]O ڧ}Փ>*ѾFe1k5r6D襢 \߯mf:88IRd15@H:RHO\*mIP)ܝ!8h h#nչil 5BH;U86W:k-2 9rdeiw~I hTT,7FZ=mx ʐ] P61WqkEY=Fi w"6Ibuѳl2CQRo&L'/nK#AGU.=ܑ7$ 8G.z7vl20ˌmZK)c4uKRJ<27e--$byFNkم5M5EDxɺTHj4klp'j*[]'u=ƲWUG%[[C=?jDqni-cCuA.U-2G \89p2rk5ps\29*=iZ[|6=X%t&1#Z4~j-Tw($Q@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DD\h?gk*F?YPiu]^u]^&G~ͦ&?A.@ٴD5?3BF$DQDD@DDD@DDt4=͒M$qs$DD@DDD@DDDAFO+"" """ """ +udc^܃ פDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DD\h?gk*F?YPiu]^u]^&G~ͦ&?A.@ٴD5?3BF$DQDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@Uv_hU5O'U5O-oD}hoj!B菴 MMXkvV}O0*i` fpqD*yq_Z?}m}jp+f|֠p+f|֠p+f|֠p+f|֠p+f|֠p+f|֠p+f|֠p+f|֠p+f|֠p+f|֠p+f|֠p+f|֠p+f|֠p+f|֠p+f|֠p+f|֠p+f|֠p+f|֠p+f|֠p+f|֠p+f|֠p+f|֠p+f|֠p+f|֠p+f|֠p+f|֠p+f|֠p+f|֠p+f|֠p+f|֠p+f|֠p+f|֠p+f|֠p+f|֠p+f|֠p+f|֠p+f|֠p+f|֠p+f|֠F?YS͸]/Z}v)ں z*eY xàcNr wMyƉwMyƋho~Z"k};4ulkHї5r fÞi?xʲFVw<A;.?O".GeާIÞi?w<A;.?O" v]4N˽O9zs'Sy"eާIÞi?w<A;.?O" v]4N˽O9zs'Sy"eާIÞi?w<A;.?O" v]4N˽O9zs'Sy"eާIÞi?w<A;.?O" v]4N˽O9zs'Sy"eާIÞi?w<A;.?O" v]4N˽O9zs'Sy"eާIÞi?w<A;.?O" v]4N˽O9zs'Sy"eާIÞi?w<A;.?O" v]4N˽O9zs'Sy"eާIÞi?w<A;.?O" v]4N˽O9zs'Sy"eާIÞi?w<A;.?O" v]4N˽O9zs'Sy"eާIÞi?w<A;.?O" v]4N˽O9zs'Sy"(fP2@e;ÙꝌhpN=IE}e~OR٠FcGCIEVJFIFHHxwdumpC    $.' ",#(7),01444'9=82<.342C  2!!22222222222222222222222222222222222222222222222222"d  !1Q"ATV23RUau#46Sqr$578BstbdecC%DHEv&Q!1A2a ?i9o#\q͉,-LxSq9*Ou;߰->,~ͺv;|mLX6\OJ=g+mvMgt^cgoMp1nh`=ײ^NHCQ3$Ol#-޽i֎^ z5/Jtz_kѧZ:[{/5Խ(":|AFhn ףRGKweuײ^K҂#-޽i֎^ z5/Jtz_kѧZ:[{/5Խ(":|AFhn ףRGKweuײ^K҂#-޽i֎^ z5/Jtz_kѧZ:[{/5Խ(":|AFhn ףRGKweuײ^K҂#-޽i֎^ z5/Jtz_kѧZ:[{/5Խ(":|AFhn ףRGKweuײ^yV_QL7P{8A=dŪ6*! R[9 k:OA+֎^ z4GKwe%nTyR;|x0BEQYB8l栐GKweuײ^Fi\=ܶܜhvjنYĿvLq}GϚ tz_kѧZ:[{/5+Xe))Rc¾HqOćnquAlA-޽i֎^ z5n %lG8tW]KOo5L퐔HxgZ:[{/5ׅi}$ڴ݉./; 0YXZ]^%q֥h +I6~幯KaMSr H!=GKweuײ^kX7 6)Pr[*HkGZ:[{/5ӭ-޽j^hn ףNtz_kѩzPDuײ^:|AFA֎^ z4GKweGZ:[{/5ӭ-޽j^hn ףNtz_kѩzPDuײ^:|AFA֎^ z4GKweGZ:[{/5ӭ-޽j^hn ףNtz_kѩzPDuײ^:|AFA֎^ z4GKweGZ:[{/5ӭ-޽j^hn ףNtz_kѩzPDuײ^zkEiѶ9bBr@<:Jע+T&䀣ϏlvA&~Yc}bǷ͜^֥m/.Rn1eBA;Y=fwI-jMsvA5dVe/ =aucA 瞶Zpc Rm=KkfІANh!ӣ4SOXZm9]98cCiam OS&J~Haԭp(zձڤ[$= ;_ ;\ |]-lv 4UNRT@JF@Bi[?Tž2t[RN-m$pݧ;U?QQ2l[|Uʛ%э^p! <\mi)dse}M̏z =hD'*u!*st̄,d`|[fJTmԥjmE I8RN;`M2=|*zJhSQGOG&j[ RͽcHF6zZddInQ*d^ve+pzZ-NXul0AyjϹSQGOGA $2d0yPSACh$ F=d{Ty鹑SQ篴GOG>zJhSQGOGҚ>nd{Ty鹑SQ篴>znd{Ty)yۭ4t~lז`aZGBH#YMڛip!~?5ka,2P05Ѭթ53n{ZePbZ7`5Jh%HF2=ڇ-m `9-归~kfчv ڂ9ׅČ-{jI-UxuJhU2GhfJ+meJ![hsώ<+-)]Զycs*l(>zJhSQGOGҚ>nd{Ty鹑SQ篴>znd{Ty)GOG>zJhSQGOGҚ>nd{Ty鹑SQ篴>znd{Ty)GOG>zJhSQGOGҚ>nd{Ty鹑SQ篴>znd{Ty)GOG>zJhSQGOGҚ>nd{Ty鹑SQ篴>z%hBFsgJhl/^w3;[g=<ٝ_j)kZscJha k;T4sdHQz{wmN0V)B28pI_n^&t=XJv:Ua%.yKX$dd|Yngyc*/"ݭU[q.4K$ 82 GQS-Xٓ63sܺKIS[U-g;H בulݹlu8om JZ}+}g9R 꾬%G r^°CmXn|8,,F*Lf8Io: Feտ~[nU7 $Vdp%nG r^:Bk͸u 3YTGDyn1Q-z/@kܗ"TnG r^p%ȷܗ!-z/@k-Fp%nG r^pQ-z/@kܗ"TnG r^p%ȷܗ!-z/@k-Fp%nG r^pQ-z/@kܗ"TnG r^p%ȷܗ!-z/@k-Fp%nG r^pQ-z/@kܗ"TnG r^p%ȷܗ# O:p Z3v!j?F+T&QB\YL974 P˛kڶx?d5C2R \{c?0n<Æ۴4}+F_tV e/ڭcE9%ʥ~%4ǤFԤIZ˄MKkf *І24:pf[*x{m, f3vC)+!R6ؔyZ*E2Cri͠(o[%p||oHjcLhVJ6mIQ%MHVe&s"0{WY8{yqe\ඤ-mLd-Z4;v %Lrf4t[;8HBnv+-fEZv6,do]}F<:DPd%NhlBcVR،b11 JͺMТQvRN$dded?z<:C#ঢ়GXjhd| uc뭰ҝuĶ ZĒ3i[^e JrJ;$pSΣ42> yu&GO:)Qd{q4|)Q<: 42> yuGO:Y3imnB- Mr3j2?&ęIfLu#KBv;j=dev)Q<:͹ 6kq]JTFh3"2%SΣ42> yu&<:C#ঢ়GX#e'-wHY4X};d| u`FJ"222=dd?!fPN)Qf$fFW 4~2> yuGO:C#ঢ়GXhd|@4|)Q<: 42> yuGO:G uaSΣ}Ȏ<ٶM-8#`tdaZGBIC4FnN[|4d]VɶYiP#5Jm4C۟ qC,N+nѣ#/q(G'*B2<.meKl6 bdg\I`wc&[Z..$e!kΤdj={(2mq5mWmd!Yt~#7:]VҦG;d|@4|)Q<: 42> yuGO:G uaSΣ}C#ঢ়GXhd|@4|)Q<: 42> yuGO:G uaSΣ}C#ঢ়GXhd|@4|)Q<: 42> yuGO:G uaSΣ}C#ঢ়GXhd|@4|)Q<: 42> yuGO:G u`堬J8ɏgx_$_7{ͥEǾ?@gx_嬬>%h+!DN6cY<|OQZzVtmNh+粒erիVğWDi=(EE vNY4Ù"GZ\_%x3T_@թk 1[KiffBӨwZĪ.Oz̙\Gm%MhV[)k;AˋUgS(()w"1M"2$/NW&RJtvШ[PYeťkmJTfDŽG`$aPҝowJIJy$E{{ 'İ;UhȋJK^iEqJ{e-w1sP=Pw9qf2҉9s$ʔGcVjQ຦ݪjYQ3n6ZԚ"_z=FjMz. q2"җLf[,-M-DjYHh5l䷎0{`Ɨ=9I*(ٔ܌J.p?Euq" C Tduw\[C/"BQ)[I2tȈի2$k.  Df̔)Rf>K jʜ%DekҕQ*!ޒl'H\2Y+: !iꙙ\5-+"Y:DFzyu|ӆsrW!wf[G ffűw3;~(U*&OL!{rijqJ5}Q]%'_}UHTrTu(To 2 +KnV2%_8תT&#UYjl-ZψI.ECRGAec9.i]4\$3$z*(ATLΧ*bF ӔVsk{5b>J80Ҹ)Ae«o&'>m{0 IO^LѸì$w49ndSSE7.:$XmfgܿWS ȧv ,Fj6Oj?*ay`_ SڼOʘ^E?X{q&u,HIy6̵Xz&ꔸal0RjB̯c+?;WS ȧx0~/~T)m^'L/" Ax0~WS ȧPm^'L/"~T)W&H+m|_5]35Ԛ[Bl6_|6rfvi̊$236#d!+eB%lm4X-O%`|GZe*jLp%; ,UY3}!PC?6+7F~Ɣ궽Z8+ǘMX' vrIxȷ r }$Mk"> |"$?!zē6rfVկ|[{]8ԡ&2PטQ6.7~[Q6<QXc\}FHʌN5V;]{#i3eݎyOoiHpqĞNSWޘ*FTLo9ǗJ?gE#<{+ Q?dӧ!6;)m $bVR}+?:4gϔqXbY98izc!nESRb44G*TfV;\#3.h98;ڛL.>TV<; ^WbDH%r#-J32(I%$DEBI|Ǎm}[sYJh4u_Q!JmqX0qܔse3q<e%%TYqSҶU8ٙU$Ii|5d;ap|-zYUr,;I;DrSE0Ed!2DD!BPa:JwmG&C9[5K4(s- U\M P̩,= 1dLe:FBҤIV;>"1Ęi꺤2᩷A8MtH%t/+ܤk=GSNa4y+KrK%i+(c2cMLJX>pг0%(~Ng9?LJ0%(~Ng4?LJ0%(~Ng4?LJ0%(~Ng4?LJ0%(~Ng4?LJ0%(~Ng4?LJ0%(~Ng4?LJ0%(~Ng4?LJo\ Q2´22Aʞ '+O\rdwG!O}"`jȘ g訿؁b+g訿؁b3Uƕv>em33hq屗}fo8Hoi0Q|23Ve4㍒nr=J-mYQGq6H;d:K3q-Z-jY=JLTlF쇍fG[)JLy$h"#$ hXP^mKJk2$9LV+,S^fNf"IKWz=vT9Uch\"D;*Q"ͩfIƫpT5Z9TGDY i"Ib\б8tiO2e'RiE_ 1 xﰷBۍ-)#Q^RDSr32; G[t{J5kGڮ'Zt= 5*r#ZieXuJҤٔIe=Dj]f#2 $E)}jf*TNvQ2NLD%h5dV'kRd2Z - T4eլDٕ@)uqU#:jJ;ZM̸Hc􂂉{%B8m17tFf'>k{kg3{6VnCjBEJ3#"̇8bB)kIRSBʼ(H%Ib-ZLVg6$w鿝6Dlߦ[꾡ytj&Cx] "2=dwX2TbF>cr mf)E[jBIFWQ%(4zŖ=HlGڗd;v̲N($)RRfI5xk+2뮶hqRRDk2""5 ؈ TIɖ#>6]늾eJ<ʹcHL)KSI.%_G؍J.-+[mBR$2"der>;'%2MKI3#4̈*W7C(XՓSZTt/:*ReiNmw\k^ٮq}s5(k_m}a\k (k_m}a\k (k_m}a\k (k_m}a\k (k_m}a\k (k_m}a\k (k_m}a\k ٮq}Y|k wG!ZfgaPy^$ڿ&?k6 w3.-6EAbami0ѱEԆ̒LԝD|c5Y]6P#j=./*<ZJ֮}6P#j=./*<ZJ֮}6P#j=./*<ZJ֮}6P#j=./*<ZJ֮}6P#j=./8bFܹHmǗ&RLdDIu "Rծ- j#j=./ 6Qң74 j#j=./ 6Qң74 j#j=./ 6Qң74 j#j=./ 6Qң74 j#j=./ 6Qң74 j#j=./ 6Qң74 j#j=./ 6Qң74 j#j=./ 6Qң74 j#j=./ 6Qң74 j#j=./ 6Qң74 j#j=./ 6Qң74 j#j=./ 6Qң74 j#j=./ 6Qң74 j#j=./ 6Qң74 j#j=./ 6Qң74 j#j=./ 6Qң74 j#j=./ 6Qң74 j#j=./ 6Qң74 j#j=./ 6Qң74 j#j=./ 6Qң74 j#j=./ 6Qң74 j#j=./ 6Qң74 j#j=./ 6Qң74 j#j=./ 6Qң74 j#j=./ 6Qң74 j#j=./ 6Qң74 j#j=./ 6Qң74 j#j=./ 6Qң74 j#j=./ 6Qң74 j#j=./ 6Qң74 j#j=./ 6Qң74 j#j=./ 6Qң74 j#j=./ 6Qң74 j#j=./ 6Qң74 j#j=./ 6Qң74 j#j=./ 6Qң7Q֪oǭ% 2[~fjJXuDZ%'^+18͎B6K743{wޚ> "ßFI1UF|' SJDcrC*u(NkT7;}>0ѱGUN5CFq2$ېJӠZ-)U# xyvvBͯRHԟ& JSkb]vU= JZYBFMFfI"~Q]faSq͵(ԕDu )\UNR!,Pmr#2r3oANc%JY\IK~Ǯ׵JI$x0ꓨe5Si#4Qqd?u m#.:k 0 4dV2,ȯhCL=Wy$u66n-#d[ 2p[-="c!B,ZvV -wKH-Mf/GzKdvNY+M[[ Pnfڬl~έ=|2fͽ?ՎKv52uN/Hi-cw oQ]Q-FT[$Q5J^^!@f&D&b[ q,OhyW3Ӿ~733j&>c|m[{e~k5eNqTP͇I8+3Po)6(mkR.V6mOТ%kկ㒱-=/?i0lNn-)IgE5YjW'ϖ8؛-麜iIe(j+o7Vtg r (eTLf3QVJڋm+QaMjK߳),$I(|~i~IP'ɤqȿɥffNTQ"25,32d \nG%mJ;'oիISq%RI"3j!Yu%7lLGx9_QmWFg+0]}?w]϶h>0ѱG*7'9E~Y MҢ"##2=F\:psi#U'~XnϐnϐøqM!ĝm]SNHqe%Ec#2FwGT|wGT|~% 2J̾Ѹ; ;!ZvqΔw3xsؾ@ѵ; ;!b)ZG65~{4h30pv$שR%rr%JyI=22 \nϐ€hnϐnϐ€7[-VMfhh\W;]2 1#l Ran'PJd{NBD#ˈl*]V3. (233mzmC5"|iJd}G'5ec &Ti4&Tv4S-{sQ6yOcQq_1k1]2F˚A#L[sXJD#NƖRHϏQ8\S-KZԥ(fgf` oEmwޚ>uzj<`:2>0ѱE` )@զY]GR6>4u𬚂c]|7 ȍ|;sþ.;B**b*.Oi_hQ̒fgqFǔRR;h~Qؙ!ڥQ1ȉ*Q(ֳ4\l7|]vpw~hDc@r;eL:B*:kuyӖڔV9'Tktmn"*36SHCJ;b̵ Fn f?LMB248aD\XjvSYJdМf=zئMİ!* 41Y *`Ȍ+gԣA>w߹ }6 IơOrt'&MI9}YeXjCĐ2Z"Ս-w߹ }3þ.;A|;s4pw~h7|]v d}>w߹ ?|;sþ.;C@g|]vpw~hh2 n A>w߹ }3þ.;A|;s4pw~h7|]v d}>w߹ ?|;sþ.;C@g|]vpw~hh2 n A>w߹ }3þ.;A|;s4pw~h7|]v d}>w߹ ?|;sþ.;C@g|]vpw~hh2 n A>w߹ }3þ.;A|;s4pw~h7|]v d}>w߹ ?|;sþ.;C@g|]vpw~hh2 n A>w߹ }3þ.;A|;s4pw~h7|]v ʰ;_Q3.~ }}?w]϶#}$iVNmM 02V+4}R|]%_FgßFIXZB<bR9Ν (ϜE$DfdfIƥYI1`[U*P ZvTҖHRY- I]V2I|cN ^E,?)J*%dJ|)խ*4$̊ױ )fQ-وimh#HYkWb3e!窓UT7 K6Ȓ˖ΒѡRw=[0$$ Jbv[4˙=.|U &Ptʓ)Lɶ"I\՞c#3|gZPҨsI*JnpyIc) Z6dyP.~!$Fs>՟y;VxG67HJ V>5ΌJH>!<#m_ O6/'(6Edl"_}@$|/>P #m_ O6/'Ha ~6Edl"_}@$|/>P #m_ O6/'Ha ~6Edl"_}@$|/>P #m_ O6/'Ha ~6Edl"_}@$|/>P #m_ O6/'Ha ~6Edl"_}@$|/>P #Q,̋|>MDREk/pt|[Nݘ(jUW2ȭE9ΎI%̈)EdIl"_}A|/>@FPm_ O/'a$m~EdIl"_}A|/>@FPm_ O/'a$m~EdIl"_}A|/>@FPm_ O/'a$m~EdIl"_}A|/>@FPm_ O/'a$m~EdIl"_}A|/>@FPm_ O$a&LaثB+λVMYlxG67HRZnRVGrQ_Rapsi"#Hff<uf<u qKoJJvc ;ғQ-E{JL3 p3 p uǃs.ǃs.EjJQ}+S1[JFdW2b~+h0WPlOP܈Ê0Ids6F$RFEk4ƗgD]A|a?ʮ@Gr#++-ViO̕-- Gd\kOqUЅ:-KC/!)KA<3$f"=v;ȵjK-9HyD$e{[k3=b`nOi46ӉE)9a^Vy.WU̮|f?at58n3TuN-gL$e56JK)ֶf"uTTԗSl*:n*[RJ"E򑙥7;\Z @wޚ>uzj<`6#}$\JNss+pw) 9lo4t 3Gn b J;:̅%9Rf␤Ub=f.R'eJ5 4m|R $$$k"iDQ"fdv/KWV5'=ת)SnS&kΘYLߵxq4qUR 4m|R $$$k"iDQ"fdv/Kghn0&u$NҵF_WI~־jIT\IXzZVNS ޒR!Q뱝zcR43DdeNQz(KK} mr:R+:$ԟ=1؜+91q-"} )N)Ii SQ c"veʏ04f7y&"3QfZGmDvc`EV\(l*Le/2˦TǗ2,Eb3\e `QcE:-!:T)-";Xo@'NLIMH~n%1ixs3R ?Q,4)~=D[9%F[&jJ3X;XbFM]W魴%R$--z>؄*)TDK66Fnj/n,k1*4Ȩ4VGjI|c o'}8Rֳz|U]qc0Ti.lEH@cVMYlw}aϣcy8_!K}aϣcy8_!9";K-É4JJouV5GIۮ3[c2-1f/GPY LG=%LV?5_"I'You L8)*3t9PeksemWou~a@ou~a@(*$̉Г{k\?,:*r2'N& J=&9hYPRd̈q\Q .Zi!R ТQ9$6,Q;ҀQ(`c;pXڬ2 3b :{0hUlIl{!{"k).248$FkIEk$Vkigl*1QҨDea;FO*FUŏʚ[8+*EB+k)Y KjOl uGhRHw$"Vwlw#'?Uk舿(oە-&#RHʹ[224@O6_1Qqk]rII\,|љ}]PDZL{ySIOW|6ƞ* HRm 鮦cw"> fzTioԝiY!kI=|Wb=ڑ&%7uś>1gl\ը1+{}?_.ϳ2{eNķZ!oIzJ=fFIqWo%$YEH.3u>4{bs? Rbbk?>RTM_걡S"D 7iHuS*&ۆ%fʂu7+$VdC(P٪aahlxym9ȶ#3N#g伻$mU<TXx=\v.E:/OT_&Z;h(ikR{䠘5L{A/_Dx˲GRqoꗶ>^&Ä*n1=s J+2>.%C](-s$J2Ep b-B^v}{X_,5T7*NoƧَ Kl:.)ºٮyneV 3mFP~$[%*dJj4&fyVǨ>)R!rˆ "ֻ";Xo^uߏ+ڄV rHlGnQ!v2;pݚLCmϊKlՑHk47V[^wGѕj_~tv#>3Z0[r Wa?_VMYlw}aϣcy8_!K}aϣcy8_!9:= :63f#"VEz>=vFs)m?@Qj8ǎMuopшOTboJiP"D٠}w ڸCHb5.ŸG;uԺY2$ue##W}kQ c GʋoU0ѱE/G>0ѱE/GES'B^]&ز4e,ZPWǾ?GWѿꐗS+B%yJi;\˃x Xh4N G[mF[57܊`J2-jQ#+fʤ)I3+׼T1{3Yx[yM)){;NkQ dR5 B%5PH n$ID;DdzV!TjSRFl̒FeDfzYdiY^"2 ڥYi4$bn"%-&g+Ja0aͨ6P_*:cQ|T`Gym.ZQ\1OOű*!0q0:r FRfYD XgYLCCza8m-0uuFE{|XΩLK6;f+-w={A )h8Uj>4G' ʲ;X3#-a>[KSQ)Mz([~Jgz:zʧɳq׈dJ++Y1Ա\=ʋ-[P:m1(VVSL|ֶ_ wѰͬB"=6{ϡD%V"AwcXf[W\iqPyDNI3I̷fFFAxGb-gkU[zo: ;ePW}54hcٖcRL]f+d(MeJƩ8ZZs=DK+'w3/_Pmd8*EmL:M4i=8Yge7Jl|bժT7(9s4iZ4egr"+@14 _R˦KT:%&RTZ7DymEkfR8i)l!3KwV+q U4[k[e6>Eii3f˗{|Ф9%FbI5^\"-"n$[i2&T+jJSs$Es*5)z̨R)-xѣ5RfDzB'Ib ::$ HۉE[+'ўVר6y~y>"8 CnJ:rJI7yU$Ԓ"ʯn{ָNҖq4gR^YI;҃,wI-r6y~=ǀ^_hj R.[qNڞr,ܗAXyRv3F¬t2)u6{ȖetDd\ciZ\'Xk2 6xIZ+ۄD6,1-=Q=썵$&(3UGO)d/;(\ۧyU29_QmWFg+0]}?w]϶h>0ѱE/G>0ѱE/GEyVe|W:ɔR,C#$j"3=e~[saMrte":"Ȓ,7( lĦQ%8ӝFB .e$EW <{R$E-:ʎ3)M)YkpѣzgO#Vm"|w̸,emV2gB^ŨMz*LͲqni,䌶U^mfCXDUdq)B[.vJg{%$gs-v#NxVvnF64CͣozH Q8Ӟo.b#-IT}$V4yK%^ڮ醢qQ=]۶\FZ%*Ih&K2\zx6J\5 7Q)wqLns"[6IXY*v)2mMNo*DfhZ'ZHVyri7Vvnl[%Bth""%k3E!;EْKK"[Qe4Y6e;k;]@ uNM90כYZrBJI2ٛP)4l ؘi6Jld6FV;\` 8\ P㍭.N- ̲K5Y_Y^TUvph(eϔR;OcTKx#BV\K+Vv׬T˧>j* }>#jwI]GT(Ȉ+$X+%IVCްasԨ*M>AumŠ) ͡2tgq;4Tu&#qYY_|F*m8TJ*d)UY'vZ]Gިȯk~0hM[։h!e $BI=n6_i׿ȑUP'ImTfږp-n9Mk3;x pPa 9lȒr{R>Fd-e+{!ā&3 ObrC,ʘ6Ե%]2eI| S+US'b13BI6N)ek b:ٴn8J~\W#&dZH6DE}F6Bl/;( 2u"M*L"r-KΥ^.qo `Uô*:- e?"CMcF"o6D> ׿Y->:)j>TQYz4m}w)NGv;b)C S--*T$τi%DVhj4R]$l{&@Oz(MƬ-DW; Tj[?bK˥^K4wɟֿ'EE{N3̼MjJKW2ӰJY՛)TQ)iH"5ב)+{^&5E"RfuG0O֑Dͽ/@QMR´jK9 J_q tYҕ(ˀ̮Zkܬ7K$-꒔ؔ:B-DK$(v-]Ű#̈́B*hdwm62;#/b|9JL(̝ykYѝJQ|gZ R6gSitt|&3GTҍ(JYZ՘ԒJ=fi64ca\r%JS)kq&F)fRwt%iAV*}hjӆ|-G6]eqYj=z.<'.tMtٸ&$DE\V; i('qe(ֵvດgӁGl[єnIR RٝZ2=U~e {t*FBg+0ьF] 5g}՟ymxG67HG67H$3((C)ZԴ!4Gs-7 ø&*L>Ts7RE)Q)63LFFv?fjrQפPԥJ+aȍ6SfȏCLr *c;pXڬ1x:SRVaOCr;6L%IwJL|u=Tw5ayBUSe J;!HA ~3/cƁ?0'2F?cm%(֓3HQ@fhߣ4oOPi6 ̿oц?1#N&|Mi@fhߣ4oOPi6 ̿oц?1#N&|Mi@fhߣ4oOP Q0=nDg CSFEr? }~3/aOe3~~r%.=R]vcUZIeAE[q)Qphߣ ~3/bs4:`/婾?0'2F*3Kyjo ZZhߣ ~3/bs4:`/婾?0'2F*3Kyjo ZZhߣ ~3/bs4:a̟JT1+i(ti#YؔYEiC2upeB*FB=~qLw_!yA[3uOthr wޚ>uzj<`6#}$]?Di \#}$]?Di PVTkmA&ɛ51'1 ٔjRRfWཌ~cיzt8NDDq(yY A({Bj8M:җIa$kufFv+jIȬCJҚBJFh{Gt[;,Ӽ]̂x ؊ >^\dHݒm> !O1Nz o%fsdlv$VJ-wUQY?zz#nd/#rDH~w4꽈,USe QUNsW6, m?1uwlq9Lf.,IAr0G]rdSwdM%&F\}Y]/R,”n5.,rBHիVV'C*yl$&3ZP[RI&ʢ!M Uij2"=.2L})Yd42/*xj2UdT0tU$J *Qm] `."/?epOb[NY[{lj1׿bsJ%mjQM;l"3;;?~ъ'E['E!UjU*Iy c<.;"|.'}U1"I԰Uwȣ$&$WޑU-FfͿAZw3k$#8jsa!̜$O dZW3"<ߦ-b5f*Zf#lpd-fobs$7GY0Gv&E>|e_[R(-eZ3˒m{)I\\S33l;IS/jgi>L |E㘶[ڥ*-.~?.>.~?h(3rTv,K̩2В5YDN#[C)mGDIRdi3SnGoGczsFD髦T[v1j(Π+(̬dfZ]̈BfYBlI}%2E-q_SEK^ji0*snƣ*KKfIO#*9De]ҕ7 =gf p2'E%)2-/A,yCsŤSӲuN|ndG}G{p\?.b. B ŗͩM6dézz_3U;_^vP4-UsB;P[dXKڍK";fP fPSh2lV+稈p >6|?n nMQ ]{>6|?n nMQ ]{>6|?n nMQ ]{>6|?n nMQ ]{>g*pR3>Nn nMQ ]c&'Qj؂=N#멒Tiؑ_>!s0)r)d.z#kZV+Q~D QS QS75G-vCq[T~dta6Lz3ʴ)devg6[M6BCJR+ {ܚ!-ɪ?Ak>(( ܚ!-ɪ?Ak>(( ܚ!-ɪ?Ak>( _uLYdJ}fJ&xFDo75G-vCq[T~d=~qLw_!yB?8lXLG~VPx F]1+!w]϶wޚ> ßFIOpQBßFIOpQC4rYYw&[v1"$c-衩g98Kx~G"\b>Leq5%:FqHJoeL(}TTʄ-9qdfICI%);\V);2̓V|ӴeJK%I'+Vk+*W#2Ua.%-Hv2dDfhQ]'of6dbWO}ǚ:|[!%dHɣ%wTkU% T4JgN-DeaU٤œiݐ2 $ns"-I3}QzSHRScѭh|ew3;%.yN܂:dj< +DuF1M.>CKrFu!*6!);1؉B dޑIY$dAWj‹nrUM52!NRL|46'UuƤG84 HJC٦3#3QXzB u7$qD.0ѱE/G>0ѱE/GEep 16sԃZ[\٥O/*YL.GPp0mȪaA6%I[J=oa"*~i^twm 譕1-M2FZFEcFoCT:t'Tl9$;^42YStj$W3 y15u.f=*LPAZW Z2sNv7i}2Vܖ#}hL")J]NBZM?/Ŗ$jr jcRLIΧyJoY|'r"# j{N7yYԣff$]fjvΔ몘ڞkq:3RKF$ܵ8R9-s LTϳVCT4(NI7 5FEEbNѣzgO#Vm"|w̸,emV2gB^ŨMz*LͲqni,䌶U^mfCXRfKR}͍[iKnDglN#0TɊ:tȈ Fw33m qNKQ)DZ̋z~d0R*̩ [g,IHިˆ9uê)1RyrsJ,h"4Zǚf0C]^Tm2:O%DZB7MFmj8UIϨَ9DDy[hBMII\\토a=]8'PS%ĥn$Ԕ""#Ip"n21ucQ/.ʚF Քm@?Ʃ)heI\^rKb]dn$Y,F[j;Ɉ.-EStqq5O':Hf-2 r_eƔKI*}f PIw\5Te† ҸfѕӘw3[OpDeYJVrhq~,1#S{UdVS4bNu?̲Sz;TÌZNҟ]uS[S n'FjQix҄2[=B R'1Ua9jHy f&fȷȬIBDĚl9 7-#D ԣY3Iܵ))a0-;7t>J%f+1{˂Vaz,Dy*QDQ2#: m':|v" 'aKbyZPfDI{^ʥPJ:RLW"4b-Z49cR3 2I}Mdzk5W|ec=`'*1TBq--,ֵT%&v"1o4=p%!*vʾSNULǼ*i(t|D“2\ZlhsH[v"#;eZu_m ݜELQӤ6DDFHm)%%}3 -]),mDKJ!Dv3-N1*)JoM޹%DDi4̮_.LTšHrK!))StLhJD&C-J/>BmfvQ$uS%4JS7 ﮄeNeq,JfTh8JB!Ӑ%g"~PEĮ;c*]:|5S:I!:5Gt3/lBL|BrvcI>*[ZDN)+3$'eޫDlCUKoFS9tN6&k[ ѭDj;VlCqD4Coy)7-J%f=Ec)Q}dǝ6AT2n:˺DEs4POLb5+54Vlq(iTIifj"2Yw .M,i%F˥$$UtI2;p ֙*٬%LE3VBR +Nh="Nk_ZLeq4$lr2BV2MI\J˿s-Q:YUvmʌVZQɻXnjSSNy-n8MI- +BVa5=lZ{h{ Xv:%DImDFW#m;aBtob#M  Wa?_ѣ'kuzj<`}?. 9lo4t )p 9lo4t 3G @OiⷣC81ֵzĀ&З!$Eȷ~VNWvQ\Gr3uOthr wޚ>uzj<`6#}$]?Di \#}$]?Di Pr Wa?_VMYlw}aϣcy8_!K}aϣcy3tMqAd%ғ$";j[4]mw҆yQq}(рmw҆yQq}(  yQq}(mwҀрmw҆yQq}(  yQq}(mwҀрmw҆yQq}(  yQq}(mwҀрmw҆yQq}(  yQq}(mwҀрmw҆yQq}(  yQq}(mwҀрmw҆yQq}(  yQq}(mwҀрmw҆yQq}(  yQq}(mwҀрmw҆yQq}(  yQq}(mwҀрmw҆yQq}(  yQq}(mwҀрmw҆yQq}(  yQq}(mwҀрmw҆yQq}(  yQq}(mwҀрmw҆yQq}(  yQq}(mwҀрmw҆yQq}(  yQq}(mwҀрmw҆yQq}(  yQq}(mwҀрmw҆yQq}(  yQq}(mwҀрmw҆yQq}(  yQq}(mwҀрmw҆yQq}(  yQq}(mwҀрmw҆yQq}(  yQq}(mwҀрmw҆yQq}(  yQq}(mwҀрmw҆yQq}( Wa?_#j=./cО{ˁ]uפ0q 3Ԟ՟y;Vq~5S㨙- RhmI+c#"#oe<#˧kwǖCw?`={W#w?`={={w?`w?`={={w?`w?`={={w?`w?`={={w?`w?`={={w?`w?`={={w?`w?`={={w?`w?`={={w?`w?`={={w?`w?`={={w?`w?`={={w?`w?`={={w?`w?`={={w?`w?`={={w?`w?`={={w?`w?`={={w?`w?`={={w?`w?`={={w?`w?`={={w?`w?`={={w?`(e[WO;ﬔ>SԄDD#3;{R"##3 ݽ}}=٠FpȦW3R$VJFIFHHxwdumpC    $.' ",#(7),01444'9=82<.342C  2!!22222222222222222222222222222222222222222222222222"\  !V1AQ"SU267Rau#3BTqrt4$8b5Cs%&Ec&Q1!Aa2"R ?a9O#Tq͔ZޑZ=h"lTWVk&mؖ%ln;H\6,cA*OYX˟ J;>a-^tNI!++\֟)"m-^tKm-^tKm-^tKm-^tKm-^tKm-^tKm-^tKm-^tKm-^tKm-^tKm-^tKm-^tKm-^tKm-^tKm-^tKm-^t| *4qFvHϘFLZ=Sj&Sw&ךڹ-z/Pk/E wGbQѠz6Nxgv׬vKJD2̳=%GmYmqz/Pk/E wDlV͌O^mً mő\Ktm4t8V~)lG l5mKDz,yʴ3eJJT%n_$8'C8f:uYY r+38[eApG l5'TLo:d#']9s*S䥧ҷٚs%*R ^o8[eAVHq В'5[ŗXb-ns$Z󭠔kI/]\zs^"- ٌmHpG l5mZQMy2SFw6*H5,"8#^pz]."8#^pz]."8#^pz]."8#^pz]."8#^pz]."8#^pz]."8#^pz]."8#^pz]."8#^pz]."8#^pz]."8#^pz]."8#^pz]."8#^pz]."8#^pz]."8#^V}Ӈ(HM̈ў1 QnJnBm.H%D/}|pÛ?A-cM>lq^ؕȬ%Ierdg%_'_ 'pvYqtPK1 3K^-Pictures/10000000000002A7000001E7AE2A6DFD.jpgJFIFHHxwdumpC    $.' ",#(7),01444'9=82<.342C  2!!22222222222222222222222222222222222222222222222222"^ !1V"AQSU26RTau#37qrt$5Bb4s8%CdEc"!Q1A2 ?hƎ[vkΚWF*a=Pw[o9.&t~F{ߒ`mgXmk=gk39M.7cxqź!q|GE/eSg ~I'ڞ]* HEAU9#]Un*9#]T䎋t^uU 䎋t^uS:-{/EV肣:-{/ENHEAU[ HEAU9#]Un*9#]T䎋t^uU 䎋t^uS:-{/EV肣:-{/ENHEAU[ HEAU9#]Un*9#]T䎋t^uU 䎋t^uS:-{/EV肣:-{/ENHEAU[ HEAU9#]Un*9#]T䎋t^uU 䎋t^uS:-{/EV肣:-{/ENHEAU|uU[G$o''J{iq=͌Fln\̂ג:-{/ENHEAUsCIgfjm#_.qo~f>Tkj$fwuI--vxk] kvwk 轗"ꯇh2FF6GQ]| ]$Y*cH#{48ڻ/f]A-7q,9#]T䎋t^uTk[4rLC'&1wu 䎋t^uS:-{/EV肣:-{/ENHEAU[ HEAU9#]Un*9#]T䎋t^uU 䎋t^uS:-{/EV肣:-{/ENHEAU[ HEAU9#]Un*9#]T䎋t^uU 䎋t^uS:-{/EV肣:-{/ENHEAU[ HEAU9#]Un*9#]T䎋t^uU 䎋t^uS:-{/EV肣:-{/ENHEAU[ HEAU}EZ5;5ۣ&7$m2UGi{lQ=eg!4s5ƺ9m&,zں_9kVڋ̵.mƒa$9WoKQˢz9mZ5;_oc3F f=a7cA Kp5A{c+Z5u1q7dA2&w- vf>LtzmKI2 E`, cVrDD#9#Eڪ-mRFÎ650{Ck齢ߢoTP jjWTnk tl v`] )?{A"뎮XC3JB:`|m-Ųk4nٷUw}v},%R[}+jaf5@7'$qiby?ْ=1MGog@+Qhj6M01ڣs2jZA4rtn,ppiÚqGhƣ޷ƳҹE-\lj=|k=)e,6824wIPﶊ=5ֆhXGP5qA $풭~ƣ޷ƳқzJqƣ޷ƳҀrKlj=|k=)Q,qƣ޷Ƴҫ+4o}-mMPk5[ddJ[+*a~u%@;n;[Gvƣ޷ƳқzJuQ sF!plk,$hBZ55{zW(655{zW*?T8<YvR2C09V~ƣ޷ƳқzJwԺM: IsF~">55{zW(655{zW(8{zScQ[Y\XcQ[YMGogrcGog655%)$3DvF6W4&A L|mWr%ڣ]tt̓ߴϕG 1FHv"X8$EGM0~|"C Xics=0Gʔcj'T3Rh!JGL!'&<ǪOʒcvh9߸#S9~LdwNIDָbu3fev'%тve˵v[[fRӶWs8|GogrcGog655%655{zW(8{zScQ[Y\XcQ[YMGogrcGog655%655{zW(8{zScQ[Y\XcQ[YMGogrcGog655%655{zW(8{zScQ[Y\XcQ[YMGogrcGog655%655{zW(8{zQհa +K Zq/Fu|[jξ,5kĸtuo{s$c php$\wۉ,y}S+/U4õdnT8sHݻv"O>-(CϽ_bӱ!Wk(xLX$dd|YYgU/1}ݭkFcW-06B裉䖜89Ahޤo Mm4eu=)a+pY<˭u˲lE[N!뱻iHf-ko'lERɫ.w!։qS7k&~g1)e};s$.s$'T3=jZ]aھ=VDIʻQV~AGOKupFpkKzNżd1kWkuiwl/Pj_SEuuIt6UI$d1(8,oLsU6X䖘=dR8oipEmHh-m lRux[jv8n#˹R+7*!2UEkuT0VV 䌷psoZ -}emChjvy܎ڝq[(p*L w=2 EUph澞^7uc.,2VvX%ݓr{CMUI7 gTDMm3Iǵͅť#=VͶk*j*[ol-՞Q Ce{Mj׾8ֹ9i {EH%dSUKU#skCf7ssvgzDPgtEVΫse΢YCD*,E{Cjm*bXڎ;IQL]fn\Fetrn0*xL݋KFy̨T߮ZoTRڟls@\#eŘ-Ʀu Φ=:E74;.wh< 4~+%cYLbkywv vR%PʆKGO#j\8|M"Rwd@h5_qPiaୁeڭՔ8n޵ ڝql5Úݠ{leNPxvsᮡe ː%Wwƽ9g9)) ǃ^eQjH^Iv9$<*mV1[kL]#\;-Z5F췑1*.W($]%k)3׶9Avp@pZXhjÒk{`h sMp(ai*EdC,akGk!Ć6PTY5PMu(kάqٱ5T=:ըVM8QA$-a۱ """ """ """ """ """ """ """ """ """ """ """ """ """ """ ""+O>-(Iۿ%ѸnƖ[)K5vmy'vwo'MO v},%R̪4^e?'?JhW肃4^e?'?Jh^{:A_K{56iO* cK`ns͒7az&i=J' f<+~yPW))uh摺! mS[ F-s3R'MO zOҚ/2*+~yPW))_ +~yPW))_MҦI[ LZIe x.ڧo L~Gg?JhN+~yPsOjZ7L$"99+{eʞBʺbGdap8-# Yj+~yPW))_'MO zOҚ/2 'MO zOҚ/2 'MO zOҚ/2 'MO zOҚ/2ԕ0BYg79/G<`PTq^̧'MO ߇..Tq^̧'MO ߇..Tq^̧'MO ߇..Tq^̧'MO ߇..Tq^̧'MO ߇..Tq^̧'MO ߇..Tq^̧'MO ߇..Tq^̧'MO ߇..Tq^̧'MO ߇..Tq^̧'MO hsH-# ʢ4^e?'?JhW肃4^e?'?JhW肃4^e?'?JhW肃4^e?'?JhW肃4^e?'?JhW肃4^e?'?JhW肃4^e?'?JhW肃4^e?'?JhW肃4^e?'?JhW肃4^e?'?JhW肃4^e?'?JhW肃4^e?'?JhW肃4^e?'?JhW肃4^e?'?JhW肃4^e?'?JhWʴ>]VL 55߫EΒlEz},%RϽ_bYzȭEn *B骦jZ5ǵ 0BtMx⩲L2u@v߂n8Io &rm` mQVҎ&)k߻rB~Q`Ӎ\9>%E 咂KxR ?M `b|gvF#c~u$ZnTMESRcuY:οݨ :,r Isw89Mf&16]MSd]I$4-;~<NfUeY!p/MLw9I$I6T3[DU[,6j"[ilq2ذ?ύ/a_;6kn7$ż[!Ji"a-1q#qjƋAďTjd6`.ٴ2=eGjdØpyZhk@vgf&\"_6wIw˅HƆoO%S&N~F? Tsv+j"mc=١Ϋč iTi}1Wי3FY~ WdsS]r-J6Ia cjѻ UwzxޢG+!ՌY`vN `X QZnΩKS\gJ k KpF{u,4p5 C!tsF$Y~Zhq!+o>tK}%t ԽBZd3F~2܄XURDuV?&RzM襏VBhE,~nXUhK!4??7CZA&RzM襏Vg rC)ct=UDBhE,~nXUhK!4??7CZA&RzM襏Vg At@SF 8"sVuTRTKR'nȔM$ٿh= XJ+]{Tb*iGh(j-Njr $RH\b=]W;ie<h]褒0ÜsGw8s+kͮkUHew<ɻ)y>7eThL6ɭM jj&3/-u;kZ@h,`AmEki)~̘Ko 5꒻`j` Nw?C cwbwQZrQ>Pa8Q5Z;p];KգW74VIIa|m:4a;BڋL,wQ4N)eVepn].Mpe<>H$OkAp4 ٣5V||;ݿx;*~۫*kUs)GaX%sih<ہwAv (ͭFX`kg˵WMl'hbٻW\5uꂂCIK, Zguvch0_md-.n_υ@hve 57膏۶NDW#0q Ae7IW$Թ nv+tKn %s#. k K {eYhkENQ.c$Y&|~3 sZHiyql29:XፒL\ր^@h؊DMZHΝv2;:8;'mʚ=KugBGqp*b Y |֪a{㎝kӖATO mI3;XclX4KA |Av" ȕ__NK)-'sN'sKE*>'sN'sKl8l,^"w?mw?mxSR*>'sN'sKl8l,^"w?mw?mxeCʣP< AҖ:s}=I󌸒pVTDDD@DDD@DDD@DDD@DDD@DDD@DDD@DDW}[7"Q4OfJ-k`3>QpV|l#m:ĽCn>WKT&}SUhi.{2zq_Z}m}jmY#M;;(2}m}jq+Qgxw6+ކ|֭F՝?ޔڳG҃/ކ|֧zq_ZVwwzSjJ zq_Z}m}jыuuLISTp@ eLh4sojJ5SZ >9Ldҵ=k\菸 6ꚸҫu}l։̦jT&tM{vf5`vdGR^.iM\uPn5q:b0{X_qmJzT%<&. VV8kA-UJbsXqsK!0ߍqfљa2"~)N$ZWXKꌀI7.w:ᙔrՇ:'JF2 7dAn&ѱ+=kow,&٣:ʺyjM]ga676=s]:uU*Y⩕jˋ\;߻#oA.IU; 55"{]KcVrꪛauT2O4aٵ4|1ZDPabYm +E|DڀK #7.iFl$Ղ`I7Ȉ#y9ܾɮǔqB@w'y߆|xRjt.4MY W1%sE)me8Qmv'iM?o<'\/kj;M1goTl^ˇwmjj:K]`D-]# Ljss031eZHʷ8Cs0LuC;c9 [j->I dӬXI[ܜɚ(4J}qu6|+mom7m{+kU4kKZ=P05ÎqjkDu\(8@'rY]N[|3OOZ 0 RYCd{Zc:~wBћJ(("S+ukIs; sfݮ4PU>)\I/-F7VYhj)'se{#H\V7Ǹnp<QN"l]G$U@Brњ .9)88ogwdwgrAnP%Ҥ2J,nlL8@s9]"u'lʴk9Q!{.?=~ן}|h&}SWUM^Jj9$ak #WnϓiuO+2rDzEn9="xFtr1̑k=HWE+p`!K_H<'/GV}27X80k8QdH#+w Dž]S8P k;aNĖ6[xN_H<,l-{Vۘ. 8t]3њoQQVP܀꩜{j#+w cuDŽ¢7\[xN_H<,*%/GV#+w cuDŽ¢XrDzEn9="x𰨖7\[xN_H<,*%/GV#+w cuDŽ¢XrDzEn9="x𰨖7\[xN_H<,*%/GV#+w cuDŽ¢XrDzEn9="x𰨖7\[xN_H<,*%/GV#+w cuDŽ¢XrDzEn9="x𰨖7\[xN_H<,*%/GV#+w cuDŽ¢XrDzEn9="x𰨖7\[xN_H<,*%/GV#+w cuDŽ¢XrDzEn9="x𰨖7\[xN_H<,*%/GV#+w cuDŽ¢XrDzEn9="x𰨖7\[xN_H<,*%/GV#+w cuDŽ¢XrDzEn9="x𰨖7\[xN_H<,*%/GV#+w cuDžWW[ޖV%\C],ʷtTض]^A{.?mz#G>MadYQ:fwX\GkG|MMXkCwZ$F;Xa6&E-+5>ؗg#w2QP mQsq"'20wlryn;R fTKdx:] MmDrs#+~3Z QJze9ӝmPgp ,+ULŲemN6IlLϼUk*\iC++7PήpܤPAOWH`uB420=pmq,E}e[Zښ I J}ξBҸڑd㹔MS3ꦙ[&.仴w\p?UueG 3m#'8]K# )ac[h'Hdt={sI$QU ER ]^A{.?tew>4sojaj ie\wʛD}hɴTգ\͟Ejn nf&{@DnCNrG|u&]nVCTUM]S=YcF-v1ꏌeg-ȭ͂y覸:eDKiݒ2a}wNCNPIO%d#he \ſ]An݂G#)_qRuNc"qy1%@}wNCNij7 e5=-#ꥂ*TZ:XGc; v-KGW|u 5udSՙ k Ѵh kqJ>;'!wO'YF Ms|STx+v0AO8.d%l}\oq˜qͽ*CNrG|ugy:}wZJ>;'!wO'Yh*;陸};'!wO'Yh*;陸};'!wO'Yh*;陸};'!wO'Yh*;陸};'!wO'Yh*;陸};'!wO'Yh*;陸};'!wO'Yh*;陸};'!wO'Yh*;陸};'!wO'Yh*;陸}4Oe߽5_*.G|MMZj[nV䔲(';Md֞f3>4sojq뫨M9{Z'C]ӌ;n5Bim1.* i(fuC2{"訠]=So{\׀%n41:nsdQ pwb3cB樚k}uQBY&56lfs7멚 C%@q\Koԫ 5uرs'v5(鍲 mWȀY9w+ ,.?v?8N]J~q@?˽]0zһa "CWc=dއz4Y9w+ ,.?v?8N]J~q@?˽]0zһa "CWc=dއz4Y9w+ ,.?v?8N]J~q@?˽]0zһa "CWc=dއz4Y9w+ ,.?v?8N]J~q@?˽]0zһa "CWc=dއz4Y9w+ ,.?v?8N]J~q@?˽]0zһa "CWc=dއz4Y9w+ ,.?v?8W5w:8(*૥:A q~dF)*5O.?=~ן}|hD}hɴTյ,Vϓi[ u3 4؞5n0￲} OQ%n0￲} OQ—'R>QF ^'МaKdIE){Bq/}A%n0￲} OQ—'R>QF ^'МaKdIE){Bq/}A%n0￲} OQ—'R>QF ^'МaKdIE){Bq/}A%n0￲} OQ—'R>QF ^'МaKdIX-1 5,נel8ʗ q x8[N0￲} ~9ݬscTy$/xH_]P%:10 oZaԔ]P" @p }_,c#je*rL]ide =f1Ζy|~O[釜UdOmv9GʐݪӚɃ[ߴ;!?ҠKl[Tpis>Գ)5hӸ3?1t"8I[F` 28뻟8 ^xT:2lM$WN3m#Jc&8 tc7da7.NА>ہ)5gT# n۹=~ŭIi;tibdK\ Î*Nj Vfb;݇jty;ػ\=G%]\8qS3rN|M ]*c&yƶ7pϋ%6.H>վ?l%ÎjS>j6'㊼s`sh~a= bϏb=Uv\x?3{[C\q^SUcqM a ?N9L_~s;nM%]+9T߇hWm=W;`}T?s{|zؿ;GQ9mn'/,X9xB=\5\)ㆺfʉ5 +FWՉ"dn&Tl{w+An~5}( p̺1cLK+5 㚿M UYm5Z펖}0g}ѻp%8泱"r9vsv]ÞbbKOa߬{vM'dI???Sl+x95A!7?{<᫓w.#Lfm5t?k=p+27 Ǵa# ێ;<ݮkwSl+5l:EklI#=I^i ii~u5@hk{m$Gk;i`c}fnZx'љ!tMMZef7qeލ/ꂖSTh.3cl|G<~wBsHR$u|5a7G+K`Fgp+,T1,V0k =!peK弟(^lXzb(aKd){BJpN0￲} i^I``TuD]^AD}hɴTvT3T#G>M?d5rxMh;ܞ0zqSRZUTMqo 6 kK3i|փ5rx]Hփ5rx]J-ξ+U;^i |k.-kK#~vi@D{P5:wCUޅAr }r78} 3D)mqH6llPW9.k<'WCUބԟoвjf6ThF֚HYpWd (k)dA3~i8;ԵR|!BpO7]T*, jc.4tϋj279XK)ԟoМ: Wz9.V@PDW['8 ^I é>wP,eo ;Q 'pi*#z" """ """ """ ,qI7MPɋmvc\i*wɴ_[TL[QڦcGh6P8GnH?l|a;QG=-=#Kexαa"3ͫIpkF]BHftלKI5-8&}q # 4 t͹z:+jqaN!<Ӭwp3)#byw~uȟV<:GpkF]B䖒g<́M߃+ё>_N,/n\7Bo uqY'c|}rr/ijzʸ ~e'խŇt -61Vz02:+3?\^?2qxHSOW8䎒oh)#ѻtiW4̵=D\^?28䎑oԮy#ytiW4̵=D\^?2_:G8-IW]B䎑Ѽ.ZUM#-OQ9W4̵=E>oN,oN,/e\^?2.}J+#` /uu5C 08!>_N, kXKbw눃-8{]Ҙ<㨒 Kis~76#h~O_+2y[5#j'cj#-Bp`kr55uT1ڲ{s4V$n.89`2)bFQ%TCTg FW k0 ee z[0gylqT7j6"wr~,- si*dki[A{.% {Xi4VOOq0zxdqÁc `-+c'<2/0I][Q`|bF=(] S(&\)-zc14ɸ{w 9ݨk5V\sI`\ytIj{r\K7W7Ҫhht GGi^"" n&}mRUVw7hX}$zk2#+\5!sciVjKYMsikD8;Gٝ`~aTu54<2h{_K4.TgIUSpK {ntak_hcNvkw2JCKQSM8\S290ssٱXKNRRN$Z]g7v5]9OqSi-]U4t5Nm i3AsNIAw8kkA% R0KlW69)GsIdPإ.65waf٫TU2嬐S 8Ÿs](zW#wۢl0 uF[kwz].Zm5CI {c3Bijn4 h2DǍl$Fgf6[]q\S@ j5a\<g I9Y#;}\AUC[WM_fJJwLJ&v-7"Vyڨ A%{0d"XcsVelYhڬmf7#k*+-zCS=zVZ]WG5Qc$aǝ.t{#8!Ȱ57A{l6jw5~s wcYݬ_--},\qGcI Pk98Ʃ0QyUn^ꅊpqyS^ND]v^#3+C.uK}cn55GF#o.<n1Z-u7 0 0{cݓKc#89 p*mEA,Y͎8a|I`Z譢<&Ͽk? Eu*r]:gP}GSݠi$n#fsr;`n۴.(y0ꚧ74dὯNUƂo嵿Ji_?A_rT+?O TDP?wMyƉE]菸 6ꚮFT#G>M?du""""" 먧Jzc Zhs\;.GzB(wkV{UMxf$\9 &b?z3РV]C咾m",OS1C= OEڈH9&P[1rs7vC3И!D GzBb?zQ). QFD9{;''W%3oRvGU;=%mN͚Usց$UҊ6QWOUG-if5c`ThvGMEZ 5E%U1n֞7]-wb4{Dvݻn~-Ԋݻn~-Ԉ;vGMR ۿo6G]Hn~-ۿou"ݻn~-Ԉ;vGT/ӫ!ݔ\UVm+D@DDD@TnY.DtTVRSCMNE anNN7:D@DDD@DDة MM:ނ>Ѡej.ߣ`oYǥ8/ǏJՔ;ߣ`oYǥ8/ǏJYM G8L? 9xE~p;s~p;s~_<"C+k OxPFym=@ׇ:IZ23F~2G<`NG<`TЯ<BroB `X} o嵿JiQłs228-HGNƵ$JUur?Bm܏ŷкA۷r?B2T~S˵ 9pp{`ҺYeA wMyƉE]菸 6ꚮFT#G>M?du""""" /!ǴF_h %:g[襙6D3.U)hk}at(ukh%T r l89o2TyQSh M`dO IhsF>ԁ̭-\zn) p9Ǚ":kx- Ieck@]5rapvb]i \;IZ>]&m M ;άsAbx#Ju';XG8'$vF7adIYeU} [*5;\=o[AG|dWk5|2 3YL]5h㛟 (MMKhL1GSEBH;U87"n&}mRUVw7hXQa}(iqś.5UhC \av)%֒||@VjDFKd{朇A_1C,ѾH\+ZK 8 W{u&ѶZ(vL-l񯑭ٗ7Tn멸_ul$j82w{RtX{\SJ-mxT9n:K._շGWƚ#'`T0콶e4SA$ȡ kI$;k QWe%=+b׵C[Ľkg;wswF'.O_!3k^a9A 0!iGm| MKљ#G> g+|\1VpgPۙWPf(]G;TsoPi+WT:5IX1qAͼ u*!5.Fgk8k@q$[J᭧KLM%I;77eٳWSsO\$Ѻfc̓ƃ`=mjYU&ɺ hke8pO:""" (?HQ?z+U=QQViL$'914InI'~ULsoGc^api-sN =b}@aw86.;wcyUV+j;eTj#(a:|RZwg~7|[Ok!4??7CX=mJZr-4QsNcU0s =nȨw[=cM{mBxv'jFuH֗8#~_a84U%5;^ӪFw4 vԨ7B&hѽH+C 15ww+')5򊖘H--yi;:,kvJUWb~WPZhN> fD7pq=#y(4XU9 LM֝7S{YJ!q:.yW'quu%d3UWUIRabv]L 1# rC)ct=T&Rz@3XU9 " !4??7CNBhE,~n?M襏SK C)ct=T&Rz@3XU9 " !4??7CNBhE,~n6aAL⍌ k`iT_?]OAt?TB DEg/4OhrD-YPB]^A{.?mz#G>M?.ϓigHW\Aέі@vߓ =SVsKU z%nfUesvh)pw9V8eߜcwud4 E_Gm],jj4G,NvZdc#zh{U-4WZWLcnق!#j`F@ȣ)pw9V8eߜcwuy֫YEU37U UA%vɮ4HXGoWظj鮚8\j'Z}+Kc ^Ih7AY4=Ԏ$|e߂ c$ Ajym)Ԝ8;[hc݅7-WTW,*I/ Qn2u\Z=y!-W<07a߸~;-6PUi-]= HGni!;fF7k :M"HtVƵI!!emKCQjng|:ųn8 DPU/Vm+tlu<.c 1`/kO8r(!ZmUVT[fsI \~ciچ ;])6;M4&(-tQDLh:f?HJc$x0G+dhYۏtWr UeWSOWlQ^݆092GYiǚhdev[qSHt̥uƞ+M.DUUtjn5P=VG}3gU#p8b!ZmFF˅@pYEI㥅160͹?dso+|C TG2(chc#cCZƁD@DDD@Q?&M 8״ΊZoc]CMYR8GS dhp8p#*֫Fl5UYmAILֽ.p$I4&;m5 $B*jh 1uXFKlj.mSEM=X!Ocǒ\\F$Hɫq[ { 36sHpqv3@9{J? o o'[hɘ*g6i[<$v;ƨi?eQGۻv w?mw?mX :Y3@H$#Y`S(mVV)r" SR*>'sN'sKl8l,^"w?mw?mxSR*>'sN'sKl8l,^"w?mw?m?3Bҿs)P9 G0>!Q~Rr!V,F[?_h+*^˿zk>4Oe߽5_-D}hɴTu?3Bp9m75]OA#Q3t7G}Oj6jq{X܌qx::EF,w+~ouCenvkFq$v[Һ:j[SC<6YFYnrGmHGQpmU eFPg19$ddg;["*4)WP02ichFi:;[:[H*|FDښ_v9{kUFъFLƧݎuqAzUFъFLƧݎuq_'KiYV#]݈[SWkn:m(WK66N4P0 ֒Fnˆ~$\ѶvN5#v\3 ^nY-kd1:s4 b;9'AW5d.{}M #teclϝ-VPVVThcik1,; DP1WD[ Lsc(-"Z&ͩK>F$C8we*!բ[ke|l=H$mxAvrM}daG{I?CJCm mHR.Ϋσ2w/U/Sk Sҿ^6 Pޕ]Ϸl_(jݥV :Ң,Gm²p²x #Hq:X$ҙjʎ8.DӰB9SRmE:cC 4`;luZLvwnF.H3EaNq0?ap2+v/p,N2 @p{!Rkk)m4-\šCnd#W'pރhWpS29*Qx `i ~O04v|MK[Y!.#R>#w84HtYEK 5udnٝCq{8Vx piꍣgƣuZwc͂btBGELWVV2> ` FJT.kNҿs)P9 @DEg/4OhrD-YPB]^A{.?mz#G>M?.ϓigH<荟EҲwZ0l)wW63 d|m,Ѽm#]#Af$'G IAlת$k7 ư##vh V Nz+h6I#{e{1čPѭgy*%rA-ҒHhl,mgk1ve1ё:~MQZ}W0g)]dn7U5v˨8Ycv I^ Tb,sH'ekÎqiߝ2F<,\mc!d%kqn܂5qg$" #v Vkq=SgjjcSv5q|9Zk’IMNʼnֆW`KuyDW\*tJ[,`Ʈl0s~VsH,Ezgsl`i\9]k`̚(.VV5 穎vֱ f7ySeE5WJm=Y2X]07.-99bWEkK|c3m{H:uN0@V(" """ (?H]U1f=h6HA `nh[e4zx, Ai/c~5et8*S5WLCi\ o3pю~}Nj.mBs0Ha`nq8VgojF8[h&}ۍ#YnfGb}}Mc5ϑJgqO_go8EXлu =UC=&KݤӀH`?}I/Œ*37go,Y"?}I/Œ*37go,Y"?}I/Œ*37go,Y"?}I/Œ]:g/mTH洊 ђNZגs|v \SO%F>79i /鯃_7M:z4ܻ-jU;JA+EV tW^^ ޕJUj_h+*ѬF[?򲠅D]^A.G|MMWS~#?*]&}SUt 2(In2vUе{ -kNۜhQi5-UM 'lT.&VCZ fᣗ[Sop5lS )"MS ֌P-P\(Mfڟzc^GNHײ2 fU߻r+җ\>7DUŅ`c7nvNH݌}qњ(B]D#Nݛ%! c2ef2HV4T,AR{˟p.דa0tc2AEѐt\[8?HLcd;oI~5rG[-2ᧂ,1咵] q(=^ Tߥqy"tN29< *\'B릑@-dCv1 ξ+U;^i |k.-kK#~Ҋ +ET-t-Ǹ#fq:pغ"Jlk,&GJa sgϕM5Kq%=a-Sqs=B\ѶvN5#v\3,7Mqŕ|㍯vƮDZ9vjapYs>s]OZ杠,L#8ݔ\908ʦYGCZ*Ddž03F=2UNmE58pqPFg ;7H/uOqHbx#Ju';XG8'$vF7ac*zGU%^;#1 c\ݗ`ִrlDPU[Mڥjn&}mR-Q-8:Rj)Smm\Ԛ(^vC9.`dPRԮ S#ObsnܫmƮGiZ4;VB@NsBUZnI# ;7J2X5_' Et’lmk.tRF9l;j2*=$ֶWlb{Y.yoTgۯI4%Sc9o $Up-%@* \ X}:]>uѸ%DuEOv\킶gïIS,il2K]vF }7*+]׊pk;G.lMqݭ߂%>zXʶV*HxN\c88p"(""" """ """ ""ҿs)P9 UZW~*!V"( 9QFu'lʂޚwMyƋhp9m75]OAtG|MMWS~#?,DEDDUT5qQEKS[[# 7X0k8kFwo#=௚{3WQIIWOQU50 A`p8'h0FA߽"譬AS]TJzh4:h$ pAދl9k K} ($SO-D WNHsI `.i.3UD>[N.g \ &Oqgn3~7*&^i~r}%,/7F F~Np7DTZj-TD}Ƥ*\w0Nﵴy A6ɬ1lT:7MKNȞ-.q fz>*.5ݷo+=k}]Y][]c $ݔlϖ+TA緀iʈ2kuv.@Ȼ'6"a XY7ZKvmdXgm˷3#%';c.3xHh-u5ᄙ*x|/620u 1. Fʺ[9.4i7CG>gW^h mRWT4;6ZF2EI꺨)n1IQld{uucq#L6\UG\eF55q}ݥnYK=dpjdaq|L8Ρi; hP^2YUZ&A؇4J`8̭?+Z_APւ\bp A_8n4Xb\>(F dvjwDmڳSQK#{5Z@UkrэPIk%0ʚ[|L+Z૎s[DQl}g8l}g.Qh(n7I6^uGo Duz)Z-nj]G+pnq8gm(!JڪY䑠sƹU\U/EQ$bYh~`VU=D@DDD@DDD@DDD@UZW~*!VJA*DEg/4OhrD-YPB]^A{.?mz#G>M?.ϓigH7Vl[=ZZ‾L_8,h.Äk㢫sA}uNw-2*<ʶv.1k|{FE4yp$H7䀡Cd׳^mTVnm3VTZK;l#gvZ-mz {9iη&*ieK#D3UΧnjx|@4=êH$ DDD@DDD@DD4d G() *Jx):A4>9#9iFUM#-OQu}鶛4Oe߽5_-D}hɴTu?3Bp9m75]OA#Qe;hGSW-]@*}MbG?h =fmN'eC]%Z֜H-KYZ*nӖ5\P̈́l V@;$dDP8nM, Zg to,q'vZq,fWIO]9(Y#ڨ̱4YoZY%ن}CKEt[[ C^DuA,ppckayzR** #f}Uq!68ΣϝSR--ҝIÃ= Xʫ^UIo|N8C+7e5 s 6H-P3^MmG 5ڏ~]a_=MIԎOr-vpeQiM![m݄Xr@9sڵ>BijdA+qؾ#͂?c$.@DDD@DDD@DDD@UVw7hZwɴ_[T Uƀ6!>۵MEoҗp:7j+U'#t[V!ApWts\i$,0Z׷wo#C<דbY 6lvnz½ b8 CC4 v>ۛUCkfJۖ{ρ<aZ k`̵:51ZtR5O@%N/?t>\" zdMmuSdjp㺡݌;DwK)-\3MQVL2S0ۆ8B{yRDs}K.#M[b9l 6l{_v}݌USO Xk$kH$4vux""" ""_SiTؠ[_UTz/jJz.*nc~2o-==@,t*AKQ Q\hX9-wu*cg\QƣGmnfes;<ݽ Gmiukheʋ}Tԗha508QT1݀O; ߣ`&L? `Aڋߣ`&L? `Aڋߣ`&L? `Aڋߣ`&L? `Aڠ}ΣEI0wX(U)d201vmIs}GSݠi$n#fsr;`n۴.(y0ꚧ74dὯM\0'\0,A}˷S?TB/v[[ԪvJUj_h+*ѬF[?򲠅D]^A.G|MMWS~#?*]&}SUt 2:ER[*b dׇjHi D@DDEQE6ffM,m9:['{ tTMҪ7W85_5FV7bg5=sʲܩ6+julnִ$? hwr@i)*vk9c]5A_% -e¦&LPD6' tTULe+Eʢ9dd lq2m` U j#{A&u# ZQH؞X {nHpytQh6 Nc].RWpy9\ߙs5 a3K nW\H[TŲ:0IDЇTpvP\H;+Xm(fJ'1RHi, ݪs>pV2;]x2Pd%! Ar81WnuY(ϓn-HJS"iǾ=CKHyaU݃*.QR'" tnc)ΩnH8w3G8Jm'F}*zv:F:)X6̐[ .;Jm/];0jJm]=HnCZi*}iMgMKGN_#@7~Aݕ`iWM$s!|?٥cC[+h8g~PB  7ߤb&~'}8}M3?X I.G<`NG<`SxLO)g~A ..?~pPB  7ߤb&~'}8}M3?X I.G<`NG<`SxLO)g~A ..?~pPB ëhTӖ w I}2Z Ndx/ǏJq^g35_qL|#ǥQzPCx"=)xE7,ҜoYǥLW>zS5_qA fH'^gx"=**m 5oNUAjTxPFym=@ׇ:IZ23F~29Vσi}Rr3VܛЯ<BroB `X} Ǖl6y'*m 5oAFMNfu"nNN7:ҿs)P9 hJk5)u4͒9"tr-xQ?TB-Q@YeZ5?gVT~ן}|h˿zk>4[EވϓigK>4soj!fGR"(" ,N_AֽfZ韪햶uw?"/FiuՒѺi$`l55-3bd2[mf6y챪gH-o'鈭ކQT0IR_<_%!Y. OhdOSMGR^TӺ2 l[Ai8ܷm %[],mLPA}#5"7{cuXG:k-2 9dKC_5220kId;U1ޤ &fc-&6v֪x4m6va89x;^_ݑ'zТ %ϤRp*ʺ[pjxh'n4fx᥸]+xt$qTFG5X?]4kP[J>X?]4kAnrCk}\k\5s@@K e.mgHf ~ h8߀N$ ;H+%}$SMP$]SVh#`RIqkS)3M+4&MWjQel"I.k^C$sͻ;2Vyqhi2Up*$9s#Vv2v@!eE%-SAM0֍{yiH `ƤvDROg՛v"{4Oy`<nBhU.*+$i99Ì-*b$~:W~*!Yob$~:W~*!V"( 9QFu'lʂޚwMyƋhp9m75]OAtG|MMWS~#?,DEDD uKM}U91Na{>F9-c?>'UYSBQC.#eYTʞ%R;V662NhIP96ɮTW͖ٙ8lSQ΍ 88'42WCq|I#f kRWYcG/6Yj)elmlfHf5Y>$nQgI"e |Wf^[Omk"m.7]@{eKdkb]F ݖ|JFںIlTS>dׇjHiJy͞"s4=Ӽ= T4t~+-luM;Q Ke7Ih\r4wBFB*̼,I9jHC_ٗ6VZțK t=gG(=Q.GM ڧSɟ{#ve1k5r7T襢 \mf:88IRd15@=)RˤLd6x$l!8h h#~2Ajym)Ԝ8;[hc֖݅2OoO[U88@N{# 4bx=VT3Y݈g:c\gvrW {Uj8C$HwJ:ˣn l$Tk1v:{Xޮ,URR:j:$}u3{ ];jJbݭ=@nCZi. SCe(f"Ys[πIiDm+xR. |vpl$oQ\鬷kg.R *SP#-- ~Hþ$4A VEMlr{eG 9VӶF7]9[=-s\;H'ڭ! dW5,S6P4{~ i1n%TCmT S ߒ;Nu# ZQH؞X {nHpytQh6 Nc].RWpy9M%ϤRp*ʺ[pjxh'n4fx᥸]+xt$qTFG5Zdi0u}&JDo.|cvop.9nG M>m7Ҷ7!#C\Ӵ#w{x _M4Z&˳/ uC{Hx ʃUGtޫm:jj&p%3c_##-.oc&v>HM21ї2.sQŭVAv9]J5º略\%~"%hNH@IYZiCO{Z]C#2g\{I-=}m}jq+zU7ŒF"^ZLaه9dIU]@ji(ٴetv=[}ܪۏCn>WKP)nPi42 j+m4aXڝfK27n(t.4ҺzWꥑfH1Ϳ]|}m}jq+KM ,đyisH 7TXtb$Ԃ djecZ7vq;zq_Z}m}j ѻcids*fG#sJup{0^@nV9ކ|֧zq_Zh|L~h%R5tFS_A%n8dpf&9Ý,YmbUGV 9l EgndThQ`yI8<9I8<dH&}5\y0T;-;1K-z_\jQR>#5 txa%>9g)=z")=z"I1R zuMS34dὬrI1/=pVfWPBȷ@ 80@XpHʥ'/@^'/@^Uykѹx$UۥM-9Fq~r7g;-oOm\ dpEtҜEt҃|Od^H)Od^H(?IW1jt?TB=kE< 5[aڲL1f;&9__? Z"u'lʴk9Q!{.?=~ן}|h&}SUt D}hɴTu?3B̎DQDD@DDD@DDiI,{6I4k;" """ """ ""# h{p?DD@DDD@DDOWP;T8H_+ 2[.Z3]5T]i 2/`MP Uyszz޼YSwt&Ш9/Aמ=jr^w=Vz_ wt*K|.Z]U:wBl  y֧%>zgNЛwB^zz޼YSwt&Ш9/Aמ=jr^w=Vz_ wt*K|.Z]U:wBl  y֧%>zgNЛwB^zz޼YSwt&Ш9/Aמ=jr^w=Vz_ wt*K|.Z]U:wBl  y֧%>zgNЛwB^zz޼YSwt&Ш9/Aמ=jr^w=Vz_ wt*K|.Z]U:wBl  y֧%>zgNЛwB^zz޼YSwt&Ш9/Aמ=jr^w=Vz_ wt*K|.Z]U:wBl  y֧%>zgNЛwB^zz޼YSwt*M%sgg5sAP ̕8 {Gmu^w=Vz6iXHLm&Y0I8;88r ,F[?_h+*^˿zk>4Oe߽5_-D}hɴTu?3Bp9m75]OA#QrD-YVg/4O/e߽5_'ޚw>4soj!R菸 6ꚺkvV}O0*i` vpq2̋Y?6t8ۏECn>WKS ]/AECn>WKS ]/AECn>WKS ]/AECn>WKS ]/AECn>WKS ]/AECn>WKS ]/AECn>WKS ]/AECn>WKS ]/AECn>WKS ]/AECn>WKS ]/AECn>WKS ]/AECn>WKS ]/AECn>WKS ]/AECn>WKS ]/AECn>WKS ]/AECn>WKS ]/AECn>WKS ]/AECn>WKS ]/AECn>WKS ]/AECn>WKS ]/AECn>WKS ]/AECn>WKS ]/AECn>WKS ]/AECn>WKS ]/AECn>WKS ]/AECn>WKS ]/AECn>WKS ]/AECn>WKS ]/AECn>WKS ]/AECn>WKS ]/AECn>WKS ]/AECn>WKS ]/AECn>WKS ]/AECn>WKS ]/AECn>WKS ]/AECn>WKS ]/AECn>WKS ]/AECn>WKS ]/AECn>WKS ]/AYeN?6tD{7#M6STK,Qs7wMyƉEtߢ4N [紳R74ehpAHec~?Qrr[*''wßOD]\?tec~?Q''wßOD@.sI]?>˾~}}9DD?tec~?Q''wßOD@.sI]?>˾~}}9DD?tec~?Q''wßOD@.sI]?>˾~}}9DD?tec~?Q''wßOD@.sI]?>˾~}}9DD?tec~?Q''wßOD@.sI]?>˾~}}9DD?tec~?Q''wßOD@.sI]?>˾~}}9DD?tec~?Q''wßOD@.sI]?>˾~}}9DD?tec~?Q''wßOD@.sI]?>˾~}}9DD?tec~?Q''wßOD@.sI]?>˾~}}9DD?tec~?Q''wßOD@.sI]?>˾~}}9DD:Q2e)מw3ow1!I8A$|zzl٠Fˌw4WJFIFHHCreated with The GIMPC    $.' ",#(7),01444'9=82<.342C  2!!22222222222222222222222222222222222222222222222222"[ !1"AQ2STUaq#B34Rru%57Vs$6bcE&Cet…'(Q!1A"2a3R ?%6-~#$%| ne=ܓ'PO&)n=uYV )IX@9 5 (4K%DxT9)Ah7C{w=~S.m4RT/ip -roT*!N:wHj 8H/{ .ɍ bÜ?-=~Crj|[s-c XE[\,M;:Zu- sfmFVqQ9kE丁Q!E$/~pٙ\a6uw\9si%u!؍Pw]97~.Q ,zTsznQoYr5mEBoj=;Tj|=B%V^c P{,ץnssfi-u^v/NO'O/(7Us؎"ʰE='_C7?z-uU/-Tvno8:->n6:UDr5mzJ[Z)ǃ᪫əhu9*7(Uk"ptW?Q9w5</ /='-UM/%5U~a^dJtF/+[tyӓ}UM3:uFw̔:6QE_ ,MU6[1jt2PuZQEm wO?]U6[W&J.S5_W^omMU6[*}cSjDv= M:UM/% >EC}aSeHϊ ZPj2d..t-4UM9h"uCʺF Y+pk9#pvmrԬLncT{]Ojzcu:5:1Nq O)pskZ~X],8ܴ|>jH2JjL7{cc'1MܙWt:NB:.##b5?82l:] hD3B56ؓsuautƌΖQOlf&r[RqO?u>D{]Ojmze]-45TB&慨tMKqXQO@B:k4ڞsOmwC}aUC}cS{6sOS+piLwN6-k{O%c˨bLk- X:۵S< [_ [W+JZ:Fjq* { kHngwY`SRap#]#u JOX[{]Oj8:SS1,25)HE4Ex3 v= +KוX^'/uN_(T"Ә/yZ🏎ުݥpT:~D{UM:S%ѯ0 z 9MU6[ S[B_u?Q,,G><PK1 3-Pictures/10000000000002A5000001E783C4177B.jpgJFIFHHCreated with The GIMPC    $.' ",#(7),01444'9=82<.342C  2!!22222222222222222222222222222222222222222222222222"[ !1"AQ2STUaq#B34Rru%57Vs$6bcE&Cet…'(Q!1A"2a3R ?%6-~#$%| ne=ܓ'PO&)n=uYV )IX@9 5 (4K%DxT9)Ah7C{w=~S.m4RT/ip -roT*!N:wHj 8H/{ .ɍ bÜ?-=~Crj|[s-c XE[\,M;:Zu- sfmFVqQ9kE丁Q!E$/~pٙ\a6uw\9si%u!؍Pw]97~.Q ,zTsznQoYr5mEBoj=;Tj|=B%V^c P{,ץnssfi-u^v/NO'O/(7Us؎"ʰE='_C7?z-uU/-Tvno8:->n6:UDr5mzJ[Z)ǃ᪫əhu9*7(Uk"ptW?Q9w5</ /='-UM/%5U~a^dJtF/+[tyӓ}UM3:uFw̔:6QE_ ,MU6[1jt2PuZQEm wO?]U6[W&J.S5_W^omMU6[*}cSjDv= M:UM/% >EC}aSeHϊ ZPj2d..t-4UM9h"uCʺF Y+pk9#pvmrԬLncT{]Ojzcu:5:1Nq O)pskZ~X],8ܴ|>jH2JjL7{cc'1MܙWt:NB:.##b5?82l:] hD3B56ؓsuautƌΖQOlf&r[RqO?u>D{]Ojmze]-45TB&慨tMKqXQO@B:k4ڞsOmwC}aUC}cS{6sOS+piLwN6-k{O%c˨bLk- X:۵S< [_ [W+JZ:Fjq* { kHngwY`SRap#]#u JOX[{]Oj8:SS1,25)HE4Ex3 v= +KוX^'/uN_(T"Ә/yZ🏎ުݥpT:~D{UM:S%ѯ0 z 9MU6[ S[B_u?Q,,G><(ә'}Ūv|Mb:SvtB/+]@9:A>?yjݥT6E۪5[Cl7(Uk"wwx<0t|x)v"*jt8*#5:9>&~5bYaU6[ ,4 /x*QTjQlFEѣ+roKSpSeJjZhnPm[9>0w9`9hH'өq~5;vPPEX@k|ʳ}nXYgZᶰnφ{⚪siF* ,a<*KsM}לj2f?S6OH"^An=`5#tcx&ڔSIЗTi9ygGx"s A.խH.A'unQ  m : ۾9@y>~M ݤpNhwB}}xa`ByS]Smp<"M4umOo?D1 3ϋZwh scАh< MTa;OM^GLQp w9ߓjN\UTj}*eltV8R67!'.5qqjSq&|?˗.vPO7d9eoIj6atOTER+LXS@'[s}ffTtdqҼ\!/:cÕAnLȪC{A<1S69\ #_>M~UVccJ.G1mK\HيZv׷cVɒWɚ9B95#b+A+z w *'loL]xN+?a'(AK$osS ^eߩ[/tީ>3`}DFNc|ZKTnJ Z:˿ip!L66auۮz-د[t.ž܋U F EфԀ5p=I|G٦N864X7ԛ@,zůe5J{CE\՝F7ݓS~] F Eޤx7Nuߍߪ{CE\T'w&?CSo>4[ nMy}mOwh Q5<~ohF1I~uc8x g٭ݐ6ޭ3imc*lkMޤTx7<}Ndo'57qnjd:?3y z8wzs#U{-G7t;I}c6h .z1מZPӧMo6'{#Ԛ>o@#U֝EM> _t1۱8!?? y9-n[b)o2&b!N}NcvORjnq|jjt| k\#|o(MNǒj=I/u|>5qu&N!S|nMMk^tޞW"5[_MN/N!Sտtz{CE\UwzS j=GƴSkoO r.!S߇&_ӈ^GƯSkoO r.!S|jÛ5?Bֿ&]ޟ*tp7~T 0cӘj|y+g6B3X/}Wl$GghP^]b.K quOHDjܶ|jrΣLߦωv:~'K#`$B9= 0 t]Gz|oV*9aɌXjGed̚ .vaKnj1WWVi1QU#_,u4 Zu/OfzTvm"B' $WH"iQwѶE)-5,T;@i S@ª.e"˸+Rw>7~L QەV;$%0YY%%G=Ƭ9SkoOQD$+.E*{ύk&ӈhzi&]ޟ|>7I45[_&O]ޟ*\BwQ~5>7s&7^ޟ*\FԚ 8Oyo'57?ᮻ>74UȸVWS7xST>7su&S~oc~7|:zS{t~o{j͍i؎yӮoO r!UݷԚ[xU0|h;vMN+oO r!AQQԚOIݠ.#TGF7wvG57@uߍ=!EjwFx7<[$3q׾7|nAۅH|rOAlo+yS='vX=!Ej#Iu%U̓c6hԚ?uԮ>74UȸObp|l{Rnޟ:I%8] #jy#߮oO r.!QN 5& 8YgvnMMl>o[z{CE\ [c#(0zSU6`MH.?CW]ޟ*;-GR b2aӰ]]oٓ@E ܬ&q_O7S/n_75M,r>ucjf5tۮnOƖ._E}Vz:XML86@Am,zW٬$t"Dܔ*#|Mk uKg S=c|w-qnf8`GI3)86 뫜KrVtvb;Q5}TL\Il3 c89NVX},Tѿtq\7pҶ;A\qTEOK$2MFfHɘ[6M)j b2ȧK'0 s羖AjޤqN>5euRXԔURR9,PL`cK󭄻'\jH)m]y /:CA: gpq׈|{ S,9ŭ6'PT0͇jYS6(+K949o{ Tܔp(1N#URMrGA#b},[Ğ#wPbFjewK[ijCa𶝢9f'MS$l 39Ļ,M "QblH4۔@$/'¨)go½'O ßOCq V4=3";$Ѻ MɅiTWbs{./g [#Ňud60b/ rξJ#pט#&fG,5C͗8]3gKSU1,gdٌ]E>1 Ppf7's\x͋Zu&aS☜8}fanS;XF2FlvkU|^ ~Gmd䆜S浭ebg(k%5m|meDLو c5'VGfeF܈~}L+Wa8jxbzf9s$/8 gPFPp 3:'AIe|yHSSz" """ """ """ """ """ """ """ """ """ ""_S(??*|!o5b=M& ˱0098ڇnH{ﵭ}4˹vsS~oTEF6w.}z6w.}zh3KߨͶ]˳^C6\AaAt / &~Yux&lPAͶ]˳^Ͷ]˳^D eܻ9Leܻ9[AͶ]˳^Ͷ]˳^D;vsb l+ 4`Km#>űXKL`o7kr@UVl2J|mv=>#g:9D%<&ZH*[:FUEQ.ac]If;'+t2!NwMCA00Z^-v6Q6kbά֜vVvwP 5SRg|F8ӾD-QO-\n/?=61/gLY_O6 Cg n2G^۷mh9K9ck״s6X%|8Lt΂1=54&`Q{1OYw?)ZwP\ז;.TSm{{?DSm{{?DSm{{?DSm{{?DSm{{?DSm{{?DSm{|#?ӽGj&ly uSL)fKhdrr[{Ϭ؟Ywhkf6cX%}1RXֹMNqQpa1ƧW%x ŽrR?1vϬ؟YwU-he 3N戎rܮ )<6w1q[Oxw' }@FB&4T8zme,CgpEj)"-\{0Mйڷ]WcmM&^jqobrǛlg>Q lg>Q ~Alg>Q lg>Q ~4˹vs˹vsAlg>Q lg>Q ~4˹vs˹vsAlg>Q lg>Q ~4˹vs˹vsAlg>Q lg>Q ~4˹vs˹vsAlg>Q lg>Q ~4˹vs˹vsAlg>Q lg>Q ~4˹vs˹vsAlg>Q lg>Q ~4˹vs˹vsAlg>Q lg>Q ~4˹vs˹vsAlg>Q lg>Q ~4˹vs˹vsAlg>Q lg>Q ~4˹vsTk,Zm89º[_B(??*!o5b=M&}7~oTE.o]m,&'SaloB=4Rֻ›aQ:W_̕ǵ~d6wǨ,=73t\ }JY*j:yidҲmge\9oqsz&OKAQWcaH%ܒbosRxw W2 93< XXsiӹƺר7n(({\:oԃm ;+$1F 3Sp3pZ^KnuN; 3MȑʼnƷ Xb IExQqzpjIؗũ7se/!MNZJgO4OR@`3c Y8 %,4,Nd3%<+s] l}jRz/'ګԞٲŮ!IS:9WKViwtr~Ւ58Ÿ/T=?k5m;6'b~MIخA15zYO͕co_O*g֜9ͤ@ܠd.؛Q,8,qJzޫcd"_<%e9M.,Qᘍp)'c.ؕ-Fڪ j`v=\ꯌl]It4T'N{l7?:nLaqkrssMXk'593QokٷY ~5CQNS,Ng &y2_1\:{w䆢)6V<8ۧNezqpHZրoklQоHţ&` y$|g1 #uW %j'I{l,7/]pN='9<} 2CEVdhJ= io GqJ<*Y6@1i{ * -l6IX$&Bn@-v;4xvaw# 3& c`ZǶ_~?[Ap:zWOُ_BtOm7Ĩki 4y$c01ᮞs3jl@_QWշϋ!5U 9PZc(xk} L Wо[TF"e)˘v㾛uq#I6fZӉT=>sy9-'Ḇ 2:jNʫM$:G 5ڢ&1M]Ph[Tm 7 " """ """ "" s-<#"&6 \z,Og"Hȝ1HXchX6ƞZG  s,LsݡsZpd,:.$ҼH͗VY]pnk4AUI)d){ZA_rSvv keä|_37.m/m vv`ê)N xy9o[pAZض ,:HHRkkq{]iv QA55tSqE3 w\G ]jms^;#Z$n+K h9"ǙoFnqS6=ٯTj_SlU{h;T&h;T uGޜuIޜ惵IbQ4S/dkG5s:N*/:Vt4ToVXLw f=q7 \<)=1L ^t}抍88=OK]OtrA{nsx:V4OL{,47>m,4Ӣ&Bx=<nE5s1fnJĨ 7pV?wup6߭,5cbu "nȈ""" """ """ F|Cwz\6U6k6twP鶋 ZT/:>4OL{4OL{uGޜuGޜ惵IbfIb`V:N?ꊏq:N?ꊏqYAڤDZ3AڤDZ0+yEGyEGRzcؙRzcؘӏNӏV|v=1Lv=1L ^t}QQ'^t}QQ+>h;T&h;T uGޜuGޜ惵IbfIb ^t}QQ'^t}QQ+>h;T&h;T uGޜuGޜ惵IbfIb ^t}QQ'^t}QQ+>h;T&h;T uGޜuGޜ惵IbfIb ^t}QQ'^t}QQ+>h;T&h;T uGޜuGޜ惵IbfIb ^t}QQ'^t}QQ+>h;T&h;T uGޜuGޜ惵IbfIb ^t}QQ'^t}QQ+>h;T&h;T uGޜuGޜ惵IbfIb ^t}QQ'^t}QQ+>h;T&h;T uGޜuGޜ惵IbfIb ^t}QQ'^t}QQ+>h;T&h;T uGޜSԳRMFp ?a[3AڤDZj'ݭcdq2H-k =Z3l4lVa7pm9ߟ}tI?=Rm.AAOU-[*%.62wF2O|QWn%KWC5QJw5tD~qwBK,n8*l Lov ]tR`pvSTrM/6AiULKgqlf6UVEJǸEfvgbvqu>UWOcIsx 7&MAk`IðǙT׼4؝H!s[Ű*j Jz)fkm&UT8.1_ՕUB<_)gc(<{X1v{[)ť8CĦF$c`,=2co+ HaƾW~-7^b;13}-P%8d]`FKz!GnqAR;MֿU3V( 2ųkom*a`0­U6[IdZܬ7sxV = M KP@3D\]QluVYT>Y$T5!JcI:\|[j9 s5g)Up42FFQlX1qJ s@j9ci햇 }< !{ɀI8 ~2i 6~%ZxIt-^ӮߙWm|Qb5#w+Llsѣ\ƥ_+J\JOb\3 ih#|Y&,IMTEg%ұs$`%[v |U+eT}T1[+K&:@g9ms-#Os i2ELhmq@Vikn7nKxw7ř7]-w<'–ן}7PWǾOT71k r1"ٯsN-~e SvbqI ji%ZI٘ h3_K.^o%*)`bk& )恱b=?f.ڒ,58}]%;[7Λ7 h,[} ߥmm94uuTvw 9o'f;~7[c?n3: 11{55h|>??+Y6VοsOӧB#lo5ADEAr*=OV5\JSh[7>Fv V#QiY3]Up w)*4QbT +h"5x~# JxwUڸw ~f֫]85W u=˯a5jF۹vnrmlQ6l7 {PB7ӳwkemzc}]ˏћZ w|N?Fmj5_pSѼf 6F\[+m㠣~bp0^PB7Ӳw[i7NͭWpk6\ڸs.L9WZF۬;7x6VAF`&"F[k۵;6\ڰVҾtO t;u=aA#~mfkem~k`rmC:潹vnG&O^E>*"kH \< ׏ћZ w|\9R<7 kӳu,mu=˯a5jF۹vnrmlײoq3kUDrHֺٝ׷s 0#~m܍;7x6Vۡ|&Ì*jhKn/aCdm~m4N!uIx4h7gؽelPqWT,5dl,48FAEu@uuN ڜHBGLevKNG*O^qz^~ѻk)TIQ4btO6.tk÷X]NKYKMH4- 8L^!ņPHk'S'Vmm/xh|HQdo e͖浹6‘Mj:5[h!$o+-o&i7 %v{{v{ŋC1 x)h) 9$Xfkk$;*ic&{X&׋N=Kv{AAmv>CZ]u8/qR}]'*O_h'*O_h'*O_h'*O_h'*O_h'*O_h%tKi)ZDNm;-F3b0a H W7TdS;} GIa.-<[n:Mpů~f֫]85SћZ w|\5o. ׽!niٻɵSѼf3^oͻf&t羮ͭWpk6\ڸk]|3 {PB7ӳwkemyu0fAߛw#N.Mz7ܸw ~f֫]85W u=˯a5jF۹vnrmlo. ׽!niٻɵ^6\ڜ~pkjᮧyu0fAߛw#N.M:0#~m܍;7x6VWrf֫]85SћZ w|\5o. ׽!niٻɵSѼf3^oͻf&={\~pkjq3kU0#~m܍;7x6Vz7_ kr4[gc}]ˏћZ w|N?Fmj5_pSѼf3^oͻf&OF`{ڂ6F\[+loq3kUͭWpkz7_ kr4[c]|3 {PB7ӳwkemw.?Fmj5_8w ~]OF`{ڂ6F\[+lu=˯a5jF۹vnrmlױͭWpk6\ڸk]|3 {PB7ӳwkemyu0fAߛw#N.Mz7ܸw ~f֫]85W u=˯a5jF۹vnrmlo. ׽!niٻɵ^6\ڜ~pkjᮧyu0fAߛw#N.M:0#~m܍;7x6VWrf֫]85SћZ w|\5o. ׽!niٻɵSѼf3^oͻf&={\~pkjq3kU0#~m܍;7x6Vz7_ kr4[gc}]ˏћZ w|N?Fmj5_pSѼf3^oͻf&OF`{ڂ6F\[+loq3kUͭWpkz7_ kr4[c]|3 {PB7ӳwkemw.?Fmj5_8w ~]OF`{ڂ6F\[+lu=˯a5jF۹vnrmlױͭWpk6\ڸk]|3 {PB7ӳwkemyu0fAߛw#N.Mz7ܸw ~f֫]85W u=˯a5jF۹vnrmlo. ׽!niٻɵ^6\ڜ~pkjᮧyu0fAߛw#N.M:0#~m܍;7x6VWrf֫]85V7E oR*En/@.Vz7_ kr4[kR4D VE`^]nK-buv#G3o 3-G-1r#4c|CE]>U(lI[TPXY` ,ZrE7.?'[S-5N'H2YȐd$6[a{eix>rMECOTqUe-h"q%gZ)&ms<56 OG8{!m5@h١&&8IH%\cо`ƫXƸXY.BuKӧ9]Yǔ'`T-2K]Xvp,𧻓1AOQ):, K"o$;v4rjV9DnLFy'Sސo{OJf`TWGtfhCuhHAS&c &ָ! F}IOƶ>FcZutF=F͖v3se;Ke;_ T`>"Ll)$뫚OZըٙk.k./p}.~ɯ踿HyO$S Z16iЂ F3Jh&18斎U7O(ˉ;%J=nO,cOOF670, 5d8b{Cr@/puS|xK㫙=R*7Nb&ZXә-yZj.u-.RbXo .)m-x>\7 <44_ރUEϐE r.7]YV0j# u sT7SSpI!yhZ1+O>΋a[[A>'šsRpfF˽ٍ3^ltd6> "mmߘSpns9 5SekN{[^, a*gi#SګX`H(&+m6aWEEbbC EC[¶=Ujk(0 DjWY[)vc`V3LLD3u."1[.E,9M3xk^U][m  ]LtT088Vٮ8`&juб4K8Hq`EݧDoɞδ6q'OR1F H^sw5U/؍SSU sDn9۳zAoɖ46Ը^U;ŝַ')&[(ZֱZᛀuZXV?ayp!t6@Z A6iD[C[]R)S;i$WX c`QDQi(%e3GI `־zW: I4Z1k36ݾQplٗsa h$h ̑\ZCW<2~ CQ]SP׹FbXJvzeEIb='(˕FrmV|[WfLX$ik7K٧B-8'YTcFS\0s K sK^_]>f*>/,8en :YVƸc -w 2\;F~~M?͗ =hk[i6NhjQ>&&bSyd8MN0V8hk$|j"FZ/ hqA-}nE푸6=sZ c% '«;-3瞦du.#/kO! SwUY-cDrH3fvpNw QC @$kiqAn9Vwmک" """ _S;!{Vv[4S'xc!7ӗ}^St?T̈́vkU/YI&[!uMU +7TpU89 \ۆ` 5*򊊾#̯z \U0AF jLE΄~NG8q)5];1MDBt\퍡E֋I[AHdKRyb} aypEqumk1iVUEJ"mZ*B34Fa0$|>ﰵ:ޖz@DDJJ nbnsxXX#om/Dm?#'T6vUuCii?:ԌG봟m?#'T6vU b?]TR1~*`P:ԌG봟PF#OWL T6vSOH~I@R1~*uCii?_0(PF#ONm?#'&OH~I b?]UDuCii?:ԌG봟m?#'T6vU b?]TR1~*`P:ԌG봟PF#OWLy=^OM,]d; 㔚\j['CSlnAYUM W6 ;D@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DD>(8$Bno|H s*߭Gʷ\fh ʋG0v ʋG0v *,\fۣ q;t~^;t~A>1n ADX?H'Gcv bdEAۣq;t~A>1n ADX?H'Gcv bdEAۣq;t~A>1n ADX?H'Gcv bdEAۣq;t~A>1n ADX?H'Gcv bdEkALLA.3n ` ;|TX h/x?H ȋGv ",|bAۣ ;t~N1n2" A8?H ȋGv ",|bAۣ ;t~N1n2" A8?H ȋGv ",|bAۣ ;t~N1n2" A8?H ȋGv ",|bAۣ ;t~N1n2" A8?H ȋGv ",|bAۣ ++l}Jv SDfHtp{YXt?5Vv?߱G07'/k^߼)2anbxbxRn%@++ib+H˿y[= P,A  nƧ nƪԲE/g8rI;̓ \,DȮw8rl5J0vjpvjXx9fId6f 3`.y@ɅCSCUrNF *h2T\ \G:,:3 E_U`DEAGQYSt{1sK~sK6{\h-|2z:54$'VEMNbm#C C>Ն"̗E #˴Nd؍-. LOJq%Kpt{ oqt72tk9|iH9%ټki)QJIY+bd wbѩ" jᯚjj1JJz)̻pk ָ6us[ *wEҋZ iSC –UORՖ@DDDAġxX㙓5ż;3MF/eQw~F)px63`aMh4I02qJ.R,r_OBmSP׾6pf$'uF.)EqOQw~cԺgkC1.fN Qw~]ye7J\tq?N)EqO]+WI8]uy/gxɐFWMMFz?9Wٚ{-lEζ+;/;_bTU0#CZ^kZK,]R_?Ϳ>{ɒٳ_Lgsw8Vw<^w{T ?)EM$Yvok-#T^YyX^YyX^YyX^YyX^YyX^YyX^YyX^YyX^YyX^YyX^YyX^YyX^YyX^YyX^YyX^YyX^YyX^YyX^YyX^YyX^YyX^YyX^YyX^YyX^YyX^YyX^YyX^YyX^YyX^YyX^դ· h }EDkXCMȶgs-U?Ɓ> 3 3*lyST~@DDD@Z͡3VP1%;'Hf;[4ApOpsjp'Elfy@8CGO|}+CUKSa>#Eh+K88^$vNn*Jj[tBMN|Yb#La 3k.LJ>?Gse^ں{QVb%4 l1i@^}Ɲ>|!D@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DD\JShW'?s/Ћ~m$ZK^T/퟽{?*Jޤ"(]M|/5 |3)5C gn!U1F lִIQ+ci76*8v%&$}|%Bڜz,ǠBzzTV6)XAh]%GUcxFꍙ Ҹn mwjl{ IGmu3 (%c9NqsAp27 I0暗fs278Ymsmߴ]S'g-D{\ZMXjA,8]x`H5ݪ)p9)꤫1nGjwo\''ı#X^' c^}m|Hl"Gʼ*_?z^T/퟽fZbDEhnڋmd} EmS:zx49Xrl5:b .7/^e=,؍%9˔IA';vа w vO #\#Y.\Kr:S᷇zϴU=3jnѤTSRMK eN '3v&MHDPqUMUT+\d{"kKYFt_M<2M3qqh䕫|*N u,{I-AAEL7NmPQMψc4[kg.whyA= 7C++`9۹m:BqZ|/;=ֶ ˿ۊ L>:l e,BCn{ jmaYK0jTdŰb|42饍ddZv M,4MO$Zi"]bFXGD_%͘\l7=mQafd:Tz^N 8| Z䆃f6NDZ=5I|xo<2Bg< Mlh9TqaQM |5du\c1 <0l2W8C'pˮ (~#KR9xH:-,p6-sHB""" """ """ """ """ """ """ ""O%P~4 ƫIT klX э"?*J޽W?KgYؑ@DDD@DDzYvKi|79y[d8M5 ?q êYQ-5O`s9UaMdv1pFFb2nt<׺\[yuC}5*l*ip7s-U6/RbT h ]Ց GFכ@<*ϼyA tRTrSIWc3.qޫXN5T|0,'asP^րu#K|sE.٧BTpI#.8YT͊lK]UϐI={1QkFPnI:Yt97{GG6La4ue. 3I+ =dٍ'@m3SqZ6|![ <ƺg{\:˩n)/)77 5UVl)WSﯤw8C?6`XMh PCSYov)USﯤg8C?.`Xt%./˩^s|}^s|}i+Sa$ش#U#ML̚.wXX9y~AHۚ*2c-#,qqmX. 6k69dVCw>i 7lWP:,7ĩAɉ#Kʧ:IN,G6lK\3&8|vg$ t:zyyI}7E-uU&%d 9sAmd2%idv34XMM%luSPe-suslQo<_ bG6y\eue.OJc!CsJbR{RGFd9CZMutoA0QC`au.h}3K.h.K\Hnpzjʈ,omtɖHlo Gs氵6dj]ENOlEUewS|!G5m0e/|5t!p; lF }tPS~Ap; ٚ91ڌ-bR>GHyKRH܂~PGJ,5R{+Y,&X Mљuԓu$bUs!5Hi1&:7溲33˪ٰ\;Fw9\AIk)'sn_07 E8x j[lt.M5#K}a15,\L<ts$jN>a"GI/,`hh"ʰtdDPW'?scU?Ɓ#EE3V9yST~Gʼ*_?z̴ROxZp > O<[_amHY]PYwًci _Rc5@C*,Gͼ4܋jX7wm}淗'{9!VGf;aw֋@72O>7" snKNoq,b 0W&$EQ>Ymcq78_t=k }8̙sihH:e7;blm%Esi-]ܛЃQqA81@j8# k HA9ikaICj'I#eahql_8v%Iq)xhs@6%P h""" """ """ """ """ """ """ """ """ """ """ """ """ """ """ *Uz@j?A F7>^Ll"NOyST~Gʼ*_?z1""*tؕ*bs<81"kr s_ɾڨLxTaOfcu3ǗT# >:~<[<3 KPǗG Ѐ ٛsq_ٜF4̣Z|^zH3=x}61hS$0b{r,u]k{IfU6(옵,ƍΩ .ܲY6.v+ӟ:?Ӣ:yCäcjYigyw2.Hԝ7. }_VE]UOW%T3_.Y En|sMAD۬2 s[NemKCˢ.V pIF b<fGSGbFl|xP@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@DDD@U?ƁXr*=OAnoEE-39yST~Gʼ*_?z̶Ĉ" -n%a3Tj { Ygs̹kfzVRk *V4_.o&=7,qcdSTt rT8nI{[zخs9.t þgokrn ^F~;u}q_ [ۃy\%DG `'#3H6:*`ts5TŪ#[+YB1sy!5ɔԝVb}M".o(p jxbDQEf0\ssISֲӉɚ1ƞFCSs&ۍ{T9sm+dqacaK-^`b8$]49"kf{-toaU4X)cOXhdcG=mW.ѽ3e*vLZZcFT^ZɄnY,aM_FwJqO}ɿ~ tff3SUOAPe´[6`.vl|xVhFgCMX_c8%IS r15{浭 A-\?)x$Yv8p@!C\*Ji&nAM$+Ct[ "UVG;ajR =$4DC\\Ɛ-;2TCYKM4 Z7iB ctϨt>6k#o i.WkXߴK0:XHbq^׍,˘AEv5B0lMZJ1y$sf5ڂ4ݡ_HD'6:Y]#^7B -H\-xd :i \s`M^ßS<ZQ0**澖V2lwy ͢dulrO$M2WJǁrFkAEv ݉fbwIY0'WSYMLfefdg!+I:5^#Ň`ie*xZ׎ly '[48tkdl%WYa.6x&359F|Cl ܑ{*[axN$suKG/h@m/.~o9beK3zO-C"'l u+gڙ}>,ɝgӑ $sZZc] !z<2: X . isk 9S sMU.)QS†I%IJ`ۖ]dJѹ0a%ۖK>6FXnE}>xPik<"n\*Jz*u; u2fe<g pjNIg62sP^[Wr(9ڙq]ik\pRJ翃lOcxQkFy@[1$m U-NM.c${'ۑ:Q>hRAxymrjx< %#e\-Hk\ ţRñfNWN細0pn[rݧlj7΃ETڊ|c3q]n 3lZX.Ӯ`uM(fRɾ[Z 0pnxg T\:7ܓ`Rz.e w sZjYmukmKTRD1 d2w]ֽqYonSs›q9=+E+Y,gD9$9xe1i*1|E͇6dh6r n"i΃IGx+O9dIY# {@%h\,ܱbX>!E+J%edRe6 Nnst/:7y@Î8 +gPmMn"^W.tw Y'(i0fi>>4OoAϜN ^\2z'Ń4ύ ֐-r>>;mB<*WDylr&qDXgD'H%`;#ø "" ?A+O%P~4- H o,Z~X>UOR׳*l2"(Om>6W ^px}DŹkl z QG]Jʈ3XٳBڱ8j9ƫIKf O^UǴsÌXm h% &Ą; *Ol5 `w@$t!Y9.uEY5>'SQȝV==,$g16tXeꓥxsesMh^ 85͵gshWSbtrp DuyT- S4BQ9d-q79F/hp[ p<}#ƺZ8eq{@ l~W@lw'=!j}u./__<ЈC p`k.ԯjg &v"f8secND/{[#kk5:;RK}\ sMU.)QS†I%IJ`ۖ]dJѹ0a%ۖK>6FXnD߻j *KO44/KO$BP74x[ *u; u2fe<C ݮڊzqJ6:FRTHZZr^ᡙZEή<,CW*z|l e}J)pCSY/!l@tU); (r9}8`7ٞk[,d͓ v1Xwꆼ_5X0F4rfsMZZ9P I|[SI0Ɛ5ʽuX#8Ji@ccsN֦J Fś_Q;]^;_#-m,{UQdGTOI4<1KN̶RSM+eV# ÚwY>?N-7>s9ϘJ/k u/<=\@1F8]%u :뮨$͏aS>;HLf5\ #c~a,Ž$qa!C!{^7.`|]W0ُ͢|:Ì`كto)&`x]8;|u0Գ9~AgᯂPilAmplu@i"gSCU-Ck]mi`lF@ZNAdTL!p 8~3i0h8.!/G41/n o!UkGTS[ץGK5s]vKqX0hWT>*噵ՖPr-栚ked. =h>bÅ+\k?|2[6k*`)QRUq[' H:/cclBqD($4Ř"4 `y1 fCRb4flЂk-#Qm6Q g5hjtĪmn'/.E=ngKNclkH{ٖZ4`W OR]&v5fchqڐtUYbuLd5fq\s@ :u5}m5TtU#g37""(ITUz@?6 |H*lyST~%$DPA*|EC׀}1y+@ݯ?z"XN RؘeqKX\̦M眯QAEt sO}ߍtD ~/ܼܽDND@ۮ>/n@DA X)D^;ͮ^׸7|};}x>>DgD'H%`;#ø" "" ?A+O%P~4-k`͘cJ*m5\48CO WC$ 9@:2 d t#E%ٽ}4(BrӸp7|YtE-3D Yt~\ Yt[+N:"D`xΈ0o;ΜYtDɃiN󠁧;Έ8`hNxΈ0iNx:"7tKޗyלYtD w8:"dyל YtDz]N.-9z]^p ItDoxΜz]DYɇIgy׼\tΈ1x8<.*\tΜ\tΈqq:qq:".tΜz]DCޗyӋyoK7tDSyӀoKXyӋy1,dyN.& kw8DHe N.,e<x:"\<[D ;$K:"H8v遣tDKg_Ɯ YtE1 58N:"Zah.w8:"Zq$A7N;Έ/t+{6@sX0p-gyӀo;Έxμ+N:"oKXDD>\i6.xg41Wϥ 7$[Qkꈋz٠FB4GVJFIFHHxwdumpC    $.' ",#(7),01444'9=82<.342C  2!!22222222222222222222222222222222222222222222222222"^  !1AV"QSU267RTaeu#3Bbqrt4$%58CEc(1Q!ARabB ?a9O#Tq͔ZޑZ=h"lTWVk&m֖ln;H\6,c[o+k;sGb9n+y]Q;-$ds3?R|'0"7#E uCr8[^`TKn+y]P܎5#r8[^`T7#E uD܎5 n+y]Q."7#E uCr8[^`TKn+y]P܎5#r8[^`T7#E uD܎5 n+y]Q."7#E uCr8[^`TKn+y]P܎5#r8[^`T7#E uD܎5 n+y]Q."7#E uCr8[^`TKn+y]P܎5#r8[^`T7#E uD܎5 n+y]Q\2Ts/ TOk۹#>񘌘Bz%FL!-IlM5w-܎5 n+y]QthS]Ùu1`EQ ,IrQV[{$7#E uCr8[^`TFi\qܶ[호pنYĿFLC%k}GpopnG q^U-*͕))R}HqOćNquD눲&V=Df6pnG q^N\& NGb׮ʣ)ifIJת܎5 I!a\]$FoزU^%qkRui;e˫PnkPeT܃4[1x?M n+y]P܎5:Bk͸u 3YTGDyn܎5 n+y]Q."7#E uCr8[^`TKn+y]P܎5#r8[^`T7#E uD܎5 n+y]Q."7#E uCr8[^`TKn+y]P܎5#r8[^`T7#E uD܎5 n+y]Q."7#E uCr8[^`TKn+y]P܎5#r8[^`T7#E uD܎5 n+y]Q."7#E uCr8[^`TKn+y]Q O:p Z3vF+T&QB\L974 P˛kڶ{Zl˲TFYg!)FyZQ\e?` x= iXiWOdpȬ^,=AuZƊsEr=#|mʥ~%4ǤFԤIZ˄nD˅hCIQw7ʞ<=AisGb-3[x}FAPh;XȏxMƔoYțpJ<-CHH{NM;_%mdAGz~;ZFiDfԕT.DfRok2#ه}c@:r23xʙmI<@[i4ڙYZi;v FEO%MH42͝*ʓɥ Dfe"#\MhTT8Ym ~6D]Dyvld^7].3{Ne9>Mկk1ʣIi5)Q2+'HDkV;ElJZ-ckqӊ-i5lD(b9)šy掅.5o߼6'jPq 6dj1*X[WVTtF(fWN#-b.Je2zGJՐs+YQiJETg8a:NQaJP0f|y;PK1 3jZZ layout-cachepVP FP )FP 2P _P FP FP P F P P F vP F xP P CP dP qP PK1 3 content.xml}rHv~E:vB%x/"5Rt(\ޘHI-`"Ŏ?a_3~/9'7$$(=1%̓'O{\v/񽳣N}Ągݞ}ym ^~?W?88}+ /2,ߋ_v黷o.ؑj} 5kmuysKqZWؑiGou0z|{v4i0~6n[ E<_AژM-vy~⧭Esѥh뷭~0),_\ǻ[۞&Mx6fHx x o:OA<،?jakoHNdk&8lwo3ct~V7C.%o< ώ$=g=E巄 =>xp0ф.b70/F(߶tl~~.9{w-g yd5*WNd%s^XUWƭDXgG? {/I皔23Xԛ1ni3ml96g wYӵ _?f (y2HGkAgf%d|=\s/,!A`.o-wf8d)?bo R{wDMfUx # 'u D>&Z%\=qˠ.R)O-@IQ@yo>&/jÐ_}/8 E~<]MƜ&M@r{6PV66xpoסZcp׹7m0r&F H ?nPd #Jlr؀ sl5U}غy`zqrc7cx~ghǼ`Խ?npTv3Gws;ŗ>p<YQx-y>{aA5qpy7 dzuVғ (,cܟ2X8zH|(,JTw>zSR~(^[l/iMISk*ّeU3 ߧV-*z'h QI;o/fcߵ+4iҁz"54Zq 8ڲt׉4 3U}纟5+ d~D*12M2DCOC b-o6¹|WD vhJg1R?R*NG2C&;W:af<w+ëK!^^!o2e+.`U V^Dj` /Z[eB eTd+(\p<ԮJIЎYs?c@5YT:!_ :pV،_ 36h oV,CvE`'"FImŔXa>C*mKoeJ[U^QPMnVKWG<`bm e(ZQv^.Իyj&YM1EJze*?ςeŮ┸YОpVe+}q{x<gQQ ܯ@/{olƁ"``T9\{<bwÛC3bcz0GIό$sWF_#5֞:Q/ƾ[dȧ9xDr`e!G]`qD +@-qDd{uvٯ:vrNa#cm*?dd yx~@$a2ĒƣctDS l6x)>f?+yF|w\5u%"6l `txObzK!/|,,dW c<ʚ3bՉ @Pw9 N9-5V FuDWĹdGǪ~ wÀxQ=jm0 eFc"뺼.F*`65 "Fa=܇\7D!>i.4ܗsHaMGΤ {s}Y:xFs tM1ɪWa'hZ; SIhZ[ WoHq-;iƨu.wB@䫽eB@U3p+^FFvILP%6 43IGJMS=F<gȑ9ABX GNQ<{ j{V'"d#>f {2G6ڟ>ƍm@3D gad݁ç7oAiKCK  lڏoQ"O ;O Pj0d&3B?<Ϙa(T9`@lv*jV(Z˦0O$RKA47W0G,(XD "hi˷a gk|ɾ= uv7x}}Jqkka`iDg~,lU`I# ShoUAd{P;46r?gJ1JVN6m%˴ƕj$Y΁9(r/*M7G6C҃P W8c)|kMMM\'˸=>^3ڟ8Rm1LBa>~k'Sz`yzS4DB%V} }ᣬSs>y͜’k 9J$*4!d'.ىS5IMEΎЈOIV9ɩ.+d.EL3,w(Ii ךE(j [/!K_L`4RX~^3KxԹv](cJ;Yx s|=.E7΍~ͫ~:3 @A'}+" \bph>5+X;~EA7!!06 ]E(sw<7GXM3;O[3c2 *zHCLa/D bS+~3wii'nܱ[_#tW}fo .la3mlj^{jS&jܼO+f<{Eϲ$ҸY(IYDdP ~tL$YoI, J)D1F˨$ᐆHOå#)IP NcJ4`H wHN玂;1%iͫ1F2A,`a%ǂ]#OM8"U8ÙF+'!paʯtІշd|vPf60&?DɍZ|@-M( Pᚫs5^eҔ،DG ~ScUN K>P9P*!/|Yu?<b^3S@l"}D`p+*T^F]*L-ixL%Hn{=Yy0+ze._?c5U-Y2M)*0?$lJeUcbU#ʭCGbR8#Z †oeďX@CiDץݫ@- &#[oF潬aǛ[߀-ӋDѡ`($?#.cE@MNe!ni3(D&P6rdAI*sۍ더稖<ݢDmA&*2;\|T,,B6b'HVv1OrtxDjm"(J4RAW N痣GT˘ͽiه7j^Q rC%5=g~԰a\A] ㈬RuA#jKF؀4 {u5}9#`ciM̈́$%7˫|xBbmU4,˩L?MLebbjPUL]\^kS)UgmKĶkg6<8+ؤ$2m}ePx۶)3O̙sytXN׹pfoun4M*khCKE9RQt[fYBn9jDNV#xqNcƺ5CH|loOnNGͦQ`@ż.GY(-]+L' =gܥU\!g{^SE`XP41WcubD~|+0 ~l8v0c[lf13{3a66l31b>m6^6=hyg l1<9 )FW<U\DLnpi+X{=_#G˯Gj]k SiʝO~ `"fP.Ji } jsc?DH0 9JC&`G:D9m(C$Ֆ%1)s){HᲔx&%JĚWJv)fZֹ"%XwRbXQJ ]J G@UXnQE7.LJRyȻfc'{x`_\^MkshSQQ5Vw7K. F=9.G+bU,`ql#TI8 [X_GZxNx AA,(0K#"@)܁$U];3!ESg :?饎USߤ!hB]$(SF))Dv~Jw!W3iN@ue8h01!$o ”TkGhSO&†}(1hBU8.HC᡽;ڒ3YXG%A>_މץ;od,wMiI4*WSXs+.s} Z/KlAdD5O%hc%llFC7d?ԝ,R%mvf퇬9u 3+k(`@j*JV_o[[Ŗ0^3L".@ʡ'wbpYmI1sdFIPeZ(Kx~#G$mKlMYMv|fD]2ߴJ,sa9-7+Ҿۼljat4K+`eU_*lIxeNren)Y,¯%t1~A$(WĽkI j8ڜqoE!,K o$r:TO7+株Zێ- *э܅bR?PZuHPͼwDk:[ZRaK S~QQ snn垪7QɩxiymvfUy҃JGKd|:-=Zc t2L}NҞ125X" IR+ }y%vN.j6[zwv |4K2U௪ـ`a~zNJԢ~KOtv6~AΚ"O@;RXZm]19z0AԯRئR[Zu'봛Ւ$*Ϊ WUt%巄]ۆSەo\SUZ9ϕ& -e4CIt[{ kur@\`|Jh7JQfK, 9ق "O"S@;7۱%cEu[߰2C"JRVZ !}}Oo.Wc(jYxY ,R"NI ZFoxobl^gԱyfDcSIotáZ=LCz+(u4==o##~bVoط^YADp1271zQ)C{[#ږo.@nM'ɉ;uɨ|4vBE,LqÞFN ͡1{2h'=6r4޶9jwƆ"qM=Z8U*iol!l7Z=m> 0ev 1L7f=A:0l,Φ&[S  ]I(,+NZ;߮W'd]:uD-^Z,{G:l*sY*!8DBQ uhjoq֘# 1(3L6L]Byˉ唿Uۣ|60!4dWo`RYF,UN`[<8&aOSPiW򸯄J Un|yM>SYhx/>cˌ9NGΘa,ጁrkTҀٙHpNq0t:# fہJ.A! gIBrPAZOv@c-VwQb)lTe|'褞@O%-7y>c#I2s>K0؍.q*OmL%hW~WNQjk,׶[grV`ʊK@o+vR ^zsŮӺw|}J߽R*-Hy;-<縐*3qچ=N`-z6}I!Q2v$')/EIKu%ZdXt/۷bL6TPKw%zPK1 3 styles.xml]˒+dz\<WuL{2"! `e> ,uVYeWo7)J$$V*\\sq/p=l,_U6T?޽fw~o޿ ^._65|OU;~Ϥ.ܽQ7a#t뽽Q0}S}D7^Ku΋^^pˠo5lZW3 !R D`C7 Tx\/q$n/C&ޟnzﰻ-?ʳZ!jt->HŻ_)L6=24wxdh av]pֻ&?WMr*3\T[ETlvIĚаW/̭[\h 6zL%tPyyWo{ER>ڈ=Toyi3>p7 Q |1,u9y*mm"Ru,{AWۼy ڻ!c5-FĂί\c!OaU` (a frlc]jN$6__)13&ՈzQu$=@V<+D@Pt ,OurUYyn`Cҍw\f\Fe!oOX\ѳE^^7@jǷJKF 󰈓Qa B*&\଑Bq p}D=0!3y0RNu/t]lWK>PsŚ9&j|&tw;vIٞ-"䳥#QeǝB 3?8v$F&iϓ>.]`:gC>%=f£MPtT9BL5槒N"s_X˦¶%jRsg25kfdzlJw]ʰ\պ i gqOB-}/3g(5̘s: $L8h~ڱ'y;:jxfo'(+F2چbd#=pB,=/[!:Vʉᮙ-\CW|h4.\>4;5ʵ7V#UE@OQWD-l a*j;'E_KL!RpKJmKaßyYiW"Tu=vWDZ٤O ǻ |dZf(2oAĤ$wxM\Y\${J.F}DYE;Y؈?}|b:+9s9~ '9=z9̝A27w*Q5}Xpb'#Gs6H~k,x/~{:KmTUCcWݯ^<;D1_4h2ﲲ4HBeldRt«vr.250 Ԗ=|? z{>̂ `KzШQQF'ZO5j=oZ5FkB)&j|~/U/X.Ӌ"$kߜViM,H[dҿ5_I=aiۦ:m:ct6mXp޶L6u*tֶ39ֳj.To}J/J^W[X!յWAʸOD2+c=&N3g\{KRZ!+OI pp`~4泣t6 A ӹ~4NǓxzt2J<38·CίvFO@h]=! K/U'Wu1':qWRtdXeGEO-Gz8)\/vQjnxLRQnsK9E$6zyH!)'#\-f~qYm8-&Ll@W$n)`һOyV\n< ƿ+^ė}5R(2+|\0a!ՔJU8}AF(3?PK= PK1 3茧Rmeta.xml OpenOffice.org 1.1.0 (Linux)Oracle Cluster File System (OCFS2) User’s GuideAdministrator1995-11-21T17:41:002005-08-08T18:17:32en-US7PT2H53M10SPK1 3 settings.xmlY[sD~W@"9IKwV%/ݱȻ.]5igZ(f`荶L~JXN:vhXj~g/|zZ-ICl0@D 7ׂR|n9x-MTd{[ЊH>!]Z WI>cMMOnMA9:l& yQՉ贗X[T[0 z= ,2rpؠ#KxD4ysѓ aƫ=ޖg`Hxޙw9:I:j ?cئ#ٓy26W훓>^ˋEAPN @sP%H@ sar4J?Aiݔ2f'8Q("LJgfJNxkNIyN7#<1\P7rST%GYhʜߞCM Ś4?ϯ}ÿߎGG}ցmݴ}ۺm[wm;޶~l{d?^}ӻn;Nӻ~wzwS_8;8nbbX?|qdzd=;mݳmn9;N9:_2αi󤭨HsKgqtndAaq Vd堂TWOh쿌P"VRZ!3fC9K{p\u`d>'Ի u@ ]]$ )GfF҅L4pAyY1^b|*HzYxL_wyTHZtxѱ_a[pabԷ6Z m. }6rnOn*|;'$dYRNڑs^wVUBbAS"Io~nF"ܭ'*h3;[ueͷ`5KAb۷%o y%_ryAZ/)V i_ L@@-Ƽ_&ݨ f(=ic΀r1]#W-a賑z \soIg)܈ӽ'-F9O\)DhhqI2j,OˣAm 0H检Z`= 7|;T3q1f1ѽ}r;e"wRP`q ɢ \{28ļL9k9$!n=IRK!&9 ᭺,51E(9Ig!Jg˵-7`"1TaB.|M0$fOHPrd=w2SҰNҟrEU~e*$1?B% >,ebz:讅X@(kC,كr\9t$*(.1&l#!δOj" MxХpfs MGd:q&L<-dN;L1/PK\PK1 3META-INF/manifest.xmlŕo0+XP1AɒmzÎ< ~2d&^#^~k;jQHXI4qd}2= ,\#måHASY r"2Jقi/mbSGF QL[G~4-)v$"$VIƐcSOxY"横;VJj|T"K6'Ћb3󼹈q[AM-dt{"7d];4X٭݁]fp3y簻m#uv<ܥom};t,םB mSxWy|Λbz\h@#U<8.$_c7|}PKC9{tPK1 319mimetypePK1 30}-DPictures/10000000000002A7000001E7CD53C040.jpgPK1 3X-Pictures/10000000000002A7000001E7A4935D3C.jpgPK1 3 -JPictures/10000000000002A7000001E7D4D9994D.jpgPK1 3 -;Pictures/10000000000002A7000001E7A08E42E5.jpgPK1 3&-fPictures/10000000000002A7000001E7673AD9DA.jpgPK1 3K^-{Pictures/10000000000002A7000001E7AE2A6DFD.jpgPK1 3-;Pictures/10000000000002A5000001E783C4177B.jpgPK1 3jZZ Glayout-cachePK1 3w%z content.xmlPK1 3= styles.xmlPK1 3茧Rb meta.xmlPK1 3\ 8%settings.xmlPK1 3C9{t,META-INF/manifest.xmlPK-ocfs2-tools-ocfs2-tools-1.8.6/documentation/users_guide.txt000066400000000000000000000251271347147137200240200ustar00rootroot00000000000000OCFS2 Users Guide 1. Introduction 2. Installation 2.1 Installing from source 2.2 Installing from RPM 3. Configuration (/etc/ocfs2/cluster.conf) 3.1. Valid parameters: 3.2. /etc/ocfs2/cluster.conf sample 3.3. Generate the configuration file. 3.3.1. Using ocfs2console (GUI) interface. 3.3.2. Using o2cb_ctl command line Interface. 3.4. Starting the OCFS2 Clustering Services 3.4.1. Enabling automatic load on boot. 3.4.2. Performing manual load. 3.4.3. Stopping O2CB. 3.5 If o2cb.init does not work on your platform 4. Creating an OCFS2 partition. 5. Mounting the OCFS2 partition. 1. Introduction ------------ OCFS2 is a general purpose cluster filesystem. Unlike the initial release of OCFS, which supported only Oracle database workloads, OCFS2 provides full support as a general purpose filesystem. OCFS2 is a complete rewrite of the previous version, designed to work as a seamless addition to the Linux kernel. 2. Installation ------------ If you are using an official OCFS2 release then the only supported method for installation is from RPM. 2.2. Installing from source ====================== You can always get the latest OCFS2 kernel source via the subversion tree: http://oss.oracle.com/projects/ocfs2/src/trunk/ Additionally, we provide a set of git tree's to pull from at: http://oss.oracle.com/git/ocfs2.git/ http://oss.oracle.com/git/ocfs2-dev.git/ The ocfs2-dev.git tree can be considered our development tree which is kept as up to date with the subversion tree as possible. ocfs2.git is a ready-for-submission tree of clean patch sets. You'll also want to get the latest ocfs2-tools subversion tree: http://oss.oracle.com/projects/ocfs2-tools/src/trunk/ Additionally we make tarballs available at: http://oss.oracle.com/projects/ocfs2-tools/files/source/ Installing ocfs2-tools is done via the standard autoconf setup of "./configure && make && make install". You will likely want to use the init script provided in vendor/common/o2cb.init - it was written for SLES9 and RHEL4 but may work on other platforms. 2.2. Installing from RPM =================== Three RPMs are required to install OCFS2. These RPMs can be obtained from the ocfs2-tools download site, http://oss.oracle.com/projects/ocfs2-tools/files/ ocfs2-tools (OCFS2 support tools) ocfs2console (GUI Interface) ocfs2 (kernel modules). 'ocfs2-tools' and 'ocfs2console' are generic for each architecture that OCFS2 supports (x86, ia64, x86_64 or ppc64). The 'ocfs2' package contains the kernel modules, which must match up exactly with the running kernel version (`uname -r` including -smp/-bigsmp/-hugemem/etc.) The naming convention for these packages is: ocfs2console-..rpm ocfs2tools-..rpm (e.g.: ocfs2console-0.99.699-1.i386.rpm) ocfs2--..rpm (e.g.: ocfs2-2.6.9-5.EL-0.99.2015-1.i686.rpm) 3. Configuration (/etc/ocfs2/cluster.conf) --------------------------------------- The main configuration file for OCFS2 is '/etc/ocfs2/cluster.conf'. This file should be the same on all nodes in the cluster, and changes to this file must be propagated to the other nodes in the cluster. If a new node is being added to the cluster, all existing nodes must have their 'cluster.conf' updated BEFORE mounting the ocfs2 partition from the new node. OCFS2 provides and strongly recommends generating and editing this file using 'ocfs2console'. This file can also be created using 'o2cb_ctl'. Refer to the man pages for more details on these commands. The configuration file /etc/ocfs2/cluster.conf is in stanza format, with one stanza describing the generic cluster attributes and one stanza for each node. 3.1. Valid parameters: ================ 3.1. Valid parameters for the cluster stanza ======================================= node_count - This parameter specifies the number of nodes in the cluster. This parameter is exclusive for cluster stanza. name - This parameter identifies the name of the cluster. 3.1. Valid parameters for the node stanza ==================================== ip_port - This parameter specifies what IP port will be used by the OCFS2 cluster stack to communicate to the other nodes. ip_address - IP address of the OCFS2 interconnect inteface. number - Node number in the cluster. For this parameter, there are two rules that needs to be followed. The node number has to be unique. name - This parameter specifies the name of this node. cluster - This is the name of the cluster. Has to match with the name specified in the cluster stanza. 3.2. /etc/ocfs2/cluster.conf sample: ============================== cluster: node_count = 3 name = OCFS2CLUSTER node: ip_port = 7777 ip_address = 139.185.118.107 number = 0 name = test1 cluster = OCFS2CLUSTER node: ip_port = 7777 ip_address = 139.185.118.106 number = 1 name = test2 cluster = OCFS2CLUSTER 3.3. Generate the configuration file. ------------------------------- 3.3.1. Using ocfs2console (GUI) interface. ---------------------------------- OCFS2 also provides a graphical utility for configuring and modifying your OCFS2 cluster. If your system has the graphical interface enabled, you can launch 'ocfs2console' to enter the graphical configuration utility. If X is running as non-root, you can use 'ssh -X root@localhost' to become root. This will allow you to run X applications as root on your current display. myuser:/home/myuser> $ ssh -X root@localhost Password: root@localhost> # ocfs2console When ocfs2console interface opens, proceed with the initial configuration. 3.3.2. Using o2cb_ctl command line Interface. ------------------------------------- This not the recommended method for creating the configuration file, but it can be used by determined users who have read and understood the 'o2cb_ctl' man page. The entire process must be performed as root. The example below will show how to create the cluster "mycluster" with two nodes (node1 and node2). First, create the cluster: # o2cb_ctl -C -n mycluster -t cluster -a name=mycluster -a node_count=2 Then, add two nodes: # o2cb_ctl -C -n node1 -t node -a number=0 -a ip_address=139.185.118.5 \ -a ip_port=7777 -a cluster=mycluster # o2cb_ctl -C -n node2 -t node -a number=0 -a ip_address=139.185.118.5 \ -a ip_port=7777 -a cluster=mycluster NOTE: During the initial creation of the configuration file, you should not make use of the o2cb_ctl parameter "-i" since there is no live configuration active. 3.4. Starting the OCFS2 Clustering Services ====================================== O2CB is the module that provides the clustering services for OCFS2. It is reponsible for node heartbeat, node management and Distributed Lock Manager (DLM). 3.4.1. Enabling automatic load on boot. =============================== To enable the automatic load of the O2CB driver on boot, execute the following script and answer the prompt. Make sure to enter the cluster name when asked or the load will fail at boot. # /etc/init.d/o2cb configure Configuring the O2CB driver. This will configure the on-boot properties of the O2CB driver. The following questions will determine whether the driver is loaded on boot. The current values will be shown in brackets ('[]'). Hitting without typing an answer will keep that current value. Ctrl-C will abort. Load O2CB driver on boot (y/n) [n]: y Cluster to start on boot (Enter "none" to clear) []: Writing O2CB configuration: OK This will configure O2CB to be loaded on boot and will load the ocfs2 modules on the next startup. 3.4.2. Performing manual load. ====================== If you don't want to configure O2CB to start on boot and don't want to configure a specific cluster to start by default, run the following commands to startup your ocfs2 cluster. # /etc/init.d/o2cb load # /etc/init.d/o2cb online mycluster This loads the modules and the live configuration of the cluster. This will not cause OCFS2 to load at boot time. 3.4.3. Stopping O2CB. ============= To stop a cluster and unload the modules: # /etc/init.d/o2cb stop 3.5 If o2cb.init does not work on your platform =========================================== Create your pseudo file system mount points: # mkdir /config /dlm Install the modules. Modprobe makes this easy: # modprobe ocfs2_dlmfs This will load configfs, ocfs2_nodemanager, ocfs2_dlm, ocfs2_dlmfs. Mount your configfs file system: # mount -t configfs none /config Mount your dlmfs file system: # mount -t ocfs2_dlmfs none /dlm Start your cluster (fill in 'CLUSTERNAME'): # o2cb_ctl -H -n CLUSTERNAME -t cluster -a online=yes You can now mount your OCFS2 partitions (see step 5) To stop the OCFS2 cluster services, make sure that all OCFS2 file systems are unmounted and then execute: # o2cb_ctl -H -n CLUSTERNAME -t cluster -a online=no 4. Creating an OCFS2 partition. --------------------------- Again, all steps here must be performed as root. Create a new physical partition using fdisk (or parted if it is an ia64 architecture). There is a minimum size for OCFS2 partitions, to store metadata and per-node information on disk. To calculate the minimal partition size for OCFS, use this formula: ((<#nodes> * ) + 40Mb) + * */ #define _XOPEN_SOURCE 600 /* Triggers magic in features.h */ #define _LARGEFILE64_SOURCE #include #include #include #include #include #include "ocfs2/ocfs2.h" #include "ocfs2/byteorder.h" static void print_usage(void) { fprintf(stderr, "Usage: check_metaecc \n"); exit(1); } /* copied from find_inode_paths.c */ static uint64_t read_number(const char *num) { uint64_t val; char *ptr; val = strtoull(num, &ptr, 0); if (!ptr || *ptr) return 0; return val; } static errcode_t check_metaecc(ocfs2_filesys *fs, uint64_t blk, char *dev, char *block) { char signature[8]; char name[256] = {0, }; struct ocfs2_block_check check; int do_check = 1; errcode_t err = 0; memcpy(signature, block, sizeof(signature)); if (!strncmp(signature, OCFS2_SUPER_BLOCK_SIGNATURE, sizeof(OCFS2_SUPER_BLOCK_SIGNATURE))) { struct ocfs2_dinode *di = (struct ocfs2_dinode *)block; check = di->i_check; snprintf(name, sizeof(name), OCFS2_SUPER_BLOCK_SIGNATURE); } else if (!strncmp(signature, OCFS2_INODE_SIGNATURE, sizeof(OCFS2_INODE_SIGNATURE))) { struct ocfs2_dinode *di; di = (struct ocfs2_dinode *)block; check = di->i_check; snprintf(name, sizeof(name), OCFS2_INODE_SIGNATURE); } else if (!strncmp(signature, OCFS2_EXTENT_BLOCK_SIGNATURE, sizeof(OCFS2_EXTENT_BLOCK_SIGNATURE))) { struct ocfs2_extent_block *eb; eb = (struct ocfs2_extent_block *)block; check = eb->h_check; snprintf(name, sizeof(name), OCFS2_EXTENT_BLOCK_SIGNATURE); } else if (!strncmp(signature, OCFS2_GROUP_DESC_SIGNATURE, sizeof(OCFS2_GROUP_DESC_SIGNATURE))) { struct ocfs2_group_desc *gd; gd = (struct ocfs2_group_desc *)block; check = gd->bg_check; snprintf(name, sizeof(name), OCFS2_GROUP_DESC_SIGNATURE); } else if (!strncmp(signature, OCFS2_XATTR_BLOCK_SIGNATURE, sizeof(OCFS2_XATTR_BLOCK_SIGNATURE))) { struct ocfs2_xattr_block *xb; xb = (struct ocfs2_xattr_block *)block; check = xb->xb_check; snprintf(name, sizeof(name), OCFS2_XATTR_BLOCK_SIGNATURE); } else if (!strncmp(signature, OCFS2_REFCOUNT_BLOCK_SIGNATURE, sizeof(OCFS2_REFCOUNT_BLOCK_SIGNATURE))) { struct ocfs2_refcount_block *rb; rb = (struct ocfs2_refcount_block *)block; check = rb->rf_check; snprintf(name, sizeof(name), OCFS2_REFCOUNT_BLOCK_SIGNATURE); } else if (!strncmp(signature, OCFS2_DX_ROOT_SIGNATURE, sizeof(OCFS2_DX_ROOT_SIGNATURE))) { struct ocfs2_dx_root_block *dx_root; dx_root = (struct ocfs2_dx_root_block *)block; check = dx_root->dr_check; snprintf(name, sizeof(name), OCFS2_DX_ROOT_SIGNATURE); } else if (!strncmp(signature, OCFS2_DX_LEAF_SIGNATURE, sizeof(OCFS2_DX_LEAF_SIGNATURE))) { struct ocfs2_dx_leaf *dx_leaf; dx_leaf = (struct ocfs2_dx_leaf *)block; check = dx_leaf->dl_check; snprintf(name, sizeof(name), OCFS2_DX_LEAF_SIGNATURE); } else { if (ocfs2_supports_dir_trailer(fs)) { struct ocfs2_dir_block_trailer *trailer; trailer = ocfs2_dir_trailer_from_block(fs, block); if (!strncmp((char *)trailer->db_signature, OCFS2_DIR_TRAILER_SIGNATURE, sizeof(OCFS2_DIR_TRAILER_SIGNATURE))) { check = trailer->db_check; snprintf(name, sizeof(name), OCFS2_DIR_TRAILER_SIGNATURE); } } else { snprintf(name, sizeof(name), "Unknow: 0x%x%x%x%x%x%x%x%x\n", signature[0], signature[1], signature[2], signature[3], signature[4], signature[5], signature[6], signature[7]); do_check = 0; } } fprintf(stderr, "Signature of block #%"PRIu64" on " "device %s : \"%s\"\n", blk, dev, name); /* modified from ocfs2_block_check_validate(), * rested code is only display format related */ if (do_check) { struct ocfs2_block_check new_check; uint32_t crc, ecc; int crc_offset, result_offset, offset; char outbuf[256] = {0,}; new_check.bc_crc32e = le32_to_cpu(check.bc_crc32e); new_check.bc_ecc = le16_to_cpu(check.bc_ecc); memset(&check, 0, sizeof(struct ocfs2_block_check)); crc_offset = snprintf(outbuf, sizeof(outbuf), "Block %4"PRIu64" ", blk); result_offset = snprintf(outbuf + crc_offset, sizeof(outbuf) - crc_offset, "CRC32: %.8"PRIx32" " "ECC: %.4"PRIx16" ", new_check.bc_crc32e, new_check.bc_ecc); result_offset += crc_offset; /* Fast path - if the crc32 validates, we're good to go */ crc = crc32_le(~0, (void *)block, fs->fs_blocksize); if (crc == new_check.bc_crc32e) { snprintf(outbuf + result_offset, sizeof(outbuf) - result_offset, "PASS\n"); fprintf(stderr, "%s", outbuf); goto do_check_end; } /* OK, try ECC fixups */ ecc = ocfs2_hamming_encode_block(block, fs->fs_blocksize); ocfs2_hamming_fix_block(block, fs->fs_blocksize, ecc ^ new_check.bc_ecc); crc = crc32_le(~0, (void *)block, fs->fs_blocksize); if (crc == new_check.bc_crc32e) { snprintf(outbuf + result_offset, sizeof(outbuf) - result_offset, "ECC Fixup\n"); fprintf(stderr, "%s", outbuf); goto do_check_end; } snprintf(outbuf + result_offset, sizeof(outbuf) - result_offset, "FAIL\n"); fprintf(stderr, "%s", outbuf); offset = snprintf(outbuf, sizeof(outbuf), "Calculated"); while (offset < crc_offset) outbuf[offset++] = ' '; snprintf(outbuf + crc_offset, sizeof(outbuf) - crc_offset, "CRC32: %.8"PRIx32" ECC: %.4"PRIx16"\n", crc, ecc); fprintf(stderr, "%s", outbuf); err = -1; do_check_end: check.bc_crc32e = cpu_to_le32(new_check.bc_crc32e); check.bc_ecc = cpu_to_le16(new_check.bc_ecc); } return err; } int main(int argc, char *argv[]) { errcode_t err; int ret = 1; int force = 0; ocfs2_filesys *fs; char *dev, *block; uint64_t blkno; int c; static struct option long_options[] = { {"force", 0, 0, 'F'}, {0, 0, 0, 0} }; while (1) { c = getopt_long(argc, argv, "F", long_options, NULL); if (c == -1) break; switch (c) { case 'F': force = 1; break; default: print_usage(); break; } } if (optind != (argc - 2)) print_usage(); initialize_ocfs_error_table(); dev = argv[optind]; blkno = read_number(argv[optind + 1]); if (blkno == 0) { fprintf(stderr, "invalid block number\n"); print_usage(); } err = ocfs2_open(dev, OCFS2_FLAG_RO, 0, 0, &fs); if (err) { com_err(argv[0], err, "while opening device \"%s\"", dev); goto out; } if (!ocfs2_meta_ecc(OCFS2_RAW_SB(fs->fs_super))) { fprintf(stderr, "metaecc feature is not enabled on volume %s, " "validation might be invalid.\n", dev); if (!force) { fprintf(stderr, "To skip this check, use --force or -F\n"); goto out; } } err = ocfs2_malloc_block(fs->fs_io, &block); if (err) { com_err(argv[0], err, "while reading block #%"PRIu64" on \"%s\"\n", blkno, dev); goto out_close; } err = ocfs2_read_blocks(fs, blkno, 1, block); if (err) { com_err(argv[0], err, "while reading block #%"PRIu64" on \"%s\"\n", blkno, dev); goto out_free; } err = check_metaecc(fs, blkno, dev, block); out_free: ocfs2_free(&block); out_close: err = ocfs2_close(fs); if (err) { com_err(argv[0], err, "while closing device \"%s\"", dev); ret = 1; } out: return ret; } ocfs2-tools-ocfs2-tools-1.8.6/extras/compute_groups.c000066400000000000000000000040741347147137200226130ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * compute_groups.c * * Lists the offsets of the group descriptors for all * block/cluster size combinations for a devuce of a given size. * * Copyright (C) 2006 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License, version 2, as published by the Free Software Foundation. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * * Authors: Tao Ma */ #include #include #include #include #include #include #include "ocfs2/ocfs2.h" #define TWO_TERA 2199023255552LL static void stringyfy(uint32_t val, char x, char *str) { if (val == 512) sprintf(str, "%c=512", x); else sprintf(str, "%c=%uK", x, val/1024); } int main (int argc, char **argv) { uint32_t cs; /* cluster bits */ uint32_t bs; /* block bits */ uint32_t cpg; /* cluster per group */ uint64_t clsoff; uint64_t bytoff; uint64_t max_size = TWO_TERA; char blkstr[20]; char clsstr[20]; if (argc > 1) max_size = strtoull(argv[1], NULL, 0); printf("Listing all group descriptor offsets for a volume of " "size %"PRIu64" bytes\n", max_size); for (bs = 9; bs < 13; bs++) { cpg = ocfs2_group_bitmap_size(1 << bs, 0, 0) * 8; stringyfy((1 << bs), 'b', blkstr); for (cs = 12; cs < 21; cs++) { for (bytoff = 0, clsoff = 0; bytoff < max_size; ) { stringyfy((1 << cs), 'c', clsstr); printf("%-15"PRIu64" %-7s %-7s\n", bytoff, clsstr, blkstr); clsoff += cpg; bytoff = clsoff * (1 << cs); } } } return 0; } ocfs2-tools-ocfs2-tools-1.8.6/extras/decode_lockres.c000066400000000000000000000071551347147137200225100ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * decode_lockres.c * * Tells you all the information about an ocfs2 lockres available * based on it's name. Very useful for debugging dlm issues. * * Copyright (C) 2004 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License, version 2, as published by the Free Software Foundation. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * * Authors: Mark Fasheh */ #include #include #include /* Begin paste from kernel module */ enum ocfs2_lock_type { OCFS2_TYPE_META = 0, OCFS2_TYPE_DATA, OCFS2_TYPE_SUPER, OCFS2_TYPE_RENAME, OCFS2_TYPE_RW, OCFS2_NUM_LOCK_TYPES }; /* lock ids are made up in the following manner: * name[0] --> type * name[1-6] --> 6 pad characters, reserved for now * name[7-22] --> block number, expressed in hex as 16 chars * name[23-30] --> i_generation, expressed in hex 8 chars * name[31] --> '\0' */ #define OCFS2_LOCK_ID_MAX_LEN 32 #define OCFS2_LOCK_ID_PAD "000000" static char ocfs2_lock_type_char[OCFS2_NUM_LOCK_TYPES] = { [OCFS2_TYPE_META] = 'M', [OCFS2_TYPE_DATA] = 'D', [OCFS2_TYPE_SUPER] = 'S', [OCFS2_TYPE_RENAME] = 'R', [OCFS2_TYPE_RW] = 'W', }; /* End paste from kernel module */ static char * ocfs2_lock_type_string[OCFS2_NUM_LOCK_TYPES] = { [OCFS2_TYPE_META] = "Metadata", [OCFS2_TYPE_DATA] = "Data", [OCFS2_TYPE_SUPER] = "Superblock", [OCFS2_TYPE_RENAME] = "Rename", [OCFS2_TYPE_RW] = "Write/Read", }; static void usage(char *program) { printf("%s LOCKRES\n", program); printf("prints out information based on the lockres name\n"); } static const char *get_lock_type_string(char c) { enum ocfs2_lock_type t; if (c == ocfs2_lock_type_char[OCFS2_TYPE_META]) t = OCFS2_TYPE_META; else if (c == ocfs2_lock_type_char[OCFS2_TYPE_DATA]) t = OCFS2_TYPE_DATA; else if (c == ocfs2_lock_type_char[OCFS2_TYPE_SUPER]) t = OCFS2_TYPE_SUPER; else if (c == ocfs2_lock_type_char[OCFS2_TYPE_RENAME]) t = OCFS2_TYPE_RENAME; else if (c == ocfs2_lock_type_char[OCFS2_TYPE_RW]) t = OCFS2_TYPE_RW; else return NULL; return ocfs2_lock_type_string[t]; } static int decode_one_lockres(const char *lockres) { const char *type; int i; unsigned long long blkno; unsigned int generation; char blkstr[17]; if ((strlen(lockres) + 1) != OCFS2_LOCK_ID_MAX_LEN) { fprintf(stderr, "Invalid lockres id \"%s\"\n", lockres); return 1; } type = get_lock_type_string(lockres[0]); if (!type) { fprintf(stderr, "Invalid lockres type, '%c'\n", lockres[0]); return 1; } printf("Lockres: %s\n", lockres); printf("Type: %s\n", type); i = 1 + strlen(OCFS2_LOCK_ID_PAD); memset(blkstr, 0, 17); memcpy(blkstr, &lockres[i], 16); blkno = strtoull(blkstr, NULL, 16); printf("Block: %llu\n", blkno); i+= 16; generation = strtoul(&lockres[i], NULL, 16); printf("Generation: 0x%08x\n", generation); printf("\n"); return 0; } int main(int argc, char **argv) { int i, status = 0; if (argc < 2) { usage(argv[0]); return 0; } for(i = 1; i < argc; i++) { status = decode_one_lockres(argv[i]); if (status) break; } return status; } ocfs2-tools-ocfs2-tools-1.8.6/extras/encode_lockres.c000066400000000000000000000045301347147137200225140ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * encode_lockres.c * * Encodes a lockres name based on information passed * * Copyright (C) 2005 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License, version 2, as published by the Free Software Foundation. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * * Authors: Mark Fasheh */ #include #include #include #include #include #include /* Begin paste from kernel module */ enum ocfs2_lock_type { OCFS2_TYPE_META = 0, OCFS2_TYPE_DATA, OCFS2_TYPE_SUPER, OCFS2_NUM_LOCK_TYPES }; /* lock ids are made up in the following manner: * name[0] --> type * name[1-6] --> 6 pad characters, reserved for now * name[7-22] --> block number, expressed in hex as 16 chars * name[23-30] --> i_generation, expressed in hex 8 chars * name[31] --> '\0' */ #define OCFS2_LOCK_ID_MAX_LEN 32 #define OCFS2_LOCK_ID_PAD "000000" static char ocfs2_lock_type_char[OCFS2_NUM_LOCK_TYPES] = { [OCFS2_TYPE_META] 'M', [OCFS2_TYPE_DATA] 'D', [OCFS2_TYPE_SUPER] 'S' }; /* End paste from kernel module */ static void usage(char *program) { printf("%s [M|D|S] [blkno] [generation]\n", program); printf("encodes a lockres name\n"); } int main(int argc, char **argv) { uint64_t blkno; uint32_t generation; unsigned long long tmp; char type; int i; if (argc < 4) { usage(argv[0]); return 0; } type = argv[1][0]; blkno = atoll(argv[2]); tmp = atoll(argv[3]); generation = (uint32_t) tmp; for (i = 0; i < OCFS2_NUM_LOCK_TYPES; i++) if (type == ocfs2_lock_type_char[i]) break; if (i == OCFS2_NUM_LOCK_TYPES) { fprintf(stderr, "Invalid lock type '%c'\n", type); return 1; } fprintf(stdout, "%c%s%016"PRIx64"%08x\n", type, OCFS2_LOCK_ID_PAD, blkno, generation); return 0; } ocfs2-tools-ocfs2-tools-1.8.6/extras/find_allocation_fragments.c000066400000000000000000000115671347147137200247400ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * find_allocation_fragments.c * * Find fragments of free space in a given allocator * * Copyright (C) 2006 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License, version 2, as published by the Free Software Foundation. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * * Authors: Mark Fasheh */ #define _XOPEN_SOURCE 600 /* Triggers magic in features.h */ #define _LARGEFILE64_SOURCE #include #include #include #include #include "ocfs2/ocfs2.h" #include "ocfs2/bitops.h" struct fragment { uint64_t f_group_blkno; uint16_t f_chain; uint16_t f_bit_start; uint16_t f_num_bits; }; #define FREE_BIT_STATS 200 int free_bit_stats[FREE_BIT_STATS]; struct fragment largest = {0, }; static void print_usage(void) { fprintf(stderr, "Usage: find_allocation_fragments \n" "Will print all free space fragments found in the allocator whose\n" "inode is located at on device \n"); } static int find_next_region(struct ocfs2_group_desc *gd, int offset, int *start, int *end) { int ret; if (offset >= gd->bg_bits) return 0; ret = ocfs2_find_next_bit_clear(gd->bg_bitmap, gd->bg_bits, offset); if (ret == gd->bg_bits) return 0; *start = ret; *end = ocfs2_find_next_bit_set(gd->bg_bitmap, gd->bg_bits, *start); return 1; } static int print_group(struct ocfs2_group_desc *gd) { int offset, start, end, free; int header = 0; offset = 0; while (find_next_region(gd, offset, &start, &end)) { if (!header) { printf("%-6s %-6s %-12s\n", "Free", "At Bit", "In Group"); header = 1; } free = end - start; printf("%-6u %-6u %"PRIu64"\n", free, start, (uint64_t)gd->bg_blkno); if (free < FREE_BIT_STATS) free_bit_stats[free]++; if (largest.f_num_bits < (end - start)) { largest.f_group_blkno = gd->bg_blkno; largest.f_chain = gd->bg_chain; largest.f_bit_start = start; largest.f_num_bits = free; } offset = end; } printf("\n"); return 0; } static int iterate_chain(ocfs2_filesys *fs, uint64_t start) { errcode_t ret; uint64_t gd_blkno; char *buf = NULL; struct ocfs2_group_desc *gd; ret = ocfs2_malloc_block(fs->fs_io, &buf); if (ret) return ret; gd_blkno = start; do { ret = ocfs2_read_group_desc(fs, gd_blkno, buf); if (ret) goto out_free; gd = (struct ocfs2_group_desc *) buf; print_group(gd); gd_blkno = gd->bg_next_group; } while (gd_blkno); out_free: ocfs2_free(&buf); return ret; } #define BITMAP_FLAGS (OCFS2_VALID_FL|OCFS2_SYSTEM_FL|OCFS2_BITMAP_FL|OCFS2_CHAIN_FL) static int iterate_allocator(ocfs2_filesys *fs, uint64_t blkno) { int i; errcode_t ret; char *buf = NULL; struct ocfs2_dinode *di; struct ocfs2_chain_list *cl; ret = ocfs2_malloc_block(fs->fs_io, &buf); if (ret) return ret; ret = ocfs2_read_inode(fs, blkno, buf); if (ret) goto out_free; di = (struct ocfs2_dinode *) buf; if (!(di->i_flags & BITMAP_FLAGS)) { ret = OCFS2_ET_CORRUPT_CHAIN; goto out_free; } printf("Allocator Inode: %"PRIu64"\n\n", blkno); cl = &di->id2.i_chain; for(i = 0; i < cl->cl_next_free_rec; i++) { ret = iterate_chain(fs, cl->cl_recs[i].c_blkno); if (ret) goto out_free; } if (largest.f_num_bits) printf("Largest empty extent of %u bits at offset %u in " "descriptor %"PRIu64"\n", largest.f_num_bits, largest.f_bit_start, largest.f_group_blkno); out_free: ocfs2_free(&buf); return ret; } int main(int argc, char **argv) { errcode_t ret; char *device; uint64_t inode; ocfs2_filesys *fs; int i; initialize_ocfs_error_table(); if (argc != 3) { print_usage(); exit(1); } device = argv[1]; inode = atoll(argv[2]); memset(free_bit_stats, 0, sizeof(free_bit_stats)); ret = ocfs2_open(device, OCFS2_FLAG_RO, 0, 0, &fs); if (ret) { com_err(argv[0], ret, "while opening file \"%s\"", device); goto out; } ret = iterate_allocator(fs, inode); if (ret) { com_err(argv[0], ret, "while iterating allocator %"PRIu64"\n", inode); goto out_close; } printf("Statistics:\n"); printf("%-6s %-6s\n", "Count", "Bits"); for (i = 1; i < FREE_BIT_STATS; ++i) { if (free_bit_stats[i]) printf("%-6u %-6u\n", free_bit_stats[i], i); } out_close: ret = ocfs2_close(fs); if (ret) { com_err(argv[0], ret, "while closing file \"%s\"", device); } out: return ret ? 1 : 0; } ocfs2-tools-ocfs2-tools-1.8.6/extras/find_dup_extents.c000066400000000000000000000142761347147137200231070ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * find_dup_extents.c * * Simple tool to iterate the inodes and find duplicate extents. * * Copyright (C) 2004 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License, version 2, as published by the Free Software Foundation. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * * Authors: Joel Becker * * This code is a port of e2fsprogs/lib/ext2fs/dir_iterate.c * Copyright (C) 1993, 1994, 1994, 1995, 1996, 1997 Theodore Ts'o. */ #define _XOPEN_SOURCE 600 /* Triggers magic in features.h */ #define _LARGEFILE64_SOURCE #include #include #include #include "ocfs2/ocfs2.h" static void print_usage(void) { fprintf(stderr, "Usage: find_dup_extents \n"); } struct walk_extents { char *argv0; ocfs2_filesys *fs; uint64_t blkno; int has_dups; ocfs2_bitmap *extent_map; ocfs2_bitmap *dup_map; }; static int extent_set_func(ocfs2_filesys *fs, struct ocfs2_extent_rec *rec, int tree_depth, uint32_t ccount, uint64_t ref_blkno, int ref_recno, void *priv_data) { errcode_t ret; struct walk_extents *we = priv_data; uint32_t cluster, i; int oldval; int b_to_c_bits = OCFS2_RAW_SB(fs->fs_super)->s_clustersize_bits - OCFS2_RAW_SB(fs->fs_super)->s_blocksize_bits; cluster = (uint32_t)(rec->e_blkno >> b_to_c_bits); for (i = 0; i < ocfs2_rec_clusters(tree_depth, rec); i++) { ret = ocfs2_bitmap_set(we->extent_map, cluster + i, &oldval); if (ret) { com_err(we->argv0, ret, "while setting bit for cluster %u", cluster + i); return OCFS2_EXTENT_ABORT; } if (oldval) { we->has_dups = 1; ret = ocfs2_bitmap_set(we->dup_map, cluster + i, NULL); if (ret) { com_err(we->argv0, ret, "while setting bit for cluster %u", cluster + i); return OCFS2_EXTENT_ABORT; } } } return 0; } static int extent_test_func(ocfs2_filesys *fs, struct ocfs2_extent_rec *rec, int tree_depth, uint32_t ccount, uint64_t ref_blkno, int ref_recno, void *priv_data) { errcode_t ret; struct walk_extents *we = priv_data; uint32_t cluster, i; int oldval; int b_to_c_bits = OCFS2_RAW_SB(fs->fs_super)->s_clustersize_bits - OCFS2_RAW_SB(fs->fs_super)->s_blocksize_bits; cluster = (uint32_t)(rec->e_blkno >> b_to_c_bits); for (i = 0; i < ocfs2_rec_clusters(tree_depth, rec); i++) { ret = ocfs2_bitmap_test(we->dup_map, cluster + i, &oldval); if (ret) { com_err(we->argv0, ret, "while checking bit for cluster %u", cluster + i); return OCFS2_EXTENT_ABORT; } if (oldval) { fprintf(stdout, "Dup! %20"PRIu64" : %u\n", we->blkno, cluster + i); } } return 0; } static errcode_t run_scan(struct walk_extents *we, int test) { errcode_t ret; uint64_t blkno; char *buf; int done; struct ocfs2_dinode *di; ocfs2_inode_scan *scan; int (*extent_func)(ocfs2_filesys *fs, struct ocfs2_extent_rec *rec, int tree_depth, uint32_t ccount, uint64_t ref_blkno, int ref_recno, void *priv_data); if (test) extent_func = extent_test_func; else extent_func = extent_set_func; ret = ocfs2_malloc_block(we->fs->fs_io, &buf); if (ret) { com_err(we->argv0, ret, "while allocating inode buffer"); return ret; } di = (struct ocfs2_dinode *)buf; ret = ocfs2_open_inode_scan(we->fs, &scan); if (ret) { com_err(we->argv0, ret, "while opening inode scan"); goto out_free; } done = 0; while (!done) { ret = ocfs2_get_next_inode(scan, &blkno, buf); if (ret) { com_err(we->argv0, ret, "while getting next inode"); goto out_close_scan; } if (blkno) { if (memcmp(di->i_signature, OCFS2_INODE_SIGNATURE, strlen(OCFS2_INODE_SIGNATURE))) continue; ocfs2_swap_inode_to_cpu(we->fs, di); if (!(di->i_flags & OCFS2_VALID_FL)) continue; if ((di->i_flags & OCFS2_SYSTEM_FL) && (di->i_flags & (OCFS2_SUPER_BLOCK_FL | OCFS2_LOCAL_ALLOC_FL | OCFS2_CHAIN_FL))) continue; if (!di->i_clusters && S_ISLNK(di->i_mode)) continue; we->blkno = blkno; ret = ocfs2_extent_iterate(we->fs, blkno, OCFS2_EXTENT_FLAG_DATA_ONLY, NULL, extent_func, we); if (ret) { com_err(we->argv0, ret, "while walking inode %"PRIu64, blkno); goto out_close_scan; } } else done = 1; } out_close_scan: ocfs2_close_inode_scan(scan); out_free: ocfs2_free(&buf); return ret; } int main(int argc, char *argv[]) { errcode_t ret; char *filename; ocfs2_filesys *fs; struct walk_extents we; initialize_ocfs_error_table(); if (argc < 2) { fprintf(stderr, "Missing filename\n"); print_usage(); return 1; } filename = argv[1]; we.argv0 = argv[0]; we.has_dups = 0; ret = ocfs2_open(filename, OCFS2_FLAG_RO, 0, 0, &fs); if (ret) { com_err(argv[0], ret, "while opening file \"%s\"", filename); goto out; } we.fs = fs; ret = ocfs2_cluster_bitmap_new(fs, "Used extent map", &we.extent_map); if (ret) { com_err(argv[0], ret, "while creating the extent map"); goto out_close; } ret = ocfs2_cluster_bitmap_new(fs, "Dup extent map", &we.dup_map); if (ret) { ocfs2_bitmap_free(&we.extent_map); com_err(argv[0], ret, "while creating the dup map"); goto out_close; } ret = run_scan(&we, 0); if (!ret && we.has_dups) ret = run_scan(&we, 1); ocfs2_bitmap_free(&we.extent_map); ocfs2_bitmap_free(&we.dup_map); out_close: ret = ocfs2_close(fs); if (ret) { com_err(argv[0], ret, "while closing file \"%s\"", filename); } out: return 0; } ocfs2-tools-ocfs2-tools-1.8.6/extras/find_hardlinks.c000066400000000000000000000144101347147137200225120ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * find_hardlinks.c * * Simple tool to iterate the directories and find hardlinks. * * Copyright (C) 2004 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License, version 2, as published by the Free Software Foundation. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * * Authors: Joel Becker * * This code is a port of e2fsprogs/lib/ext2fs/dir_iterate.c * Copyright (C) 1993, 1994, 1994, 1995, 1996, 1997 Theodore Ts'o. */ #define _XOPEN_SOURCE 600 /* Triggers magic in features.h */ #define _LARGEFILE64_SOURCE #include #include #include #include #include "ocfs2/ocfs2.h" static void print_usage(void) { fprintf(stderr, "Usage: find_hardlinks [-q]\n"); } struct walk_path { char *argv0; ocfs2_filesys *fs; char *path; int quiet; int has_dups; int check_dups; ocfs2_bitmap *inode_map; ocfs2_bitmap *dup_map; }; static int walk_tree_func(struct ocfs2_dir_entry *dentry, uint64_t blocknr, int offset, int blocksize, char *buf, void *priv_data) { errcode_t ret; int len, oldval; int reti = 0; char *old_path, *path; struct walk_path *wp = priv_data; if (!strncmp(dentry->name, ".", dentry->name_len) || !strncmp(dentry->name, "..", dentry->name_len)) return 0; len = strlen(wp->path); if (len + dentry->name_len > 4095) { fprintf(stderr, "name is too long in %s\n", wp->path); return OCFS2_DIRENT_ABORT; } ret = ocfs2_malloc0(4096, &path); if (ret) { com_err(wp->argv0, ret, "while allocating path memory in %s\n", wp->path); return OCFS2_DIRENT_ABORT; } memcpy(path, wp->path, len); memcpy(path + len, dentry->name, dentry->name_len); if (dentry->file_type == OCFS2_FT_DIR) path[len + dentry->name_len] = '/'; if (wp->check_dups) { ret = ocfs2_bitmap_test(wp->dup_map, dentry->inode, &oldval); if (oldval) { fprintf(stdout, "Dup! %20"PRIu64" %s\n", (uint64_t)dentry->inode, path); } goto out; } oldval = 0; ret = ocfs2_bitmap_set(wp->inode_map, dentry->inode, &oldval); if (ret) { com_err(wp->argv0, ret, "while setting bitmap bit %"PRIu64"\n", (uint64_t)dentry->inode); reti = OCFS2_DIRENT_ABORT; goto out; } if (oldval) { wp->has_dups = 1; ret = ocfs2_bitmap_set(wp->dup_map, dentry->inode, NULL); if (ret) { com_err(wp->argv0, ret, "while setting dup bit %"PRIu64"\n", (uint64_t)dentry->inode); reti = OCFS2_DIRENT_ABORT; goto out; } } if (!wp->quiet) fprintf(stdout, "%20"PRIu64" %s\n", (uint64_t)dentry->inode, path); if (dentry->file_type == OCFS2_FT_DIR) { old_path = wp->path; wp->path = path; ret = ocfs2_dir_iterate(wp->fs, dentry->inode, 0, NULL, walk_tree_func, wp); if (ret) { com_err(wp->argv0, ret, "while walking %s", wp->path); reti = OCFS2_DIRENT_ABORT; } wp->path = old_path; } out: ocfs2_free(&path); return reti; } extern int opterr, optind; extern char *optarg; int main(int argc, char *argv[]) { errcode_t ret; uint64_t blkno; char *filename; ocfs2_filesys *fs; struct walk_path wp = {0}; blkno = OCFS2_SUPER_BLOCK_BLKNO; initialize_ocfs_error_table(); wp.argv0 = argv[0]; wp.quiet = 0; wp.has_dups = 0; wp.check_dups = 0; if (argc < 2) { fprintf(stderr, "Missing filename\n"); print_usage(); return 1; } filename = argv[1]; if (argc > 2) { if (!strcmp(argv[2], "-q")) wp.quiet = 1; } ret = ocfs2_open(filename, OCFS2_FLAG_RO, 0, 0, &fs); if (ret) { com_err(argv[0], ret, "while opening file \"%s\"", filename); goto out; } wp.fs = fs; ret = ocfs2_block_bitmap_new(fs, "Inode bitmap", &wp.inode_map); if (ret) { com_err(argv[0], ret, "while creating the inode bitmap"); goto out_close; } ret = ocfs2_block_bitmap_new(fs, "Duplicate inode bitmap", &wp.dup_map); if (ret) { com_err(argv[0], ret, "while creating the duplicate inode bitmap"); goto out_close; } ocfs2_bitmap_set(wp.inode_map, OCFS2_RAW_SB(fs->fs_super)->s_system_dir_blkno, NULL); ocfs2_bitmap_set(wp.inode_map, OCFS2_RAW_SB(fs->fs_super)->s_root_blkno, NULL); fprintf(stdout, "Walking system directory...\n"); wp.path = "/"; ret = ocfs2_dir_iterate(fs, OCFS2_RAW_SB(fs->fs_super)->s_system_dir_blkno, 0, NULL, walk_tree_func, &wp); if (ret) { com_err(argv[0], ret, "while walking sysdm dir inode %"PRIu64" on \"%s\"\n", blkno, filename); goto out_close; } wp.path = "/"; fprintf(stdout, "Walking root directory...\n"); ret = ocfs2_dir_iterate(fs, OCFS2_RAW_SB(fs->fs_super)->s_root_blkno, 0, NULL, walk_tree_func, &wp); if (ret) { com_err(argv[0], ret, "while walking root inode %"PRIu64" on \"%s\"\n", blkno, filename); goto out_close; } if (wp.has_dups) { fprintf(stdout, "Hardlinks found\n"); wp.check_dups = 1; fprintf(stdout, "Scanning system directory for dups...\n"); wp.path = "/"; ret = ocfs2_dir_iterate(fs, OCFS2_RAW_SB(fs->fs_super)->s_system_dir_blkno, 0, NULL, walk_tree_func, &wp); if (ret) { com_err(argv[0], ret, "while dup scanning sysdm dir inode %"PRIu64 " on \"%s\"\n", blkno, filename); goto out_close; } wp.path = "/"; fprintf(stdout, "Scanning root directory for dups...\n"); ret = ocfs2_dir_iterate(fs, OCFS2_RAW_SB(fs->fs_super)->s_root_blkno, 0, NULL, walk_tree_func, &wp); if (ret) { com_err(argv[0], ret, "while dup scanning root inode %"PRIu64 " on \"%s\"\n", blkno, filename); goto out_close; } } out_close: if (wp.inode_map) ocfs2_bitmap_free(&wp.inode_map); if (wp.dup_map) ocfs2_bitmap_free(&wp.dup_map); ret = ocfs2_close(fs); if (ret) { com_err(argv[0], ret, "while closing file \"%s\"", filename); } out: return 0; } ocfs2-tools-ocfs2-tools-1.8.6/extras/find_inode_paths.c000066400000000000000000000110241347147137200230260ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * find_inode_paths.c * * Simple tool to take an inode block number and find all paths * leading to it. * * Copyright (C) 2004 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License, version 2, as published by the Free Software Foundation. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * * Authors: Mark Fasheh, Joel Becker * * This code is a port of e2fsprogs/lib/ext2fs/dir_iterate.c * Copyright (C) 1993, 1994, 1994, 1995, 1996, 1997 Theodore Ts'o. */ #define _XOPEN_SOURCE 600 /* Triggers magic in features.h */ #define _LARGEFILE64_SOURCE #include #include #include #include #include "ocfs2/ocfs2.h" static void print_usage(void) { fprintf(stderr, "Usage: find_inode_paths \n"); } struct walk_path { char *argv0; ocfs2_filesys *fs; char *path; int quiet; uint64_t inode; }; static int walk_tree_func(struct ocfs2_dir_entry *dentry, uint64_t blocknr, int offset, int blocksize, char *buf, void *priv_data) { errcode_t ret; int len; int reti = 0; char *old_path, *path; struct walk_path *wp = priv_data; if (!strncmp(dentry->name, ".", dentry->name_len) || !strncmp(dentry->name, "..", dentry->name_len)) return 0; len = strlen(wp->path); if (len + dentry->name_len > 4095) { fprintf(stderr, "name is too long in %s\n", wp->path); return OCFS2_DIRENT_ABORT; } ret = ocfs2_malloc0(4096, &path); if (ret) { com_err(wp->argv0, ret, "while allocating path memory in %s\n", wp->path); return OCFS2_DIRENT_ABORT; } memcpy(path, wp->path, len); memcpy(path + len, dentry->name, dentry->name_len); if (dentry->file_type == OCFS2_FT_DIR) path[len + dentry->name_len] = '/'; if (!wp->quiet) fprintf(stdout, "[trace] %13"PRIu64" %s\n", (uint64_t)dentry->inode, path); if (dentry->inode == wp->inode) fprintf(stdout, "[found] %13"PRIu64" %s\n", (uint64_t)dentry->inode, path); if (dentry->file_type == OCFS2_FT_DIR) { old_path = wp->path; wp->path = path; ret = ocfs2_dir_iterate(wp->fs, dentry->inode, 0, NULL, walk_tree_func, wp); if (ret) { com_err(wp->argv0, ret, "while walking %s", wp->path); reti = OCFS2_DIRENT_ABORT; } wp->path = old_path; } ocfs2_free(&path); return reti; } static uint64_t read_number(const char *num) { uint64_t val; char *ptr; val = strtoull(num, &ptr, 0); if (!ptr || *ptr) return 0; return val; } extern int opterr, optind; extern char *optarg; int main(int argc, char *argv[]) { errcode_t ret; uint64_t blkno; char *filename; ocfs2_filesys *fs; struct walk_path wp; blkno = OCFS2_SUPER_BLOCK_BLKNO; initialize_ocfs_error_table(); wp.argv0 = argv[0]; wp.quiet = 1; if (argc < 3) { fprintf(stderr, "Missing filename\n"); print_usage(); return 1; } filename = argv[1]; wp.inode = read_number(argv[2]); if (!wp.inode) { fprintf(stderr, "invalid inode number\n"); print_usage(); return 1; } ret = ocfs2_open(filename, OCFS2_FLAG_RO, 0, 0, &fs); if (ret) { com_err(argv[0], ret, "while opening file \"%s\"", filename); goto out; } wp.fs = fs; fprintf(stdout, "Finding all paths leading to inode %"PRIu64"\n", wp.inode); if (!wp.quiet) fprintf(stdout, "Walking system directory...\n"); wp.path = "/"; ret = ocfs2_dir_iterate(fs, OCFS2_RAW_SB(fs->fs_super)->s_system_dir_blkno, 0, NULL, walk_tree_func, &wp); if (ret) { com_err(argv[0], ret, "while walking sysdm dir inode %"PRIu64" on \"%s\"\n", blkno, filename); goto out_close; } wp.path = "/"; if (!wp.quiet) fprintf(stdout, "Walking root directory...\n"); ret = ocfs2_dir_iterate(fs, OCFS2_RAW_SB(fs->fs_super)->s_root_blkno, 0, NULL, walk_tree_func, &wp); if (ret) { com_err(argv[0], ret, "while walking root inode %"PRIu64" on \"%s\"\n", blkno, filename); goto out_close; } out_close: ret = ocfs2_close(fs); if (ret) { com_err(argv[0], ret, "while closing file \"%s\"", filename); } out: return 0; } ocfs2-tools-ocfs2-tools-1.8.6/extras/mark_journal_dirty.c000066400000000000000000000127371347147137200234440ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * mark_journal_dirty.c * * Marks the journal for a given slot # as dirty. * * Copyright (C) 2005 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License, version 2, as published by the Free Software Foundation. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * * Authors: Mark Fasheh */ #define _XOPEN_SOURCE 600 /* Triggers magic in features.h */ #define _LARGEFILE64_SOURCE #include #include #include #include #include "ocfs2/byteorder.h" #include "ocfs2/ocfs2.h" static int debug = 0; static void print_usage(void) { fprintf(stderr, "Usage: mark_journal_dirty \n"); fprintf(stderr, "Will insert node into slot and " "mark the journal in as needing recovery.\n"); } static errcode_t mark_journal(ocfs2_filesys *fs, uint64_t blkno) { errcode_t ret; char *buf = NULL; struct ocfs2_dinode *di; ret = ocfs2_malloc_block(fs->fs_io, &buf); if (ret) return ret; ret = ocfs2_read_inode(fs, blkno, buf); if (ret) goto out_free; di = (struct ocfs2_dinode *) buf; if (!(di->i_flags & OCFS2_JOURNAL_FL)) { ret = OCFS2_ET_INVALID_ARGUMENT; fprintf(stderr, "Block %"PRIu64" is not a journal inode!\n", blkno); goto out_free; } di->id1.journal1.ij_flags |= OCFS2_JOURNAL_DIRTY_FL; ret = ocfs2_write_inode(fs, blkno, buf); out_free: ocfs2_free(&buf); return ret; } static errcode_t write_back_slot_map(ocfs2_filesys *fs, uint64_t slot_map_blkno, char *slots_buf) { errcode_t ret; char *di_buf = NULL; struct ocfs2_dinode *di; uint64_t block; struct ocfs2_extent_list *el; ret = ocfs2_malloc_block(fs->fs_io, &di_buf); if (ret) return ret; ret = ocfs2_read_inode(fs, slot_map_blkno, di_buf); if (ret) goto out_free; di = (struct ocfs2_dinode *) di_buf; el = &di->id2.i_list; block = el->l_recs[0].e_blkno; if (el->l_tree_depth || !block) { ret = OCFS2_ET_INVALID_EXTENT_LOOKUP; goto out_free; } if (debug) fprintf(stdout, "Write back slot data at block %"PRIu64"\n", block); ret = io_write_block(fs->fs_io, block, 1, slots_buf); out_free: ocfs2_free(&di_buf); return ret; } static errcode_t insert_node_into_slot(ocfs2_filesys *fs, int node, int slot) { errcode_t ret; int i, num_slots; char *buf = NULL; uint64_t slot_map_blkno; int len = fs->fs_blocksize; int16_t *slots; ret = ocfs2_lookup_system_inode(fs, SLOT_MAP_SYSTEM_INODE, -1, &slot_map_blkno); if (ret) goto out; ret = ocfs2_read_whole_file(fs, slot_map_blkno, &buf, &len); if (ret) goto out; num_slots = fs->fs_super->id2.i_super.s_max_slots; if (debug) fprintf(stdout, "%d slots on this device\n", num_slots); if (len < fs->fs_blocksize) { ret = OCFS2_ET_SHORT_READ; goto out; } slots = (int16_t *) buf; /* We'll allow the caller to put a different node in a * currently filled slot, but we must watch out for duplicate * nodes in slots. */ for(i = 0; i < num_slots; i++) { if (le16_to_cpu(slots[i]) == node) { ret = OCFS2_ET_INTERNAL_FAILURE; fprintf(stdout, "node %d already found in slot_map " "slot %d\n", node, i); goto out; } } slots[slot] = cpu_to_le16(node); ret = write_back_slot_map(fs, slot_map_blkno, buf); out: return ret; } static unsigned int read_number(const char *num) { unsigned long val; char *ptr; val = strtoul(num, &ptr, 0); if (!ptr || *ptr) return -1; return (unsigned int) val; } extern int opterr, optind; extern char *optarg; int main(int argc, char *argv[]) { errcode_t ret; int slot, node; uint64_t journal_blkno; char *filename; ocfs2_filesys *fs; initialize_ocfs_error_table(); if (argc < 4) { fprintf(stderr, "Missing parameters\n"); print_usage(); return 1; } filename = argv[1]; node = read_number(argv[2]); if (node == -1) { fprintf(stderr, "invalid node number\n"); print_usage(); return 1; } slot = read_number(argv[3]); if (slot == -1) { fprintf(stderr, "invalid slot number\n"); print_usage(); return 1; } ret = ocfs2_open(filename, OCFS2_FLAG_RW, 0, 0, &fs); if (ret) { com_err(argv[0], ret, "while opening file \"%s\"", filename); goto out_close; } if (debug) fprintf(stdout, "Inserting node %d into slot %d\n", node, slot); ret = insert_node_into_slot(fs, node, slot); if (ret) { com_err(argv[0], ret, "while inserting node\n"); goto out_close; } ret = ocfs2_lookup_system_inode(fs, JOURNAL_SYSTEM_INODE, slot, &journal_blkno); if (ret) { com_err(argv[0], ret, "while looking up journal in slot %d\n", slot); goto out_close; } if (debug) fprintf(stdout, "Marking journal (block %"PRIu64") in slot " "%d\n", journal_blkno, slot); ret = mark_journal(fs, journal_blkno); if (ret) { com_err(argv[0], ret, "while marking journal dirty"); goto out_close; } out_close: ret = ocfs2_close(fs); if (ret) { com_err(argv[0], ret, "while closing file \"%s\"", filename); } return 0; } ocfs2-tools-ocfs2-tools-1.8.6/extras/resize_slotmap.c000066400000000000000000000104441347147137200225760ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * resize_slotmap.c * * Copyright (C) 2011 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License, version 2, as published by the Free Software Foundation. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. */ #define _XOPEN_SOURCE 600 /* Triggers XOPEN2K in features.h */ #define _LARGEFILE64_SOURCE #include #include #include #include #include #include #include "ocfs2/byteorder.h" #include "ocfs2/ocfs2.h" char *progname; static void usage(void) { fprintf(stderr, "usage: %s device size\nUtility to resize //slotmap " "in an OCFS2 file system.\n", progname); exit(1); } static errcode_t resize_slot_map_file(ocfs2_filesys *fs, uint64_t slotsize) { errcode_t ret; uint64_t blkno, maxsize, minsize; ocfs2_cached_inode *ci = NULL; struct ocfs2_dinode *di; int c; ret = ocfs2_lookup_system_inode(fs, SLOT_MAP_SYSTEM_INODE, 0, &blkno); if (ret) goto out; ret = ocfs2_read_cached_inode(fs, blkno, &ci); if (ret) goto out; di = ci->ci_inode; if (!(di->i_flags & OCFS2_VALID_FL) || !(di->i_flags & OCFS2_SYSTEM_FL)) { ret = OCFS2_ET_INTERNAL_FAILURE; goto out; } maxsize = ocfs2_clusters_to_bytes(fs, di->i_clusters); minsize = OCFS2_MAX_SLOTS * sizeof(struct ocfs2_extended_slot); if (slotsize > maxsize) { fprintf(stderr, "Error: The requested size (%"PRIu64" bytes) is " "larger than the allocated size (%"PRIu64" bytes).\n", slotsize, maxsize); ret = OCFS2_ET_INVALID_ARGUMENT; goto out; } if (slotsize < minsize) { fprintf(stderr, "Error: The requested size (%"PRIu64" bytes) is " "smaller than the minimum acceptable size " "(%"PRIu64" bytes).\n", slotsize, minsize); ret = OCFS2_ET_INVALID_ARGUMENT; goto out; } fprintf(stdout, "About to change the size of //slotmap from %llu bytes " "to %"PRIu64" bytes.\nContinue(y/N)? ", di->i_size, slotsize); while (1) { c = getchar(); if (!isalpha(c)) continue; if (c == 'Y' || c == 'y') break; /* We should add OCFS2_ET_ABORT_OPERATION */ ret = OCFS2_ET_TOO_MANY_SLOTS; goto out; } di->i_size = slotsize; di->i_mtime = time(NULL); ret = ocfs2_write_cached_inode(fs, ci); if (ret) goto out; out: if (ci) ocfs2_free_cached_inode(fs, ci); return ret; } extern int opterr, optind; extern char *optarg; int main(int argc, char *argv[]) { errcode_t ret; char *device, *p; uint64_t size; ocfs2_filesys *fs = NULL; int c; initialize_ocfs_error_table(); #define INSTALL_SIGNAL(sig) \ do { \ if (signal(sig, handle_signal) == SIG_ERR) { \ printf("Could not set " #sig "\n"); \ goto bail; \ } \ } while (0) setbuf(stdout, NULL); setbuf(stderr, NULL); setbuf(stdin, NULL); progname = basename(argv[0]); if (argc < 3) usage(); device = argv[1]; size = strtoull(argv[2], &p, 0); if (!p || *p) { fprintf(stderr, "Error: Invalid size.\n"); usage(); } fprintf(stdout, "\nWARNING!!! Running %s with the file system mounted " "could lead to file system damage.\n", progname); fprintf(stdout, "Please ensure that the device \"%s\" is _not_ mounted " "on any node in the cluster.\nContinue(y/N)? ", device); while (1) { c = getchar(); if (!isalpha(c)) continue; if (c == 'Y' || c == 'y') break; goto out; } ret = ocfs2_open(device, OCFS2_FLAG_RW, 0, 0, &fs); if (ret) { com_err(progname, ret, "while opening device \"%s\"", device); usage(); } ret = resize_slot_map_file(fs, size); if (!ret) fprintf(stdout, "Changed the size of //slotmap on device " "\"%s\" to %"PRIu64" bytes.\n", device, size); if (ret && ret != OCFS2_ET_TOO_MANY_SLOTS) com_err(progname, ret, "while resizing //slotmap on device " "\"%s\"", device); out: if (fs) ocfs2_close(fs); return 0; } ocfs2-tools-ocfs2-tools-1.8.6/extras/set_random_bits.c000066400000000000000000000114331347147137200227110ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * set_random_bits.c * * Set a (not-so-random) pattern of alternating set bits on the global * bitmap (or any file you give me the block offset of). Note that * this will not clear any bits that have already been set. * * Copyright (C) 2004 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License, version 2, as published by the Free Software Foundation. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * * Authors: Mark Fasheh */ #define _XOPEN_SOURCE 600 /* Triggers XOPEN2K in features.h */ #define _LARGEFILE64_SOURCE #include #include #include #include #include #include "ocfs2/ocfs2.h" static uint64_t read_number(const char *num) { uint64_t val; char *ptr; val = strtoull(num, &ptr, 0); if (!ptr || *ptr) return 0; return val; } static void print_usage(void) { fprintf(stderr, "Usage: set_random_bits -i \n"); } struct walk_block { struct ocfs2_dinode *di; char *buf; uint32_t used; }; #define BITCOUNT(x) (((BX_(x)+(BX_(x)>>4)) & 0x0F0F0F0F) % 255) #define BX_(x) ((x) - (((x)>>1)&0x77777777) \ - (((x)>>2)&0x33333333) \ - (((x)>>3)&0x11111111)) static int walk_blocks_func(ocfs2_filesys *fs, uint64_t blkno, uint64_t bcount, uint16_t ext_flags, void *priv_data) { struct walk_block *wb = priv_data; errcode_t ret; int i; uint32_t *up; ret = ocfs2_read_blocks(fs, blkno, 1, wb->buf); if (ret) { com_err("walk_blocks_func", ret, "while reading block %"PRIu64, blkno); return OCFS2_BLOCK_ABORT; } /* set every other bit */ up = (uint32_t *) wb->buf; for(i = 0; i < (fs->fs_blocksize / sizeof(uint32_t)); i++) { up[i] |= 0x55555555; wb->used += BITCOUNT(up[i]); } ret = io_write_block(fs->fs_io, blkno, 1, wb->buf); if (ret) { com_err("walk_blocks_func", ret, "while writing block %"PRIu64, blkno); return OCFS2_BLOCK_ABORT; } return 0; } extern int opterr, optind; extern char *optarg; int main(int argc, char *argv[]) { errcode_t ret; uint64_t blkno, sys_blkno; int c; char *filename, *buf; const char *bitmap_name = ocfs2_system_inodes[GLOBAL_BITMAP_SYSTEM_INODE].si_name; ocfs2_filesys *fs; struct ocfs2_dinode *di; struct walk_block wb; blkno = OCFS2_SUPER_BLOCK_BLKNO; initialize_ocfs_error_table(); while ((c = getopt(argc, argv, "i:")) != EOF) { switch (c) { case 'i': blkno = read_number(optarg); if (blkno <= OCFS2_SUPER_BLOCK_BLKNO) { fprintf(stderr, "Invalid inode block: %s\n", optarg); print_usage(); return 1; } break; default: print_usage(); return 1; break; } } if (optind >= argc) { fprintf(stderr, "Missing filename\n"); print_usage(); return 1; } filename = argv[optind]; ret = ocfs2_open(filename, OCFS2_FLAG_RW, 0, 0, &fs); if (ret) { com_err(argv[0], ret, "while opening file \"%s\"", filename); goto out; } if (blkno == OCFS2_SUPER_BLOCK_BLKNO) { sys_blkno = OCFS2_RAW_SB(fs->fs_super)->s_system_dir_blkno; ret = ocfs2_lookup(fs, sys_blkno, bitmap_name, strlen(bitmap_name), NULL, &blkno); if (ret) { com_err(argv[0], ret, "while looking up \"%s\"", bitmap_name); goto out; } } ret = ocfs2_malloc_block(fs->fs_io, &buf); if (ret) { com_err(argv[0], ret, "while allocating inode buffer"); goto out_close; } memset(&wb, 0, sizeof(wb)); ret = ocfs2_read_inode(fs, blkno, buf); if (ret) { com_err(argv[0], ret, "while reading inode %"PRIu64, blkno); goto out_free; } di = (struct ocfs2_dinode *)buf; wb.di = di; ret = ocfs2_malloc_block(fs->fs_io, &wb.buf); if (ret) { com_err(argv[0], ret, "while allocating block buffer"); goto out_free; } ret = ocfs2_block_iterate(fs, blkno, 0, walk_blocks_func, &wb); if (ret) { com_err(argv[0], ret, "while walking blocks"); goto out_free; } di->id1.bitmap1.i_used = wb.used; ret = ocfs2_write_inode(fs, blkno, buf); if (ret) { com_err(argv[0], ret, "while reading inode %"PRIu64, blkno); goto out_free; } out_free: if (wb.buf) ocfs2_free(&wb.buf); ocfs2_free(&buf); out_close: ret = ocfs2_close(fs); if (ret) { com_err(argv[0], ret, "while closing file \"%s\"", filename); } out: return 0; } ocfs2-tools-ocfs2-tools-1.8.6/extras/verify_backup_super000077500000000000000000000110121347147137200233570ustar00rootroot00000000000000#!/bin/bash # # Copyright (C) 2007 Oracle. All rights reserved. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public # License, version 2, as published by the Free Software Foundation. # # 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., 51 Franklin St, Fifth Floor, # Boston, MA 02110-1301 USA. # # Author: Sunil Mushran 03/01/2007 # ################################################################ # # verify_backup_super # # This script lists all the objects using the clusters that are # reserved for OCFS2's backup superblocks. This script is # useful for users unable to retroactively enable backup super # block using tunefs.ocfs2 --backup-super. # ################################################################ usage() { echo "usage ${APP} /dev/sdX" exit 1 } get_sizes() { bsbits=`${DEBUGFS} -R "stats" ${device} 2>/dev/null | ${AWK} '/Block Size Bits/ {print $4;}'` csbits=`${DEBUGFS} -R "stats" ${device} 2>/dev/null | ${AWK} '/Cluster Size Bits/ {print $8;}'` numcls=`${DEBUGFS} -R "stats" ${device} 2>/dev/null | ${AWK} '/Clusters:/ {print $4;}'` if [ -z "$bsbits" ] || [ -a "$csbits" ] || [ -a "$numcls" ]; then echo "error: not an OCFS2 device" exit 1 fi #in bytes blocksize=$[$[2 ** $[$bsbits - 9]] * 512] clustsize=$[$[2 ** $[$csbits - 9]] * 512] #in blocks numblocks=$[$numcls * $clustsize / $blocksize] } # Ensure version is >= 1.2.3 verify_version() { ret=`${DEBUGFS} --version 2>&1 | ${AWK} '/debugfs.ocfs2/ { flds=split($2, ver, "."); if (flds >= 3) { if (ver[1] > 1) {print 1;} else if (ver[2] < 2) {print 0;} else if (ver[2] > 2) {print 1;} else if (ver[3] > 2) {print 1;} else {print 0}; } else {print 0;} }'` if [ "${ret}" = "0" ]; then echo "Upgrade to ocfs2-tools 1.2.3 or later" exit 1 fi } # Feature Compat: 1 BackupSuper has_backup_super() { feat=`${DEBUGFS} -R "stats" ${device} 2>/dev/null | ${AWK} 'BEGIN {fnd=0;} /Feature Compat/ {if (match($0, "BackupSuper")) fnd=1;} END {print fnd;}'` if [ "${feat}" = "1" ]; then echo "Backup super block already enabled on device ${device}" exit 0 fi } get_icheck_args() { if [ $numblocks -le ${backup[0]} ]; then echo "Device $device too small to hold backup superblocks" exit 0 fi for i in `${SEQ} 0 5` do block=$[${backup[$i]} / $[2 ** $[$bsbits - 9]]] if [ $block -lt $numblocks ]; then icheckargs="$icheckargs $block" else break fi done } DEBUGFS=`which debugfs.ocfs2` AWK=`which awk` SEQ=`which seq` TEE=`which tee` DATE=`which date` APP=`basename $0` if [ -z "${DEBUGFS}" ]; then echo "error: \"debugfs.ocfs2\" not found in path"; exit 1; fi if [ -z "${AWK}" ]; then echo "error: \"awk\" not found in path"; exit 1; fi if [ -z "${SEQ}" ]; then echo "error: \"seq\" not found in path"; exit 1; fi if [ -z "${TEE}" ]; then echo "error: \"tee\" not found in path"; exit 1; fi if [ -z "${DATE}" ]; then echo "error: \"date\" not found in path"; exit 1; fi blocksize=0 bsbits=0 csbits=0 clustersize=0 numblocks=0 icheckargs="" YMD=`${DATE} +%Y%m%d_%H%M%S` TEMPFILE=/tmp/__${YMD}__ # Backup super offsets in 512 byte blocks backup[0]=2097152 backup[1]=8388608 backup[2]=33554432 backup[3]=134217728 backup[4]=536870912 backup[5]=2147483648 device=$1 if [ -z "${device}" ]; then usage ; fi get_sizes verify_version has_backup_super get_icheck_args echo "Locating inodes using blocks${icheckargs} on device ${device}" #echo "${DEBUGFS} -R \"icheck${icheckargs}\" ${device}" ${DEBUGFS} -R "icheck${icheckargs}" ${device} | ${TEE} ${TEMPFILE} inodes=`${AWK} 'BEGIN {out = "";} // {if (strtonum($2) != 0) out = out " " "<"$2">";} END {print out};' ${TEMPFILE}` #echo $inodes if [ -z "${inodes}" ]; then echo "All Backup superblock clusters are unused" exit 0 fi echo "Matching inodes to object names" #echo "${DEBUGFS} -R \"findpath ${inodes}\" ${device}" ${DEBUGFS} -R "findpath ${inodes}" ${device} ocfs2-tools-ocfs2-tools-1.8.6/extras/watch-hb.sh000077500000000000000000000016501347147137200214250ustar00rootroot00000000000000#!/bin/bash die() { echo $@", exiting" > /dev/stderr exit 1 } usage() { echo "-r region the hb region to watch" echo "-d device the hb device to watch" } while getopts ":r:d:" opt; do case $opt in r) region="$OPTARG" dev=$(cat $region/dev) || die "couldn't read $region/dev" [ -z "$dev" ] && \ die "$region/dev isn't set, is the region active?" eval "$device=/dev/$dev" eval "slot_bytes=$(cat $region/$attr)" || die "couldn't read $region/$attr" ;; d) dev="$OPTARG" eval "device=$dev" eval "slot_bytes=512" ;; \?) usage esac done [ -z "$device" ] && \ die "a region must be specified with -r or a device with -d" watch -n 3 -d \ "echo \"cat //heartbeat\" | debugfs.ocfs2 -n $device 2>/dev/null \ | od -A d -x | \ awk '( NF > 2 ) { \$(1) = \$1 / $slot_bytes; if (\$1 == int(\$1)) print \$0 }'" ocfs2-tools-ocfs2-tools-1.8.6/fsck.ocfs2/000077500000000000000000000000001347147137200200225ustar00rootroot00000000000000ocfs2-tools-ocfs2-tools-1.8.6/fsck.ocfs2/.gitignore000066400000000000000000000001001347147137200220010ustar00rootroot00000000000000cscope* stamp-md5 *.sw? *.cmd fsck.ocfs2 *.8 *.d prompt-codes.h ocfs2-tools-ocfs2-tools-1.8.6/fsck.ocfs2/Cscope.make000066400000000000000000000003721347147137200220770ustar00rootroot00000000000000.PHONY: cscope cscope: rm -f cscope.* echo "-k" >> cscope.files echo "-I include" >> cscope.files find . -name '*.[ch]' >>cscope.files find ../libocfs2/ -name '*.[ch]' >> cscope.files find ../libo2dlm/ -name '*.[ch]' >> cscope.files cscope -b ocfs2-tools-ocfs2-tools-1.8.6/fsck.ocfs2/Makefile000066400000000000000000000052761347147137200214740ustar00rootroot00000000000000TOPDIR = .. include $(TOPDIR)/Preamble.make sbindir = $(root_sbindir) SBIN_PROGRAMS = fsck.ocfs2 DEFINES += -DVERSION=\"$(VERSION)\" INCLUDES = -I$(TOPDIR)/include -Iinclude LIBOCFS2_LIBS = -L$(TOPDIR)/libocfs2 -locfs2 LIBOCFS2_DEPS = $(TOPDIR)/libocfs2/libocfs2.a LIBO2DLM_LIBS = -L$(TOPDIR)/libo2dlm -lo2dlm $(DL_LIBS) LIBO2DLM_DEPS = $(TOPDIR)/libo2dlm/libo2dlm.a LIBO2CB_LIBS = -L$(TOPDIR)/libo2cb -lo2cb LIBO2CB_DEPS = $(TOPDIR)/libo2cb/libo2cb.a ifneq ($(BUILD_FSDLM_SUPPORT),) LIBO2CB_LIBS += -ldlm_lt endif ifneq ($(BUILD_CMAP_SUPPORT),) LIBO2CB_LIBS += -lcmap endif LIBTOOLS_INTERNAL_LIBS = -L$(TOPDIR)/libtools-internal -ltools-internal LIBTOOLS_INTERNAL_DEPS = $(TOPDIR)/libtools-internal/libtools-internal.a ifndef OCFS2_DYNAMIC_FSCK LDFLAGS += -static endif CFILES = fsck.c \ dirblocks.c \ dirparents.c \ extent.c \ icount.c \ journal.c \ pass0.c \ pass1.c \ pass1b.c \ pass2.c \ pass3.c \ pass4.c \ pass5.c \ problem.c \ refcount.c \ slot_recovery.c \ strings.c \ util.c \ xattr.c HFILES = include/fsck.h \ include/xattr.h \ include/dirblocks.h \ include/dirparents.h \ include/extent.h \ include/icount.h \ include/journal.h \ include/pass0.h \ include/pass1.h \ include/pass1b.h \ include/pass2.h \ include/pass3.h \ include/pass4.h \ include/pass5.h \ include/problem.h \ include/refcount.h \ include/slot_recovery.h \ include/o2fsck_strings.h \ include/util.h OBJS = $(subst .c,.o,$(CFILES)) MANS = fsck.ocfs2.8 fsck.ocfs2.checks.8 DIST_FILES = $(CFILES) $(HFILES) $(addsuffix .in,$(MANS)) DIST_RULES = dist-subdircreate dist-subdircreate: $(TOPDIR)/mkinstalldirs $(DIST_DIR)/include fsck.ocfs2: $(OBJS) $(LIBOCFS2_DEPS) $(LIBO2DLM_DEPS) $(LIBO2CB_DEPS) $(LIBTOOLS_INTERNAL_DEPS) $(LINK) $(LIBOCFS2_LIBS) $(LIBO2DLM_LIBS) $(LIBO2CB_LIBS) $(LIBTOOLS_INTERNAL_LIBS) $(COM_ERR_LIBS) $(AIO_LIBS) $(OBJS): prompt-codes.h # make it hard to add a prompt call # without documenting what it does prompt-codes.h: fsck.ocfs2.checks.8.in @echo generating $@ by parsing $^ @rm -f .$@.tmp @( echo '#ifndef __PROMPT_CODES_H__'; \ echo '#define __PROMPT_CODES_H__'; \ for a in `grep '^.SS ".*"' $^ | awk -F\" '{print $$2}'`; do\ echo '#define PR_'$$a' \'; \ echo ' (struct prompt_code){ .str = "'$$a'" }';\ done; \ echo '#endif'; \ ) > $@ .PHONY: check-prompt-dups check-prompt-dups: fsck.ocfs2 @rm -f .$@.tmp @nm $^ | \ awk -F'[ .]' '/fsck_prompt_callers_with_code/ {print $$3}' | \ sort | uniq -c | awk '($$1 > 1){print $$0}' > .$@.tmp @cat .$@.tmp @test ! -s .$@.tmp CLEAN_RULES += o2fsck-clean .PHONY: o2fsck-clean o2fsck-clean: rm -f prompt-codes.h include $(TOPDIR)/Postamble.make ocfs2-tools-ocfs2-tools-1.8.6/fsck.ocfs2/dirblocks.c000066400000000000000000000134401347147137200221440ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * Copyright (C) 2004 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License, version 2, as published by the Free Software Foundation. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * * -- * * Just a simple rbtree wrapper to record directory blocks and the inodes * that own them. */ #include #include #include #include #include #include #include "ocfs2/ocfs2.h" #include "fsck.h" #include "dirblocks.h" #include "util.h" #include "extent.h" #define NUM_RA_BLOCKS 1024 static void o2fsck_readahead_dirblocks(o2fsck_state *ost, struct rb_node *node, struct rb_node **last_read_node) { ocfs2_filesys *fs = ost->ost_fs; o2fsck_dirblock_entry *dbe; struct io_vec_unit *ivus = NULL; char *buf = NULL; int buflen = NUM_RA_BLOCKS * fs->fs_blocksize; uint32_t offset = 0; int i; errcode_t ret; *last_read_node = NULL; if (!fs->fs_io) return; if (buflen > io_get_cache_size(fs->fs_io)) return; ret = ocfs2_malloc_blocks(fs->fs_io, NUM_RA_BLOCKS, &buf); if (ret) goto out; memset(buf, 0, buflen); ret = ocfs2_malloc(sizeof(struct io_vec_unit) * NUM_RA_BLOCKS, &ivus); if (ret) goto out; for (i = 0; node && (i < NUM_RA_BLOCKS); ++i, node = rb_next(node)) { dbe = rb_entry(node, o2fsck_dirblock_entry, e_node); ivus[i].ivu_blkno = dbe->e_blkno; ivus[i].ivu_buf = buf + offset; ivus[i].ivu_buflen = fs->fs_blocksize; offset += fs->fs_blocksize; *last_read_node = node; } ret = io_vec_read_blocks(fs->fs_io, ivus, i); out: ocfs2_free(&ivus); ocfs2_free(&buf); } errcode_t o2fsck_add_dir_block(o2fsck_dirblocks *db, uint64_t ino, uint64_t blkno, uint64_t blkcount) { struct rb_node ** p = &db->db_root.rb_node; struct rb_node * parent = NULL; o2fsck_dirblock_entry *dbe, *tmp_dbe; errcode_t ret = 0; ret = ocfs2_malloc0(sizeof(o2fsck_dirblock_entry), &dbe); if (ret) goto out; dbe->e_ino = ino; dbe->e_blkno = blkno; dbe->e_blkcount = blkcount; while (*p) { parent = *p; tmp_dbe = rb_entry(parent, o2fsck_dirblock_entry, e_node); if (dbe->e_blkno < tmp_dbe->e_blkno) p = &(*p)->rb_left; else if (dbe->e_blkno > tmp_dbe->e_blkno) p = &(*p)->rb_right; } rb_link_node(&dbe->e_node, parent, p); rb_insert_color(&dbe->e_node, &db->db_root); db->db_numblocks++; out: return ret; } uint64_t o2fsck_search_reidx_dir(struct rb_root *root, uint64_t dino) { struct rb_node *node = root->rb_node; o2fsck_dirblock_entry *dbe; while (node) { dbe = rb_entry(node, o2fsck_dirblock_entry, e_node); if (dino < dbe->e_ino) node = node->rb_left; else if (dino > dbe->e_ino) node = node->rb_right; else return dbe->e_ino; } return 0; } static errcode_t o2fsck_add_reidx_dir_ino(struct rb_root *root, uint64_t dino) { struct rb_node **p = &root->rb_node; struct rb_node *parent = NULL; o2fsck_dirblock_entry *dp, *tmp_dp; errcode_t ret = 0; ret = ocfs2_malloc0(sizeof (o2fsck_dirblock_entry), &dp); if (ret) goto out; dp->e_ino = dino; while(*p) { parent = *p; tmp_dp = rb_entry(parent, o2fsck_dirblock_entry, e_node); if (dp->e_ino < tmp_dp->e_ino) p = &(*p)->rb_left; else if (dp->e_ino > tmp_dp->e_ino) p = &(*p)->rb_right; else { ret = OCFS2_ET_INTERNAL_FAILURE; ocfs2_free(&dp); goto out; } } rb_link_node(&dp->e_node, parent, p); rb_insert_color(&dp->e_node, root); out: return ret; } errcode_t o2fsck_try_add_reidx_dir(struct rb_root *root, uint64_t dino) { errcode_t ret = 0; uint64_t ino; ino = o2fsck_search_reidx_dir(root, dino); if (ino) goto out; ret = o2fsck_add_reidx_dir_ino(root, dino); out: return ret; } void o2fsck_dir_block_iterate(o2fsck_state *ost, dirblock_iterator func, void *priv_data) { o2fsck_dirblocks *db = &ost->ost_dirblocks; o2fsck_dirblock_entry *dbe; struct rb_node *node, *last_read_node = NULL; unsigned ret; int readahead = 1; for (node = rb_first(&db->db_root); node; node = rb_next(node)) { dbe = rb_entry(node, o2fsck_dirblock_entry, e_node); if (readahead) o2fsck_readahead_dirblocks(ost, node, &last_read_node); readahead = 0; ret = func(dbe, priv_data); if (ret & OCFS2_DIRENT_ABORT) break; if (ost->ost_prog) tools_progress_step(ost->ost_prog, 1); if (last_read_node == node) readahead = 1; } } static errcode_t ocfs2_rebuild_indexed_dir(ocfs2_filesys *fs, uint64_t ino) { errcode_t ret = 0; char *di_buf = NULL; struct ocfs2_dinode *di; ret = ocfs2_malloc_block(fs->fs_io, &di_buf); if (ret) goto out; ret = ocfs2_read_inode(fs, ino, di_buf); if (ret) goto out; di = (struct ocfs2_dinode *)di_buf; /* do not rebuild indexed tree for inline directory */ if (di->i_dyn_features & OCFS2_INLINE_DATA_FL) goto out; ret = ocfs2_dx_dir_truncate(fs, ino); if (ret) goto out; ret = ocfs2_dx_dir_build(fs, ino); out: if (di_buf) ocfs2_free(&di_buf); return ret; } errcode_t o2fsck_rebuild_indexed_dirs(ocfs2_filesys *fs, struct rb_root *root) { struct rb_node *node; o2fsck_dirblock_entry *dbe; uint64_t ino; errcode_t ret = 0; for (node = rb_first(root); node; node = rb_next(node)) { dbe = rb_entry(node, o2fsck_dirblock_entry, e_node); ino = dbe->e_ino; ret = ocfs2_rebuild_indexed_dir(fs, ino); if (ret) goto out; } out: return ret; } ocfs2-tools-ocfs2-tools-1.8.6/fsck.ocfs2/dirparents.c000066400000000000000000000066461347147137200223550ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * Copyright (C) 1993-2004 by Theodore Ts'o. * Copyright (C) 2004 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License, version 2, as published by the Free Software Foundation. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * * -- * * An rbtree to record a directory's parent information. _dirent records * the inode who had a directory entry that points to the directory in * question. _dot_dot records the inode that the directory's ".." points to; * who it thinks its parent is. */ #include #include #include #include #include "ocfs2/ocfs2.h" #include "fsck.h" #include "dirparents.h" #include "util.h" /* XXX callers are supposed to make sure they don't call with dup inodes. * we'll see. */ errcode_t o2fsck_add_dir_parent(struct rb_root *root, uint64_t ino, uint64_t dot_dot, uint64_t dirent, unsigned in_orphan_dir) { struct rb_node ** p = &root->rb_node; struct rb_node * parent = NULL; o2fsck_dir_parent *dp, *tmp_dp; errcode_t ret = 0; dp = calloc(1, sizeof(*dp)); if (dp == NULL) { ret = OCFS2_ET_NO_MEMORY; goto out; } dp->dp_ino = ino; dp->dp_dot_dot = dot_dot; dp->dp_dirent = dirent; dp->dp_connected = 0; dp->dp_loop_no = 0; dp->dp_in_orphan_dir = in_orphan_dir ? 1 : 0; while (*p) { parent = *p; tmp_dp = rb_entry(parent, o2fsck_dir_parent, dp_node); if (dp->dp_ino < tmp_dp->dp_ino) p = &(*p)->rb_left; else if (dp->dp_ino > tmp_dp->dp_ino) p = &(*p)->rb_right; else { ret = OCFS2_ET_INTERNAL_FAILURE; goto out; } } rb_link_node(&dp->dp_node, parent, p); rb_insert_color(&dp->dp_node, root); out: if (ret && dp) free(dp); return ret; } o2fsck_dir_parent *o2fsck_dir_parent_lookup(struct rb_root *root, uint64_t ino) { struct rb_node *node = root->rb_node; o2fsck_dir_parent *dp; while (node) { dp = rb_entry(node, o2fsck_dir_parent, dp_node); if (ino < dp->dp_ino) node = node->rb_left; else if (ino > dp->dp_ino) node = node->rb_right; else return dp; } return NULL; } o2fsck_dir_parent *o2fsck_dir_parent_first(struct rb_root *root) { struct rb_node *node = rb_first(root); o2fsck_dir_parent *dp = NULL; if (node) dp = rb_entry(node, o2fsck_dir_parent, dp_node); return dp; } o2fsck_dir_parent *o2fsck_dir_parent_next(o2fsck_dir_parent *from) { struct rb_node *node = rb_next(&from->dp_node); o2fsck_dir_parent *dp = NULL; if (node) dp = rb_entry(node, o2fsck_dir_parent, dp_node); return dp; } void ocfsck_remove_dir_parent(struct rb_root *root, uint64_t ino) { o2fsck_dir_parent *dp = NULL; struct rb_node *p = root->rb_node; while (p) { dp = rb_entry(p, o2fsck_dir_parent, dp_node); if (dp->dp_ino > ino) p = p->rb_left; else if (dp->dp_ino < ino) p = p->rb_right; else break; } if(!p) goto out; rb_erase(&dp->dp_node, root); free(dp); out: return; } ocfs2-tools-ocfs2-tools-1.8.6/fsck.ocfs2/extent.c000066400000000000000000000345531347147137200215070ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * Copyright (C) 2004 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License, version 2, as published by the Free Software Foundation. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * * -- * * o2fsck_check_extents(), the only function this file exports, is called * by pass 0 to verify the extent trees that hang off of inodes. * * XXX * test reasonable/dup block references * fix up the i_ fields that depend on the extent trees * * this could be much more clever when it finds an extent block that is * self-consistent but in a crazy part of the file system that would lead * us to not trust it. it should first try and follow the suballoc bits * back to a possibly orphaned desc. failing that it should record the * reference/block and continue on. once it has parsed the rest of eb and * fleshed out the bits of the extent block allocators it could go back * and allocate a new block and copy the orphan into it. */ #include #include #include "ocfs2/ocfs2.h" #include "extent.h" #include "fsck.h" #include "problem.h" #include "util.h" #include "refcount.h" static const char *whoami = "extent.c"; static errcode_t check_eb(o2fsck_state *ost, struct extent_info *ei, uint64_t owner, uint64_t blkno, uint32_t offset, int no_holes, int *is_valid) { int changed = 0; char *buf = NULL; struct ocfs2_extent_block *eb; errcode_t ret; /* XXX test that the block isn't already used */ /* we only consider an extent block invalid if we were able to read * it and it didn't have a extent block signature */ *is_valid = 1; ret = ocfs2_malloc_block(ost->ost_fs->fs_io, &buf); if (ret) { com_err(whoami, ret, "while allocating a block-sized buffer " "for an extent block"); goto out; } ret = ocfs2_read_extent_block_nocheck(ost->ost_fs, blkno, buf); if (ret) { com_err(whoami, ret, "reading extent block at %"PRIu64" in " "owner %"PRIu64" for verification", blkno, owner); if (ret == OCFS2_ET_BAD_EXTENT_BLOCK_MAGIC) *is_valid = 0; goto out; } eb = (struct ocfs2_extent_block *)buf; if (eb->h_blkno != blkno && prompt(ost, PY, PR_EB_BLKNO, "An extent block at %"PRIu64" in owner %"PRIu64" " "claims to be located at block %"PRIu64". Update the " "extent block's location?", blkno, owner, (uint64_t)eb->h_blkno)) { eb->h_blkno = blkno; changed = 1; } if (eb->h_fs_generation != ost->ost_fs_generation) { if (prompt(ost, PY, PR_EB_GEN, "An extent block at %"PRIu64" in owner " "%"PRIu64" has a generation of %x which doesn't " "match the volume's generation of %x. Consider " "this extent block invalid?", blkno, owner, eb->h_fs_generation, ost->ost_fs_generation)) { *is_valid = 0; goto out; } if (prompt(ost, PY, PR_EB_GEN_FIX, "Update the extent block's generation to match the " "volume?")) { eb->h_fs_generation = ost->ost_fs_generation; changed = 1; } } /* XXX worry about suballoc node/bit */ /* XXX worry about next_leaf_blk */ check_el(ost, ei, owner, &eb->h_list, ocfs2_extent_recs_per_eb(ost->ost_fs->fs_blocksize), offset, no_holes, &changed); if (changed) { ret = ocfs2_write_extent_block(ost->ost_fs, blkno, buf); if (ret) { com_err(whoami, ret, "while writing an updated extent " "block at %"PRIu64" for owner %"PRIu64, blkno, owner); goto out; } } out: if (buf) ocfs2_free(&buf); return 0; } /* the caller will check if er->e_blkno is out of range to determine if it * should try removing the record */ static errcode_t check_er(o2fsck_state *ost, struct extent_info *ei, uint64_t owner, struct ocfs2_extent_list *el, struct ocfs2_extent_rec *er, uint32_t offset, int no_holes, int *changed) { errcode_t ret = 0; uint32_t clusters; clusters = ocfs2_rec_clusters(el->l_tree_depth, er); verbosef("cpos %u clusters %u blkno %"PRIu64"\n", er->e_cpos, clusters, (uint64_t)er->e_blkno); if (ocfs2_block_out_of_range(ost->ost_fs, er->e_blkno)) goto out; if (no_holes && (er->e_cpos != offset) && prompt(ost, PY, PR_NO_HOLES, "Extent record of owner %"PRIu64" is incorrectly " "set to %d instead of %d. Fix?", owner, er->e_cpos, offset)) { er->e_cpos = offset; *changed = 1; } if ((er->e_cpos < offset) && prompt(ost, PY, PR_EXTENT_OVERLAP, "Extent record of owner %"PRIu64" is incorrectly " "set to %d instead of %d. Fix?", owner, er->e_cpos, offset)) { er->e_cpos = offset; *changed = 1; } if (el->l_tree_depth) { int is_valid = 0; /* we only expect a given depth when we descend to extent blocks * from a previous depth. these start at 0 when the inode * is checked */ ei->ei_expect_depth = 1; ei->ei_expected_depth = el->l_tree_depth - 1; check_eb(ost, ei, owner, er->e_blkno, offset, no_holes, &is_valid); if (!is_valid && prompt(ost, PY, PR_EXTENT_EB_INVALID, "The extent record for cluster offset " "%"PRIu32" in owner %"PRIu64" refers to an invalid " "extent block at %"PRIu64". Clear the reference " "to this invalid block?", er->e_cpos, owner, (uint64_t)er->e_blkno)) { er->e_blkno = 0; *changed = 1; } ret = 0; goto out; } if (ei->chk_rec_func) ret = ei->chk_rec_func(ost, owner, el, er, changed, offset, no_holes, ei->para); /* XXX offer to remove leaf records with er_clusters set to 0? */ /* XXX check that the blocks that are referenced aren't already * used */ out: return ret; } errcode_t check_el(o2fsck_state *ost, struct extent_info *ei, uint64_t owner, struct ocfs2_extent_list *el, uint16_t max_recs, uint32_t offset, int no_holes, int *changed) { int trust_next_free = 1; struct ocfs2_extent_rec *er; uint64_t max_size; uint16_t i; uint32_t clusters; size_t cpy; verbosef("depth %u count %u next_free %u\n", el->l_tree_depth, el->l_count, el->l_next_free_rec); if (ei->ei_expect_depth && el->l_tree_depth != ei->ei_expected_depth && prompt(ost, PY, PR_EXTENT_LIST_DEPTH, "Extent list in owner %"PRIu64" is recorded as " "being at depth %u but we expect it to be at depth %u. " "update the list?", owner, el->l_tree_depth, ei->ei_expected_depth)) { el->l_tree_depth = ei->ei_expected_depth; *changed = 1; } if (el->l_count > max_recs && prompt(ost, PY, PR_EXTENT_LIST_COUNT, "Extent list in owner %"PRIu64" claims to have %u " "records, but the maximum is %u. Fix the list's count?", owner, el->l_count, max_recs)) { el->l_count = max_recs; *changed = 1; } if (max_recs > el->l_count) max_recs = el->l_count; if (el->l_next_free_rec > max_recs) { if (prompt(ost, PY, PR_EXTENT_LIST_FREE, "Extent list in owner %"PRIu64" claims %u " "as the next free chain record, but fsck believes " "the largest valid value is %u. Clamp the next " "record value?", owner, el->l_next_free_rec, max_recs)) { el->l_next_free_rec = el->l_count; *changed = 1; } else { trust_next_free = 0; } } if (trust_next_free) max_recs = el->l_next_free_rec; for (i = 0; i < max_recs; i++) { er = &el->l_recs[i]; clusters = ocfs2_rec_clusters(el->l_tree_depth, er); /* * For a sparse file, we may find an empty record * in the left most record. Just skip it. */ if ((OCFS2_RAW_SB(ost->ost_fs->fs_super)->s_feature_incompat & OCFS2_FEATURE_INCOMPAT_SPARSE_ALLOC) && el->l_tree_depth && !i && !clusters) continue; /* returns immediately if blkno is out of range. * descends into eb. checks that data er doesn't * reference past the volume or anything crazy. */ check_er(ost, ei, owner, el, er, offset, no_holes, changed); if (el->l_tree_depth) offset += er->e_int_clusters; else offset += er->e_leaf_clusters; if (changed) clusters = ocfs2_rec_clusters(el->l_tree_depth, er); /* offer to remove records that point to nowhere */ if (ocfs2_block_out_of_range(ost->ost_fs, er->e_blkno) && prompt(ost, PY, PR_EXTENT_BLKNO_RANGE, "Extent record %u in owner %"PRIu64" " "refers to a block that is out of range. Remove " "this record from the extent list?", i, owner)) { if (!trust_next_free) { printf("Can't remove the record because " "next_free_rec hasn't been fixed\n"); continue; } cpy = (max_recs - i - 1) * sizeof(*er); /* shift the remaining recs into this ones place */ if (cpy != 0) { memcpy(er, er + 1, cpy); memset(&el->l_recs[max_recs - 1], 0, sizeof(*er)); i--; } el->l_next_free_rec--; max_recs--; *changed = 1; continue; } /* we've already accounted for the extent block as part of * the extent block chain groups */ if (el->l_tree_depth) continue; /* mark the data clusters as used */ if (ei->mark_rec_alloc_func) ei->mark_rec_alloc_func(ost, er, clusters, ei->para); ei->ei_clusters += clusters; max_size = (er->e_cpos + clusters) << OCFS2_RAW_SB(ost->ost_fs->fs_super)->s_clustersize_bits; if (max_size > ei->ei_max_size) ei->ei_max_size = max_size; } return 0; } errcode_t o2fsck_check_extent_rec(o2fsck_state *ost, uint64_t owner, struct ocfs2_extent_list *el, struct ocfs2_extent_rec *er, int *changed, uint32_t offset, int no_holes, void *para) { uint32_t clusters, last_cluster; uint64_t first_block; struct ocfs2_super_block *sb = OCFS2_RAW_SB(ost->ost_fs->fs_super); struct ocfs2_dinode *di = para; clusters = ocfs2_rec_clusters(el->l_tree_depth, er); first_block = ocfs2_blocks_to_clusters(ost->ost_fs, er->e_blkno); first_block = ocfs2_clusters_to_blocks(ost->ost_fs, first_block); if (first_block != er->e_blkno && prompt(ost, PY, PR_EXTENT_BLKNO_UNALIGNED, "The extent record for cluster offset %"PRIu32" " "in owner %"PRIu64" refers to block %"PRIu64" which isn't " "aligned with the start of a cluster. Point the extent " "record at block %"PRIu64" which starts this cluster?", er->e_cpos, owner, (uint64_t)er->e_blkno, first_block)) { er->e_blkno = first_block; *changed = 1; } /* imagine blkno 0, 1 er_clusters. last_cluster is 1 and * fs_clusters is 1, which is ok.. */ last_cluster = ocfs2_blocks_to_clusters(ost->ost_fs, er->e_blkno) + clusters; if (last_cluster > ost->ost_fs->fs_clusters && prompt(ost, PY, PR_EXTENT_CLUSTERS_OVERRUN, "The extent record for cluster offset %"PRIu32" " "in inode %"PRIu64" refers to an extent that goes beyond " "the end of the volume. Truncate the extent by %"PRIu32" " "clusters to fit it in the volume?", er->e_cpos, owner, last_cluster - ost->ost_fs->fs_clusters)) { clusters -= last_cluster - ost->ost_fs->fs_clusters; ocfs2_set_rec_clusters(el->l_tree_depth, er, clusters); *changed = 1; } if (!ocfs2_writes_unwritten_extents(sb) && (er->e_flags & OCFS2_EXT_UNWRITTEN) && prompt(ost, PY, PR_EXTENT_MARKED_UNWRITTEN, "The extent record for cluster offset %"PRIu32" " "in owner %"PRIu64" has the UNWRITTEN flag set, but " "this filesystem does not support unwritten extents. " "Clear the UNWRITTEN flag?", er->e_cpos, (uint64_t)di->i_blkno)) { er->e_flags &= ~OCFS2_EXT_UNWRITTEN; *changed = 1; } if (((!ocfs2_refcount_tree(sb)) || !(di->i_dyn_features & OCFS2_HAS_REFCOUNT_FL)) && (er->e_flags & OCFS2_EXT_REFCOUNTED) && prompt(ost, PY, PR_EXTENT_MARKED_REFCOUNTED, "The extent record for cluster offset %"PRIu32" at block " "%"PRIu64" in inode %"PRIu64" has the REFCOUNTED flag set, " "while it shouldn't have that flag. " "Clear the REFCOUNTED flag?", er->e_cpos, (uint64_t)er->e_blkno, (uint64_t)di->i_blkno)) { er->e_flags &= ~OCFS2_EXT_REFCOUNTED; *changed = 1; } return 0; } errcode_t o2fsck_mark_tree_clusters_allocated(o2fsck_state *ost, struct ocfs2_extent_rec *rec, uint32_t clusters, void *para) { struct ocfs2_dinode *di = para; errcode_t ret = 0; if (rec->e_flags & OCFS2_EXT_REFCOUNTED) ret = o2fsck_mark_clusters_refcounted(ost, di->i_refcount_loc, di->i_blkno, ocfs2_blocks_to_clusters(ost->ost_fs, rec->e_blkno), clusters, rec->e_cpos); else o2fsck_mark_clusters_allocated(ost, ocfs2_blocks_to_clusters(ost->ost_fs, rec->e_blkno), clusters); return ret; } errcode_t o2fsck_check_extents(o2fsck_state *ost, struct ocfs2_dinode *di) { errcode_t ret; struct extent_info ei = {0, }; int changed = 0; ei.chk_rec_func = o2fsck_check_extent_rec; ei.mark_rec_alloc_func = o2fsck_mark_tree_clusters_allocated; ei.para = di; ret = check_el(ost, &ei, di->i_blkno, &di->id2.i_list, ocfs2_extent_recs_per_inode(ost->ost_fs->fs_blocksize), 0, S_ISDIR(di->i_mode), &changed); if (S_ISDIR(di->i_mode) && !(di->i_dyn_features & OCFS2_INLINE_DATA_FL)) { struct ocfs2_extent_list *el = &(di->id2.i_list); struct ocfs2_extent_rec *er = &(el->l_recs[el->l_next_free_rec - 1]); uint64_t expected; if (el->l_tree_depth) expected = er->e_cpos + er->e_int_clusters; else expected = er->e_cpos + er->e_leaf_clusters; expected = ocfs2_clusters_to_bytes(ost->ost_fs, expected); if ((di->i_size > expected) && prompt(ost, PY, PR_INODE_SIZE, "Inode %"PRIu64" has a size of " "%"PRIu64" but has %"PRIu64" bytes of actual " "data. Correct the file size?", (uint64_t)di->i_blkno, (uint64_t)di->i_size, expected)) { di->i_size = expected; /* * Remove dirindexing... will be rebuilt in pass 2 * * TODO: This clearing does not free the blocks used * for indexing. It expects the fsck to reclaim the * blocks. We might want to clean this up by using a * refactored ocfs2_dx_dir_truncate(). */ if (di->i_dyn_features & OCFS2_INDEXED_DIR_FL) { di->i_dx_root = 0ULL; di->i_dyn_features &= ~OCFS2_INDEXED_DIR_FL; } changed = 1; } } if (changed) o2fsck_write_inode(ost, di->i_blkno, di); return ret; } ocfs2-tools-ocfs2-tools-1.8.6/fsck.ocfs2/fsck.c000066400000000000000000000720341347147137200211220ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * Copyright (C) 1993-2004 Theodore Ts'o. * Copyright (C) 2004 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License, version 2, as published by the Free Software Foundation. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * * -- * Roughly o2fsck performs the following operations. Each pass' file has * more details. * * journal.c: try and replay the journal for each node * pass0.c: make sure all the chain allocators are consistent * pass1.c: walk allocated inodes and verify them, including their extents * reflect valid inodes in the inode chain allocators * reflect allocated clusters in the cluster chain allocator * pass2.c: verify directory entries, record some linkage metadata * pass3.c: make sure all dirs are reachable * pass4.c: resolve inode's link counts, move disconnected inodes to lost+found * pass5.c: load global quota file, merge node-local quota files to global * quota file, recompute quota usage and recreate quota files * * When hacking on this keep the following in mind: * * - fsck -n is a good read-only on-site diagnostic tool. This means that fsck * _should not_ write to the file system unless it has asked prompt() to do * so. It should also not exit if prompt() returns 0. prompt() should give * as much detail as possible as it becomes an error log. * - to make life simpler, memory allocation is a fatal error. It would be * very exciting to have allocation failure trick fsck -y into tearing * apart the fs because it didn't have memorty to track what was in use. * We should have reasonable memory demands in relation to the size of * the fs. * - I'm still of mixed opinions about IO errors. For now they're fatal. * One needs to dd a volume off a busted device before fixing it. * thoughts? */ #include #include #include #include #include #include #include #include #include "ocfs2/ocfs2.h" #include "fsck.h" #include "icount.h" #include "journal.h" #include "pass0.h" #include "pass1.h" #include "pass2.h" #include "pass3.h" #include "pass4.h" #include "pass5.h" #include "problem.h" #include "util.h" #include "slot_recovery.h" int verbose = 0; static char *whoami = "fsck.ocfs2"; static o2fsck_state _ost; static int cluster_locked = 0; static void mark_magical_clusters(o2fsck_state *ost); static errcode_t fsck_lock_fs(o2fsck_state *ost); static void fsck_unlock_fs(o2fsck_state *ost); static void handle_signal(int sig) { switch (sig) { case SIGTERM: case SIGINT: printf("\nProcess Interrupted.\n"); if (cluster_locked && _ost.ost_fs->fs_dlm_ctxt) { ocfs2_release_cluster(_ost.ost_fs); cluster_locked = 0; } if (_ost.ost_fs->fs_dlm_ctxt) ocfs2_shutdown_dlm(_ost.ost_fs, whoami); if (_ost.ost_fs) ocfs2_close(_ost.ost_fs); exit(1); } return ; } /* Call this with SIG_BLOCK to block and SIG_UNBLOCK to unblock */ static void block_signals(int how) { sigset_t sigs; sigfillset(&sigs); sigdelset(&sigs, SIGTRAP); sigdelset(&sigs, SIGSEGV); sigprocmask(how, &sigs, NULL); } static void print_usage(void) { fprintf(stderr, "Usage: fsck.ocfs2 {-y|-n|-p} [ -fGnuvVy ] [ -b superblock block ]\n" " [ -B block size ] [-r num] device\n" "\n" "Critical flags for emergency repair:\n" " -n Check but don't change the file system\n" " -y Answer 'yes' to all repair questions\n" " -p Automatic repair (no questions, only safe repairs)\n" " -f Force checking even if file system is clean\n" " -F Ignore cluster locking (dangerous!)\n" " -r restore backup superblock(dangerous!)\n" "\n" "Less critical flags:\n" " -b superblock Treat given block as the super block\n" " -B blocksize Force the given block size\n" " -G Ask to fix mismatched inode generations\n" " -P Show progress\n" " -t Show I/O statistics\n" " -tt Show I/O statistics per pass\n" " -u Access the device with buffering\n" " -V Output fsck.ocfs2's version\n" " -v Provide verbose debugging output\n" ); } static uint64_t read_number(const char *num) { uint64_t val; char *ptr; val = strtoull(num, &ptr, 0); if (!ptr || *ptr) return 0; return val; } extern int opterr, optind; extern char *optarg; static errcode_t o2fsck_state_init(ocfs2_filesys *fs, o2fsck_state *ost) { errcode_t ret; ret = o2fsck_icount_new(fs, &ost->ost_icount_in_inodes); if (ret) { com_err(whoami, ret, "while allocating inode icount"); return ret; } ret = o2fsck_icount_new(fs, &ost->ost_icount_refs); if (ret) { com_err(whoami, ret, "while allocating reference icount"); return ret; } ret = ocfs2_block_bitmap_new(fs, "directory inodes", &ost->ost_dir_inodes); if (ret) { com_err(whoami, ret, "while allocating dir inodes bitmap"); return ret; } ret = ocfs2_block_bitmap_new(fs, "regular file inodes", &ost->ost_reg_inodes); if (ret) { com_err(whoami, ret, "while allocating reg inodes bitmap"); return ret; } ret = ocfs2_cluster_bitmap_new(fs, "allocated clusters", &ost->ost_allocated_clusters); if (ret) { com_err(whoami, ret, "while allocating a bitmap to track " "allocated clusters"); return ret; } return 0; } errcode_t o2fsck_state_reinit(ocfs2_filesys *fs, o2fsck_state *ost) { errcode_t ret; ocfs2_bitmap_free(&ost->ost_dir_inodes); ost->ost_dir_inodes = NULL; ocfs2_bitmap_free(&ost->ost_reg_inodes); ost->ost_reg_inodes = NULL; ocfs2_bitmap_free(&ost->ost_allocated_clusters); ost->ost_allocated_clusters = NULL; if (ost->ost_duplicate_clusters) { ocfs2_bitmap_free(&ost->ost_duplicate_clusters); ost->ost_duplicate_clusters = NULL; } o2fsck_icount_free(ost->ost_icount_in_inodes); ost->ost_icount_in_inodes = NULL; o2fsck_icount_free(ost->ost_icount_refs); ost->ost_icount_refs = NULL; ret = o2fsck_state_init(fs, ost); if (ret) { com_err(whoami, ret, "while initializing o2fsck_state."); return ret; } mark_magical_clusters(ost); return 0; } static void o2fsck_state_release(o2fsck_state *ost) { if (ost->ost_dir_inodes) ocfs2_bitmap_free(&ost->ost_dir_inodes); if (ost->ost_reg_inodes) ocfs2_bitmap_free(&ost->ost_reg_inodes); if (ost->ost_allocated_clusters) ocfs2_bitmap_free(&ost->ost_allocated_clusters); if (ost->ost_duplicate_clusters) ocfs2_bitmap_free(&ost->ost_duplicate_clusters); if (ost->ost_icount_in_inodes) o2fsck_icount_free(ost->ost_icount_in_inodes); if (ost->ost_icount_refs) o2fsck_icount_free(ost->ost_icount_refs); } static errcode_t check_superblock(o2fsck_state *ost) { struct ocfs2_dinode *di = ost->ost_fs->fs_super; struct ocfs2_super_block *sb = OCFS2_RAW_SB(di); errcode_t ret = 0; if (sb->s_max_slots == 0) { printf("The superblock max_slots field is set to 0.\n"); ret = OCFS2_ET_CORRUPT_SUPERBLOCK; } ost->ost_fs_generation = di->i_fs_generation; /* XXX do we want checking for different revisions of ocfs2? */ return ret; } static errcode_t write_out_superblock(o2fsck_state *ost) { struct ocfs2_dinode *di = ost->ost_fs->fs_super; struct ocfs2_super_block *sb = OCFS2_RAW_SB(di); if (sb->s_feature_incompat & OCFS2_FEATURE_INCOMPAT_RESIZE_INPROG) sb->s_feature_incompat &= ~OCFS2_FEATURE_INCOMPAT_RESIZE_INPROG; if (sb->s_feature_incompat & OCFS2_FEATURE_INCOMPAT_TUNEFS_INPROG) { sb->s_feature_incompat &= ~OCFS2_FEATURE_INCOMPAT_TUNEFS_INPROG; sb->s_tunefs_flag = 0; } if (ost->ost_num_clusters) di->i_clusters = ost->ost_num_clusters; sb->s_errors = ost->ost_saw_error; sb->s_lastcheck = time(NULL); sb->s_mnt_count = 0; return ocfs2_write_super(ost->ost_fs); } static void scale_time(time_t secs, unsigned *scaled, char **units) { if (secs < 60) { *units = "seconds"; goto done; } secs /= 60; if (secs < 60) { *units = "minutes"; goto done; } secs /= 60; if (secs < 24) { *units = "hours"; goto done; } secs /= 24; *units = "days"; done: *scaled = secs; } /* avoid "warning: `%c' yields only last 2 digits of year in some locales" */ static size_t ftso_strftime(char *s, size_t max, const char *fmt, const struct tm *tm) { return strftime(s, max, fmt, tm); } static int fs_is_clean(o2fsck_state *ost, char *filename) { struct ocfs2_super_block *sb = OCFS2_RAW_SB(ost->ost_fs->fs_super); time_t now = time(NULL); time_t next = sb->s_lastcheck + sb->s_checkinterval; static char reason[4096] = {'\0', }; struct tm local; if (ost->ost_force) strcpy(reason, "was run with -f"); else if ((OCFS2_RAW_SB(ost->ost_fs->fs_super)->s_feature_incompat & OCFS2_FEATURE_INCOMPAT_RESIZE_INPROG)) strcpy(reason, "incomplete volume resize detected"); else if ((OCFS2_RAW_SB(ost->ost_fs->fs_super)->s_feature_incompat & OCFS2_FEATURE_INCOMPAT_TUNEFS_INPROG)) strcpy(reason, "incomplete tunefs operation detected"); else if (sb->s_state & OCFS2_ERROR_FS) strcpy(reason, "contains a file system with errors"); else if (sb->s_max_mnt_count > 0 && sb->s_mnt_count > sb->s_max_mnt_count) { sprintf(reason, "has been mounted %u times without being " "checked", sb->s_mnt_count); } else if (sb->s_checkinterval > 0 && now >= next) { unsigned scaled_time; char *scaled_units; scale_time(now - sb->s_lastcheck, &scaled_time, &scaled_units); sprintf(reason, "has gone %u %s without being checked", scaled_time, scaled_units); } if (reason[0]) { printf("%s %s, check forced.\n", filename, reason); return 0; } reason[0] = '\0'; if (sb->s_max_mnt_count > 0) sprintf(reason, "after %u additional mounts", sb->s_max_mnt_count - sb->s_mnt_count); if (sb->s_checkinterval > 0) { localtime_r(&next, &local); if (reason[0]) ftso_strftime(reason + strlen(reason), sizeof(reason) - strlen(reason), " or by %c, whichever comes first", &local); else ftso_strftime(reason, sizeof(reason), "by %c", &local); } printf("%s is clean.", filename); if (reason[0]) printf(" It will be checked %s.\n", reason); return 1; } static void print_label(o2fsck_state *ost) { unsigned char *label = OCFS2_RAW_SB(ost->ost_fs->fs_super)->s_label; size_t i, max = sizeof(OCFS2_RAW_SB(ost->ost_fs->fs_super)->s_label); for(i = 0; i < max && label[i]; i++) { if (isprint(label[i])) printf("%c", label[i]); else printf("."); } if (i == 0) printf(""); printf("\n"); } static void print_uuid(o2fsck_state *ost) { unsigned char *uuid = OCFS2_RAW_SB(ost->ost_fs->fs_super)->s_uuid; size_t i, max = sizeof(OCFS2_RAW_SB(ost->ost_fs->fs_super)->s_uuid); for(i = 0; i < max; i++) printf("%02X", uuid[i]); printf("\n"); } static void mark_magical_clusters(o2fsck_state *ost) { uint32_t cluster; cluster = ocfs2_blocks_to_clusters(ost->ost_fs, ost->ost_fs->fs_first_cg_blkno); if (cluster != 0) o2fsck_mark_clusters_allocated(ost, 0, cluster); } static void print_version(void) { fprintf(stderr, "%s %s\n", whoami, VERSION); } #define P_(singular, plural, n) ((n) == 1 ? (singular) : (plural)) static void show_stats(o2fsck_state *ost) { uint32_t num_links; if (!ost->ost_show_stats) return; num_links = ost->ost_links_count - ost->ost_dir_count; printf("\n # of inodes with depth 0/1/2/3/4/5: %u/%u/%u/%u/%u/%u\n", ost->ost_tree_depth_count[0], ost->ost_tree_depth_count[1], ost->ost_tree_depth_count[2], ost->ost_tree_depth_count[3], ost->ost_tree_depth_count[4], ost->ost_tree_depth_count[5]); printf(" # of orphaned inodes found/deleted: %u/%u\n", ost->ost_orphan_count, ost->ost_orphan_deleted_count); printf(P_("\n%12u regular file", "\n%12u regular files", ost->ost_file_count), ost->ost_file_count); printf(P_(" (%u inline,", " (%u inlines,", ost->ost_inline_file_count), ost->ost_inline_file_count); printf(P_(" %u reflink)\n", " %u reflinks)\n", ost->ost_reflinks_count), ost->ost_reflinks_count); printf(P_("%12u directory", "%12u directories", ost->ost_dir_count), ost->ost_dir_count); printf(P_(" (%u inline)\n", " (%u inlines)\n", ost->ost_inline_dir_count), ost->ost_inline_dir_count); printf(P_("%12u character device file\n", "%12u character device files\n", ost->ost_chardev_count), ost->ost_chardev_count); printf(P_("%12u block device file\n", "%12u block device files\n", ost->ost_blockdev_count), ost->ost_blockdev_count); printf(P_("%12u fifo\n", "%12u fifos\n", ost->ost_fifo_count), ost->ost_fifo_count); printf(P_("%12u link\n", "%12u links\n", num_links), num_links); printf(P_("%12u symbolic link", "%12u symbolic links", ost->ost_symlinks_count), ost->ost_symlinks_count); printf(P_(" (%u fast symbolic link)\n", " (%u fast symbolic links)\n", ost->ost_fast_symlinks_count), ost->ost_fast_symlinks_count); printf(P_("%12u socket\n", "%12u sockets\n", ost->ost_sockets_count), ost->ost_sockets_count); printf("\n"); } static errcode_t open_and_check(o2fsck_state *ost, char *filename, int open_flags, uint64_t blkno, uint64_t blksize) { errcode_t ret; ret = ocfs2_open(filename, open_flags, blkno, blksize, &ost->ost_fs); if (ret) { com_err(whoami, ret, "while opening \"%s\"", filename); goto out; } ret = check_superblock(ost); if (ret) { printf("fsck saw unrecoverable errors in the super block and " "will not continue.\n"); ocfs2_close(ost->ost_fs); goto out; } out: return ret; } static errcode_t maybe_replay_journals(o2fsck_state *ost, char *filename, int open_flags, uint64_t blkno, uint64_t blksize) { int replayed = 0, should = 0, has_dirty = 0; errcode_t ret = 0; ret = o2fsck_should_replay_journals(ost->ost_fs, &should, &has_dirty); if (ret) goto out; ost->ost_has_journal_dirty = has_dirty ? 1 : 0; if (!should) goto out; if (!(ost->ost_fs->fs_flags & OCFS2_FLAG_RW)) { printf("** Skipping journal replay because -n was " "given. There may be spurious errors that " "journal replay would fix. **\n"); goto out; } printf("%s wasn't cleanly unmounted by all nodes. Attempting to " "replay the journals for nodes that didn't unmount cleanly\n", filename); /* journal replay is careful not to use ost as we only really * build it up after spraying the journal all over the disk * and reopening */ ret = o2fsck_replay_journals(ost->ost_fs, &replayed); if (ret) goto out; /* if the journals were replayed we close the fs and start * over */ if (!replayed) goto out; fsck_unlock_fs(ost); ret = ocfs2_close(ost->ost_fs); if (ret) { com_err(whoami, ret, "while closing \"%s\"", filename); goto out; } ret = open_and_check(ost, filename, open_flags, blkno, blksize); if (ret) { com_err(whoami, ret, "while opening and checking \"%s\"", filename); goto out; } ret = fsck_lock_fs(ost); out: return ret; } /* * Do the slot recovery, replay truncate log, local aloc and orphan dir. * If there is any error, a force check is enabled. */ static errcode_t o2fsck_slot_recovery(o2fsck_state *ost) { errcode_t ret = 0; if (!(ost->ost_fs->fs_flags & OCFS2_FLAG_RW)) { printf("** Skipping slot recovery because -n was " "given. **\n"); goto out; } ret = o2fsck_replay_local_allocs(ost->ost_fs); if (ret) goto out; ret = o2fsck_replay_truncate_logs(ost->ost_fs); if (ret) goto out; /* * If the user want a force-check, orphan_dir will be * replayed after the full check. */ if (!ost->ost_force) { ret = o2fsck_replay_orphan_dirs(ost); if (ret) com_err(whoami, ret, "while trying to replay the orphan" " directory"); } out: return ret; } static errcode_t recover_backup_super(o2fsck_state *ost, char* device, int sb_num) { errcode_t ret; uint64_t offsets[OCFS2_MAX_BACKUP_SUPERBLOCKS], blksize, sb; ocfs2_filesys *fs = NULL; if (sb_num < 1 || sb_num > OCFS2_MAX_BACKUP_SUPERBLOCKS) return -1; ocfs2_get_backup_super_offsets(NULL, offsets, ARRAY_SIZE(offsets)); /* iterate all the blocksize to get the right one. */ for (blksize = OCFS2_MIN_BLOCKSIZE; blksize <= OCFS2_MAX_BLOCKSIZE; blksize <<= 1) { sb = offsets[sb_num - 1] / blksize; /* Here we just give the possible value of block num and * block size to ocfs2_open and this function will check * them and return '0' if they meet the right one. */ ret = ocfs2_open(device, OCFS2_FLAG_RW, sb, blksize, &fs); if (!ret) break; } if (ret) goto bail; /* recover the backup information to superblock. */ if (prompt(ost, PN, PR_RECOVER_BACKUP_SUPERBLOCK, "Recover superblock information from backup block" "#%"PRIu64"?", sb)) { fs->fs_super->i_blkno = OCFS2_SUPER_BLOCK_BLKNO; ret = ocfs2_write_primary_super(fs); if (ret) goto bail; } /* no matter whether the user recover the superblock or not above, * we should return 0 in case the superblock can be opened * without the recovery. */ ret = 0; bail: if (fs) ocfs2_close(fs); return ret; } static errcode_t recover_cluster_info(o2fsck_state *ost) { errcode_t ret; struct o2cb_cluster_desc disk = {NULL, NULL}, running = {NULL, NULL}; ret = o2cb_running_cluster_desc(&running); if (ret) goto bail; ret = ocfs2_fill_cluster_desc(ost->ost_fs, &disk); if (ret) goto bail; /* * If the disk matches the running cluster, there is nothing we * can fix. */ if ((!running.c_stack && !disk.c_stack) || (running.c_stack && running.c_cluster && disk.c_stack && disk.c_cluster && !strcmp(running.c_stack, disk.c_stack) && !strcmp(running.c_cluster, disk.c_cluster))) goto bail; #define RECOVER_CLUSTER_WARNING \ "The running cluster is using the %s stack\n" \ "%s%s, but the filesystem is configured for\n" \ "the %s stack%s%s. Thus, %s cannot\n" \ "determine whether the filesystem is in use or not. This utility can\n"\ "reconfigure the filesystem to use the currently running cluster configuration.\n"\ "DANGER: YOU MUST BE ABSOLUTELY SURE THAT NO OTHER NODE IS USING THIS\n"\ "FILESYSTEM BEFORE MODIFYING ITS CLUSTER CONFIGURATION.\n" \ "Recover cluster configuration information the running cluster?" /* recover the backup information to superblock. */ if (prompt(ost, PN, PR_RECOVER_CLUSTER_INFO, RECOVER_CLUSTER_WARNING, running.c_stack ? running.c_stack : "classic o2cb", running.c_stack ? "with the cluster name " : "", running.c_stack ? running.c_cluster : "", disk.c_stack ? disk.c_stack : "classic o2cb", disk.c_stack ? " with the cluster name " : "", disk.c_stack ? disk.c_cluster : "", whoami)) { ret = ocfs2_set_cluster_desc(ost->ost_fs, &running); if (ret) goto bail; } /* no matter whether the user recover the superblock or not above, * we should return 0 in case the superblock can be opened * without the recovery. */ ret = 0; bail: o2cb_free_cluster_desc(&running); o2cb_free_cluster_desc(&disk); return ret; } static errcode_t fsck_lock_fs(o2fsck_state *ost) { errcode_t ret; ret = o2cb_init(); if (ret) { com_err(whoami, ret, "while initializing the cluster"); goto bail; } block_signals(SIG_BLOCK); ret = ocfs2_initialize_dlm(ost->ost_fs, whoami); if (ret == O2CB_ET_INVALID_STACK_NAME || ret == O2CB_ET_INVALID_CLUSTER_NAME || ret == O2CB_ET_INVALID_HEARTBEAT_MODE) { block_signals(SIG_UNBLOCK); ret = recover_cluster_info(ost); if (ret) { com_err(whoami, ret, "while recovering cluster information"); goto bail; } block_signals(SIG_BLOCK); ret = ocfs2_initialize_dlm(ost->ost_fs, whoami); } if (ret) { block_signals(SIG_UNBLOCK); com_err(whoami, ret, "while initializing the DLM"); goto bail; } ret = ocfs2_lock_down_cluster(ost->ost_fs); if (ret) { if (ost->ost_fs->fs_dlm_ctxt) ocfs2_shutdown_dlm(ost->ost_fs, whoami); block_signals(SIG_UNBLOCK); com_err(whoami, ret, "while locking down the cluster"); goto bail; } cluster_locked = 1; block_signals(SIG_UNBLOCK); bail: return ret; } static void fsck_unlock_fs(o2fsck_state *ost) { if (!ost->ost_fs) return; block_signals(SIG_BLOCK); if (ost->ost_fs->fs_dlm_ctxt) ocfs2_release_cluster(ost->ost_fs); cluster_locked = 0; if (ost->ost_fs->fs_dlm_ctxt) ocfs2_shutdown_dlm(ost->ost_fs, whoami); block_signals(SIG_UNBLOCK); } int main(int argc, char **argv) { char *filename; int64_t blkno, blksize; o2fsck_state *ost = &_ost; int c, open_flags = OCFS2_FLAG_RW | OCFS2_FLAG_STRICT_COMPAT_CHECK; int sb_num = 0; int fsck_mask = FSCK_OK; int slot_recover_err = 0; errcode_t ret; int mount_flags; int proceed = 1; memset(ost, 0, sizeof(o2fsck_state)); ost->ost_ask = 1; ost->ost_dirblocks.db_root = RB_ROOT; ost->ost_dir_parents = RB_ROOT; ost->ost_refcount_trees = RB_ROOT; /* These mean "autodetect" */ blksize = 0; blkno = 0; initialize_ocfs_error_table(); initialize_o2dl_error_table(); initialize_o2cb_error_table(); setlinebuf(stderr); setlinebuf(stdout); tools_progress_disable(); while ((c = getopt(argc, argv, "b:B:DfFGnupavVytPr:")) != EOF) { switch (c) { case 'b': blkno = read_number(optarg); if (blkno < OCFS2_SUPER_BLOCK_BLKNO) { fprintf(stderr, "Invalid blkno: %s\n", optarg); fsck_mask |= FSCK_USAGE; print_usage(); goto out; } break; case 'B': blksize = read_number(optarg); if (blksize < OCFS2_MIN_BLOCKSIZE) { fprintf(stderr, "Invalid blksize: %s\n", optarg); fsck_mask |= FSCK_USAGE; print_usage(); goto out; } break; case 'D': ost->ost_compress_dirs = 1; break; case 'F': ost->ost_skip_o2cb = 1; break; case 'f': ost->ost_force = 1; break; case 'G': ost->ost_fix_fs_gen = 1; break; case 'n': open_flags &= ~OCFS2_FLAG_RW; open_flags |= OCFS2_FLAG_RO; /* Fall through */ case 'a': case 'p': /* * Like extN, -a maps to -p, which is * 'preen'. This means only fix things * that don't require human interaction. * Unlike extN, this is only journal * replay for now. To make it smarter, * ost->ost_answer needs to learn a * new mode. */ ost->ost_ask = 0; ost->ost_answer = 0; break; case 'P': tools_progress_enable(); break; case 'y': ost->ost_ask = 0; ost->ost_answer = 1; break; case 'u': open_flags |= OCFS2_FLAG_BUFFERED; break; case 'v': verbose = 1; break; case 'V': print_version(); exit(FSCK_USAGE); case 'r': sb_num = read_number(optarg); break; case 't': if (ost->ost_show_stats) ost->ost_show_extended_stats = 1; ost->ost_show_stats = 1; break; default: fsck_mask |= FSCK_USAGE; print_usage(); goto out; } } if (!(open_flags & OCFS2_FLAG_RW) && ost->ost_compress_dirs) { fprintf(stderr, "Compress directories (-D) incompatible with read-only mode\n"); fsck_mask |= FSCK_USAGE; print_usage(); goto out; } if (blksize % OCFS2_MIN_BLOCKSIZE) { fprintf(stderr, "Invalid blocksize: %"PRId64"\n", blksize); fsck_mask |= FSCK_USAGE; print_usage(); goto out; } if (optind >= argc) { fprintf(stderr, "Missing filename\n"); fsck_mask |= FSCK_USAGE; print_usage(); goto out; } filename = argv[optind]; print_version(); ret = ocfs2_check_if_mounted(filename, &mount_flags); if (ret) { com_err(whoami, ret, "while determining whether %s is mounted.", filename); fsck_mask |= FSCK_ERROR; goto out; } if (mount_flags & (OCFS2_MF_MOUNTED | OCFS2_MF_BUSY)) { if (!(open_flags & OCFS2_FLAG_RW)) fprintf(stdout, "\nWARNING!!! Running fsck.ocfs2 (read-" "only) on a mounted filesystem may detect " "invalid errors.\n\n"); else { fprintf(stdout, "\nRunning fsck.ocfs2 on a " "mounted filesystem may cause SEVERE " "filesystem damage, abort.\n\n"); fsck_mask |= FSCK_CANCELED; goto out; } proceed = 0; } if (proceed && ost->ost_skip_o2cb) { fprintf(stdout, "\nWARNING!!! You have disabled the cluster check. " "Continue only if you\nare absolutely sure that NO " "node has this filesystem mounted or is\notherwise " "accessing it. If unsure, do NOT continue.\n\n"); proceed = 0; } if (!proceed) { fprintf(stdout, "Do you really want to continue (y/N): "); if (toupper(getchar()) != 'Y') { printf("Aborting operation.\n"); fsck_mask |= FSCK_CANCELED; goto out; } } if (signal(SIGTERM, handle_signal) == SIG_ERR) { com_err(whoami, 0, "Could not set SIGTERM"); exit(1); } if (signal(SIGINT, handle_signal) == SIG_ERR) { com_err(whoami, 0, "Could not set SIGINT"); exit(1); } /* recover superblock should be called at first. */ if (sb_num) { ret = recover_backup_super(ost, filename, sb_num); if (ret) { com_err(whoami, ret, "recover superblock failed.\n"); fsck_mask |= FSCK_ERROR; goto out; } } ret = open_and_check(ost, filename, open_flags, blkno, blksize); if (ret) { fsck_mask |= FSCK_ERROR; goto out; } if (open_flags & OCFS2_FLAG_RW && !ost->ost_skip_o2cb && !ocfs2_mount_local(ost->ost_fs)) { ret = fsck_lock_fs(ost); if (ret) { fsck_mask |= FSCK_ERROR; goto close; } } printf("Checking OCFS2 filesystem in %s:\n", filename); printf(" Label: "); print_label(ost); printf(" UUID: "); print_uuid(ost); printf(" Number of blocks: %"PRIu64"\n", ost->ost_fs->fs_blocks); printf(" Block size: %u\n", ost->ost_fs->fs_blocksize); printf(" Number of clusters: %"PRIu32"\n", ost->ost_fs->fs_clusters); printf(" Cluster size: %u\n", ost->ost_fs->fs_clustersize); printf(" Number of slots: %u\n\n", OCFS2_RAW_SB(ost->ost_fs->fs_super)->s_max_slots); /* Let's get enough of a cache to replay the journals */ o2fsck_init_cache(ost, O2FSCK_CACHE_MODE_JOURNAL); if (open_flags & OCFS2_FLAG_RW) { ret = o2fsck_check_journals(ost); if (ret) { printf("fsck saw unrecoverable errors in the journal " "files and will not continue.\n"); fsck_mask |= FSCK_ERROR; goto unlock; } } ret = maybe_replay_journals(ost, filename, open_flags, blkno, blksize); if (ret) { printf("fsck encountered unrecoverable errors while " "replaying the journals and will not continue\n"); fsck_mask |= FSCK_ERROR; goto unlock; } /* Grow the cache */ o2fsck_init_cache(ost, O2FSCK_CACHE_MODE_FULL); /* allocate all this junk after we've replayed the journal and the * sb should be stable */ if (o2fsck_state_init(ost->ost_fs, ost)) { fprintf(stderr, "error allocating run-time state, exiting..\n"); fsck_mask |= FSCK_ERROR; goto unlock; } ret = o2fsck_slot_recovery(ost); if (ret) { printf("fsck encountered errors while recovering slot " "information, check forced.\n"); slot_recover_err = 1; ost->ost_force = 1; } if (fs_is_clean(ost, filename)) { fsck_mask = FSCK_OK; goto clear_dirty_flag; } #if 0 o2fsck_mark_block_used(ost, 0); o2fsck_mark_block_used(ost, 1); o2fsck_mark_block_used(ost, OCFS2_SUPER_BLOCK_BLKNO); #endif mark_magical_clusters(ost); /* XXX we don't use the bad blocks inode, do we? */ /* XXX for now it is assumed that errors returned from a pass * are fatal. these can be fixed over time. */ ret = o2fsck_pass0(ost); if (ret) { com_err(whoami, ret, "while performing pass 0"); goto done; } ret = o2fsck_pass1(ost); if (ret) { com_err(whoami, ret, "while performing pass 1"); goto done; } ret = o2fsck_pass2(ost); if (ret) { com_err(whoami, ret, "while performing pass 2"); goto done; } ret = o2fsck_pass3(ost); if (ret) { com_err(whoami, ret, "while performing pass 3"); goto done; } ret = o2fsck_pass4(ost); if (ret) { com_err(whoami, ret, "while performing pass 4"); goto done; } ret = o2fsck_pass5(ost); if (ret) { com_err(whoami, ret, "while performing pass 5"); goto done; } done: if (ret) fsck_mask |= FSCK_ERROR; else { fsck_mask = FSCK_OK; ost->ost_saw_error = 0; printf("All passes succeeded.\n\n"); o2fsck_print_resource_track(NULL, ost, &ost->ost_rt, ost->ost_fs->fs_io); show_stats(ost); } clear_dirty_flag: if (ost->ost_fs->fs_flags & OCFS2_FLAG_RW) { ret = write_out_superblock(ost); if (ret) com_err(whoami, ret, "while writing back the " "superblock(s)"); if (fsck_mask == FSCK_OK) { if (slot_recover_err) { ret = o2fsck_slot_recovery(ost); if (ret) { com_err(whoami, ret, "while doing slot " "recovery."); goto unlock; } } ret = o2fsck_clear_journal_flags(ost); if (ret) { com_err(whoami, ret, "while clear dirty " "journal flag."); goto unlock; } ret = ocfs2_format_slot_map(ost->ost_fs); if (ret) com_err(whoami, ret, "while format slot " "map."); } } unlock: fsck_unlock_fs(ost); close: if (ost->ost_fs) { ret = ocfs2_close(ost->ost_fs); if (ret) { com_err(whoami, ret, "while closing file \"%s\"", filename); /* XXX I wonder about this error.. */ fsck_mask |= FSCK_ERROR; } } o2fsck_state_release(ost); out: return fsck_mask; } ocfs2-tools-ocfs2-tools-1.8.6/fsck.ocfs2/fsck.ocfs2.8.in000066400000000000000000000107501347147137200224640ustar00rootroot00000000000000.TH "fsck.ocfs2" "8" "January 2012" "Version @VERSION@" "OCFS2 Manual Pages" .SH "NAME" fsck.ocfs2 \- Check an \fIOCFS2\fR file system. .SH "SYNOPSIS" \fBfsck.ocfs2\fR [ \fB\-pafFGnuvVy\fR ] [ \fB\-b\fR \fIsuperblock block\fR ] [ \fB\-B\fR \fIblock size\fR ] \fIdevice\fR .SH "DESCRIPTION" .PP \fBfsck.ocfs2\fR is used to check an OCFS2 file system. \fIdevice\fR is the file where the file system is stored (e.g. \fI/dev/sda1\fR). It will almost always be a device file but a regular file will work as well. .SH "OPTIONS" .TP \fB\-a\fR This option does the same thing as the \fB-p\fR option. It is provided for backwards compatibility only: it is suggested that people use the \fB-p\fR option whenever possible. .TP \fB\-b\fR \fIsuperblock block\fR Normally, \fBfsck.ocfs2\fR will read the superblock from the first block of the device. This option specifies an alternate block that the superblock should be read from. (Use \fB\-r\fR instead of this option.) .TP \fB\-B\fR \fIblocksize\fR The \fIblock size\fR, specified in bytes, can range from 512 to 4096. A value of 0, the default, is used to indicate that the blocksize should be automatically detected. .TP \fB\-D\fR Optimize directories in filesystem. This option causes fsck.ocfs2 to coalesce the directory entries in order to improve the filesystem performance. .TP \fB\-f\fR Force checking even if the file system is clean. .TP \fB\-F\fR By default \fBfsck.ocfs2\fR will check with the cluster services to ensure that the volume is not in-use (mounted) on any node in the cluster before proceeding. \fB-F\fR skips this check and should only be used when it can be guaranteed that the volume is not mounted on any node in the cluster. \fBWARNING: If the cluster check is disabled and the volume is mounted on one or more nodes, file system corruption is very likely. If unsure, do not use this option.\fR .TP \fB\-G\fR Usually \fBfsck.ocfs2\fR will silently assume inodes whose generation number does not match the generation number of the super block are unused inodes. This option causes \fBfsck.ocfs2\fR to ask the user if these inodes should in fact be marked unused. .TP \fB\-n\fR Give the 'no' answer to all questions that fsck will ask. This guarantees that the file system will not be modified and the device will be opened read-only. The output of \fBfsck.ocfs2\fR with this option can be redirected to produce a record of a file system's faults. .TP \fB\-p\fR Automatically repair ("preen") the file system. This option will cause \fBfsck.ocfs2\fR to automatically fix any problem that can be safely corrected without human intervention. If there are problems that require intervention, the descriptions will be printed and fsck.ocfs2 will exit with the value 4 logically or'd into the exit code. (See the \fBEXIT CODE\fR section.) This option is normally used by the system's boot scripts. .TP \fB\-P\fR Show progress. .TP \fB\-r\fR \fIbackup-number\fR \fImkfs.ocfs2\fR makes up to 6 backup copies of the superblock at offsets 1G, 4G, 16G, 64G, 256G and 1T depending on the size of the volume. Use this option to specify the backup, 1 thru 6, to use to recover the superblock. .TP \fB\-t\fR Show I/O statistics. If this option is specified twice, it shows the statistics on a pass by pass basis. .TP \fB\-y\fR Give the 'yes' answer to all questions that fsck will ask. This will repair all faults that \fBfsck.ocfs2\fR finds but will not give the operator a chance to intervene if \fBfsck.ocfs2\fR decides that it wants to drastically repair the file system. .TP \fB\-v\fR This option causes \fBfsck.ocfs2\fR to produce a very large amount of debugging output. .TP \fB\-V\fR Print version information and exit. .SH EXIT CODE The exit code returned by \fBfsck.ocfs2\fR is the sum of the following conditions: .br \ 0\ \-\ No errors .br \ 1\ \-\ File system errors corrected .br \ 2\ \-\ File system errors corrected, system should .br \ \ \ \ be rebooted .br \ 4\ \-\ File system errors left uncorrected .br \ 8\ \-\ Operational error .br \ 16\ \-\ Usage or syntax error .br \ 32\ \-\ fsck.ocfs2 canceled by user request .br \ 128\ \-\ Shared library error .br .SH "SEE ALSO" .BR debugfs.ocfs2(8) .BR fsck.ocfs2.checks(8) .BR mkfs.ocfs2(8) .BR mount.ocfs2(8) .BR mounted.ocfs2(8) .BR o2cluster(8) .BR o2image(8) .BR o2info(1) .BR tunefs.ocfs2(8) .SH "AUTHORS" Oracle Corporation. This man page entry derives some text, especially the exit code summary, from .BR e2fsck(8) by Theodore Y. Ts'o . .SH "COPYRIGHT" Copyright \(co 2004, 2012 Oracle. All rights reserved. ocfs2-tools-ocfs2-tools-1.8.6/fsck.ocfs2/fsck.ocfs2.checks.8.in000066400000000000000000001316411347147137200237260ustar00rootroot00000000000000.TH "fsck.ocfs2.checks" "8" "January 2012" "Version @VERSION@" "OCFS2 Manual Pages" .SH "NAME" fsck.ocfs2.checks \- Consistency checks that .BR fsck.ocfs2(8) performs and its means for fixing inconsistencies. .SH "DESCRIPTION" .PP .BR fsck.ocfs2(8) is used to check an OCFS2 file system. It performs many consistency checks and will offer to fix faults that it finds. This man page lists the problems it may find and describes their fixes. The problems are indexed by the error number that .BR fsck.ocfs2(8) emits when it describes the problem and asks if it should be fixed. The prompts are constructed such that answering 'no' results in no changes to the file system. This may result in errors later on that stop .BR fsck.ocfs2(8) from proceeding. .SH "CHECKS" \" escape.c .SS "EB_BLKNO" Extent blocks contain a record of the disk block where they are located. An extent block was found at a block that didn't match its recorded location. Answering yes will update the data structure in the extent block to reflect its real location on disk. .SS "EB_GEN" Extent blocks are created with a generation number to match the generation number of the volume at the time of creation. An extent block was found which contains a generation number that doesn't match. Answering yes implies that the generation number is correct and that the extent block is from a previous file system. The extent block will be ignored and the file that contains it will lose the data it referenced. .SS "EB_GEN_FIX" Extent blocks are created with a generation number to match the generation number of the volume at the time of creation. An extent block was found which contains a generation number that doesn't match. Answering yes implies that the generation number in the extent block is incorrect and that the extent block is valid. The generation number in the block is updated to match the generation number in the volume. .SS "EXTENT_MARKED_UNWRITTEN" An extent record has the UNWRITTEN flag set, but the filesystem feature set does not include unwritten extents. Answering yes clears the UNWRITTEN flag. This is safe to do; as the feature is disabled anyway. .SS "EXTENT_MARKED_REFCOUNTED" An extent record has the REFCOUNTED flag set, but neither the filesystem nor the file has the REFCOUNTED flag set. Answering yes clears the REFCOUNTED flag. .SS "EXTENT_BLKNO_UNALIGNED" The block that marks the start of an extent should always fall on the start of a cluster. An extent was found that starts part-way into a cluster. Answering yes moves the start of the extent back to the start of the addressed cluster. This may add data to the middle of the file that contains this extent. .SS "EXTENT_CLUSTERS_OVERRUN" An extent was found which claims to contain clusters which are beyond the end of the volume. Answering yes clamps the extent to the end of the volume. This may result in a reduced file size for the file that contains the extent, but it couldn't have addressed those final clusters anyway. One can imagine this problem arising if there are problems shrinking a volume. .SS "EXTENT_EB_INVALID" Deep extent trees are built by forming a tree out of extent blocks. An extent tree references an invalid extent block. Answering yes stops the tree from referencing the invalid extent block. This may truncate data from the file which contains the tree. .SS "EXTENT_LIST_DEPTH" Extent lists contain a record of their depth in the tree. An extent list was found whose recorded depth doesn't match the position they have in the tree. Answering yes updates the depth field in the list to match the tree on disk. .SS "EXTENT_LIST_COUNT" The number of entries in an extent list is bounded by either the size of the inode or the size of the block which contains it. An extent list was found which claims to have more entries than would fit in its container. Answering yes updates the count field in the extent list to match the container. Answering no to this question may stop further fixes from being done because the count value can not be trusted. .SS "EXTENT_LIST_FREE" The number of free entries in an extent list must be less than the total number of entries in the list. A list was found which claims to have more free entries than possible entries. Answering yes sets the number of free entries in the list equal to the total possible entries. .SS "EXTENT_BLKNO_RANGE" An extent record was found which references a block which can not be referenced by an extent. The referenced block is either very early in the volume, and thus reserved, or beyond the end of the volume. Answering yes removes this extent record from the tree. This may remove data from the file which owns the tree but any such data was inaccessible. .SS "CHAIN_CPG" The bitmap inode indicates a different clusters per group than the group descriptor. This value is typically static and only modified by tunefs during volume resize and that too only on volumes having only one cluster group. Answering yes updates the clusters per group on the bitmap inode to the corresponding value in the group descriptor. .SS "SUPERBLOCK_CLUSTERS" The super block indicates a different total clusters value than the global bitmap. This is only possible due to a failed volume resize operation. Answering yes updates the total clusters in the super block to the value specified in the global bitmap. .SS "FIXED_CHAIN_CLUSTERS" The global bitmap inode was repaired, resulting in a change to the total cluster count of the filesystem. Answering yes updates the total clusters in the super block to the value specified in the global bitmap. \" pass0.c .SS "GROUP_UNEXPECTED_DESC" The group descriptors that make up the global bitmap chain allocator reside at predictable locations on disk. A group descriptor was found in the global bitmap allocator which isn't at one of these locations and so shouldn't be in the allocator. Answering yes removes this descriptor from the global bitmap allocator. .SS "GROUP_EXPECTED_DESC" The group descriptors that make up the global bitmap chain allocator reside at predictable locations on disk. A group descriptor at one of these locations was not linked into the global bitmap allocator. Answering yes will relink this group into the allocator. .SS "GROUP_GEN" A group descriptor was found with a generation number that doesn't match the generation number of the volume. Answering yes sets the group descriptor's generation equal to the generation number in the volume. .SS "GROUP_PARENT" Group descriptors contain a pointer to the allocator inode which contains the chain they belong to. A group descriptor was found in an allocator inode that doesn't match the descriptor's parent pointer. Answering yes updates the group descriptor's parent pointer to match the inode it resides in. .SS "GROUP_DUPLICATE" Group descriptors contain a pointer to the allocator inode which contains the chain they belong to. A group descriptor was found in two allocator inodes so it may be duplicated. Answering yes removes the group descriptor from current allocator inode. .SS "GROUP_BLKNO" Group descriptors have a field which records their block location on disk. A group descriptor was found at a given location but is recorded as being located somewhere else. Answering yes updates the group descriptor's recorded location to match where it actually is found on disk. .SS "GROUP_CHAIN" Group descriptors are found in a number of different singly-linked chains in an allocator inode. A group descriptor records the chain number that it is linked in. A group descriptor was found whose chain field doesn't match the chain it was found in. Answering yes sets the group descriptor's chain field to match the chain it is found in. .SS "GROUP_FREE_BITS" A group descriptor records the number of bits in its bitmap that are free. A group descriptor was found which claims to have more free bits than are valid in its bitmap. Answering yes decreases the number of recorded free bits so that it equals the total number of bits in the group descriptor's bitmap. .SS "GROUP_CHAIN_LOOP" A chain may loop if the next field of the group descriptor points to one of the previous group descriptors in the chain. This causes the ocfs2 code, both user space and kernel module to loop forever. Answering yes breaks the loop at an optimum location so that all the existing group descriptors are in the chain. However, it cannot re-connect stray group descriptors and must rely on the rest of the fsck code to fix it. .SS "CHAIN_COUNT" The chain list embedded in an inode is limited by the block size and the number of bytes consumed by the rest of the inode. A chain list header was found which claimed that there are more entries in the list then could fit in the inode. Answering yes resets the header's cl_count member to the maximum size allowed by the block size after accounting for the space consumed by the inode. .SS "CHAIN_NEXT_FREE" This is identical to CHAIN_COUNT except that it is testing and fixing the pointer to the next free list entry recorded in the cl_next_free_rec member instead of the total number of entries. .SS "CHAIN_EMPTY" Chain entries need to be packed such that there are no chains without descriptors found before the chain that is marked as free by the chain header. A chain without descriptors was found found before that chain that was marked free. Answering yes will remove the unused chain and shift the remaining chains forward in the list. .SS "CHAIN_I_CLUSTERS" Chain allocator inodes have an i_clusters value that represents the number of clusters used by the allocator. An inode was found whose i_clusters value doesn't match the number of clusters its chains cover. Answering yes updates i_clusters in the inode to reflect what was actually found by walking the chain. .SS "CHAIN_I_SIZE" Chain allocator inodes multiply the number of bytes per cluster by the their i_clusters value and store it in i_size. An inode was found which didn't have the correct value in its i_size. Answering yes updates i_size to be the product of i_clusters and the cluster size. Nothing else uses this value, and previous versions of tools didn't calculate it properly, so don't be too worried if this error appears. .SS "CHAIN_GROUP_BITS" The inode that contains an embedded chain list has fields which record the total number of bits covered by the chain as well as the amount free. These fields didn't match what was found in the chain. Answering yes updates the fields in the inode to reflect what was actually found by walking the chain. .SS "CHAIN_HEAD_LINK_RANGE" The header that starts a chain tried to reference a group descriptor at a block number that couldn't be valid. Answering yes will clear the reference to this invalid block and truncate the chain that it started. .SS "CHAIN_LINK_GEN" A reference was made to a group descriptor whose generation number doesn't match the generation of the volume. Answering yes to this question implies that the group descriptor is invalid and the chain is truncated at the point that it referred to this invalid group descriptor. Answering no to this question considers the group descriptor as valid and its generation may be fixed. .SS "CHAIN_LINK_MAGIC" Chains are built by chain headers and group descriptors which are linked together by block references. A reference was made to a group descriptor at a given block but a valid group descriptor signature wasn't found at that block. Answering yes clears the reference to this invalid block and truncates the chain at the point of the reference. .SS "CHAIN_LINK_RANGE" Chains are built by chain headers and group descriptors which are linked together by block references. A reference a block was found which can't possibly be valid because it was either too small or extended beyond the volume. Answering yes truncates the chain in question by zeroing the invalid block reference. This shortens the chain in question and could result in more fixes later if the part of the chain that couldn't be referenced was valid at some point. .SS "CHAIN_BITS" A chain's header contains members which record the total number of bits in the chain as well as the number of bits that are free. After walking through a chain it was found that the number of bits recorded in its header don't match what was found by totalling up the group descriptors. Answering yes updates the c_total and c_free members of the header to reflect what was found in the group descriptors in the chain. .SS "DISCONTIG_BG_DEPTH" A discontiguous block group has an extent list which records all the clusters allocated to it. Discontiguous block groups only support extent lists with a tree depth of 0. A block group claims to have a tree depth greater than 0. Answering yes will set the tree depth of the extent list to 0. .SS "DISCONTIG_BG_COUNT" A discontiguous block group has an extent list which records all the clusters allocated to it. A block group claims to have more records than can actually fit. Answering yes will set the record count to the maximum possible. .SS "DISCONTIG_BG_REC_RANGE" Block groups set aside clusters to be used for metadata. A discontiguous block group claims to contain clusters beyond the end of the volume. Answering yes will remove the block group. .SS "DISCONTIG_BG_CORRUPT_LEAVES" A discontiguous block group has an extent list which records all the clusters allocated to it. A group has more than one extent claiming to have an impossible number of clusters. Answering yes will remove the block group. .SS "DISCONTIG_BG_CLUSTERS" Extent records in a discontiguous block group were found having more clusters allocated then a block group can have. Answering yes will remove the block group. .SS "DISCONTIG_BG_LESS_CLUSTERS" Extent records in a discontiguous block group were found having less clusters allocated then a block group can have. Answering yes will remove the block group. .SS "DISCONTIG_BG_NEXT_FREE_REC" A discontiguous block group has an extent list which records all the clusters allocated to it. A group was found with fewer filled in extents than it claims to have. The filled in extents describe a complete and correct group. Answering yes will set the used extent count to the number of filled extents. .SS "DISCONTIG_BG_LIST_CORRUPT" A discontiguous block group has an extent list which records all the clusters allocated to it. The group claims to have more extents than is possible, and the existing extents contain errors. Answering yes will remove the block group. .SS "DISCONTIG_BG_REC_CORRUPT" A discontiguous block group has a extent list which records all the clusters allocated to it. A group was found with one extent claiming too many clusters but the sum of the remaining extents are equal to the total clusters a group must have. Answering yes will remove the block group. .SS "DISCONTIG_BG_LEAF_CLUSTERS" A discontiguous block group has a extent list which records all the clusters allocated to it. A group was found with one extent claiming too many clusters, but the remaining extents are correct. Answering yes will set the number of the clusters on the broken extent to the difference between the total clusters a group must have and the sum of the remaining extents. \" pass1.c .SS "INODE_ALLOC_REPAIR" The inode allocator did not accurately reflect the set of inodes that are free and in use in the volume. Answering yes will update the inode allocator bitmaps. Each bit that doesn't match the state of its inode will be inverted. .SS "INODE_SUBALLOC" Each inode records the node whose allocator is responsible for the inode. An inode was found in a given node's allocator but the inode itself claimed to belong to a different node. Answering yes will correct the inode to point to the node's allocator that it belongs to. .SS "LALLOC_SIZE" Each node has a local allocator contained in a block that is used to allocate clusters in batches. A node's local allocator claims to reflect more bytes than are possible for the volume's block size. Answering yes decreases the local allocator's size to reflect the volume's block size. .SS "LALLOC_NZ_USED" A given node's local allocator isn't in use but it claims to have bits in use in its bitmap. Answering yes zeros this used field. .SS "LALLOC_NZ_BM" A given node's local allocator isn't in use but it has a field which records the bitmap as starting at a non-zero cluster offset. Answering yes zeros the bm_off field. .SS "LALLOC_BM_OVERRUN" Each local allocator contains a reference to the first cluster that its bitmap addresses. A given local allocator was found which references a starting cluster that is beyond the end of the volume. Answering yes resets the given local allocator. No allocated data will be lost. .SS "LALLOC_BM_SIZE" The given local allocator claims to cover more bits than are possible for the size in bytes of its bitmap. Answering yes decreases the number of bits the allocator covers to reflect the size in bytes of the bitmap and resets the allocator. No allocated data will be lost. .SS "LALLOC_BM_STRADDLE" The given local allocator claims to cover a region of clusters which extents beyond the end of the volume. Answering yes resets the given local allocator. No allocated data will be lost. .SS "LALLOC_USED_OVERRUN" The given local allocator claims to have more bits in use than it has total bits in its bitmap. Answering yes decreases the number of bits used so that it equals the total number of available bits. .SS "LALLOC_CLEAR" A local allocator inode was found to have problems. This gives the operator a chance to just reset the local allocator inode. Answering yes clears the local allocator. No information is lost but the global bitmap allocator may need to be updated to reflect clusters that were reserved for the local allocator but were free. .SS "DEALLOC_COUNT" The given truncate log inode contains a count that is greater than the value that is possible given the size of the inode. Answering yes resets the count value to the possible maximum. .SS "DEALLOC_USED" The given truncate log inode claims to have more records in use than it is possible to store in the inode. Answering yes resets the record of the number used to the maximum value possible. .SS "TRUNCATE_REC_START_RANGE" A truncate record was found which claims to start at a cluster that is beyond the number of clusters in the volume. Answering yes will clear the truncate record. This may result in previously freed space being marked as allocated. This will be fixed up later as the allocator is updated to match what is used by the file system. .SS "TRUNCATE_REC_WRAP" Clusters are recorded as 32bit values. A truncate record was found which claims to have enough clusters to cause this value to wrap. This could never be the case and is a sure sign of corruption. Answering yes will clear the truncate record. This may result in previously freed space being marked as allocated. This will be fixed up later as the allocator is updated to match what is used by the file system. .SS "TRUNCATE_REC_RANGE" A truncate record was found which claims to reference a region of clusters which partially extends beyond the number of clusters in the volume. Answering yes will clear the truncate record. This may result in previously freed space being marked as allocated. This will be fixed up later as the allocator is updated to match what is used by the file system. .SS "INODE_GEN" Inodes are created with a generation number to match the generation number of the volume at the time of creation. An Inode was found which contains a generation number that doesn't match. Answering yes implies that the generation number is correct and that the inode is from a previous file system. The inode will be recorded as free. .SS "INODE_GEN_FIX" Inodes are created with a generation number to match the generation number of the volume at the time of creation. An inode was found which contains a generation number that doesn't match. Answering yes implies that the generation number in the inode is incorrect and that the inode is valid. The generation number in the inode is updated to match the generation number in the volume. .SS "INODE_BLKNO" Inodes contain a field that must match the block that they reside in. An inode was found at a block that doesn't match the field in the inode. Answering yes updates the field to match the inode's position on disk. .SS "ROOT_NOTDIR" The super block contains a reference to the inode that contains the root directory. This block was found to contain an inode that isn't a directory. Answering yes clears this inode. The operator will be asked to recreate the root directory at a point in the near future. .SS "INODE_NZ_DTIME" Inodes contain a field describing the time at which they were deleted. This can not be set for an inode that is still in use. An inode was found which is in use but which contains a non-zero dtime. Answering yes implies that the inode is still valid and resets its dtime to zero. .SS "LINK_FAST_DATA" The target name for a symbolic link is stored either as file contents for that inode or in the inode structure itself on disk. Only small destination names are stored in the inode structure. The i_blocks field of the inode indicates that the name is stored in the inode when it is zero. An inode was found that has both i_blocks set to zero and file contents. Answering yes clears the inode and so deletes the link. .SS "LINK_NULLTERM" The targets of links on disk must be null terminated. A link was found whose target wasn't null terminated. Answering yes clears the inode and so deletes the link. .SS "LINK_SIZE" The size of a link on disk must match the length of its target string. A link was found whose size does not. Answering yes updates the link's size to reflect the length of its target string. .SS "LINK_BLOCKS" Links can not be sparse. There must be exactly as many blocks allocated as are needed to cover its size. A link was found which doesn't have enough blocks allocated to cover its size. Answering yes clears the link's inode thus deleting the link. .SS "DIR_ZERO" Directories must at least contain a block that has the "." and ".." entries. A directory was found which doesn't contain any blocks. Answering yes to this question clears the directory's inode thus deleting the directory. .SS "INODE_SIZE" Certain inodes record the size of the data they reference in an i_size field. This can be the number of bytes in a file, directory, or symlink target which are stored in data mapped by extents of clusters. This error occurs when the extent lists are walked and the amount of data found does not match what is stored in i_size. Answering yes to this question updates the inode's i_size to match the amount of data referenced by the extent lists. It is vitally important that i_size matches the extent lists and so answering yes is strongly encouraged. .SS "INODE_SPARSE_SIZE" Certain inodes record the size of the data they reference in an i_size field. This can be the number of bytes in a file, directory, or symlink target which are stored in data mapped by extents of clusters. This error occurs when a sparse inode was found that had data allocated past its i_size. Answering yes to this question will update the inode's i_size to cover all of its allocated storage. It is vitally important that i_size matches the extent lists and so answering yes is strongly encouraged. .SS "INODE_INLINE_SIZE" Inodes can only fit a certain amount of inline data. This inode has its data inline but claims an i_size larger than will actually fit. Answering yes to this question updates the inode's i_size to the maximum available inline space. .SS "INODE_CLUSTERS" Inodes contain a record of how many clusters are allocated to them. An inode was found whose recorded number of clusters doesn't match the number of blocks that were found associated with the inode. Answering yes resets the inode's number of clusters to reflect the number of blocks that were associated with the file. .SS "INODE_SPARSE_CLUSTERS" Inodes contain a record of how many clusters are allocated to them. An sparse inode was found whose recorded number of clusters doesn't match the number of blocks that were found associated with the inode. Answering yes resets the inode's number of clusters to reflect the number of blocks that were associated with the file. .SS "INODE_INLINE_CLUSTERS" Inlined inode should not have allocated clusters. An inode who has inline data flag set was found with clusters allocated. Answering yes resets the inode's number of clusters to zero. .SS "LALLOC_REPAIR" An active local allocator did not accurately reflect the set of clusters that are free and in use in its region. Answering yes will update the local allocator bitmap. Each bit that doesn't match the use of its cluster will be inverted. .SS "LALLOC_USED" A local allocator records the number of bits that are used in its bitmap. An allocator was found whose used value doesn't reflect the number of bits that are set in its bitmap. Answering yes sets the used value to match the number of bits set in the allocator's bitmap. .SS "CLUSTER_ALLOC_BIT" A specific cluster's use didn't match the setting of its bit in the cluster allocator. Answering yes will invert the bit in the allocator to match the use of the cluster -- either allocated and in use or free. .SS "REFCOUNT_FLAG_INVALID" Refcount file can only exist in a volume with refcount supported, Fsck has found that a file in a non-refcount volume has refcount flag set. Answering yes remove this flag from the file. .SS "REFCOUNT_LOC_INVALID" Refcount loc can only be valid if the file has refcount flag set. Fsck has found that a file has refcount loc while it doesn't have refcount flag set. Answering yes reset refcount loc to zero for the file. .SS "RB_BLKNO" refcount blocks contain a record of the disk block where they are located. An refcount block was found at a block that didn't match its recorded location. Answering yes will update the data structure in the refcount block to reflect its real location on disk. .SS "RB_GEN" Refcount blocks are created with a generation number to match the generation number of the volume at the time of creation. An refcount block was found which contains a generation number that doesn't match. Answering yes implies that the generation number is correct and that the refcount block is from a previous file system. The refcount block will be removed and the file that uses it will lose the refcounted information, but it may be regenerated later. .SS "RB_GEN_FIX" Refcount blocks are created with a generation number to match the generation number of the volume at the time of creation. An refcount block was found which contains a generation number that doesn't match. Answering yes implies that the generation number in the refcount block is incorrect and that the refcount block is valid. The generation number in the block is updated to match the generation number in the volume. .SS "RB_PARENT" refcount blocks contain a record of the parent this disk block belongs to. An refcount block was found storing a wrong parent location. Answering yes will update the data structure in the refcount block to reflect its parent's real location on disk. .SS "REFCOUNT_LIST_COUNT" The number of entries in a refcount list is bounded by the size of the block which contains it. An refcount list was found which claims to have more entries than would fit in its container. Answering yes updates the count field in the refcount list to match the container. Answering no to this question may stop further fixes from being done because the count value can not be trusted. .SS "REFCOUNT_LIST_USED" The number of free entries in a refcount list must be less than the total number of entries in the list. A list was found which claims to have more free entries than possible entries. Answering yes sets the number of free entries in the list equal to the total possible entries. .SS "REFCOUNT_CLUSTER_RANGE" A refcount record was found which references a cluster which can not be referenced by a refcount. The referenced cluster is either very early in the volume, and thus reserved, or beyond the end of the volume. Answering yes removes this refcount record from the tree. .SS "REFCOUNT_CLUSTER_COLLISION" A refcount record was found which references a cluster which has a collision with the previous valid refcount record. Answering yes removes this refcount record from the tree. .SS "REFCOUNT_LIST_EMPTY" A refcount list was found which has no refcount record in it. It is normally caused by a corrupted refcount record. Answering yes removes this refcount block from the tree. It will be re-generated in refcounted extent records handler if all the other information is sane. .SS "REFCOUNT_BLOCK_INVALID" Refcount block stores the refcount record for physical clusters of a file. It is found referring an invalid refcount block. Answering yes remove this refcount block. .SS "REFCOUNT_CLUSTERS" Refcount tree contains a record of how many clusters are allocated to them. A tree was found whose recorded number of clusters doesn't match the number of blocks that were found associated with it. Answering yes resets the number of clusters to reflect the real number of clusters that were associated with the tree. .SS "REFCOUNT_ROOT_BLOCK_INVALID" Root refcount block is the root of the refcount record for a file. It is found referring an invalid refcount block. Answering yes remove this refcount block and clear refcount flag from this file. .SS "REFCOUNT_REC_REDUNDANT" Refcount record is used to store the refcount for physical clusters. Some refcount record is found to have no physical clusters corresponding to it. Answering yes remove the refcount record. .SS "REFCOUNT_COUNT_INVALID" Refcount record is used to store the refcount for physical clusters. A record record is found whichs claims the wrong refcount for some physical clusters. Answering yes update the corresponding refcount record. .SS "REFCOUNT_COUNT" Refcount tree contains a record of how many files referring to this tree. A tree was found whose recorded number of files doesn't match the real files referring to the tree. Answering yes resets the number of files to reflect the real number of files that were associated with the tree. \" pass1b.c .SS "DUP_CLUSTERS_SYSFILE_CLONE" A system file inode claims clusters that are also claimed by another inode. ocfs2 does not allow this. System files may be cloned but may not be deleted. Allocation system files may not be cloned or deleted. Answering yes will copy the data of this inode to newly allocated extents. This will break the claim on the overcommitted clusters. .SS "DUP_CLUSTERS_CLONE" An inode claims clusters that are also claimed by another inode. ocfs2 does not allow this. Answering yes will copy the data of this inode to newly allocated extents. This will break the claim on the overcommitted clusters. .SS "DUP_CLUSTERS_DELETE" An inode claims clusters that are also claimed by another inode. ocfs2 does not allow this. Answering yes will remove this inode, thus breaking its claim on the overcommitted clusters. .SS "DUP_CLUSTERS_ADD_REFCOUNT" An inode claims clusters that are also claimed by another inode. ocfs2 does not allow this. Answering yes will try to add a refcount record for all these inodes, so that they will share the cluster. \" pass2.c .SS "DIRENT_DOTTY_DUP" There can be only one instance of both the "." and ".." entries in a directory. A directory entry was found which duplicated one of these entries. Answering yes will remove the duplicate directory entry. .SS "DIRENT_NOT_DOTTY" The first and second directory entries in a directory must be "." and ".." respectively. One of these directory entries was found to not match these rules. Answering yes will force the directory entry to be either "." or "..". This might consume otherwise valid entries and cause some files to appear in lost+found. .SS "DIRENT_DOT_INODE" The inode field of the "." directory entry must refer to the directory inode that contains the given directory block. A "." entry was found which doesn't do so. Answering yes sets the directory entry's inode reference to the parent directory that contains the entry. .SS "DIRENT_DOT_EXCESS" A "." directory entry was found whose lengths exceeds the amount required for the single dot in the name. Answering yes creates another empty directory entry in this excess space. .SS "DIRENT_ZERO" A directory entry was found with a zero length name. Answering yes clears the directory entry so its space can be reused. .SS "DIRENT_NAME_CHARS" Directory entries can not contain either the NULL character (ASCII 0) or the forward slash (ASCII 47). A directory entry was found which contains either. Answering yes will change each instance of these forbidden characters into a period (ASCII 46). .SS "DIRENT_INODE_RANGE" Each directory entry contains a inode field which the entry's name corresponds to. An entry was found which referenced an inode number that is invalid for the current volume. Answering yes clears this entry so its space can be reused. If the entry once corresponded to a real inode and was corrupted this inode may appear in lost+found. .SS "DIRENT_INODE_FREE" Each directory entry contains a inode field which the entry's name corresponds to. An entry was found which referenced an inode number that isn't in use. Answering yes clears this directory entry. .SS "DIRENT_TYPE" Each directory entry contains a field which describes the type of file that the entry refers to. An entry was found whose type doesn't match the inode it is referring to. Answering yes resets the entry's type to match the target inode. .SS "DIR_PARENT_DUP" Each directory can only be pointed to by one directory entry in a parent directory. A directory entry was found which was the second entry to point to a given directory inode. Answering yes clears this entry which was the second to refer to a given directory. This reflects the policy that hard links to directories are not allowed. .SS "DIRENT_DUPLICATE" File names within a directory must be unique. A file name occurred in more than one directory entry in a given directory. Answering yes renames the duplicate entry to a name that doesn't collide with recent entries and is unlikely to collide with future entries in the directory. .SS "DIRENT_LENGTH" There are very few directory entry lengths that are valid. The lengths must be greater than the minimum required to record a single character directory, be rounded to 12 bytes, be within the amount of space remaining in a directory block, and be properly rounded for the size of the name of the directory entry. An entry was found which didn't meet these criteria. Answering yes will try to repair the directory entry. This runs a very good chance of invalidating all the entries in the directory block. Orphaned inodes may appear in lost+found. .SS "DIR_TRAILER_INODE" A directory block trailer is a fake directory entry at the end of the block. The trailer has compatibility fields for when it is viewed as a directory entry. The inode field must be zero. Answering yes will set the inode field to zero. .SS "DIR_TRAILER_NAME_LEN" A directory block trailer is a fake directory entry at the end of the block. The trailer has compatibility fields for when it is viewed as a directory entry. The name length field must be zero. Answering yes will set the name length field to zero. .SS "DIR_TRAILER_REC_LEN" A directory block trailer is a fake directory entry at the end of the block. The trailer has compatibility fields for when it is viewed as a directory entry. The record length field must be equal to the size of the trailer. Answering yes will set the record length field to the size of the trailer. .SS "DIR_TRAILER_BLKNO" A directory block trailer is a fake directory entry at the end of the block. The self-referential block number is incorrect. Answering yes will set the block number to the correct block on disk. .SS "DIR_TRAILER_PARENT_INODE" A directory block trailer is a fake directory entry at the end of the block. It has a pointer to the directory inode it belongs to. This pointer is incorrect. Answering yes will set the parent inode pointer to the inode referencing this directory block. \" pass3.c .SS "ROOT_DIR_MISSING" The super block contains a reference to the inode that serves as the root directory. This reference points to an inode that isn't in use. Answering yes will create a new inode and update the super block to refer to this inode as the root directory. .SS "LOSTFOUND_MISSING" The super block contains a reference to the inode that serves as the lost+found directory. This reference points to an inode that isn't in use. Answering yes will create a new lost+found directory in the root directory. .SS "DIR_NOT_CONNECTED" Every directory in the file system should be reachable by a directory entry in its parent directory. This is verified by walking every directory in the system. A directory inode was found during this walk which doesn't have a parent directory entry. Answering yes moves this directory entry into the lost+found directory and gives it a name based on its inode number. .SS "DIR_DOTDOT" A directory inode's ".." directory entry must refer to the parent directory. A directory was found whose ".." doesn't refer to its parent. Answering yes will read the directory block for the given directory and update its ".." entry to reflect its parent. \" pass4.c .SS "INODE_NOT_CONNECTED" Most all inodes in the system should be referenced by a directory entry. An inode was found which isn't referred to by any directory entry. Answering yes moves this inode into the lost+found directory and gives it a name based on its inode number. .SS "INODE_COUNT" Each inode records the number of directory entries that refer to it. An inode was found whose recorded count doesn't match the number of entries that refer to it. Answering yes sets the inode's count to match the number of referring directory entries. .SS "INODE_ORPHANED" While files are being deleted they are placed in an internal directory. If the machine crashes while this is taking place the files will be left in this directory. Fsck has found an inode in this directory and would like to finish the job of truncating and removing it. Answering yes removes the file data associated with the inode and frees the inode. .SS "RECOVER_BACKUP_SUPERBLOCK" When \fIfsck.ocfs2\fR successfully uses the specified backup superblock, it provides the user with this option to overwrite the existing superblock with that backup. Answering yes will refresh the superblock from the backup. Answering no will only disable the copying of the backup superblock and will not effect the remaining \fIfsck.ocfs2\fR processing. .SS "ORPHAN_DIR_MISSING" While files are being deleted they are placed in an internal directory, named orphan directory. If an orphan directory does not exist, an OCFS2 volume cannot be mounted successfully. Fsck has found the orphan directory is missing and would like to create it for future use. Answering yes creates the orphan directory in the system directory. .SS "JOURNAL_FILE_INVALID" OCFS2 uses JDB for journalling and some journal files exist in the system directory. Fsck has found some journal files that are invalid. Answering yes to this question will regenerate the invalid journal files. .SS "JOURNAL_UNKNOWN_FEATURE" Fsck has found some journal files with unknown features. Other journals on the filesystem have only known features, so this is likely a corruption. If you think your filesystem may be newer than this version of fsck.ocfs2, say N here and grab the latest version of fsck.ocfs2. Answering yes resets the journal features to match other journals. .SS "JOURNAL_MISSING_FEATURE" Fsck has found some journal files have features that are not set on all journal files. All journals on filesystem should have the same set of features. Answering yes will set all journals to the union of set features. .SS "JOURNAL_TOO_SMALL" Fsck has found some journal files are too small. Answering yes extends these journals. .SS "RECOVER_CLUSTER_INFO" The currently active cluster stack is different than the one the filesystem is configured for. Thus, fsck.ocfs2 cannot determine whether the filesystem is mounted on an another node or not. The recommended solution is to exit and run fsck.ocfs2 on this device from a node that has the appropriate active cluster stack. However, you can proceed with the fsck if you are sure that the volume is not in use on any node. Answering yes reconfigures the filesystem to use the current cluster stack. DANGER: YOU MUST BE ABSOLUTELY SURE THAT NO OTHER NODE IS USING THIS FILESYSTEM BEFORE CONTINUING. OTHERWISE, YOU CAN CORRUPT THE FILESYSTEM AND LOSE DATA. .SS "INLINE_DATA_FLAG_INVALID" Inline file can only exist in a volume with inline supported, Fsck has found that a file in a non-inline volume has inline flag set. Answering yes remove this flag from the file. .SS "INLINE_DATA_COUNT_INVALID" For an inline file, there is a limit for id2.id_data.id_count. Fsck has found that this value isn't right. Answering yes change this value to the right number. .SS "XATTR_BLOCK_INVALID" Extended attributes are stored off an extended attribute block referenced by the inode. This inode references an invalid extended attribute block. Answering yes will remove this block. .SS "XATTR_COUNT_INVALID" The count of extended attributes in an inode, block, or bucket does not match the number of entries found by fsck. Answering yes will change this to the correct count. .SS "XATTR_ENTRY_INVALID" An extended attribute entry points to already used space. Answering yes will remove this entry. .SS "XATTR_NAME_OFFSET_INVALID" The name_offset field of an extended attribute entry is not correct. Without a correct name_offset field, the entry cannot be used. Answering yes will remove this entry. .SS "XATTR_VALUE_INVALID" The value region of an extended attribute points to already used space. Answering yes will remove this entry. .SS "XATTR_LOCATION_INVALID" The xe_local field and xe_value_size field of an extended attribute entry does not match. So the entry cannot be used. Answering yes will remove this entry. .SS "XATTR_HASH_INVALID" Extended attributes use a hash of their name for lookup purposes. The name_hash of this extended attribute entry is not correct. Answering yes will change this to the correct hash. .SS "XATTR_FREE_START_INVALID" Extended attributes use free_start to indicate the offset of the free space in inode, block, or bucket. The free_start field of this object is not correct. Answering yes will change this to the correct offset. .SS "XATTR_VALUE_LEN_INVALID" Extended attributes use name_value_len to store the total length of all entry's name and value in inode, block or bucket. the name_value_len filed of this object is not correct. Answering yes will change this to the correct value. .SS "XATTR_BUCKET_COUNT_INVALID" The count of extended attributes bucket pointed by one extent record does not match the number of buckets found by fsck. Answering yes will change this to the correct count. \" pass5.c .SS "QMAGIC_INVALID" The magic number in the header of quota file does not match the proper number. Answering yes will make fsck use values in the quota file header anyway. .SS "QTREE_BLK_INVALID" Block with references to other blocks with quota data is corrupted. Answering yes will make fsck use references in the block. .SS "DQBLK_INVALID" The structure with quota limits was found in a corrupted block. Answering yes will use the values of limits for the user / group. .SS "DUP_DQBLK_INVALID" The structure with quota limits was found in a corrupted block and fsck has already found quota limits for this user / group. Answering yes will use new values of limits for the user / group. .SS "DUP_DQBLK_VALID" The structure with quota limits was found in a correct block but fsck has already found quota limits for this user / group. Answering yes will use new values of limits for the user / group. .SS "IV_DX_TREE" A directory index was found on an inode but that feature is not enabled on the file system. Answering yes will truncate the invalid index. .SS "DX_LOOKUP_FAILED" A directory entry is missing an entry in the directory index. The missing index entry will cause lookups on this name to fail. Answering yes will rebuild the directory index, restoring the missing entry. .SS "NO_HOLES" A metadata structure encountered a hole where it should not. Examples of such structures are directories, refcount trees, dx_trees etc. Answering yes will remove the hole by updating the offset to the expected value. .SS "EXTENT_OVERLAP" The extents of the file overlap, which means there could be two or more possible data for a particular offset for the file. Answering yes will serialize the extents. .SS "DX_TREE_CORRUPT" The index tree of the directory is corrupt. Answering yes will rebuild the directory index, in pass 2. .SS "DX_TREE_MISSING" The index of this directory is missing. Answering yes will rebuild the directory index. .SS "BAD_CRC32" The metadata block has a bad CRC32, which means either the block or the crc32 field is corrupted. Answering yes will recalculate the CRC32. .SH "SEE ALSO" .BR debugfs.ocfs2(8) .BR fsck.ocfs2(8) .BR mkfs.ocfs2(8) .BR mount.ocfs2(8) .BR mounted.ocfs2(8) .BR o2cluster(8) .BR o2image(8) .BR o2info(1) .BR tunefs.ocfs2(8) .SH "AUTHORS" Oracle Corporation. .SH "COPYRIGHT" Copyright \(co 2004, 2012 Oracle. All rights reserved. ocfs2-tools-ocfs2-tools-1.8.6/fsck.ocfs2/icount.c000066400000000000000000000127201347147137200214710ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * Copyright (C) 1993-2004 by Theodore Ts'o. * Copyright (C) 2004 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License, version 2, as published by the Free Software Foundation. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * * -- * * A trivial rbtree that stores a u16 icount indexed by an inode's block * number. */ #include #include #include #include #include #include "ocfs2/ocfs2.h" #include "fsck.h" #include "icount.h" #include "util.h" typedef struct _icount_node { struct rb_node in_node; uint64_t in_blkno; uint16_t in_icount; } icount_node; /* XXX this is currently fragile in that it requires that the caller make * sure that the node doesn't already exist in the tree. */ static void icount_insert(o2fsck_icount *icount, icount_node *in) { struct rb_node ** p = &icount->ic_multiple_tree.rb_node; struct rb_node * parent = NULL; icount_node *tmp_in; while (*p) { parent = *p; tmp_in = rb_entry(parent, icount_node, in_node); if (in->in_blkno < tmp_in->in_blkno) p = &(*p)->rb_left; else if (in->in_blkno > tmp_in->in_blkno) p = &(*p)->rb_right; } rb_link_node(&in->in_node, parent, p); rb_insert_color(&in->in_node, &icount->ic_multiple_tree); } static icount_node *icount_search(o2fsck_icount *icount, uint64_t blkno, icount_node **next) { struct rb_node *node = icount->ic_multiple_tree.rb_node; icount_node *in, *last_left = NULL; while (node) { in = rb_entry(node, icount_node, in_node); if (blkno < in->in_blkno) { last_left = in; node = node->rb_left; } else if (blkno > in->in_blkno) node = node->rb_right; else return in; } if (next && last_left) *next = last_left; return NULL; } /* keep it simple for now by always updating both data structures */ errcode_t o2fsck_icount_set(o2fsck_icount *icount, uint64_t blkno, uint16_t count) { icount_node *in; errcode_t ret = 0; if (count == 1) o2fsck_bitmap_set(icount->ic_single_bm, blkno, NULL); else o2fsck_bitmap_clear(icount->ic_single_bm, blkno, NULL); in = icount_search(icount, blkno, NULL); if (in) { if (count < 2) { rb_erase(&in->in_node, &icount->ic_multiple_tree); free(in); } else { in->in_icount = count; } } else if (count > 1) { in = calloc(1, sizeof(*in)); if (in == NULL) { ret = OCFS2_ET_NO_MEMORY; goto out; } in->in_blkno = blkno; in->in_icount = count; icount_insert(icount, in); } out: return ret; } uint16_t o2fsck_icount_get(o2fsck_icount *icount, uint64_t blkno) { icount_node *in; int was_set; uint16_t ret = 0; ocfs2_bitmap_test(icount->ic_single_bm, blkno, &was_set); if (was_set) { ret = 1; goto out; } in = icount_search(icount, blkno, NULL); if (in) ret = in->in_icount; out: return ret; } /* again, simple before efficient. We just find the old value and * use _set to make sure that the new value updates both the bitmap * and the tree */ void o2fsck_icount_delta(o2fsck_icount *icount, uint64_t blkno, int delta) { int was_set; uint16_t prev_count; icount_node *in; if (delta == 0) return; ocfs2_bitmap_test(icount->ic_single_bm, blkno, &was_set); if (was_set) { prev_count = 1; } else { in = icount_search(icount, blkno, NULL); if (in == NULL) prev_count = 0; else prev_count = in->in_icount; } if (prev_count + delta < 0) com_err(__FUNCTION__, OCFS2_ET_INTERNAL_FAILURE, "while droping icount from %"PRIu16" bt %d for " "inode %"PRIu64, prev_count, delta, blkno); o2fsck_icount_set(icount, blkno, prev_count + delta); } errcode_t o2fsck_icount_new(ocfs2_filesys *fs, o2fsck_icount **ret) { o2fsck_icount *icount; errcode_t err; icount = calloc(1, sizeof(*icount)); if (icount == NULL) return OCFS2_ET_NO_MEMORY; err = ocfs2_block_bitmap_new(fs, "inodes with single link_count", &icount->ic_single_bm); if (err) { free(icount); com_err("icount", err, "while allocating single link_count bm"); return err; } icount->ic_multiple_tree = RB_ROOT; *ret = icount; return 0; } errcode_t o2fsck_icount_next_blkno(o2fsck_icount *icount, uint64_t start, uint64_t *found) { uint64_t next_bit; errcode_t ret; icount_node *in, *next = NULL; ret = ocfs2_bitmap_find_next_set(icount->ic_single_bm, start, &next_bit); in = icount_search(icount, start, &next); if (in == NULL) in = next; if (in) { if (ret == OCFS2_ET_BIT_NOT_FOUND) *found = in->in_blkno; else *found = next_bit < in->in_blkno ? next_bit : in->in_blkno; ret = 0; } else { if (ret != OCFS2_ET_BIT_NOT_FOUND) *found = next_bit; } return ret; } void o2fsck_icount_free(o2fsck_icount *icount) { struct rb_node *node; icount_node *in; ocfs2_bitmap_free(&icount->ic_single_bm); while((node = rb_first(&icount->ic_multiple_tree)) != NULL) { in = rb_entry(node, icount_node, in_node); rb_erase(node, &icount->ic_multiple_tree); free(in); } free(icount); } ocfs2-tools-ocfs2-tools-1.8.6/fsck.ocfs2/include/000077500000000000000000000000001347147137200214455ustar00rootroot00000000000000ocfs2-tools-ocfs2-tools-1.8.6/fsck.ocfs2/include/dirblocks.h000066400000000000000000000036001347147137200235710ustar00rootroot00000000000000/* * dirblocks.h * * Copyright (C) 2002 Oracle Corporation. All rights reserved. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * * Author: Zach Brown */ #ifndef __O2FSCK_DIRBLOCKS_H__ #define __O2FSCK_DIRBLOCKS_H__ #include "ocfs2/ocfs2.h" #include "ocfs2/kernel-rbtree.h" typedef struct _o2fsck_dirblocks { struct rb_root db_root; uint64_t db_numblocks; } o2fsck_dirblocks; typedef struct _o2fsck_dirblock_entry { struct rb_node e_node; uint64_t e_ino; uint64_t e_blkno; uint64_t e_blkcount; } o2fsck_dirblock_entry; typedef unsigned (*dirblock_iterator)(o2fsck_dirblock_entry *, void *priv_data); errcode_t o2fsck_add_dir_block(o2fsck_dirblocks *db, uint64_t ino, uint64_t blkno, uint64_t blkcount); struct _o2fsck_state; void o2fsck_dir_block_iterate(struct _o2fsck_state *ost, dirblock_iterator func, void *priv_data); uint64_t o2fsck_search_reidx_dir(struct rb_root *root, uint64_t dino); errcode_t o2fsck_try_add_reidx_dir(struct rb_root *root, uint64_t dino); errcode_t o2fsck_rebuild_indexed_dirs(ocfs2_filesys *fs, struct rb_root *root); errcode_t o2fsck_check_dir_index(struct _o2fsck_state *ost, struct ocfs2_dinode *di); #endif /* __O2FSCK_DIRBLOCKS_H__ */ ocfs2-tools-ocfs2-tools-1.8.6/fsck.ocfs2/include/dirparents.h000066400000000000000000000035411347147137200237740ustar00rootroot00000000000000/* * dirparents.h * * Copyright (C) 2002 Oracle Corporation. All rights reserved. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * * Author: Zach Brown */ #ifndef __O2FSCK_DIRPARENTS_H__ #define __O2FSCK_DIRPARENTS_H__ #include "ocfs2/kernel-rbtree.h" typedef struct _o2fsck_dir_parent { struct rb_node dp_node; uint64_t dp_ino; /* The dir inode in question. */ uint64_t dp_dot_dot; /* The parent according to the dir's own * '..' entry */ uint64_t dp_dirent; /* The inode that has a dirent which points * to this directory. */ /* used by pass3 to walk the dir_parent structs and ensure * connectivity */ uint64_t dp_loop_no; unsigned dp_connected:1, dp_in_orphan_dir:1; } o2fsck_dir_parent; errcode_t o2fsck_add_dir_parent(struct rb_root *root, uint64_t ino, uint64_t dot_dot, uint64_t dirent, unsigned in_orphan_dir); o2fsck_dir_parent *o2fsck_dir_parent_lookup(struct rb_root *root, uint64_t ino); o2fsck_dir_parent *o2fsck_dir_parent_first(struct rb_root *root); o2fsck_dir_parent *o2fsck_dir_parent_next(o2fsck_dir_parent *from); void ocfsck_remove_dir_parent(struct rb_root *root, uint64_t ino); #endif /* __O2FSCK_DIRPARENTS_H__ */ ocfs2-tools-ocfs2-tools-1.8.6/fsck.ocfs2/include/extent.h000066400000000000000000000043371347147137200231340ustar00rootroot00000000000000/* * Copyright (C) 2002 Oracle Corporation. All rights reserved. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. */ #ifndef __O2FSCK_EXTENT_H__ #define __O2FSCK_EXTENT_H__ #include "fsck.h" typedef errcode_t (check_leaf_er_func)(o2fsck_state *ost, uint64_t owner, struct ocfs2_extent_list *el, struct ocfs2_extent_rec *er, int *changed, uint32_t offset, int no_holes, void *para); typedef errcode_t (mark_leaf_er_alloc_func)(o2fsck_state *ost, struct ocfs2_extent_rec *er, uint32_t clusters, void *para); struct extent_info { uint64_t ei_max_size; uint64_t ei_clusters; uint64_t ei_last_eb_blk; uint16_t ei_expected_depth; unsigned ei_expect_depth:1; check_leaf_er_func *chk_rec_func; mark_leaf_er_alloc_func *mark_rec_alloc_func; void *para; }; errcode_t o2fsck_check_extents(o2fsck_state *ost, struct ocfs2_dinode *di); errcode_t check_el(o2fsck_state *ost, struct extent_info *ei, uint64_t owner, struct ocfs2_extent_list *el, uint16_t max_recs, uint32_t offset, int no_holes, int *changed); errcode_t o2fsck_check_extent_rec(o2fsck_state *ost, uint64_t owner, struct ocfs2_extent_list *el, struct ocfs2_extent_rec *er, int *changed, uint32_t offset, int no_holes, void *para); errcode_t o2fsck_mark_tree_clusters_allocated(o2fsck_state *ost, struct ocfs2_extent_rec *rec, uint32_t clusters, void *para); #endif /* __O2FSCK_EXTENT_H__ */ ocfs2-tools-ocfs2-tools-1.8.6/fsck.ocfs2/include/fsck.h000066400000000000000000000100041347147137200225370ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * fsck.h * * Copyright (C) 2002 Oracle Corporation. All rights reserved. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * * Author: Zach Brown */ #ifndef __O2FSCK_FSCK_H__ #define __O2FSCK_FSCK_H__ #include "icount.h" #include "dirblocks.h" #include "tools-internal/progress.h" struct refcount_file; /* * This structure is used for keeping track of how much resources have * been used for a particular pass of fsck.ocfs2. */ struct o2fsck_resource_track { struct timeval rt_real_time; struct timeval rt_user_time; struct timeval rt_sys_time; struct ocfs2_io_stats rt_io_stats; }; typedef struct _o2fsck_state { ocfs2_filesys *ost_fs; ocfs2_cached_inode *ost_global_inode_alloc; ocfs2_cached_inode **ost_inode_allocs; ocfs2_bitmap *ost_dir_inodes; ocfs2_bitmap *ost_reg_inodes; ocfs2_bitmap *ost_allocated_clusters; ocfs2_bitmap *ost_duplicate_clusters; /* This is no more than a cache of what we know the i_link_count * in each inode to currently be. If an inode is marked in used_inodes * this had better be up to date. */ o2fsck_icount *ost_icount_in_inodes; /* this records references to each inode from other directory * entries, including '.' and '..'. */ o2fsck_icount *ost_icount_refs; o2fsck_dirblocks ost_dirblocks; uint32_t ost_fs_generation; uint64_t ost_lostfound_ino; uint32_t ost_num_clusters; struct rb_root ost_dir_parents; struct rb_root ost_refcount_trees; struct refcount_file *ost_latest_file; unsigned ost_ask:1, /* confirm with the user */ ost_answer:1, /* answer if we don't ask the user */ ost_force:1, /* -f supplied; force check */ ost_skip_o2cb:1,/* -F: ignore cluster services */ ost_write_inode_alloc_asked:1, ost_write_inode_alloc:1, ost_write_error:1, ost_write_cluster_alloc_asked:1, ost_write_cluster_alloc:1, ost_saw_error:1, /* if we think there are still errors * on disk we'll mark the sb as having * errors as we exit */ ost_stale_mounts:1, /* set when reading publish blocks * that still indicated mounted */ ost_fix_fs_gen:1, ost_has_journal_dirty:1, ost_compress_dirs:1, ost_show_stats:1, ost_show_extended_stats:1; errcode_t ost_err; struct o2fsck_resource_track ost_rt; struct tools_progress *ost_prog; /* counters */ uint32_t ost_file_count; uint32_t ost_inline_file_count; uint32_t ost_dir_count; uint32_t ost_inline_dir_count; uint32_t ost_reflinks_count; uint32_t ost_links_count; uint32_t ost_chardev_count; uint32_t ost_sockets_count; uint32_t ost_fifo_count; uint32_t ost_blockdev_count; uint32_t ost_symlinks_count; uint32_t ost_fast_symlinks_count; uint32_t ost_orphan_count; uint32_t ost_orphan_deleted_count; #define OCFS2_MAX_PATH_DEPTH 5 uint32_t ost_tree_depth_count[OCFS2_MAX_PATH_DEPTH + 1]; } o2fsck_state; errcode_t o2fsck_state_reinit(ocfs2_filesys *fs, o2fsck_state *ost); #define kbytes(x) (((x) + 1023) / 1024) #define mbytes(x) (((x) + 1048575) / 1048576) #define gbytes(x) (((x) + 1073741823) / 1073741824) /* The idea is to let someone off-site run fsck and have it give us * enough information to diagnose problems with */ extern int verbose; #define verbosef(fmt, args...) do { \ if (verbose) \ printf("%s:%d | " fmt, __FUNCTION__, __LINE__, args);\ } while (0) #endif /* __O2FSCK_FSCK_H__ */ ocfs2-tools-ocfs2-tools-1.8.6/fsck.ocfs2/include/icount.h000066400000000000000000000030221347147137200231140ustar00rootroot00000000000000/* * icount.h * * Copyright (C) 2002 Oracle Corporation. All rights reserved. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * * Author: Zach Brown */ #ifndef __O2FSCK_ICOUNT_H__ #define __O2FSCK_ICOUNT_H__ #include "ocfs2/ocfs2.h" #include "ocfs2/kernel-rbtree.h" typedef struct _o2fsck_icount { ocfs2_bitmap *ic_single_bm; struct rb_root ic_multiple_tree; } o2fsck_icount; errcode_t o2fsck_icount_set(o2fsck_icount *icount, uint64_t blkno, uint16_t count); uint16_t o2fsck_icount_get(o2fsck_icount *icount, uint64_t blkno); errcode_t o2fsck_icount_new(ocfs2_filesys *fs, o2fsck_icount **ret); void o2fsck_icount_free(o2fsck_icount *icount); void o2fsck_icount_delta(o2fsck_icount *icount, uint64_t blkno, int delta); errcode_t o2fsck_icount_next_blkno(o2fsck_icount *icount, uint64_t start, uint64_t *found); #endif /* __O2FSCK_ICOUNT_H__ */ ocfs2-tools-ocfs2-tools-1.8.6/fsck.ocfs2/include/journal.h000066400000000000000000000023041347147137200232670ustar00rootroot00000000000000/* * journal.h * * Copyright (C) 2002 Oracle Corporation. All rights reserved. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * * Author: Zach Brown */ #ifndef __O2FSCK_JOURNAL_H__ #define __O2FSCK_JOURNAL_H__ #include "fsck.h" errcode_t o2fsck_replay_journals(ocfs2_filesys *fs, int *replayed); errcode_t o2fsck_should_replay_journals(ocfs2_filesys *fs, int *should, int *has_dirty); errcode_t o2fsck_clear_journal_flags(o2fsck_state *ost); errcode_t o2fsck_check_journals(o2fsck_state *ost); #endif /* __O2FSCK_JOURNAL_H__ */ ocfs2-tools-ocfs2-tools-1.8.6/fsck.ocfs2/include/o2fsck_strings.h000066400000000000000000000026431347147137200245630ustar00rootroot00000000000000/* * strings.h * * Copyright (C) 2002 Oracle Corporation. All rights reserved. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * * Author: Zach Brown */ #ifndef __O2FSCK_STRINGS_H__ #define __O2FSCK_STRINGS_H__ #include "ocfs2/ocfs2.h" #include "ocfs2/kernel-rbtree.h" typedef struct _o2fsck_strings { struct rb_root s_root; size_t s_allocated; } o2fsck_strings; int o2fsck_strings_exists(o2fsck_strings *strings, char *string, size_t strlen); errcode_t o2fsck_strings_insert(o2fsck_strings *strings, char *string, size_t strlen, int *is_dup); void o2fsck_strings_init(o2fsck_strings *strings); void o2fsck_strings_free(o2fsck_strings *strings); size_t o2fsck_strings_bytes_allocated(o2fsck_strings *strings); #endif /* __O2FSCK_STRINGS_H__ */ ocfs2-tools-ocfs2-tools-1.8.6/fsck.ocfs2/include/pass0.h000066400000000000000000000017301347147137200226450ustar00rootroot00000000000000/* * pass0.h * * Copyright (C) 2002 Oracle Corporation. All rights reserved. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * * Author: Zach Brown */ #ifndef __O2FSCK_PASS0_H__ #define __O2FSCK_PASS0_H__ #include "fsck.h" errcode_t o2fsck_pass0(o2fsck_state *ost); #endif /* __O2FSCK_PASS0_H__ */ ocfs2-tools-ocfs2-tools-1.8.6/fsck.ocfs2/include/pass1.h000066400000000000000000000020121347147137200226400ustar00rootroot00000000000000/* * pass1.h * * Copyright (C) 2002 Oracle Corporation. All rights reserved. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * * Author: Zach Brown */ #ifndef __O2FSCK_PASS1_H__ #define __O2FSCK_PASS1_H__ #include "fsck.h" errcode_t o2fsck_pass1(o2fsck_state *ost); void o2fsck_free_inode_allocs(o2fsck_state *ost); #endif /* __O2FSCK_PASS1_H__ */ ocfs2-tools-ocfs2-tools-1.8.6/fsck.ocfs2/include/pass1b.h000066400000000000000000000012371347147137200230120ustar00rootroot00000000000000/* * pass1b.h * * Copyright (C) 2009 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License version 2 as published by the Free Software Foundation. * * 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. */ #ifndef __O2FSCK_PASS1B_H__ #define __O2FSCK_PASS1B_H__ #include "fsck.h" errcode_t ocfs2_pass1_dups(o2fsck_state *ost); #endif /* __O2FSCK_PASS1_H__ */ ocfs2-tools-ocfs2-tools-1.8.6/fsck.ocfs2/include/pass2.h000066400000000000000000000020341347147137200226450ustar00rootroot00000000000000/* * pass2.h * * Copyright (C) 2002 Oracle Corporation. All rights reserved. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * * Author: Zach Brown */ #ifndef __O2FSCK_PASS2_H__ #define __O2FSCK_PASS2_H__ #include "fsck.h" errcode_t o2fsck_pass2(o2fsck_state *ost); int o2fsck_test_inode_allocated(o2fsck_state *ost, uint64_t blkno); #endif /* __O2FSCK_PASS2_H__ */ ocfs2-tools-ocfs2-tools-1.8.6/fsck.ocfs2/include/pass3.h000066400000000000000000000020271347147137200226500ustar00rootroot00000000000000/* * pass3.h * * Copyright (C) 2002 Oracle Corporation. All rights reserved. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * * Author: Zach Brown */ #ifndef __O2FSCK_PASS3_H__ #define __O2FSCK_PASS3_H__ #include "fsck.h" errcode_t o2fsck_pass3(o2fsck_state *ost); void o2fsck_reconnect_file(o2fsck_state *ost, uint64_t inode); #endif /* __O2FSCK_PASS3_H__ */ ocfs2-tools-ocfs2-tools-1.8.6/fsck.ocfs2/include/pass4.h000066400000000000000000000020331347147137200226460ustar00rootroot00000000000000/* * pass4.h * * Copyright (C) 2002 Oracle Corporation. All rights reserved. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * * Author: Zach Brown */ #ifndef __O2FSCK_PASS4_H__ #define __O2FSCK_PASS4_H__ #include "fsck.h" errcode_t replay_orphan_dir(o2fsck_state *ost, int slot_recovery); errcode_t o2fsck_pass4(o2fsck_state *ost); #endif /* __O2FSCK_PASS4_H__ */ ocfs2-tools-ocfs2-tools-1.8.6/fsck.ocfs2/include/pass5.h000066400000000000000000000013521347147137200226520ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * pass5.h * * Copyright (C) 2004, 2008 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License version 2 as published by the Free Software Foundation. * * 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. */ #ifndef __O2FSCK_PASS5_H__ #define __O2FSCK_PASS5_H__ #include "fsck.h" errcode_t o2fsck_pass5(o2fsck_state *ost); #endif /* __O2FSCK_PASS4_H__ */ ocfs2-tools-ocfs2-tools-1.8.6/fsck.ocfs2/include/problem.h000066400000000000000000000041451347147137200232620ustar00rootroot00000000000000/* * problem.h * * Copyright (C) 2002 Oracle Corporation. All rights reserved. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * * Author: Zach Brown */ #ifndef __O2FSCK_PROBLEM_H__ #define __O2FSCK_PROBLEM_H__ /* prompt flags. */ #define PY (1 << 0) /* default to yes when asked and no answer forced */ #define PN (1 << 1) /* default to no when asked and no answer forced */ #include "fsck.h" /* returns non-zero for yes and zero for no. The caller is expected to * provide a thorough description of the state and the action that will * be taken depending on the answer. Without \n termination. * * The code argument is a digit that identifies the error. it is used by * input to regression testing utils and referenced in fsck.ocfs2.checks(8). * each code is only supposed to have one call site. we create this * funny symbol so that the 'check-prompt-callers' makefile target * can go groveling through nm to find out if a code has more than * one call site. */ #define prompt(ost, flags, code, fmt...) ({ \ static int fsck_prompt_callers_with_code_##code = __LINE__; \ int _ret = fsck_prompt_callers_with_code_##code; \ _ret = prompt_input(ost, flags, code, fmt); \ _ret; \ }) struct prompt_code { const char *str; }; #include "../prompt-codes.h" int prompt_input(o2fsck_state *ost, unsigned flags, struct prompt_code code, const char *fmt, ...) __attribute__ ((format (printf, 4, 5))); #endif /* __O2FSCK_PROBLEM_H__ */ ocfs2-tools-ocfs2-tools-1.8.6/fsck.ocfs2/include/refcount.h000066400000000000000000000020361347147137200234440ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * refcount.h * * Copyright (C) 2009 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License version 2 as published by the Free Software Foundation. * * 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. */ #ifndef __O2FSCK_REFCOUNT_H__ #define __O2FSCK_REFCOUNT_H__ #include "fsck.h" errcode_t o2fsck_check_refcount_tree(o2fsck_state *ost, struct ocfs2_dinode *di); errcode_t o2fsck_mark_clusters_refcounted(o2fsck_state *ost, uint64_t rf_blkno, uint64_t i_blkno, uint64_t p_cpos, uint32_t clusters, uint32_t v_cpos); errcode_t o2fsck_check_mark_refcounted_clusters(o2fsck_state *ost); #endif /* __O2FSCK_REFCOUNT_H__ */ ocfs2-tools-ocfs2-tools-1.8.6/fsck.ocfs2/include/slot_recovery.h000066400000000000000000000016411347147137200245170ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * slot_recovery.c * * Slot recovery handler. * * Copyright (C) 2008 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License version 2 as published by the Free Software Foundation. * * 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. */ #ifndef __O2FSCK_SLOT_RECOVERY_H__ #define __O2FSCK_SLOT_RECOVERY_H__ #include "fsck.h" errcode_t o2fsck_replay_truncate_logs(ocfs2_filesys *fs); errcode_t o2fsck_replay_local_allocs(ocfs2_filesys *fs); errcode_t o2fsck_replay_orphan_dirs(o2fsck_state *ost); #endif /* __O2FSCK_SLOT_RECOVERY_H__ */ ocfs2-tools-ocfs2-tools-1.8.6/fsck.ocfs2/include/util.h000066400000000000000000000073141347147137200226000ustar00rootroot00000000000000/* * util.h * * Copyright (C) 2002 Oracle Corporation. All rights reserved. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * * Author: Zach Brown */ #ifndef __O2FSCK_UTIL_H__ #define __O2FSCK_UTIL_H__ #include #include "fsck.h" /* we duplicate e2fsck's error codes to make everyone's life easy */ #define FSCK_OK 0 /* No errors */ #define FSCK_NONDESTRUCT 1 /* File system errors corrected */ #define FSCK_REBOOT 2 /* System should be rebooted */ #define FSCK_UNCORRECTED 4 /* File system errors left uncorrected */ #define FSCK_ERROR 8 /* Operational error */ #define FSCK_USAGE 16 /* Usage or syntax error */ #define FSCK_CANCELED 32 /* Aborted with a signal or ^C */ #define FSCK_LIBRARY 128 /* Shared library error */ /* Managing the I/O cache */ enum o2fsck_cache_hint { O2FSCK_CACHE_MODE_NONE = 0, O2FSCK_CACHE_MODE_JOURNAL, /* Enough of a cache to replay a journal */ O2FSCK_CACHE_MODE_FULL, /* Enough of a cache to recover the filesystem */ }; void o2fsck_init_cache(o2fsck_state *ost, enum o2fsck_cache_hint hint); int o2fsck_worth_caching(int blocks_to_read); void o2fsck_reset_blocks_cached(void); void o2fsck_write_inode(o2fsck_state *ost, uint64_t blkno, struct ocfs2_dinode *di); void o2fsck_mark_cluster_allocated(o2fsck_state *ost, uint32_t cluster); void o2fsck_mark_clusters_allocated(o2fsck_state *ost, uint32_t cluster, uint32_t num); void o2fsck_mark_cluster_unallocated(o2fsck_state *ost, uint32_t cluster); errcode_t o2fsck_type_from_dinode(o2fsck_state *ost, uint64_t ino, uint8_t *type); errcode_t o2fsck_read_publish(o2fsck_state *ost); size_t o2fsck_bitcount(unsigned char *bytes, size_t len); errcode_t handle_slots_system_file(ocfs2_filesys *fs, int type, errcode_t (*func)(ocfs2_filesys *fs, struct ocfs2_dinode *di, int slot)); /* How to abort but clean up the cluster state */ void o2fsck_abort(void); /* * Wrap the ocfs2 bitmap functions to abort when errors are found. They're * not supposed to fail, so we want to handle it. */ void __o2fsck_bitmap_set(ocfs2_bitmap *bitmap, uint64_t bitno, int *oldval, const char *where); void __o2fsck_bitmap_clear(ocfs2_bitmap *bitmap, uint64_t bitno, int *oldval, const char *where); /* These wrappers pass the caller into __o2fsck_bitmap_*() */ #define o2fsck_bitmap_set(_map, _bit, _old) \ __o2fsck_bitmap_set((_map), (_bit), (_old), __FUNCTION__) #define o2fsck_bitmap_clear(_map, _bit, _old) \ __o2fsck_bitmap_clear((_map), (_bit), (_old), __FUNCTION__); void o2fsck_init_resource_track(struct o2fsck_resource_track *rt, io_channel *channel); void o2fsck_compute_resource_track(struct o2fsck_resource_track *rt, io_channel *channel); void o2fsck_print_resource_track(char *pass, o2fsck_state *ost, struct o2fsck_resource_track *rt, io_channel *channel); void o2fsck_add_resource_track(struct o2fsck_resource_track *rt1, struct o2fsck_resource_track *rt2); #endif /* __O2FSCK_UTIL_H__ */ ocfs2-tools-ocfs2-tools-1.8.6/fsck.ocfs2/include/xattr.h000066400000000000000000000014201347147137200227550ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * xattr.h * * Copyright (C) 2004, 2008 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License version 2 as published by the Free Software Foundation. * * 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. */ #ifndef __O2FSCK_XATTR_H__ #define __O2FSCK_XATTR_H__ #include "fsck.h" errcode_t o2fsck_check_xattr(o2fsck_state *ost, struct ocfs2_dinode *di); #endif /* __O2FSCK_XATTR_H__ */ ocfs2-tools-ocfs2-tools-1.8.6/fsck.ocfs2/journal.c000066400000000000000000000651541347147137200216530ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * Copyright (C) 2000 Andreas Dilger * Copyright (C) 2000 Theodore Ts'o * Copyright (C) 2004,2008 Oracle. All rights reserved. * * Parts of the code are based on fs/jfs/journal.c by Stephen C. Tweedie * Copyright (C) 1999 Red Hat Software * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License version 2 as published by the Free Software Foundation. * * 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. * * -- * This replays the jbd2 journals for each slot. First all the journals are * walked to detect inconsistencies. Only journals with no problems will be * replayed. IO errors during replay will just result in partial journal * replay, just like jbd2 does in the kernel. Journals that don't pass * consistency checks, like having overlapping blocks or strange fields, are * ignored and left for later passes to clean up. * XXX * future passes need to guarantee journals exist and are the same size * pass fsck trigger back up, write dirty fs, always zap/write * revocation code is totally untested * some setup errors, like finding the dlm system inode, are fatal */ #include #include #include #include "ocfs2/byteorder.h" #include "ocfs2/ocfs2.h" #include "fsck.h" #include "journal.h" #include "pass1.h" #include "problem.h" #include "util.h" static const char *whoami = "journal recovery"; struct journal_info { int ji_slot; unsigned ji_replay:1; uint64_t ji_ino; struct rb_root ji_revoke; journal_superblock_t *ji_jsb; uint64_t ji_jsb_block; ocfs2_cached_inode *ji_cinode; unsigned ji_set_final_seq:1; uint32_t ji_final_seq; /* we keep our own bitmap for detecting overlapping journal blocks */ ocfs2_bitmap *ji_used_blocks; }; struct revoke_entry { struct rb_node r_node; uint64_t r_block; uint32_t r_seq; }; static int seq_gt(uint32_t x, uint32_t y) { int32_t diff = x - y; return diff > 0; } static int seq_geq(uint32_t x, uint32_t y) { int32_t diff = x - y; return diff >= 0; } static errcode_t revoke_insert(struct rb_root *root, uint64_t block, uint32_t seq) { struct rb_node ** p = &root->rb_node; struct rb_node * parent = NULL; struct revoke_entry *re; while (*p) { parent = *p; re = rb_entry(parent, struct revoke_entry, r_node); if (block < re->r_block) p = &(*p)->rb_left; else if (block > re->r_block) p = &(*p)->rb_right; else { if (seq_gt(seq, re->r_seq)) re->r_seq = seq; return 0; } } re = malloc(sizeof(struct revoke_entry)); if (re == NULL) return OCFS2_ET_NO_MEMORY; re->r_block = block; re->r_seq = seq; rb_link_node(&re->r_node, parent, p); rb_insert_color(&re->r_node, root); return 0; } static int revoke_this_block(struct rb_root *root, uint64_t block, uint32_t seq) { struct rb_node *node = root->rb_node; struct revoke_entry *re; while (node) { re = rb_entry(node, struct revoke_entry, r_node); if (block < re->r_block) node = node->rb_left; else if (block > re->r_block) node = node->rb_right; else { /* only revoke if we've recorded a revoke entry for * this block that is <= the seq that we're interested * in */ if (re && !seq_gt(seq, re->r_seq)) { verbosef("%"PRIu64" is revoked\n", block); return 1; } } } return 0; } static void revoke_free_all(struct rb_root *root) { struct revoke_entry *re; struct rb_node *node; while((node = rb_first(root)) != NULL) { re = rb_entry(node, struct revoke_entry, r_node); rb_erase(node, root); free(re); } } static errcode_t add_revoke_records(struct journal_info *ji, char *buf, size_t max, uint32_t seq) { journal_revoke_header_t jr; uint32_t *blkno; /* XXX 640k ought to be enough for everybody */ size_t i, num; errcode_t err = 0; memcpy(&jr, buf, sizeof(jr)); jr.r_count = be32_to_cpu(jr.r_count); if (jr.r_count < sizeof(jr) || jr.r_count > max) { verbosef("corrupt r_count: %X", jr.r_count); return OCFS2_ET_BAD_JOURNAL_REVOKE; } num = (jr.r_count - sizeof(jr)) / sizeof(blkno); blkno = (uint32_t *)(buf + sizeof(jr)); for (i = 0; i < num; i++, blkno++) { err = revoke_insert(&ji->ji_revoke, be32_to_cpu(*blkno), seq); if (err) break; } return err; } static uint64_t jwrap(journal_superblock_t *jsb, uint64_t block) { uint64_t diff = jsb->s_maxlen - jsb->s_first; if (diff == 0) /* ugh */ return 0; while (block >= jsb->s_maxlen) block -= diff; return block; } static errcode_t count_tags(ocfs2_filesys *fs, journal_superblock_t *jsb, char *buf, uint64_t *nr_ret) { char *tagp, *last; journal_block_tag_t *tag; int tag_bytes = ocfs2_journal_tag_bytes(jsb); uint64_t nr = 0; if (jsb->s_blocksize < sizeof(journal_header_t) + tag_bytes) return OCFS2_ET_BAD_JOURNAL_TAG; tagp = &buf[sizeof(journal_header_t)]; last = &buf[jsb->s_blocksize - tag_bytes]; for(; tagp <= last; tagp += tag_bytes) { tag = (journal_block_tag_t *)tagp; nr++; if (ocfs2_block_out_of_range(fs, ocfs2_journal_tag_block(tag, tag_bytes))) return OCFS2_ET_BAD_JOURNAL_TAG; if (tag->t_flags & cpu_to_be32(JBD2_FLAG_LAST_TAG)) break; if (!(tag->t_flags & cpu_to_be32(JBD2_FLAG_SAME_UUID))) tagp += 16; } *nr_ret = nr; return 0; } static errcode_t lookup_journal_block(ocfs2_filesys *fs, struct journal_info *ji, uint64_t blkoff, uint64_t *blkno, int check_dup) { errcode_t ret; uint64_t contig; int was_set; ret = ocfs2_extent_map_get_blocks(ji->ji_cinode, blkoff, 1, blkno, &contig, NULL); if (ret) { com_err(whoami, ret, "while looking up logical block " "%"PRIu64" in slot %d's journal", blkoff, ji->ji_slot); goto out; } if (check_dup) { o2fsck_bitmap_set(ji->ji_used_blocks, *blkno, &was_set); if (was_set) { printf("Logical block %"PRIu64" in slot %d's journal " "maps to block %"PRIu64" which has already " "been used in another journal.\n", blkoff, ji->ji_slot, *blkno); ret = OCFS2_ET_DUPLICATE_BLOCK; } } out: return ret; } static errcode_t read_journal_block(ocfs2_filesys *fs, struct journal_info *ji, uint64_t blkoff, char *buf, int check_dup) { errcode_t err; uint64_t blkno; err = lookup_journal_block(fs, ji, blkoff, &blkno, check_dup); if (err) return err; err = ocfs2_read_blocks(fs, blkno, 1, buf); if (err) com_err(whoami, err, "while reading block %"PRIu64" of slot " "%d's journal", blkno, ji->ji_slot); return err; } static errcode_t replay_blocks(ocfs2_filesys *fs, struct journal_info *ji, char *buf, uint64_t seq, uint64_t *next_block) { char *tagp; journal_block_tag_t *tag; size_t i, num; char *io_buf = NULL; errcode_t err, ret = 0; int tag_bytes = ocfs2_journal_tag_bytes(ji->ji_jsb); uint32_t t_flags; uint64_t block64; tagp = buf + sizeof(journal_header_t); num = (ji->ji_jsb->s_blocksize - sizeof(journal_header_t)) / tag_bytes; ret = ocfs2_malloc_blocks(fs->fs_io, 1, &io_buf); if (ret) { com_err(whoami, ret, "while allocating a block buffer"); goto out; } for(i = 0; i < num; i++, tagp += tag_bytes, (*next_block)++) { tag = (journal_block_tag_t *)tagp; t_flags = be32_to_cpu(tag->t_flags); block64 = ocfs2_journal_tag_block(tag, tag_bytes); *next_block = jwrap(ji->ji_jsb, *next_block); verbosef("recovering journal block %"PRIu64" to disk block " "%"PRIu64"\n", *next_block, block64); if (revoke_this_block(&ji->ji_revoke, block64, seq)) goto skip_io; err = read_journal_block(fs, ji, *next_block, io_buf, 1); if (err) { ret = err; goto skip_io; } if (t_flags & JBD2_FLAG_ESCAPE) { uint32_t magic = cpu_to_be32(JBD2_MAGIC_NUMBER); memcpy(io_buf, &magic, sizeof(magic)); } err = io_write_block(fs->fs_io, block64, 1, io_buf); if (err) ret = err; skip_io: if (t_flags & JBD2_FLAG_LAST_TAG) i = num; /* be sure to increment next_block */ if (!(t_flags & JBD2_FLAG_SAME_UUID)) tagp += 16; } *next_block = jwrap(ji->ji_jsb, *next_block); out: if (io_buf) ocfs2_free(&io_buf); return ret; } static errcode_t walk_journal(ocfs2_filesys *fs, int slot, struct journal_info *ji, char *buf, int recover) { errcode_t err, ret = 0; uint32_t next_seq; uint64_t next_block, nr; journal_superblock_t *jsb = ji->ji_jsb; journal_header_t jh; next_seq = jsb->s_sequence; next_block = jsb->s_start; /* s_start == 0 when we have nothing to do */ if (next_block == 0) return 0; /* ret is set when bad tags are seen in the first scan and when there * are io errors in the recovery scan. Only stop walking the journal * when bad tags are seen in the first scan. */ while(recover || !ret) { verbosef("next_seq %"PRIu32" final_seq %"PRIu32" next_block " "%"PRIu64"\n", next_seq, ji->ji_final_seq, next_block); if (recover && seq_geq(next_seq, ji->ji_final_seq)) break; /* only mark the blocks used on the first pass */ err = read_journal_block(fs, ji, next_block, buf, !recover); if (err) { ret = err; break; } next_block = jwrap(jsb, next_block + 1); memcpy(&jh, buf, sizeof(jh)); jh.h_magic = be32_to_cpu(jh.h_magic); jh.h_blocktype = be32_to_cpu(jh.h_blocktype); jh.h_sequence = be32_to_cpu(jh.h_sequence); verbosef("jh magic %x\n", jh.h_magic); if (jh.h_magic != JBD2_MAGIC_NUMBER) break; verbosef("jh block %x\n", jh.h_blocktype); verbosef("jh seq %"PRIu32"\n", jh.h_sequence); if (jh.h_sequence != next_seq) break; switch(jh.h_blocktype) { case JBD2_DESCRIPTOR_BLOCK: verbosef("found a desc type %x\n", jh.h_blocktype); /* replay the blocks described in the desc block */ if (recover) { err = replay_blocks(fs, ji, buf, next_seq, &next_block); if (err) ret = err; continue; } /* just record the blocks as used and carry on */ err = count_tags(fs, jsb, buf, &nr); if (err) ret = err; else next_block = jwrap(jsb, next_block + nr); break; case JBD2_COMMIT_BLOCK: verbosef("found a commit type %x\n", jh.h_blocktype); next_seq++; break; case JBD2_REVOKE_BLOCK: verbosef("found a revoke type %x\n", jh.h_blocktype); add_revoke_records(ji, buf, jsb->s_blocksize, next_seq); break; default: verbosef("unknown type %x\n", jh.h_blocktype); break; } } verbosef("done scanning with seq %"PRIu32"\n", next_seq); if (!recover) { ji->ji_set_final_seq = 1; ji->ji_final_seq = next_seq; } else if (ji->ji_final_seq != next_seq) { printf("Replaying slot %d's journal stopped at seq %"PRIu32" " "but an initial scan indicated that it should have " "stopped at seq %"PRIu32"\n", ji->ji_slot, next_seq, ji->ji_final_seq); if (ret == 0) err = OCFS2_ET_IO; } return ret; } static errcode_t prep_journal_info(ocfs2_filesys *fs, int slot, struct journal_info *ji) { errcode_t err; err = ocfs2_malloc_blocks(fs->fs_io, 1, &ji->ji_jsb); if (err) com_err(whoami, err, "while allocating space for slot %d's " "journal superblock", slot); err = ocfs2_lookup_system_inode(fs, JOURNAL_SYSTEM_INODE, slot, &ji->ji_ino); if (err) { com_err(whoami, err, "while looking up the journal inode for " "slot %d", slot); goto out; } err = ocfs2_read_cached_inode(fs, ji->ji_ino, &ji->ji_cinode); if (err) { com_err(whoami, err, "while reading cached inode %"PRIu64" " "for slot %d's journal", ji->ji_ino, slot); goto out; } if (!(ji->ji_cinode->ci_inode->id1.journal1.ij_flags & OCFS2_JOURNAL_DIRTY_FL)) goto out; err = lookup_journal_block(fs, ji, 0, &ji->ji_jsb_block, 1); if (err) goto out; /* XXX be smarter about reading in the whole super block if it * spans multiple blocks */ err = ocfs2_read_journal_superblock(fs, ji->ji_jsb_block, (char *)ji->ji_jsb); if (err) { com_err(whoami, err, "while reading block %"PRIu64" as slot " "%d's journal super block", ji->ji_jsb_block, ji->ji_slot); goto out; } ji->ji_replay = 1; verbosef("slot: %d jsb start %u maxlen %u\n", slot, ji->ji_jsb->s_start, ji->ji_jsb->s_maxlen); out: return err; } /* * We only need to replay the journals if the inode's flag is set and s_start * indicates that there is actually pending data in the journals. * * In the simple case of an unclean shutdown we don't want to have to build up * enough state to be able to truncate the inodes waiting in the orphan dir. * ocfs2 in the kernel only fixes up the orphan dirs if the journal dirty flag * is set. So after replaying the journals we clear s_startin the journals to * stop a second journal replay but leave the dirty bit set so that the kernel * will truncate the orphaned inodes. */ errcode_t o2fsck_should_replay_journals(ocfs2_filesys *fs, int *should, int *has_dirty) { uint16_t i, max_slots; char *buf = NULL; uint64_t blkno; errcode_t ret; ocfs2_cached_inode *cinode = NULL; int is_dirty; uint64_t contig; journal_superblock_t *jsb; *should = 0; max_slots = OCFS2_RAW_SB(fs->fs_super)->s_max_slots; ret = ocfs2_malloc_block(fs->fs_io, &buf); if (ret) { com_err(whoami, ret, "while allocating room to read journal " "blocks"); goto out; } jsb = (journal_superblock_t *)buf; for (i = 0; i < max_slots; i++) { ret = ocfs2_lookup_system_inode(fs, JOURNAL_SYSTEM_INODE, i, &blkno); if (ret) { com_err(whoami, ret, "while looking up the journal " "inode for slot %d", i); goto out; } if (cinode) { ocfs2_free_cached_inode(fs, cinode); cinode = NULL; } ret = ocfs2_read_cached_inode(fs, blkno, &cinode); if (ret) { com_err(whoami, ret, "while reading cached inode " "%"PRIu64" for slot %d's journal", blkno, i); goto out; } is_dirty = cinode->ci_inode->id1.journal1.ij_flags & OCFS2_JOURNAL_DIRTY_FL; verbosef("slot %d JOURNAL_DIRTY_FL: %d\n", i, is_dirty); if (!is_dirty) continue; else *has_dirty = 1; ret = ocfs2_extent_map_get_blocks(cinode, 0, 1, &blkno, &contig, NULL); if (ret) { com_err(whoami, ret, "while looking up the journal " "super block in slot %d's journal", i); goto out; } /* XXX be smarter about reading in the whole super block if it * spans multiple blocks */ ret = ocfs2_read_journal_superblock(fs, blkno, buf); if (ret) { com_err(whoami, ret, "while reading the journal " "super block in slot %d's journal", i); goto out; } if (jsb->s_start) *should = 1; } out: if (buf) ocfs2_free(&buf); if (cinode) ocfs2_free_cached_inode(fs, cinode); return ret; } /* Try and replay the slots journals if they're dirty. This only returns * a non-zero error if the caller should not continue. */ errcode_t o2fsck_replay_journals(ocfs2_filesys *fs, int *replayed) { errcode_t err = 0, ret = 0; struct journal_info *jis = NULL, *ji; journal_superblock_t *jsb; char *buf = NULL; int journal_trouble = 0; uint16_t i, max_slots; ocfs2_bitmap *used_blocks = NULL; max_slots = OCFS2_RAW_SB(fs->fs_super)->s_max_slots; ret = ocfs2_block_bitmap_new(fs, "journal blocks", &used_blocks); if (ret) { com_err(whoami, ret, "while allocating journal block bitmap"); goto out; } ret = ocfs2_malloc_blocks(fs->fs_io, 1, &buf); if (ret) { com_err(whoami, ret, "while allocating room to read journal " "blocks"); goto out; } ret = ocfs2_malloc0(sizeof(struct journal_info) * max_slots, &jis); if (ret) { com_err(whoami, ret, "while allocating an array of block " "numbers for journal replay"); goto out; } printf("Checking each slot's journal.\n"); for (i = 0, ji = jis; i < max_slots; i++, ji++) { ji->ji_used_blocks = used_blocks; ji->ji_revoke = RB_ROOT; ji->ji_slot = i; /* sets ji->ji_replay */ err = prep_journal_info(fs, i, ji); if (err) { printf("Slot %d seems to have a corrupt journal.\n", i); journal_trouble = 1; continue; } if (!ji->ji_replay) { verbosef("slot %d is clean\n", i); continue; } err = walk_journal(fs, i, ji, buf, 0); if (err) { printf("Slot %d's journal can not be replayed.\n", i); journal_trouble = 1; } } for (i = 0, ji = jis; i < max_slots; i++, ji++) { if (!ji->ji_replay) continue; printf("Replaying slot %d's journal.\n", i); err = walk_journal(fs, i, ji, buf, 1); if (err) { journal_trouble = 1; continue; } jsb = ji->ji_jsb; /* reset the journal */ jsb->s_start = 0; if (ji->ji_set_final_seq) jsb->s_sequence = ji->ji_final_seq + 1; /* we don't write back a clean 'mounted' bit here. That would * have to also include having recovered the orphan dir. we * updated s_start, though, so we won't replay the journal * again. */ err = ocfs2_write_journal_superblock(fs, ji->ji_jsb_block, (char *)ji->ji_jsb); if (err) { com_err(whoami, err, "while writing slot %d's journal " "super block", i); journal_trouble = 1; } else { printf("Slot %d's journal replayed successfully.\n", i); *replayed = 1; } } /* this is awkward, but we want fsck -n to tell us as much as it * can so we don't want to ask to proceed here. */ if (journal_trouble) printf("*** There were problems replaying journals. Be " "careful in telling fsck to make repairs to this " "filesystem.\n"); ret = 0; out: if (jis) { for (i = 0, ji = jis; i < max_slots; i++, ji++) { if (ji->ji_jsb) ocfs2_free(&ji->ji_jsb); if (ji->ji_cinode) ocfs2_free_cached_inode(fs, ji->ji_cinode); revoke_free_all(&ji->ji_revoke); } ocfs2_free(&jis); } if (buf) ocfs2_free(&buf); if (used_blocks) ocfs2_bitmap_free(&used_blocks); return ret; } struct journal_check_info { errcode_t i_error; uint32_t i_clusters; ocfs2_fs_options i_features; }; struct journal_check_context { int jc_max_slots; int jc_this_slot; uint32_t jc_max_clusters; /* Size of the largest journal found */ ocfs2_fs_options jc_max_features; /* Union all features in good journals */ struct journal_check_info *jc_info; /* One per slot */ }; static errcode_t check_journals_func(o2fsck_state *ost, ocfs2_cached_inode *ci, struct journal_check_context *jc) { errcode_t err, ret; ocfs2_filesys *fs = ost->ost_fs; uint64_t contig; uint64_t blkno; char *buf = NULL; journal_superblock_t *jsb; struct journal_check_info *ji = &(jc->jc_info[jc->jc_this_slot]); ret = ocfs2_malloc_blocks(fs->fs_io, 1, &buf); if (ret) goto out; err = ocfs2_extent_map_get_blocks(ci, 0, 1, &blkno, &contig, NULL); if (err) { ji->i_error = err; goto out; } ji->i_clusters = ci->ci_inode->i_clusters; err = ocfs2_read_journal_superblock(fs, blkno, buf); if (err) { ji->i_error = err; goto out; } jsb = (journal_superblock_t *)buf; ji->i_features.opt_compat = jsb->s_feature_compat; ji->i_features.opt_ro_compat = jsb->s_feature_ro_compat; ji->i_features.opt_incompat = jsb->s_feature_incompat; if (!ji->i_clusters) { ji->i_error = OCFS2_ET_JOURNAL_TOO_SMALL; goto out; } jc->jc_max_clusters = ocfs2_max(jc->jc_max_clusters, ci->ci_inode->i_clusters); jc->jc_max_features.opt_compat |= jsb->s_feature_compat; jc->jc_max_features.opt_ro_compat |= jsb->s_feature_ro_compat; jc->jc_max_features.opt_incompat |= jsb->s_feature_incompat; ji->i_error = 0; out: if (buf) ocfs2_free(&buf); return ret; } static errcode_t fix_journals_func(o2fsck_state *ost, ocfs2_cached_inode *ci, struct journal_check_context *jc) { errcode_t err, ret = 0; ocfs2_filesys *fs = ost->ost_fs; char fname[OCFS2_MAX_FILENAME_LEN]; struct journal_check_info *ji = &(jc->jc_info[jc->jc_this_slot]); uint32_t min_clusters = ocfs2_clusters_in_bytes(fs, OCFS2_MIN_JOURNAL_SIZE); ocfs2_sprintf_system_inode_name(fname, OCFS2_MAX_FILENAME_LEN, JOURNAL_SYSTEM_INODE, jc->jc_this_slot); if (ji->i_error && (ji->i_error != OCFS2_ET_JOURNAL_TOO_SMALL) && (ji->i_error != OCFS2_ET_UNSUPP_FEATURE) && (ji->i_error != OCFS2_ET_RO_UNSUPP_FEATURE)) { if (prompt(ost, PY, PR_JOURNAL_FILE_INVALID, "journal file %s is invalid, regenerate it?", fname)) { err = ocfs2_make_journal(fs, ci->ci_blkno, jc->jc_max_clusters, &jc->jc_max_features); ji->i_error = err; } goto out; } if ((ji->i_error == OCFS2_ET_UNSUPP_FEATURE) || (ji->i_error == OCFS2_ET_RO_UNSUPP_FEATURE)) { if (prompt(ost, PN, PR_JOURNAL_UNKNOWN_FEATURE, "journal file %s has unknown features. " "However, other journals have only known " "features, so this is likely a corruption. " "If you think your filesystem may be newer " "than this version of fsck.ocfs2, say N here " "and grab the latest version of fsck.ocfs2. " "Reset the journal features to match other " "journals?", fname)) { err = ocfs2_make_journal(fs, ci->ci_blkno, ocfs2_max(ji->i_clusters, min_clusters), &jc->jc_max_features); if (!err) { ji->i_features.opt_compat = jc->jc_max_features.opt_compat; ji->i_features.opt_ro_compat = jc->jc_max_features.opt_ro_compat; ji->i_features.opt_incompat = jc->jc_max_features.opt_incompat; } ji->i_error = err; } } else if ((ji->i_features.opt_compat != jc->jc_max_features.opt_compat) || (ji->i_features.opt_ro_compat != jc->jc_max_features.opt_ro_compat) || (ji->i_features.opt_incompat != jc->jc_max_features.opt_incompat)) { if (prompt(ost, PY, PR_JOURNAL_MISSING_FEATURE, "journal file %s is missing features that " "are set on other journal files. Set these " "features?", fname)) { err = ocfs2_make_journal(fs, ci->ci_blkno, ocfs2_max(ji->i_clusters, min_clusters), &jc->jc_max_features); if (!err) { ji->i_features.opt_compat = jc->jc_max_features.opt_compat; ji->i_features.opt_ro_compat = jc->jc_max_features.opt_ro_compat; ji->i_features.opt_incompat = jc->jc_max_features.opt_incompat; } ji->i_error = err; } } if (ji->i_clusters != jc->jc_max_clusters) { if (prompt(ost, PY, PR_JOURNAL_TOO_SMALL, "journal file %s is too small, extend it?", fname)) { err = ocfs2_make_journal(fs, ci->ci_blkno, jc->jc_max_clusters, &ji->i_features); ji->i_error = err; } } out: return ret; } static errcode_t check_journal_walk(o2fsck_state *ost, errcode_t (*func)(o2fsck_state *ost, ocfs2_cached_inode *ci, struct journal_check_context *jc), struct journal_check_context *jc) { errcode_t ret = 0; uint64_t blkno; ocfs2_filesys *fs = ost->ost_fs; uint16_t i, max_slots = OCFS2_RAW_SB(fs->fs_super)->s_max_slots; ocfs2_cached_inode *ci = NULL; for (i = 0; i < max_slots; i++) { ret = ocfs2_lookup_system_inode(fs, JOURNAL_SYSTEM_INODE, i, &blkno); if (ret) break; ret = ocfs2_read_cached_inode(fs, blkno, &ci); if (ret) break; jc->jc_this_slot = i; ret = func(ost, ci, jc); if (ret) break; ocfs2_free_cached_inode(fs, ci); ci = NULL; } ocfs2_free_cached_inode(fs, ci); return ret; } errcode_t o2fsck_check_journals(o2fsck_state *ost) { errcode_t ret; int i; ocfs2_filesys *fs = ost->ost_fs; int have_one_good_journal = 0, problem_is_consistent = 1; errcode_t known_problem = 0; struct journal_check_context jc = { .jc_max_slots = OCFS2_RAW_SB(fs->fs_super)->s_max_slots, }; struct journal_check_info *ji; ret = ocfs2_malloc0(sizeof(struct journal_check_info) * jc.jc_max_slots, &jc.jc_info); if (ret) { com_err(whoami, ret, "while checking journals"); goto out; } ret = check_journal_walk(ost, check_journals_func, &jc); if (ret) { com_err(whoami, ret, "while checking journals"); goto out; } /* * We now know the state of all our journals. If we have at least * one good journal, we have a sane state to fix the others from. * We require all our journals to have identical configuration. * Any inconsistencies (invalid size, bad feature flags) are * probably corruption or a failed tunefs. * * If we don't have a good journal, but all the journals have the * exact same problem, we may be able to handle it as well. We * currently know how to handle these problems: * * JOURNAL_TOO_SMALL * * We simply allocate a default journal size. * * UNSUPP_FEATURE & RO_UNSUPP_FEATURE * * If one journal has an unsupported feature bit set, it's probably * corruption. If all the journals have the exact same feature * bit set, it's certainly a feature we don't understand, and we * want the user to upgrade their fsck. */ for (i = 0; i < jc.jc_max_slots; i++) { ji = &jc.jc_info[i]; if (!ji->i_error) { have_one_good_journal = 1; continue; } if ((ji->i_error != OCFS2_ET_JOURNAL_TOO_SMALL) && (ji->i_error != OCFS2_ET_UNSUPP_FEATURE) && (ji->i_error != OCFS2_ET_RO_UNSUPP_FEATURE)) { problem_is_consistent = 0; continue; } if (known_problem) { if (known_problem != ji->i_error) problem_is_consistent = 0; continue; } known_problem = ji->i_error; } if (!have_one_good_journal) { if (!problem_is_consistent || !known_problem) { ret = jc.jc_info[0].i_error; com_err(whoami, ret, "while checking journals"); goto out; } if ((known_problem == OCFS2_ET_UNSUPP_FEATURE) || (known_problem == OCFS2_ET_RO_UNSUPP_FEATURE)) { com_err(whoami, known_problem, "on all journals. Please upgrade to the " "latest version of fsck.ocfs2"); ret = known_problem; goto out; } if (known_problem != OCFS2_ET_JOURNAL_TOO_SMALL) { ret = known_problem; com_err(whoami, ret, "for all journals"); goto out; } /* Force a valid cluster count for the journals */ jc.jc_max_clusters = ocfs2_clusters_in_bytes(fs, OCFS2_MIN_JOURNAL_SIZE); } ret = check_journal_walk(ost, fix_journals_func, &jc); out: if (jc.jc_info) ocfs2_free(&jc.jc_info); return ret; } static errcode_t ocfs2_clear_journal_flag(ocfs2_filesys *fs, struct ocfs2_dinode *di, int slot) { errcode_t ret = 0; if (!(di->i_flags & OCFS2_VALID_FL) || !(di->i_flags & OCFS2_SYSTEM_FL) || !(di->i_flags & OCFS2_JOURNAL_FL)) return OCFS2_ET_INVALID_ARGUMENT; if (!(di->id1.journal1.ij_flags & OCFS2_JOURNAL_DIRTY_FL)) goto bail; di->id1.journal1.ij_flags &= ~OCFS2_JOURNAL_DIRTY_FL; ret = ocfs2_write_inode(fs, di->i_blkno, (char *)di); if (!ret) printf("Slot %d's journal dirty flag removed\n", slot); bail: return ret; } errcode_t o2fsck_clear_journal_flags(o2fsck_state *ost) { if (!ost->ost_has_journal_dirty) return 0; return handle_slots_system_file(ost->ost_fs, JOURNAL_SYSTEM_INODE, ocfs2_clear_journal_flag); } ocfs2-tools-ocfs2-tools-1.8.6/fsck.ocfs2/pass0.c000066400000000000000000001303321347147137200212160ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * Copyright (C) 1993-2004 by Theodore Ts'o. * Copyright (C) 2004 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License, version 2, as published by the Free Software Foundation. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * * -- * * Pass 0 verifies that the basic linkage of the various chain allocators is * intact so that future passes can use them in place safely. The actual * bitmaps in the allocators aren't worried about here. Later passes will * clean them up by loading them in to memory, updating them, and writing them * back out. * * Pass 1, for example, wants to iterate over the inode blocks covered by the * inode chain allocators so it can verify them and update the allocation * bitmaps for inodes that are still in use. * * The cluster chain allocator is a special case because its group descriptors * are at regular predictable offsets throughout the volume. fsck forces these * block descriptors into service and removes and block descriptors in the * chain that aren't at these offsets. * * pass0 updates group descriptor chains on disk. * * XXX * track blocks and clusters we see here that iteration won't * verify more inode fields? * make sure blocks don't overlap as part of cluster tracking * make sure _bits is correct, pass in from callers * generalize the messages to chain allocators instead of inode allocators */ #include #include #include #include #include "ocfs2/ocfs2.h" #include "ocfs2/bitops.h" #include "dirblocks.h" #include "dirparents.h" #include "icount.h" #include "fsck.h" #include "pass0.h" #include "pass1.h" #include "problem.h" #include "util.h" static const char *whoami = "pass0"; struct chain_state { uint32_t cs_free_bits; uint32_t cs_total_bits; uint32_t cs_chain_no; uint16_t cs_cpg; }; static void find_max_free_bits(struct ocfs2_group_desc *gd, int *max_free_bits) { int end = 0; int start; int free_bits; *max_free_bits = 0; while (end < gd->bg_bits) { start = ocfs2_find_next_bit_clear(gd->bg_bitmap, gd->bg_bits, end); if (start >= gd->bg_bits) break; end = ocfs2_find_next_bit_set(gd->bg_bitmap, gd->bg_bits, start); free_bits = end - start; *max_free_bits += free_bits; } } /* check whether the group really exists in the specified chain of * the specified allocator file. */ static errcode_t check_group_parent(ocfs2_filesys *fs, uint64_t group, uint64_t ino, uint16_t chain,int *exist) { errcode_t ret; uint64_t gd_blkno; char *buf = NULL, *gd_buf = NULL; struct ocfs2_dinode *di = NULL; struct ocfs2_group_desc *gd = NULL; struct ocfs2_chain_rec *cr = NULL; ret = ocfs2_malloc_block(fs->fs_io, &buf); if (ret) goto out; ret = ocfs2_read_inode(fs, ino, buf); if (ret) { goto out; } di = (struct ocfs2_dinode *)buf; if (!(di->i_flags & OCFS2_VALID_FL) || !(di->i_flags & OCFS2_BITMAP_FL) || !(di->i_flags & OCFS2_CHAIN_FL)) goto out; if (di->id1.bitmap1.i_total == 0) goto out; if (di->id2.i_chain.cl_next_free_rec <= chain) goto out; cr = &di->id2.i_chain.cl_recs[chain]; ret = ocfs2_malloc_block(fs->fs_io, &gd_buf); if (ret) goto out; gd_blkno = cr->c_blkno; while (gd_blkno) { if (gd_blkno == group) { *exist = 1; break; } ret = ocfs2_read_group_desc(fs, gd_blkno, gd_buf); if (ret) goto out; gd = (struct ocfs2_group_desc *)gd_buf; gd_blkno = gd->bg_next_group; } out: if (gd_buf) ocfs2_free(&gd_buf); if (buf) ocfs2_free(&buf); return ret; } static void check_discontig_bg(o2fsck_state *ost, int cpg, struct ocfs2_group_desc *bg, int *changed, int *clear_ref) { uint64_t blkno = bg->bg_blkno; int next_free, i, total_clusters = 0; int fix_pos = -1; struct ocfs2_extent_rec *rec; if (bg->bg_list.l_tree_depth && prompt(ost, PY, PR_DISCONTIG_BG_DEPTH, "Discontiguous Group descriptor at block %"PRIu64" has " "a tree depth %u which is greater than 0. " "Change it to 0?", blkno, bg->bg_list.l_tree_depth)) { bg->bg_list.l_tree_depth = 0; *changed = 1; } if ((bg->bg_list.l_count > ocfs2_extent_recs_per_gd(ost->ost_fs->fs_blocksize)) && prompt(ost, PY, PR_DISCONTIG_BG_COUNT, "Discontiguous group descriptor at block %"PRIu64" has " "an extent count of %u, but discontiguous groups can " "only hold %u records. Set it to %u?", blkno, bg->bg_list.l_count, ocfs2_extent_recs_per_gd(ost->ost_fs->fs_blocksize), ocfs2_extent_recs_per_gd(ost->ost_fs->fs_blocksize))) { bg->bg_list.l_count = ocfs2_extent_recs_per_gd(ost->ost_fs->fs_blocksize); *changed = 1; } if (bg->bg_list.l_next_free_rec > bg->bg_list.l_count) next_free = bg->bg_list.l_count; else next_free = bg->bg_list.l_next_free_rec; for (i = 0; i < next_free; i++) { rec = &bg->bg_list.l_recs[i]; /* * We treat e_blkno = 0 and e_leaf_cluster = 0 as the * end of the extent list so that we can find the proper * l_next_free_rec. */ if (!rec->e_blkno && !rec->e_leaf_clusters) break; if (ocfs2_block_out_of_range(ost->ost_fs, rec->e_blkno) || ocfs2_block_out_of_range(ost->ost_fs, rec->e_blkno + ocfs2_clusters_to_blocks(ost->ost_fs, rec->e_leaf_clusters) - 1)) { if (prompt(ost, PY, PR_DISCONTIG_BG_REC_RANGE, "Discontiguous block group %"PRIu64" in " "chain %d of inode %"PRIu64" claims " "clusters which is out of range. " "Drop this group?", blkno, bg->bg_chain, (uint64_t)bg->bg_parent_dinode)) *clear_ref = 1; goto out; } if (rec->e_leaf_clusters > cpg) { if (fix_pos >= 0) { if (prompt(ost, PY, PR_DISCONTIG_BG_CORRUPT_LEAVES, "Discontiguous block group %"PRIu64 " in chain %d of inode %"PRIu64" " "has errors in more than one extent " "record. Record %d claims %u " "clusters and record %d claims %u " "clusters, but a group does not " "contain more than %u clusters. " "Drop this group?", blkno, bg->bg_chain, (uint64_t)bg->bg_parent_dinode, fix_pos, bg->bg_list.l_recs[fix_pos].e_leaf_clusters, i, rec->e_leaf_clusters, cpg)) *clear_ref = 1; goto out; } fix_pos = i; continue; } if ((total_clusters + rec->e_leaf_clusters) > cpg) { if (total_clusters == cpg) { if (fix_pos >= 0) { /* * We have to drop the group here since * both l_next_free_rec and a extent * record have errors. */ if (prompt(ost, PY, PR_DISCONTIG_BG_LIST_CORRUPT, "Discontiguous group " "descriptor at block " "%"PRIu64" claims to use %u " "extents but only has %u " "filled in. The filled in " "records contain errors. " "Drop this group?", blkno, bg->bg_list.l_next_free_rec, i)) *clear_ref = 1; goto out; } /* * break out here so that we can fix the * l_next_free_rec. */ break; } else { if (prompt(ost, PY, PR_DISCONTIG_BG_CLUSTERS, "Discontiguous group descriptor at " "block %"PRIu64" claims to have %u " "clusters but a group does not " "contain more than %u clusters. " "Drop the group?", blkno, total_clusters + rec->e_leaf_clusters, cpg)) *clear_ref = 1; goto out; } } else total_clusters += rec->e_leaf_clusters; } if (bg->bg_list.l_next_free_rec != i) { /* Change l_next_free_rec since the extent list look sane. */ if (prompt(ost, PY, PR_DISCONTIG_BG_NEXT_FREE_REC, "Discontiguous group descriptor at " "block %"PRIu64" claims to use %u " "extents but only has %u filled in. " "The filled in records appear to " "be correct. Set the used extent " "count to the number filled in?", blkno, bg->bg_list.l_next_free_rec, i)) { bg->bg_list.l_next_free_rec = i; *changed = 1; } } if (fix_pos < 0 && total_clusters < cpg) { if (prompt(ost, PY, PR_DISCONTIG_BG_LESS_CLUSTERS, "Discontiguous group descriptor at " "block %"PRIu64" claims to have %u " "clusters but a group does not " "contain less than %u clusters. " "Drop the group?", blkno, total_clusters, cpg)) *clear_ref = 1; goto out; } if (fix_pos >= 0) { rec = &bg->bg_list.l_recs[fix_pos]; if (total_clusters == cpg) { if (prompt(ost, PY, PR_DISCONTIG_BG_REC_CORRUPT, "Extent record %d of discontiguous group " "descriptor at block %"PRIu64" claims %u " "clusters, but a group does not " "contain more than %u clusters. " "Drop the group?", fix_pos, blkno, rec->e_leaf_clusters, cpg)) *clear_ref = 1; goto out; } if (prompt(ost, PY, PR_DISCONTIG_BG_LEAF_CLUSTERS, "Extent record %d of discontiguous group " "descriptor %"PRIu64" claims %u clusters, " "but it should only have %u. Correct it?", fix_pos, blkno, rec->e_leaf_clusters, cpg - total_clusters)) { rec->e_leaf_clusters = cpg - total_clusters; *changed = 1; } } out: return; } static errcode_t repair_group_desc(o2fsck_state *ost, struct ocfs2_dinode *di, struct chain_state *cs, struct ocfs2_group_desc *bg, uint64_t blkno, int *clear_ref) { errcode_t ret = 0; int changed = 0; int max_free_bits = 0; verbosef("checking desc at %"PRIu64"; blkno %"PRIu64" size %u bits %u " "free_bits %u chain %u generation %u\n", blkno, (uint64_t)bg->bg_blkno, bg->bg_size, bg->bg_bits, bg->bg_free_bits_count, bg->bg_chain, bg->bg_generation); if (bg->bg_generation != ost->ost_fs_generation && prompt(ost, PY, PR_GROUP_GEN, "Group descriptor at block %"PRIu64" has " "a generation of %"PRIx32" which doesn't match the " "volume's generation of %"PRIx32". Change the generation " "in the descriptor to match the volume?", blkno, bg->bg_generation, ost->ost_fs_generation)) { bg->bg_generation = ost->ost_fs_generation; changed = 1; } /* XXX maybe for advanced pain we could check to see if these * kinds of descs have valid generations for the inodes they * reference */ if ((bg->bg_parent_dinode != di->i_blkno)) { int exist = 0; ret = check_group_parent(ost->ost_fs, bg->bg_blkno, bg->bg_parent_dinode, bg->bg_chain, &exist); /* If we finds that the group really exists in the specified * chain of the specified alloc inode, then this may be a * duplicated group and we may need to remove it from current * inode. */ if (!ret && exist && prompt(ost, PY, PR_GROUP_DUPLICATE, "Group descriptor at block %"PRIu64" is " "referenced by inode %"PRIu64" but thinks its parent inode " "is %"PRIu64" and we can also see it in that inode." " So it may be duplicated. Remove it from this inode?", blkno, (uint64_t)di->i_blkno, (uint64_t)bg->bg_parent_dinode)) { *clear_ref = 1; goto out; } if (prompt(ost, PY, PR_GROUP_PARENT, "Group descriptor at block %"PRIu64" is " "referenced by inode %"PRIu64" but thinks its parent inode " "is %"PRIu64". Fix the descriptor's parent inode?", blkno, (uint64_t)di->i_blkno, (uint64_t)bg->bg_parent_dinode)) { bg->bg_parent_dinode = di->i_blkno; changed = 1; } } if ((bg->bg_blkno != blkno) && prompt(ost, PY, PR_GROUP_BLKNO, "Group descriptor read from block %"PRIu64" " "claims to be located at block %"PRIu64". Update its " "recorded block location?", blkno, (uint64_t)di->i_blkno)) { bg->bg_blkno = blkno; changed = 1; } if ((bg->bg_chain != cs->cs_chain_no) && prompt(ost, PY, PR_GROUP_CHAIN, "Group descriptor at block %"PRIu64" was " "found in chain %u but it claims to be in chain %u. Update " "the descriptor's recorded chain?", blkno, cs->cs_chain_no, bg->bg_chain)) { bg->bg_chain = cs->cs_chain_no; changed = 1; } find_max_free_bits(bg, &max_free_bits); if ((bg->bg_free_bits_count > max_free_bits) && prompt(ost, PY, PR_GROUP_FREE_BITS, "Group descriptor at block %"PRIu64" claims to " "have %u free bits which is more than %u bits" " indicated by the bitmap. " "Drop its free bit count down to the total?", blkno, bg->bg_free_bits_count, max_free_bits)) { bg->bg_free_bits_count = max_free_bits; changed = 1; } if (ocfs2_gd_is_discontig(bg)) check_discontig_bg(ost, cs->cs_cpg, bg, &changed, clear_ref); if (*clear_ref) goto out; /* XXX check bg_bits vs cpg/bpc. */ if (changed) { ret = ocfs2_write_group_desc(ost->ost_fs, bg->bg_blkno, (char *)bg); if (ret) { com_err(whoami, ret, "while writing a group " "descriptor to block %"PRIu64" somewhere in " "chain %d in group allocator inode %"PRIu64, (uint64_t)bg->bg_blkno, cs->cs_chain_no, (uint64_t)di->i_blkno); ost->ost_saw_error = 1; } } cs->cs_total_bits += bg->bg_bits; cs->cs_free_bits += bg->bg_free_bits_count; out: return ret; } /* we do this here instead of check_chain so that we can have two relatively * digesitible routines instead of one enormous spaghetti-fed monster. we've * already had a chance to repair the chains so any remaining damage is * the fault of -n, etc, and can simply abort us */ static void unlink_group_desc(o2fsck_state *ost, struct ocfs2_dinode *di, struct ocfs2_group_desc *bg, uint64_t blkno) { struct ocfs2_chain_list *cl; struct ocfs2_chain_rec *cr; uint16_t i, max_count; struct ocfs2_group_desc *link; int unlink = 0; char *buf = NULL; uint64_t next_desc; errcode_t ret; cl = &di->id2.i_chain; max_count = ocfs2_min(cl->cl_next_free_rec, (__u16)ocfs2_chain_recs_per_inode(ost->ost_fs->fs_blocksize)); ret = ocfs2_malloc_block(ost->ost_fs->fs_io, &buf); if (ret) { com_err(whoami, ret, "while allocating block buffers"); goto out; } link = (struct ocfs2_group_desc *)buf; for (cr = cl->cl_recs, i = 0; i < max_count && cr->c_blkno; i++, cr++) { if (cr->c_blkno == blkno) { cr->c_blkno = bg->bg_next_group; unlink = 1; break; } next_desc = cr->c_blkno; while(next_desc) { ret = ocfs2_read_group_desc(ost->ost_fs, next_desc, (char *)link); if (ret) { com_err(whoami, ret, "while reading a " "group descriptor from block %"PRIu64, next_desc); goto out; } if (link->bg_next_group != blkno) { next_desc = link->bg_next_group; continue; } link->bg_next_group = bg->bg_next_group; ret = ocfs2_write_group_desc(ost->ost_fs, next_desc, (char *)link); if (ret) { com_err(whoami, ret, "while writing a group " "descriptor to block %"PRIu64" " "somewhere in chain %d in group " "allocator inode %"PRIu64, next_desc, i, (uint64_t)di->i_blkno); ost->ost_saw_error = 1; goto out; } /* we only try to remove it once.. to do more we'd * have to truncate chains at the offender rather than * just removing it as a link to avoid creating * chains that all reference the offender's children. * we'd also need to update the cr/inode counts * for each bg removed.. sounds weak. */ unlink = 1; break; } if (unlink) break; } if (!unlink) goto out; /* XXX this is kind of risky.. how can we trust next_free_rec? */ if (cl->cl_next_free_rec == i + 1 && cr->c_blkno == 0) cl->cl_next_free_rec--; cr->c_free -= bg->bg_free_bits_count; cr->c_total -= bg->bg_bits; di->id1.bitmap1.i_used -= bg->bg_bits - bg->bg_free_bits_count; di->id1.bitmap1.i_total -= bg->bg_bits; di->i_clusters -= (bg->bg_bits / cl->cl_bpc); di->i_size = (uint64_t)di->i_clusters * ost->ost_fs->fs_clustersize; ret = ocfs2_write_inode(ost->ost_fs, di->i_blkno, (char *)di); if (ret) { /* XXX ugh, undo the bitmap math? */ com_err(whoami, ret, "while writing global bitmap inode " "%"PRIu64, (uint64_t)di->i_blkno); ost->ost_saw_error = 1; goto out; } out: if (buf) ocfs2_free(&buf); } static void mark_group_used(o2fsck_state *ost, struct chain_state *cs, uint64_t blkno, int just_desc, struct ocfs2_group_desc *desc) { int i; uint16_t clusters = 0; if (just_desc) clusters = 1; else if (!desc || !ocfs2_gd_is_discontig(desc)) clusters = cs->cs_cpg; if (clusters) { o2fsck_mark_clusters_allocated(ost, ocfs2_blocks_to_clusters(ost->ost_fs, blkno), clusters); return; } /* Now check the discontiguous group case. */ for (i = 0; i < desc->bg_list.l_next_free_rec; i++) { struct ocfs2_extent_rec *rec = &desc->bg_list.l_recs[i]; o2fsck_mark_clusters_allocated(ost, ocfs2_blocks_to_clusters(ost->ost_fs, rec->e_blkno), rec->e_leaf_clusters); } } /* * Due to a glitch in old mkfs, cl->cl_cpg for the GLOBAL BITMAP could be * less than the max possible for volumes having just one cluster * group. Fix. */ static errcode_t maybe_fix_clusters_per_group(o2fsck_state *ost, struct ocfs2_dinode *di) { struct ocfs2_chain_list *cl; struct ocfs2_group_desc *gd; uint16_t new_cl_cpg = 0; uint64_t blkno; char *buf = NULL; int ret = 0; cl = &(di->id2.i_chain); if (cl->cl_next_free_rec > 1) goto out; ret = ocfs2_malloc_block(ost->ost_fs->fs_io, &buf); if (ret) { com_err(whoami, ret, "while allocating block buffers " "to fix cl_cpg"); goto out; } gd = (struct ocfs2_group_desc *) buf; blkno = cl->cl_recs[0].c_blkno; ret = ocfs2_read_group_desc(ost->ost_fs, blkno, (char *)gd); if ((ret == OCFS2_ET_BAD_GROUP_DESC_MAGIC) || (!ret && gd->bg_generation != ost->ost_fs_generation)) { if (prompt(ost, PY, PR_GROUP_EXPECTED_DESC, "Group descriptor at block %"PRIu64" is corrupted. " "Go fixing it in the following steps?", blkno)) { ret = 0; } goto out; } else if (ret) { com_err(whoami, ret, "while reading group descriptor " "at block %"PRIu64" to fix cl_cpg", blkno); goto out; } new_cl_cpg = 8 * gd->bg_size; if (cl->cl_cpg == new_cl_cpg) goto out; if (prompt(ost, PY, PR_CHAIN_CPG, "Global bitmap at block %"PRIu64" has clusters per group " "set to %u instead of %u. Fix?", (uint64_t)di->i_blkno, cl->cl_cpg, new_cl_cpg)) { cl->cl_cpg = new_cl_cpg; ret = ocfs2_write_inode(ost->ost_fs, di->i_blkno, (char *)di); if (ret) { com_err(whoami, ret, "while writing global bitmap inode " "%"PRIu64" to fix cl_cpg", (uint64_t)di->i_blkno); ost->ost_saw_error = 1; ret = 0; } } out: if (buf) ocfs2_free(&buf); return ret; } static errcode_t break_loop(o2fsck_state *ost, struct ocfs2_chain_rec *chain, unsigned int max_depth) { uint64_t *list; int i; unsigned int depth = 0; uint64_t blkno = chain->c_blkno; char *buf; struct ocfs2_group_desc *gd; errcode_t ret = ocfs2_malloc0(sizeof(uint64_t) * max_depth, &list); if (ret) goto out; ret = ocfs2_malloc_block(ost->ost_fs->fs_io, &buf); if (ret) goto out; gd = (struct ocfs2_group_desc *)buf; while (blkno && (depth<=max_depth)) { list[depth++] = blkno; ret = ocfs2_read_group_desc(ost->ost_fs, blkno, buf); if (ret) goto out; blkno = gd->bg_next_group; for (i=0; ibg_next_group = 0; verbosef("Breaking gd loop %"PRIu64"\n", blkno); ret = ocfs2_write_group_desc(ost->ost_fs, blkno, buf); goto out; } } out: if (list) ocfs2_free(&list); if (buf) ocfs2_free(&buf); return ret; } /* this takes a slightly ridiculous number of arguments :/ */ static errcode_t check_chain(o2fsck_state *ost, struct ocfs2_dinode *di, struct chain_state *cs, struct ocfs2_chain_rec *chain, char *buf1, char *buf2, int *chain_changed, ocfs2_bitmap *allowed, ocfs2_bitmap *forbidden, unsigned int max_depth) { struct ocfs2_group_desc *bg1 = (struct ocfs2_group_desc *)buf1; struct ocfs2_group_desc *bg2 = (struct ocfs2_group_desc *)buf2; uint64_t blkno; errcode_t ret = 0; int depth = 0, clear_ref = 0; verbosef("free %u total %u blkno %"PRIu64"\n", chain->c_free, chain->c_total, (uint64_t)chain->c_blkno); while(1) { /* fetch the next reference */ if (depth == 0) blkno = chain->c_blkno; else { /* we only mark a group as used if it wasn't * contentious. if we weren't supposed to find it we * mark it for a future pass to consider. we do * this here just as we're about to take the reference * to the next group, implying that we've just * decided that bg1 is valid. */ blkno = bg1->bg_blkno; if (allowed) { int was_set; ocfs2_bitmap_test(allowed, blkno, &was_set); if (was_set) { o2fsck_bitmap_clear(allowed, blkno, &was_set); mark_group_used(ost, cs, bg1->bg_blkno, allowed != NULL, bg1); } else if (forbidden) o2fsck_bitmap_set(forbidden, blkno, &was_set); } else mark_group_used(ost, cs, bg1->bg_blkno, allowed != NULL, bg1); blkno = bg1->bg_next_group; } /* we're done */ if (blkno == 0) break; /* is it even feasible? */ if (ocfs2_block_out_of_range(ost->ost_fs, blkno)) { if (prompt(ost, PY, PR_CHAIN_LINK_RANGE, "Chain %d in allocator at inode " "%"PRIu64" contains a reference at depth " "%d to block %"PRIu64" which is out " "of range. Truncate this chain?", cs->cs_chain_no, (uint64_t)di->i_blkno, depth, blkno)) { clear_ref = 1; break; } /* this will just result in a bad blkno from * the read below.. */ } ret = ocfs2_read_group_desc(ost->ost_fs, blkno, (char *)bg2); if (ret == OCFS2_ET_BAD_GROUP_DESC_MAGIC) { if (prompt(ost, PY, PR_CHAIN_LINK_MAGIC, "Chain %d in allocator at inode " "%"PRIu64" contains a reference at depth " "%d to block %"PRIu64" which doesn't have " "a valid checksum. Truncate this chain?", cs->cs_chain_no, (uint64_t)di->i_blkno, depth, blkno)) { clear_ref = 1; break; } /* we're not interested in following a broken desc */ ret = 0; break; } if (ret) { com_err(whoami, ret, "while reading a group " "descriptor from block %"PRIu64" as pointed " "to by chain %d in allocator at inode " "%"PRIu64" at depth %d", blkno, cs->cs_chain_no, (uint64_t)di->i_blkno, depth); goto out; } if (bg2->bg_generation != ost->ost_fs_generation && prompt(ost, PY, PR_CHAIN_LINK_GEN, "Group descriptor at block %"PRIu64" " "has a generation of %"PRIx32" which doesn't match " "the volume's generation of %"PRIx32". Unlink " "this group descriptor?", blkno, bg2->bg_generation, ost->ost_fs_generation)) { clear_ref = 1; break; } ret = repair_group_desc(ost, di, cs, bg2, blkno, &clear_ref); if (ret) goto out; /* we found a duplicate chain, so we need to clear them from * current chain. * * Please note that all the groups below this group will also * be removed from this chain because this is the mechanism * of removing slots in tunefs.ocfs2. */ if (clear_ref) break; /* the loop will now start by reading bg1->next_group */ memcpy(buf1, buf2, ost->ost_fs->fs_blocksize); depth++; if (depth > max_depth) { if (prompt(ost, PY, PR_GROUP_CHAIN_LOOP, "Loop detected in chain %d at block %"PRIu64 ". Break the loop?",cs->cs_chain_no, (uint64_t) chain->c_blkno)) ret = break_loop(ost, chain, max_depth); break; } } /* we hit the premature end of a chain.. clear the last * ref we were working from */ if (clear_ref) { if (depth == 0) { chain->c_blkno = 0; *chain_changed = 1; } else { bg1->bg_next_group = 0; ret = ocfs2_write_group_desc(ost->ost_fs, bg1->bg_blkno, (char *)bg1); if (ret) { com_err(whoami, ret, "while writing a group " "descriptor at depth %d in chain %d " "in group allocator inode %"PRIu64" " "to block %"PRIu64, depth, cs->cs_chain_no, (uint64_t)di->i_blkno, (uint64_t)bg1->bg_blkno); ost->ost_saw_error = 1; } } } if (cs->cs_total_bits != chain->c_total || cs->cs_free_bits != chain->c_free) { if (prompt(ost, PY, PR_CHAIN_BITS, "Chain %d in allocator inode %"PRIu64" " "has %u bits marked free out of %d total bits " "but the block groups in the chain have %u " "free out of %u total. Fix this by updating " "the chain record?", cs->cs_chain_no, (uint64_t)di->i_blkno, chain->c_free, chain->c_total, cs->cs_free_bits, cs->cs_total_bits)) { chain->c_total = cs->cs_total_bits; chain->c_free = cs->cs_free_bits; *chain_changed = 1; } } out: return ret; } /* If this returns 0 then the inode allocator had better be amenable to * iteration. */ static errcode_t verify_chain_alloc(o2fsck_state *ost, struct ocfs2_dinode *di, char *buf1, char *buf2, ocfs2_bitmap *allowed, ocfs2_bitmap *forbidden) { struct chain_state cs = {0, }; struct ocfs2_chain_list *cl; uint16_t i, max_count; struct ocfs2_chain_rec *cr; uint32_t free = 0, total = 0; int changed = 0, trust_next_free = 1; errcode_t ret = 0; uint64_t chain_bytes; unsigned int num_gds, max_chain_len; if (memcmp(di->i_signature, OCFS2_INODE_SIGNATURE, strlen(OCFS2_INODE_SIGNATURE))) { printf("Allocator inode %"PRIu64" doesn't have an inode " "signature. fsck won't repair this.\n", (uint64_t)di->i_blkno); ret = OCFS2_ET_BAD_INODE_MAGIC; goto out; } if (!(di->i_flags & OCFS2_VALID_FL)) { printf("Allocator inode %"PRIu64" is not active. fsck won't " "repair this.\n", (uint64_t)di->i_blkno); ret = OCFS2_ET_INODE_NOT_VALID; goto out; } if (!(di->i_flags & OCFS2_CHAIN_FL)) { printf("Allocator inode %"PRIu64" doesn't have the CHAIN_FL " "flag set. fsck won't repair this.\n", (uint64_t)di->i_blkno); /* not _entirely_ accurate, but pretty close. */ ret = OCFS2_ET_INODE_NOT_VALID; goto out; } /* XXX should we check suballoc_node? */ cl = &di->id2.i_chain; num_gds = (di->i_clusters + cl->cl_cpg)/cl->cl_cpg; max_chain_len = (num_gds + cl->cl_count)/cl->cl_count; verbosef("cl cpg %u bpc %u count %u next %u gds %u max_ch_len %u\n", cl->cl_cpg, cl->cl_bpc, cl->cl_count, cl->cl_next_free_rec, num_gds, max_chain_len); max_count = ocfs2_chain_recs_per_inode(ost->ost_fs->fs_blocksize); /* first, no rec should have a totally invalid blkno */ for (i = 0; i < max_count; i++) { cr = &cl->cl_recs[i]; if (cr->c_blkno != 0&& ocfs2_block_out_of_range(ost->ost_fs, cr->c_blkno) && prompt(ost, PY, PR_CHAIN_HEAD_LINK_RANGE, "Chain %d in allocator inode %"PRIu64" " "contains an initial block reference to %"PRIu64" " "which is out of range. Clear this reference?", i, (uint64_t)di->i_blkno, (uint64_t)cr->c_blkno)) { cr->c_blkno = 0; changed = 1; } } /* make sure cl_count is clamped to the size of the inode */ if (cl->cl_count > max_count && prompt(ost, PY, PR_CHAIN_COUNT, "Allocator inode %"PRIu64" claims to have %u " "chains, but the maximum is %u. Fix the inode's count?", (uint64_t)di->i_blkno, cl->cl_count, max_count)) { cl->cl_count = max_count; changed = 1; } if (max_count > cl->cl_count) max_count = cl->cl_count; if (cl->cl_next_free_rec > max_count) { if (prompt(ost, PY, PR_CHAIN_NEXT_FREE, "Allocator inode %"PRIu64" claims %u " "as the next free chain record, but fsck believes " "the largest valid value is %u. Clamp the next " "record value?", (uint64_t)di->i_blkno, cl->cl_next_free_rec, max_count)) { cl->cl_next_free_rec = cl->cl_count; changed = 1; } else { trust_next_free = 0; } } /* iterate over all chains if we don't trust next_free_rec to mark * the end of used chains */ if (trust_next_free) max_count = cl->cl_next_free_rec; for (i = 0; i < max_count; i++) { cr = &cl->cl_recs[i]; /* reset for each run */ cs = (struct chain_state) { .cs_chain_no = i, .cs_cpg = cl->cl_cpg, }; ret = check_chain(ost, di, &cs, cr, buf1, buf2, &changed, allowed, forbidden, max_chain_len); /* XXX what? not checking ret? */ if (cr->c_blkno != 0) { free += cs.cs_free_bits; total += cs.cs_total_bits; continue; } else if (allowed && forbidden) continue; if (prompt(ost, PY, PR_CHAIN_EMPTY, "Chain %d in allocator inode %"PRIu64" " "is empty. Remove it from the chain record " "array in the inode and shift further chains " "into its place?", cs.cs_chain_no, (uint64_t)di->i_blkno)) { if (!trust_next_free) { printf("Can't remove the chain because " "next_free_rec hasn't been fixed\n"); continue; } /* when we move a chain to a different rec we have * to update bg_chain in all the descs in the chain. * we copy the last chain into the missing spot * instead of shifting everyone over a spot * to minimize the number of chains we have to * update */ if (i < (cl->cl_next_free_rec - 1)) { *cr = cl->cl_recs[cl->cl_next_free_rec - 1]; memset(&cl->cl_recs[cl->cl_next_free_rec - 1], 0, sizeof(struct ocfs2_chain_rec)); i--; } cl->cl_next_free_rec--; max_count--; changed = 1; continue; } } for (i = cl->cl_next_free_rec; i < cl->cl_count; i++) memset(&cl->cl_recs[i], 0, sizeof(struct ocfs2_chain_rec)); if (di->id1.bitmap1.i_total != total || (di->id1.bitmap1.i_used != total - free)) { if (prompt(ost, PY, PR_CHAIN_GROUP_BITS, "Allocator inode %"PRIu64" has %u bits " "marked used out of %d total bits but the chains " "have %u used out of %u total. Fix this by " "updating the inode counts?", (uint64_t)di->i_blkno, di->id1.bitmap1.i_used, di->id1.bitmap1.i_total, total - free, total)) { di->id1.bitmap1.i_used = total - free; di->id1.bitmap1.i_total = total; changed = 1; } } total /= cl->cl_bpc; if (di->i_clusters != total && prompt(ost, PY, PR_CHAIN_I_CLUSTERS, "Allocator inode %"PRIu64" has %"PRIu32" clusters " "represented in its allocator chains but has an " "i_clusters value of %"PRIu32". Fix this by updating " "i_clusters?", (uint64_t)di->i_blkno, total, di->i_clusters)) { di->i_clusters = total; changed = 1; } chain_bytes = (uint64_t)total * ost->ost_fs->fs_clustersize; if (di->i_size != chain_bytes && prompt(ost, PY, PR_CHAIN_I_SIZE, "Allocator inode %"PRIu64" has %"PRIu32" clusters " "represented in its allocator chain which accounts for " "%"PRIu64" total bytes, but its i_size is %"PRIu64". " "Fix this by updating i_size?", (uint64_t)di->i_blkno, di->id1.bitmap1.i_total, chain_bytes, (uint64_t)di->i_size)) { di->i_size = chain_bytes; changed = 1; } if (changed) { ret = ocfs2_write_inode(ost->ost_fs, di->i_blkno, (char *)di); if (ret) { com_err(whoami, ret, "while writing inode alloc inode " "%"PRIu64, (uint64_t)di->i_blkno); ost->ost_saw_error = 1; ret = 0; } } out: return ret; } /* we know that the bitmap descs are at predictable places in the fs. we * walk these locations and make sure there are valid group descs * there. We fill a bitmap with the valid ones so that when we later walk * the chains we can restrict it to the set of expected blocks and also * be sure to add blocks that aren't linked in */ static errcode_t verify_bitmap_descs(o2fsck_state *ost, struct ocfs2_dinode *di, char *buf1, char *buf2) { struct ocfs2_cluster_group_sizes cgs; uint16_t max_recs; uint16_t bits, chain; uint64_t blkno; struct ocfs2_group_desc *bg = (struct ocfs2_group_desc *)buf1; errcode_t ret; struct chain_state cs; struct ocfs2_chain_rec *rec; ocfs2_bitmap *allowed = NULL, *forbidden = NULL; int was_set, i; /* XXX ugh, only used by mark_ */ cs.cs_cpg = di->id2.i_chain.cl_cpg; ret = ocfs2_block_bitmap_new(ost->ost_fs, "allowed group descriptors", &allowed); if (ret) { com_err(whoami, ret, "while allocating allowed bitmap descs " "bitmap"); goto out; } ret = ocfs2_block_bitmap_new(ost->ost_fs, "forbidden group " "descriptors", &forbidden); if (ret) { com_err(whoami, ret, "while allocating forbidden descs " "bitmap"); goto out; } ocfs2_calc_cluster_groups(ost->ost_fs->fs_clusters, ost->ost_fs->fs_blocksize, &cgs); max_recs = ocfs2_chain_recs_per_inode(ost->ost_fs->fs_blocksize); for (i = 0, blkno = ost->ost_fs->fs_first_cg_blkno; i < cgs.cgs_cluster_groups; i++, blkno = i * ocfs2_clusters_to_blocks(ost->ost_fs, cgs.cgs_cpg)) { o2fsck_bitmap_set(allowed, blkno, NULL); } ret = verify_chain_alloc(ost, di, buf1, buf2, allowed, forbidden); if (ret) { com_err(whoami, ret, "while looking up chain allocator inode " "%"PRIu64, (uint64_t)di->i_blkno); goto out; } /* remove descs that we found in the chain that we didn't expect */ for (blkno = ost->ost_fs->fs_first_cg_blkno; !ocfs2_bitmap_find_next_set(forbidden, blkno, &blkno); blkno++) { if (!prompt(ost, PY, PR_GROUP_UNEXPECTED_DESC, "Block %"PRIu64" is a group " "descriptor in the bitmap chain allocator but it " "isn't at one of the pre-determined locations and " "so shouldn't be in the allocator. Remove it " "from the chain?", blkno)) { mark_group_used(ost, &cs, blkno, 1, NULL); continue; } ret = ocfs2_read_group_desc(ost->ost_fs, blkno, (char *)bg); if (ret) { com_err(whoami, ret, "while reading a cluster bitmap " "group descriptor from block %"PRIu64, blkno); continue; } unlink_group_desc(ost, di, bg, blkno); } /* find the blocks that we think should have been in the chains * but which weren't found */ for (i = 0, blkno = ost->ost_fs->fs_first_cg_blkno; i < cgs.cgs_cluster_groups; i++, blkno = i * ocfs2_clusters_to_blocks(ost->ost_fs, cgs.cgs_cpg)) { if (ocfs2_bitmap_test(allowed, blkno, &was_set)) continue; if (!was_set) continue; if (!prompt(ost, PY, PR_GROUP_EXPECTED_DESC, "Block %"PRIu64" should be a group " "descriptor for the bitmap chain allocator but it " "wasn't found in any chains. Reinitialize it as " "a group desc and link it into the bitmap " "allocator?", blkno)) continue; /* some input that init_desc might need */ if (i == cgs.cgs_cluster_groups - 1) bits = cgs.cgs_tail_group_bits; else bits = cgs.cgs_cpg; chain = i % max_recs; /* we've been asked to link in this desc specifically. we're * using the predictability of the group descs to rebuild * its values.. we only preserve the bitmap if the signature * and generation match this volume */ ret = ocfs2_read_group_desc(ost->ost_fs, blkno, (char *)bg); if ((ret == OCFS2_ET_BAD_GROUP_DESC_MAGIC) || (!ret && bg->bg_generation != ost->ost_fs_generation)) { memset(bg, 0, ost->ost_fs->fs_blocksize); ocfs2_init_group_desc(ost->ost_fs, bg, blkno, ost->ost_fs_generation, di->i_blkno, bits, chain, 0); ret = 0; } if (ret) { com_err(whoami, ret, "while reading a cluster bitmap " "group descriptor from block %"PRIu64, blkno); continue; } /* first some easy fields */ bg->bg_size = ocfs2_group_bitmap_size( ost->ost_fs->fs_blocksize, 0, OCFS2_RAW_SB(ost->ost_fs->fs_super)->s_feature_incompat); bg->bg_bits = bits; bg->bg_parent_dinode = di->i_blkno; bg->bg_blkno = blkno; ocfs2_set_bit(0, bg->bg_bitmap); bg->bg_free_bits_count = bg->bg_bits - o2fsck_bitcount(bg->bg_bitmap, (bg->bg_bits + 7)/ 8); /* we have to be kind of careful with the chain */ chain = ocfs2_min(chain, di->id2.i_chain.cl_next_free_rec); chain = ocfs2_min(chain, max_recs); bg->bg_chain = chain; /* now really link it in */ rec = &di->id2.i_chain.cl_recs[bg->bg_chain]; bg->bg_next_group = rec->c_blkno; ret = ocfs2_write_group_desc(ost->ost_fs, blkno, (char *)bg); if (ret) { com_err(whoami, ret, "while writing a cluster group " "descriptor at block %"PRIu64, blkno); ost->ost_saw_error = 1; continue; } /* and update the calling inode */ rec->c_free += bg->bg_free_bits_count; rec->c_total += bg->bg_bits; rec->c_blkno = blkno; /* ugh */ if (di->id2.i_chain.cl_next_free_rec == bg->bg_chain && di->id2.i_chain.cl_next_free_rec < max_recs) di->id2.i_chain.cl_next_free_rec++; di->id1.bitmap1.i_used += bg->bg_bits - bg->bg_free_bits_count; di->id1.bitmap1.i_total += bg->bg_bits; di->i_clusters += (bg->bg_bits / di->id2.i_chain.cl_bpc); di->i_size = (uint64_t)di->i_clusters * ost->ost_fs->fs_clustersize; ret = ocfs2_write_inode(ost->ost_fs, di->i_blkno, (char *)di); if (ret) { com_err(whoami, ret, "while writing global bitmap inode " "%"PRIu64, (uint64_t)di->i_blkno); ost->ost_saw_error = 1; goto out; } mark_group_used(ost, &cs, bg->bg_blkno, 1, bg); } out: if (allowed) ocfs2_bitmap_free(&allowed); if (forbidden) ocfs2_bitmap_free(&forbidden); return ret; } /* this returns an error if it didn't leave the allocators in a state that * the iterators will be able to work with. There is probably some room * for more resiliance here. */ errcode_t o2fsck_pass0(o2fsck_state *ost) { errcode_t ret; uint64_t blkno; uint32_t pre_repair_clusters; char *blocks = NULL; struct ocfs2_dinode *di = NULL; ocfs2_filesys *fs = ost->ost_fs; ocfs2_cached_inode **ci; int max_slots = OCFS2_RAW_SB(fs->fs_super)->s_max_slots; int i, type, bitmap_retried = 0; struct o2fsck_resource_track rt; printf("Pass 0a: Checking cluster allocation chains\n"); o2fsck_init_resource_track(&rt, fs->fs_io); /* * The I/O buffer is 3 blocks. We apportion our I/O buffer * thusly: * * blocks[0] is the allocator inode we're working on. * blocks[1] & blocks[2] are used to hold group descriptors * in functions below this one. */ ret = ocfs2_malloc_blocks(fs->fs_io, 3, &blocks); if (ret) { com_err(whoami, ret, "while allocating block buffers"); goto out; } di = (struct ocfs2_dinode *)blocks; ret = ocfs2_malloc0(max_slots * sizeof(ocfs2_cached_inode *), &ost->ost_inode_allocs); if (ret) { com_err(whoami, ret, "while cached inodes for each node"); goto out; } ret = ocfs2_lookup_system_inode(fs, GLOBAL_BITMAP_SYSTEM_INODE, 0, &blkno); if (ret) { com_err(whoami, ret, "while looking up the global bitmap " "inode"); goto out; } ret = ocfs2_read_inode(ost->ost_fs, blkno, (char *)di); if (ret) { com_err(whoami, ret, "reading global bitmap inode " "%"PRIu64" for verification", blkno); goto out; } verbosef("found global bitmap %"PRIu64" at block %"PRIu64"\n", (uint64_t)di->i_blkno, blkno); ret = maybe_fix_clusters_per_group(ost, di); if (ret) goto out; /* * during resize, we may update the global bitmap but fails to * to update i_clusters in superblock, so ask the user which one * to use before checking. */ if (fs->fs_super->i_clusters != di->i_clusters) { if (prompt(ost, PY, PR_SUPERBLOCK_CLUSTERS, "Superblock has clusters set to %u instead of %u " "recorded in global_bitmap, it may be caused by an " "unsuccessful resize. Trust global_bitmap?", fs->fs_super->i_clusters, di->i_clusters)) { ost->ost_num_clusters = di->i_clusters; fs->fs_clusters = di->i_clusters; fs->fs_blocks = ocfs2_clusters_to_blocks(fs, fs->fs_clusters); ret = o2fsck_state_reinit(fs, ost); if (ret) { com_err(whoami, ret, "while reinit " "o2fsck_state."); goto out; } } } /* Warm up the cache with the groups */ ret = ocfs2_cache_chain_allocator_blocks(fs, di); if (ret) verbosef("Caching global bitmap failed, err %d\n", (int)ret); retry_bitmap: pre_repair_clusters = di->i_clusters; ret = verify_bitmap_descs(ost, di, blocks + ost->ost_fs->fs_blocksize, blocks + (ost->ost_fs->fs_blocksize * 2)); if (ret) goto out; if (pre_repair_clusters != di->i_clusters) { if (prompt(ost, PY, PR_FIXED_CHAIN_CLUSTERS, "Repair of global_bitmap changed the filesystem " "from %u clusters to %u clusters. Trust " "global_bitmap?", pre_repair_clusters, di->i_clusters)) { ost->ost_num_clusters = di->i_clusters; fs->fs_clusters = di->i_clusters; fs->fs_blocks = ocfs2_clusters_to_blocks(fs, fs->fs_clusters); ret = o2fsck_state_reinit(fs, ost); if (ret) { com_err(whoami, ret, "while reinit " "o2fsck_state."); goto out; } /* * The reinit clears the bits found during the * scan of the global bitmap. We need to go over * them again. They really should come out clean * this time. If they don't, we probably have * a serious problem. * * In an interactive run, the user can keep * retrying and abort when they give up. In a * non-interactive mode, we can't loop forever. */ if (ost->ost_ask || !bitmap_retried) { bitmap_retried = 1; verbosef("Restarting global_bitmap %s\n", "scan"); goto retry_bitmap; } } } o2fsck_compute_resource_track(&rt, fs->fs_io); o2fsck_print_resource_track("Pass 0a", ost, &rt, fs->fs_io); o2fsck_add_resource_track(&ost->ost_rt, &rt); printf("Pass 0b: Checking inode allocation chains\n"); o2fsck_init_resource_track(&rt, fs->fs_io); /* first the global inode alloc and then each of the node's * inode allocators */ type = GLOBAL_INODE_ALLOC_SYSTEM_INODE; i = -1; for ( ; i < max_slots; i++, type = INODE_ALLOC_SYSTEM_INODE) { ret = ocfs2_lookup_system_inode(fs, type, i, &blkno); if (ret) { com_err(whoami, ret, "while looking up the inode " "allocator type %d for node %d\n", type, i); goto out; } ret = ocfs2_read_inode(ost->ost_fs, blkno, (char *)di); if (ret) { com_err(whoami, ret, "reading inode alloc inode " "%"PRIu64" for verification", blkno); goto out; } verbosef("found inode alloc %"PRIu64" at block %"PRIu64"\n", (uint64_t)di->i_blkno, blkno); /* Warm up the cache with the groups */ ret = ocfs2_cache_chain_allocator_blocks(fs, di); if (ret) verbosef("Caching inode alloc failed, err %d\n", (int)ret); ret = verify_chain_alloc(ost, di, blocks + ost->ost_fs->fs_blocksize, blocks + (ost->ost_fs->fs_blocksize * 2), NULL, NULL); /* XXX maybe helped by the alternate super block */ if (ret) goto out; if (i == -1) ci = &ost->ost_global_inode_alloc; else ci = &ost->ost_inode_allocs[i]; ret = ocfs2_read_cached_inode(ost->ost_fs, blkno, ci); if (ret) { com_err(whoami, ret, "while reading node %d's inode " "allocator inode %"PRIu64, i, blkno); goto out; } ret = ocfs2_load_chain_allocator(ost->ost_fs, *ci); if (ret) { com_err(whoami, ret, "while loading inode %"PRIu64" " "as a chain allocator", blkno); ocfs2_free_cached_inode(ost->ost_fs, *ci); *ci = NULL; goto out; } } o2fsck_compute_resource_track(&rt, fs->fs_io); o2fsck_print_resource_track("Pass 0b", ost, &rt, fs->fs_io); o2fsck_add_resource_track(&ost->ost_rt, &rt); printf("Pass 0c: Checking extent block allocation chains\n"); o2fsck_init_resource_track(&rt, fs->fs_io); for (i = 0; i < max_slots; i++) { ret = ocfs2_lookup_system_inode(fs, EXTENT_ALLOC_SYSTEM_INODE, i, &blkno); if (ret) { com_err(whoami, ret, "while looking up the extent " "allocator type %d for node %d\n", type, i); goto out; } ret = ocfs2_read_inode(ost->ost_fs, blkno, (char *)di); if (ret) { com_err(whoami, ret, "reading extent alloc inode " "%"PRIu64" for verification", blkno); goto out; } verbosef("found extent alloc %"PRIu64" at block %"PRIu64"\n", (uint64_t)di->i_blkno, blkno); /* Warm up the cache with the groups */ ret = ocfs2_cache_chain_allocator_blocks(fs, di); if (ret) verbosef("Caching extent alloc failed, err %d\n", (int)ret); ret = verify_chain_alloc(ost, di, blocks + ost->ost_fs->fs_blocksize, blocks + (ost->ost_fs->fs_blocksize * 2), NULL, NULL); /* XXX maybe helped by the alternate super block */ if (ret) goto out; } o2fsck_compute_resource_track(&rt, fs->fs_io); o2fsck_print_resource_track("Pass 0c", ost, &rt, fs->fs_io); o2fsck_add_resource_track(&ost->ost_rt, &rt); out: if (blocks) ocfs2_free(&blocks); if (ret) o2fsck_free_inode_allocs(ost); return ret; } ocfs2-tools-ocfs2-tools-1.8.6/fsck.ocfs2/pass1.c000066400000000000000000001321421347147137200212200ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * Copyright (C) 1993-2004 by Theodore Ts'o. * Copyright (C) 2004 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License, version 2, as published by the Free Software Foundation. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * * -- * * Pass 1 is arguably where the greatest concentration of special rules * in fsck live. It walks through all the inodes that it can get its hands * on and verifies them. For now it only walks the inode allocator groups * that pass 0 was able to verify. One can imagine it getting other potential * inodes from other places. * * The complexity comes in deciding that inodes are valid. There are different * critera for system inodes, allocator inodes, and the usual different * unix inode file types. * * Pass 1 build up in-memory copies of the inode allocators that are written * back as the real inode allocators if inconsistencies are found between * the bitmaps and the inodes. It also builds up many inode-dependent data * structures that are used by future passes: * - icount map of inodes to what their current on-disk i_link_count is * - bitmap of which inodes are directories or regular files * - directory blocks that it finds off of directory inodes * * Pass 1 also compiles a bitmap of all clusters used by the filesystem. * If any clusters are shared by more than one inode, a bitmap of * duplicate clusters is also created. * * The end of Pass 1 is when the found block bitmap should contain all the * blocks in the system that are in use. This is used to derive the set of * clusters that should be allocated. The cluster chain allocator is loaded * and synced up with this set and potentially written back. After that point * fsck can use libocfs2 to allocate and free clusters as usual. * * XXX * check many, many, more i_ fields for each inode type * make sure the inode's dtime/count/valid match in update_inode_alloc * more carefully track cluster use in conjunction with pass 0 * free an inodes chains and extents and such if we free it */ #include #include #include #include "ocfs2/ocfs2.h" #include "ocfs2/bitops.h" #include "dirblocks.h" #include "dirparents.h" #include "extent.h" #include "icount.h" #include "fsck.h" #include "pass1.h" #include "pass1b.h" #include "problem.h" #include "util.h" #include "xattr.h" #include "refcount.h" static const char *whoami = "pass1"; void o2fsck_free_inode_allocs(o2fsck_state *ost) { uint16_t i; ocfs2_free_cached_inode(ost->ost_fs, ost->ost_global_inode_alloc); for (i = 0; i < OCFS2_RAW_SB(ost->ost_fs->fs_super)->s_max_slots; i++) ocfs2_free_cached_inode(ost->ost_fs, ost->ost_inode_allocs[i]); ocfs2_free(&ost->ost_inode_allocs); } /* update our in memory images of the inode chain alloc bitmaps. these * will be written out at the end of pass1 and the library will read * them off disk for use from then on. */ static void update_inode_alloc(o2fsck_state *ost, struct ocfs2_dinode *di, uint64_t blkno, int val) { int16_t slot; uint16_t max_slots, yn; errcode_t ret = OCFS2_ET_INTERNAL_FAILURE; ocfs2_cached_inode *cinode; int oldval; val = !!val; if (ost->ost_write_inode_alloc_asked && !ost->ost_write_inode_alloc) return; max_slots = OCFS2_RAW_SB(ost->ost_fs->fs_super)->s_max_slots; for (slot = OCFS2_INVALID_SLOT; slot != max_slots; slot++, ret = OCFS2_ET_INTERNAL_FAILURE) { if (slot == OCFS2_INVALID_SLOT) cinode = ost->ost_global_inode_alloc; else cinode = ost->ost_inode_allocs[slot]; /* we might have had trouble reading the chains in pass 0 */ if (cinode == NULL) continue; ret = ocfs2_chain_force_val(ost->ost_fs, cinode, blkno, val, &oldval); if (ret) { if (ret != OCFS2_ET_INVALID_BIT) com_err(whoami, ret, "while trying to set " "inode %"PRIu64"'s allocation to '%d' " "in slot %"PRId16"'s chain", blkno, val, slot); continue; } /* hmm, de hmm. it would be kind of nice if the bitmaps * didn't use 'int' but rather some real boolean construct */ oldval = !!oldval; /* this slot covers the inode. see if we've changed the * bitmap and if the user wants us to keep tracking it and * write back the new map */ if (oldval != val && !ost->ost_write_inode_alloc_asked) { yn = prompt(ost, PY, PR_INODE_ALLOC_REPAIR, "Inode %"PRIu64" is marked as %s but its " "position in the inode allocator is " "marked as %s. Fix the allocation of this " "and all future inodes?", blkno, val ? "valid" : "invalid", oldval ? "in use" : "free"); ost->ost_write_inode_alloc_asked = 1; ost->ost_write_inode_alloc = !!yn; } break; } if (ret) { com_err(whoami, ret, "while trying to set inode %"PRIu64"'s " "allocation to '%d'. None of the slots chain " "allocator's had a group covering the inode.", blkno, val); goto out; } verbosef("updated inode %"PRIu64" alloc to %d from %d in slot " "%"PRId16"\n", blkno, val, oldval, slot); /* make sure the inode's fields are consistent if it's allocated */ if (val == 1 && slot != (int16_t)di->i_suballoc_slot && prompt(ost, PY, PR_INODE_SUBALLOC, "Inode %"PRIu64" indicates that it was allocated " "from slot %"PRId16" but slot %"PRId16"'s chain allocator " "covers the inode. Fix the inode's record of where it is " "allocated?", blkno, di->i_suballoc_slot, slot)) { di->i_suballoc_slot = slot; o2fsck_write_inode(ost, di->i_blkno, di); } out: return; } static errcode_t verify_local_alloc(o2fsck_state *ost, struct ocfs2_dinode *di) { struct ocfs2_local_alloc *la = &di->id2.i_lab; uint32_t max; int broken = 0, changed = 0, clear = 0; errcode_t ret = 0; verbosef("la_bm_off %u size %u total %u used %u\n", la->la_bm_off, la->la_size, di->id1.bitmap1.i_total, di->id1.bitmap1.i_used); max = ocfs2_local_alloc_size(ost->ost_fs->fs_blocksize); if (la->la_size > max) { broken = 1; if (prompt(ost, PY, PR_LALLOC_SIZE, "Local alloc inode %"PRIu64" claims to " "have %u bytes of bitmap data but %u bytes is the " "maximum allowed. Set the inode's count to the " "maximum?", (uint64_t)di->i_blkno, la->la_size, max)) { la->la_size = max; changed = 1; } } if (di->id1.bitmap1.i_total == 0) { /* ok, it's not used. we don't mark these errors as * 'broken' as the kernel shouldn't care.. right? */ if (di->id1.bitmap1.i_used != 0) { if (prompt(ost, PY, PR_LALLOC_NZ_USED, "Local alloc inode %"PRIu64" " "isn't in use bit its i_used isn't 0. Set it to " "0?", (uint64_t)di->i_blkno)) { di->id1.bitmap1.i_used = 0; changed = 1; } } if (la->la_bm_off != 0) { if (prompt(ost, PY, PR_LALLOC_NZ_BM, "Local alloc inode %"PRIu64" " "isn't in use bit its i_bm_off isn't 0. Set it " "to 0?", (uint64_t)di->i_blkno)) { la->la_bm_off = 0; changed = 1; } } goto out; } if (la->la_bm_off >= ost->ost_fs->fs_clusters) { broken = 1; if (prompt(ost, PY, PR_LALLOC_BM_OVERRUN, "Local alloc inode %"PRIu64" claims to " "contain a bitmap that starts at cluster %u but " "the volume contains %u clusters. Mark the local " "alloc bitmap as unused?", (uint64_t)di->i_blkno, la->la_bm_off, ost->ost_fs->fs_clusters)) { clear = 1; } } if (di->id1.bitmap1.i_total > la->la_size * 8) { broken = 1; if (prompt(ost, PY, PR_LALLOC_BM_SIZE, "Local alloc inode %"PRIu64" claims to " "have a bitmap with %u bits but the inode can only " "fit %u bits. Clamp the bitmap size to this " "maxmum?", (uint64_t)di->i_blkno, di->id1.bitmap1.i_total, la->la_size * 8)) { di->id1.bitmap1.i_total = la->la_size * 8; changed = 1; } } if (la->la_bm_off + di->id1.bitmap1.i_total > ost->ost_fs->fs_clusters) { broken = 1; if (prompt(ost, PY, PR_LALLOC_BM_STRADDLE, "Local alloc inode %"PRIu64" claims to " "have a bitmap that covers clusters numbered %u " "through %u but %u is the last valid cluster. " "Mark the local bitmap as unused?", (uint64_t)di->i_blkno, la->la_bm_off, la->la_bm_off + di->id1.bitmap1.i_total - 1, ost->ost_fs->fs_clusters - 1)) { clear = 1; } /* we can't possibly check _used if bm/off and total are * so busted */ goto out; } if (di->id1.bitmap1.i_used > di->id1.bitmap1.i_total) { broken = 1; if (prompt(ost, PY, PR_LALLOC_USED_OVERRUN, "Local alloc inode %"PRIu64" claims to " "contain a bitmap with %u bits and %u used. Set " "i_used down to %u?", (uint64_t)di->i_blkno, di->id1.bitmap1.i_total, di->id1.bitmap1.i_used, di->id1.bitmap1.i_total)) { di->id1.bitmap1.i_used = di->id1.bitmap1.i_total; changed = 1; } } out: if (broken && !clear && prompt(ost, PY, PR_LALLOC_CLEAR, "Local alloc inode %"PRIu64" contained errors. " "Mark it as unused instead of trying to correct its " "bitmap?", (uint64_t)di->i_blkno)) { clear = 1; } if (clear) { di->id1.bitmap1.i_total = 0; di->id1.bitmap1.i_used = 0; la->la_bm_off = 0; memset(la->la_bitmap, 0, ocfs2_local_alloc_size(ost->ost_fs->fs_blocksize)); changed = 1; } if (changed) { ret = ocfs2_write_inode(ost->ost_fs, di->i_blkno, (char *)di); if (ret) { com_err(whoami, ret, "while writing local alloc inode " "%"PRIu64, (uint64_t)di->i_blkno); ost->ost_write_error = 1; ret = 0; } } return ret; } /* this just makes sure the truncate log contains consistent data, it doesn't * do anything with it yet */ static errcode_t verify_truncate_log(o2fsck_state *ost, struct ocfs2_dinode *di) { struct ocfs2_truncate_log *tl = &di->id2.i_dealloc; uint16_t max, i; int changed = 0; errcode_t ret = 0; verbosef("tl_count %u tl_used %u (tl_reserved1 %u)\n", tl->tl_count, tl->tl_used, tl->tl_reserved1); max = ocfs2_truncate_recs_per_inode(ost->ost_fs->fs_blocksize); if (tl->tl_count > max && prompt(ost, PY, PR_DEALLOC_COUNT, "Truncate log inode %"PRIu64" claims space for %u records but only %u " "records are possible. Set the inode's count to the maximum?", (uint64_t)di->i_blkno, tl->tl_count, max)) { tl->tl_count = max; changed = 1; } if (tl->tl_used > tl->tl_count && prompt(ost, PY, PR_DEALLOC_USED, "Truncate log inode %"PRIu64" claims to be using %u records but the " "inode can only hold %u records. Change the number used to reflect " "the maximum possible in the inode?", (uint64_t)di->i_blkno, tl->tl_used, tl->tl_count)) { tl->tl_used = tl->tl_count; changed = 1; } for (i = 0; i < ocfs2_min(max, tl->tl_used); i++) { struct ocfs2_truncate_rec *tr = &tl->tl_recs[i]; int zero = 0; verbosef("t_start %u t_clusters %u\n", tr->t_start, tr->t_clusters); if (tr->t_start == 0) continue; if (tr->t_start >= ost->ost_fs->fs_clusters && prompt(ost, PY, PR_TRUNCATE_REC_START_RANGE, "Truncate record at offset %u in truncate log " "inode %"PRIu64" starts at cluster %u but there " "are %u clusters in the volume. Remove this record " "from the log?", i, (uint64_t)di->i_blkno, tr->t_start, ost->ost_fs->fs_clusters)) { zero = 1; } if (tr->t_start + tr->t_clusters < tr->t_start && prompt(ost, PY, PR_TRUNCATE_REC_WRAP, "Truncate record at offset %u in truncate log " "inode %"PRIu64" starts at cluster %u and contains " "%u clusters. It can't have this many clusters " "as that overflows the number of possible clusters " "in a volume. Remove this record from the log?", i, (uint64_t)di->i_blkno, tr->t_start, tr->t_clusters)) { zero = 1; } if (tr->t_start + tr->t_clusters > ost->ost_fs->fs_clusters && prompt(ost, PY, PR_TRUNCATE_REC_RANGE, "Truncate record at offset %u in truncate log " "inode %"PRIu64" starts at cluster %u and contains " "%u clusters. It can't have this many clusters " "as this volume only has %u clusters. Remove this " "record from the log?", i, (uint64_t)di->i_blkno, tr->t_start, tr->t_clusters, ost->ost_fs->fs_clusters)) { zero = 1; } if (zero) { tr->t_start = 0; tr->t_clusters = 0; changed = 1; } } if (changed) { ret = ocfs2_write_inode(ost->ost_fs, di->i_blkno, (char *)di); if (ret) { com_err(whoami, ret, "while writing truncate log inode " "%"PRIu64, (uint64_t)di->i_blkno); ost->ost_write_error = 1; ret = 0; } } return ret; } /* Check the basics of the ocfs2_dinode itself. If we find problems * we clear the VALID flag and the caller will see that and update * inode allocations and write the inode to disk. * * XXX the o2fsck_write_inode helpers need to be fixed here*/ static void o2fsck_verify_inode_fields(ocfs2_filesys *fs, o2fsck_state *ost, uint64_t blkno, struct ocfs2_dinode *di) { int clear = 0; int depth; verbosef("checking inode %"PRIu64"'s fields\n", blkno); if (di->i_fs_generation != ost->ost_fs_generation) { if (prompt(ost, PY, PR_INODE_GEN, "Inode read from block %"PRIu64" looks " "like it is valid but it has a generation of %x " "that doesn't match the current volume's " "generation of %x. This is probably a harmless " "old inode. Mark it deleted?", blkno, di->i_fs_generation, ost->ost_fs_generation)) { clear = 1; goto out; } if (prompt(ost, PY, PR_INODE_GEN_FIX, "Update the inode's generation to match " "the volume?")) { di->i_fs_generation = ost->ost_fs_generation; o2fsck_write_inode(ost, blkno, di); } } /* do we want to detect and delete corrupt system dir/files here * so we can recreate them later ? */ /* also make sure the journal inode is ok? */ /* clamp inodes to > OCFS2_SUPER_BLOCK_BLKNO && < fs->fs_blocks? */ /* XXX need to compare the lifetime of inodes (uninitialized? * in use? orphaned? deleted? garbage?) to understand what * fsck can do to fix it up */ if (di->i_blkno != blkno && prompt(ost, PY, PR_INODE_BLKNO, "Inode read from block %"PRIu64" has i_blkno set " "to %"PRIu64". Set the inode's i_blkno value to reflect " "its location on disk?", blkno, (uint64_t)di->i_blkno)) { di->i_blkno = blkno; o2fsck_write_inode(ost, blkno, di); } /* offer to clear a non-directory root inode so that * pass3:check_root() can re-create it */ if ((di->i_blkno == fs->fs_root_blkno) && !S_ISDIR(di->i_mode) && prompt(ost, PY, PR_ROOT_NOTDIR, "Root inode isn't a directory. Clear it in " "preparation for fixing it?")) { clear = 1; goto out; } if (di->i_dtime && prompt(ost, PY, PR_INODE_NZ_DTIME, "Inode %"PRIu64" is in use but has a non-zero dtime. Reset " "the dtime to 0?", (uint64_t)di->i_blkno)) { di->i_dtime = 0ULL; o2fsck_write_inode(ost, blkno, di); } if ((di->i_dyn_features & OCFS2_INLINE_DATA_FL) && !ocfs2_support_inline_data(OCFS2_RAW_SB(fs->fs_super)) && prompt(ost, PY, PR_INLINE_DATA_FLAG_INVALID, "Inode %"PRIu64" has inline flag set but the volume " "doesn't support it. Clear it?", (uint64_t)di->i_blkno)) { di->i_dyn_features &= ~OCFS2_INLINE_DATA_FL; o2fsck_write_inode(ost, blkno, di); } if ((di->i_dyn_features & OCFS2_HAS_REFCOUNT_FL) && !ocfs2_refcount_tree(OCFS2_RAW_SB(fs->fs_super)) && prompt(ost, PY, PR_REFCOUNT_FLAG_INVALID, "Inode %"PRIu64" has refcount flag set but the volume " "doesn't support it. Clear it?", (uint64_t)di->i_blkno)) { di->i_dyn_features &= ~OCFS2_HAS_REFCOUNT_FL; o2fsck_write_inode(ost, blkno, di); } if (ocfs2_refcount_tree(OCFS2_RAW_SB(fs->fs_super)) && (!(di->i_dyn_features & OCFS2_HAS_REFCOUNT_FL) && di->i_refcount_loc) && prompt(ost, PY, PR_REFCOUNT_LOC_INVALID, "Inode %"PRIu64" doesn't have refcount flag set but have " "refcount loc set. Clear it?", (uint64_t)di->i_blkno)) { di->i_refcount_loc = 0; o2fsck_write_inode(ost, blkno, di); } if (S_ISDIR(di->i_mode)) { o2fsck_bitmap_set(ost->ost_dir_inodes, blkno, NULL); o2fsck_add_dir_parent(&ost->ost_dir_parents, blkno, 0, 0, di->i_flags & OCFS2_ORPHANED_FL); ost->ost_dir_count++; if (di->i_dyn_features & OCFS2_INLINE_DATA_FL) ost->ost_inline_dir_count++; } else if (S_ISREG(di->i_mode)) { o2fsck_bitmap_set(ost->ost_reg_inodes, blkno, NULL); ost->ost_file_count++; if (di->i_dyn_features & OCFS2_INLINE_DATA_FL) ost->ost_inline_file_count++; } else if (S_ISLNK(di->i_mode)) { /* we only make sure a link's i_size matches * the link names length in the file data later when * we walk the inode's blocks */ ost->ost_symlinks_count++; if (!di->i_clusters) ost->ost_fast_symlinks_count++; } else if (S_ISCHR(di->i_mode)) { /* i_size? what other sanity testing for devices? */ ost->ost_chardev_count++; } else if (S_ISBLK(di->i_mode)) { ost->ost_blockdev_count++; } else if (S_ISFIFO(di->i_mode)) { ost->ost_fifo_count++; } else if (S_ISSOCK(di->i_mode)) { ost->ost_sockets_count++; } else { clear = 1; goto out; } if (!(S_ISLNK(di->i_mode) && !di->i_clusters) && !(di->i_dyn_features & OCFS2_INLINE_DATA_FL) && !(di->i_flags & (OCFS2_LOCAL_ALLOC_FL | OCFS2_CHAIN_FL | OCFS2_DEALLOC_FL))) { depth = di->id2.i_list.l_tree_depth; depth = ocfs2_min(depth, OCFS2_MAX_PATH_DEPTH); ost->ost_tree_depth_count[depth]++; } if (di->i_dyn_features & OCFS2_HAS_REFCOUNT_FL) ost->ost_reflinks_count++; /* put this after all opportunities to clear so we don't have to * unwind it */ if (di->i_links_count) { o2fsck_icount_set(ost->ost_icount_in_inodes, di->i_blkno, di->i_links_count); if (di->i_links_count > 1) ost->ost_links_count++; } /* orphan inodes are a special case. if -n is given pass4 will try * and assert that their links_count should include the dirent * reference from the orphan dir. */ if (di->i_flags & OCFS2_ORPHANED_FL && di->i_links_count == 0) o2fsck_icount_set(ost->ost_icount_in_inodes, di->i_blkno, 1); if (di->i_flags & OCFS2_LOCAL_ALLOC_FL) verify_local_alloc(ost, di); else if (di->i_flags & OCFS2_DEALLOC_FL) verify_truncate_log(ost, di); out: /* XXX when we clear we need to also free whatever blocks may have * hung off this inode that haven't already been reserved. we want * to do this on the transition from valid to invalid, not just * any time we see an invalid inode (somewhat obviously). */ if (clear) { di->i_flags &= ~OCFS2_VALID_FL; o2fsck_write_inode(ost, blkno, di); /* if we cleared the inode then we're going to be * forbidding directory entries from referencing it.. we * should back-out the inode count we found in the inode * so that we're not surprised when there aren't any * references to it in pass 4 */ o2fsck_icount_set(ost->ost_icount_in_inodes, di->i_blkno, 0); } } struct verifying_blocks { unsigned vb_clear:1, vb_saw_link_null:1; uint64_t vb_link_len; uint64_t vb_num_blocks; uint64_t vb_last_block; o2fsck_state *vb_ost; struct ocfs2_dinode *vb_di; errcode_t vb_ret; }; /* last_block and num_blocks would be different in a sparse file */ static void vb_saw_block(struct verifying_blocks *vb, uint64_t bcount) { vb->vb_num_blocks++; if (bcount > vb->vb_last_block) vb->vb_last_block = bcount; } static errcode_t process_link_block(struct verifying_blocks *vb, uint64_t blkno) { char *buf = NULL, *null; errcode_t ret = 0; unsigned int blocksize = vb->vb_ost->ost_fs->fs_blocksize; if (vb->vb_saw_link_null) goto out; ret = ocfs2_malloc_blocks(vb->vb_ost->ost_fs->fs_io, 1, &buf); if (ret) { com_err(whoami, ret, "while allocating room to read a block " "of link data"); goto out; } ret = ocfs2_read_blocks(vb->vb_ost->ost_fs, blkno, 1, buf); if (ret) goto out; null = memchr(buf, 0, blocksize); if (null != NULL) { vb->vb_link_len += null - buf; vb->vb_saw_link_null = 1; } else { vb->vb_link_len += blocksize; } out: if (buf) ocfs2_free(&buf); return ret; } static void check_link_data(struct verifying_blocks *vb) { struct ocfs2_dinode *di = vb->vb_di; o2fsck_state *ost = vb->vb_ost; uint64_t expected, link_max; char *null; verbosef("found a link: num %"PRIu64" last %"PRIu64" len " "%"PRIu64" null %d\n", (uint64_t)vb->vb_num_blocks, (uint64_t)vb->vb_last_block, (uint64_t)vb->vb_link_len, vb->vb_saw_link_null); if (di->i_clusters == 0 && vb->vb_num_blocks > 0 && prompt(ost, PY, PR_LINK_FAST_DATA, "Symlink inode %"PRIu64" claims to be a fast symlink " "but has file data. Clear the inode?", (uint64_t)di->i_blkno)) { vb->vb_clear = 1; return; } /* if we're a fast link we doctor the verifying_blocks book-keeping * to satisfy the following checks */ if (di->i_clusters == 0) { link_max = ost->ost_fs->fs_blocksize - offsetof(typeof(*di), id2.i_symlink); null = memchr(di->id2.i_symlink, 0, link_max); if (null != NULL) { vb->vb_saw_link_null = 1; vb->vb_link_len = (char *)null - (char *)di->id2.i_symlink; } else vb->vb_link_len = link_max; expected = 0; } else expected = ocfs2_blocks_in_bytes(ost->ost_fs, vb->vb_link_len + 1); /* XXX this could offer to null terminate */ if (!vb->vb_saw_link_null) { if (prompt(ost, PY, PR_LINK_NULLTERM, "The target of symlink inode %"PRIu64" " "isn't null terminated. Clear the inode?", (uint64_t)di->i_blkno)) { vb->vb_clear = 1; return; } } if (di->i_size != vb->vb_link_len) { if (prompt(ost, PY, PR_LINK_SIZE, "The target of symlink inode %"PRIu64" " "is %"PRIu64" bytes long on disk, but i_size is " "%"PRIu64" bytes long. Update i_size to reflect " "the length on disk?", (uint64_t)di->i_blkno, vb->vb_link_len, (uint64_t)di->i_size)) { di->i_size = vb->vb_link_len; o2fsck_write_inode(ost, di->i_blkno, di); return; } } /* maybe we don't shrink link target allocations, I don't know, * someone will holler if this is wrong :) */ if (vb->vb_num_blocks < expected) { if (prompt(ost, PN, PR_LINK_BLOCKS, "The target of symlink inode %"PRIu64" " "fits in %"PRIu64" blocks but the inode has " "%"PRIu64" allocated. Clear the inode?", (uint64_t)di->i_blkno, expected, vb->vb_num_blocks)) { vb->vb_clear = 1; return; } } } static int verify_block(ocfs2_filesys *fs, uint64_t blkno, uint64_t bcount, uint16_t ext_flags, void *priv_data) { struct verifying_blocks *vb = priv_data; struct ocfs2_dinode *di = vb->vb_di; o2fsck_state *ost = vb->vb_ost; errcode_t ret = 0; /* someday we may want to worry about holes in files here */ if (S_ISDIR(di->i_mode)) { verbosef("adding dir block %"PRIu64"\n", blkno); ret = o2fsck_add_dir_block(&ost->ost_dirblocks, (uint64_t)di->i_blkno, blkno, bcount); if (ret) { com_err(whoami, ret, "while trying to track block in " "directory inode %"PRIu64, (uint64_t)di->i_blkno); } } else if (S_ISLNK(di->i_mode)) ret = process_link_block(vb, blkno); if (ret) { vb->vb_ret = ret; return OCFS2_BLOCK_ABORT; } vb_saw_block(vb, bcount); return 0; } /* This may be accessed many times for the same cluster. * currently I haven't find a good way to avoid this since * we use ocfs2_block_iterate_inode to clear the clusters. * Anyway, it works well in clearing clusters. */ static int clear_block(ocfs2_filesys *fs, uint64_t blkno, uint64_t bcount, uint16_t ext_flags, void *priv_data) { struct verifying_blocks *vb = priv_data; o2fsck_state *ost = vb->vb_ost; uint32_t clusters = ocfs2_blocks_to_clusters(ost->ost_fs, blkno); o2fsck_mark_cluster_unallocated(ost, clusters); return 0; } static errcode_t o2fsck_check_dx_dir(o2fsck_state *ost, struct ocfs2_dinode *di) { errcode_t ret = 0; char *buf = NULL; struct ocfs2_dx_root_block *dx_root; ocfs2_filesys *fs = ost->ost_fs; struct extent_info ei = {0,}; int changed = 0; if (!ocfs2_supports_indexed_dirs(OCFS2_RAW_SB(fs->fs_super))) goto out; if (!ocfs2_dir_indexed(di)) goto out; ret = ocfs2_malloc_block(fs->fs_io, &buf); if (ret) goto out; ret = ocfs2_read_dx_root(fs, (uint64_t)di->i_dx_root, buf); if (ret) goto out; dx_root = (struct ocfs2_dx_root_block *)buf; if (dx_root->dr_flags & OCFS2_DX_FLAG_INLINE) goto out; ei.chk_rec_func = o2fsck_check_extent_rec; ei.mark_rec_alloc_func = o2fsck_mark_tree_clusters_allocated; ei.para = di; ret = check_el(ost, &ei, di->i_blkno, &dx_root->dr_list, ocfs2_extent_recs_per_dx_root(fs->fs_blocksize), 0, 0, &changed); if (ret) goto out; if (changed) { ret = ocfs2_write_dx_root(fs, (uint64_t)di->i_dx_root, (char *)dx_root); if (ret) com_err(whoami, ret, "while writing an updated " "dx_root block at %"PRIu64" for inode %"PRIu64, (uint64_t)di->i_dx_root, (uint64_t)di->i_blkno); } out: if (buf) ocfs2_free(&buf); return ret; } /* * this verifies i_size and i_clusters for inodes that use i_list to * reference extents of data. */ static errcode_t o2fsck_check_blocks(ocfs2_filesys *fs, o2fsck_state *ost, uint64_t blkno, struct ocfs2_dinode *di) { uint64_t expected = 0, unexpected = 0; errcode_t ret = 0; struct verifying_blocks vb = { .vb_ost = ost, .vb_di = di, }; /* don't bother to verify for inodes that don't have i_list, * we have to trust i_mode/i_clusters to tell us that a symlink * has put target data in the union instead of i_list */ if ((di->i_flags & (OCFS2_SUPER_BLOCK_FL | OCFS2_LOCAL_ALLOC_FL | OCFS2_BITMAP_FL | OCFS2_CHAIN_FL | OCFS2_DEALLOC_FL)) || (S_ISLNK(di->i_mode) && di->i_clusters == 0)) return 0; if (di->i_dyn_features & OCFS2_INLINE_DATA_FL) { /* * We add i_blkno as the dir block, so when the dir's * inode_no is the same as dir_block_no, we can tell * that this dir is inlinded and help us in the following * directory check. */ if (S_ISDIR(di->i_mode)) { ret = o2fsck_add_dir_block(&ost->ost_dirblocks, di->i_blkno, di->i_blkno, 0); if (ret) return ret; } goto size_cluster_check; } ret = o2fsck_check_extents(ost, di); if (ret == 0) ret = ocfs2_block_iterate_inode(fs, di, OCFS2_BLOCK_FLAG_APPEND, verify_block, &vb); if (vb.vb_ret) ret = vb.vb_ret; if (ret) { com_err(whoami, ret, "while iterating over the blocks for " "inode %"PRIu64, (uint64_t)di->i_blkno); goto out; } ret = o2fsck_check_dx_dir(ost, di); if (ret && prompt(ost, PY, PR_DX_TREE_CORRUPT, "Inode %"PRIu64" has invalid dx tree. " "Reset for later rebuild?", (uint64_t)di->i_blkno)) { ocfs2_dx_dir_truncate(fs, di->i_blkno); di->i_dx_root = 0ULL; di->i_dyn_features &= ~OCFS2_INDEXED_DIR_FL; o2fsck_write_inode(ost, di->i_blkno, di); ret = 0; } if (S_ISLNK(di->i_mode)) check_link_data(&vb); if (S_ISDIR(di->i_mode) && vb.vb_num_blocks == 0 && prompt(ost, PY, PR_DIR_ZERO, "Inode %"PRIu64" is a zero length directory, clear it?", (uint64_t)di->i_blkno)) { vb.vb_clear = 1; } /* * We have a helper function,clear_block, that clears an inode and * backs it out of any book-keeping that it might have been included * in, as though it was never seen. */ if (vb.vb_clear) { di->i_links_count = 0; o2fsck_icount_set(ost->ost_icount_in_inodes, di->i_blkno, di->i_links_count); di->i_dtime = time(NULL); /* clear valid flag and stuff. */ ret = ocfs2_block_iterate_inode(fs, di, OCFS2_BLOCK_FLAG_APPEND, clear_block, &vb); di->i_flags &= ~OCFS2_VALID_FL; o2fsck_write_inode(ost, di->i_blkno, di); /* for a directory, we also need to clear it * from the dir_parent rb-tree. */ if (S_ISDIR(di->i_mode)) ocfsck_remove_dir_parent(&ost->ost_dir_parents, di->i_blkno); goto out; } size_cluster_check: /* * i_size and i_cluster mean quite different between a non-sparse * and sparse file system. * * For a non-sparse file system, the file size should be within the * clusters it is allocated, and the cluster size should be the same * as the number we calculate from extent iteration. * * For a sparse file, the file size can be greater than the real * last block offsets recorded in the extent list, but it shouldn't be * less than that cluster offset since we have already allocated some * blocks at that offset, so if the size is too small, fix it to the * end of the visible cluster end. It is also reasonable for a file * which has no allocated blocks but any number of byte sizes, * so we don't need to check its size either. * * In an inline file, i_clusters should be zero and i_size should be * less than the max inline data size. */ if (di->i_dyn_features & OCFS2_INLINE_DATA_FL) { uint16_t max_inline = ocfs2_max_inline_data_with_xattr(fs->fs_blocksize, di); /* id_count is check first. */ if (di->id2.i_data.id_count != max_inline && prompt(ost, PY, PR_INLINE_DATA_COUNT_INVALID, "Inode %"PRIu64" is inline file and its id_count " "is %u which should be %u. Correct this " "count?", (uint64_t)di->i_blkno, di->id2.i_data.id_count, max_inline)) { di->id2.i_data.id_count = max_inline; o2fsck_write_inode(ost, blkno, di); } /* i_size is checked for symlinks elsewhere */ if (di->i_size > max_inline && prompt(ost, PY, PR_INODE_INLINE_SIZE, "Inode %"PRIu64 "has a size of %"PRIu64" which exceeds the max " "inline data size %u. " "Correct the file size?", (uint64_t)di->i_blkno, (uint64_t)di->i_size, max_inline)) { di->i_size = max_inline; o2fsck_write_inode(ost, blkno, di); } if (di->i_clusters > 0 && prompt(ost, PY, PR_INODE_INLINE_CLUSTERS, "Inode %"PRIu64" has %"PRIu32" clusters but it has " "inline data flag set. " "Correct the number of clusters?", (uint64_t)di->i_blkno, di->i_clusters)) { di->i_clusters = 0; o2fsck_write_inode(ost, blkno, di); } } else if (OCFS2_RAW_SB(fs->fs_super)->s_feature_incompat & OCFS2_FEATURE_INCOMPAT_SPARSE_ALLOC) { if (vb.vb_num_blocks > 0) { expected = ocfs2_blocks_to_clusters(fs, vb.vb_last_block + 1); expected *= fs->fs_clustersize; unexpected = expected - fs->fs_clustersize; /* * NOTE: * - i_size is checked for symlinks elsewhere * - We're not going to check this for dirs * since it would be legal for a dir inode * whose i_size(in clusters) was less than * i_clusters, even on a sparsed filesystem */ if (!S_ISLNK(di->i_mode) && !S_ISDIR(di->i_mode) && di->i_size <= unexpected && prompt(ost, PY, PR_INODE_SPARSE_SIZE, "Inode %"PRIu64 " has a size of %"PRIu64" but has %"PRIu64 " blocks of actual data. " "Correct the file size?", (uint64_t)di->i_blkno, (uint64_t)di->i_size, vb.vb_last_block + 1)) { di->i_size = expected; o2fsck_write_inode(ost, blkno, di); } } if (vb.vb_num_blocks > 0) expected = ocfs2_clusters_in_blocks(fs, vb.vb_num_blocks); if (di->i_clusters != expected && prompt(ost, PY, PR_INODE_SPARSE_CLUSTERS, "Inode %"PRIu64" has %"PRIu32" clusters but its " "blocks fit in %"PRIu64" clusters. " "Correct the number of clusters?", (uint64_t)di->i_blkno, di->i_clusters, expected)) { di->i_clusters = expected; o2fsck_write_inode(ost, blkno, di); } } else { if (vb.vb_num_blocks > 0) expected = (vb.vb_last_block + 1) * fs->fs_blocksize; /* i_size is checked for symlinks elsewhere */ if (!S_ISLNK(di->i_mode) && di->i_size > expected && prompt(ost, PY, PR_INODE_SIZE, "Inode %"PRIu64" has a size of " "%"PRIu64" but has %"PRIu64" bytes of actual data. " "Correct the file size?", (uint64_t)di->i_blkno, (uint64_t)di->i_size, expected)) { di->i_size = expected; o2fsck_write_inode(ost, blkno, di); } if (vb.vb_num_blocks > 0) expected = ocfs2_clusters_in_blocks(fs, vb.vb_last_block + 1); if (di->i_clusters != expected && prompt(ost, PY, PR_INODE_CLUSTERS, "Inode %"PRIu64" has %"PRIu32" clusters but its " "blocks fit in %"PRIu64" clusters. Correct the " "number of clusters?", (uint64_t)di->i_blkno, di->i_clusters, expected)) { di->i_clusters = expected; o2fsck_write_inode(ost, blkno, di); } } out: return ret; } /* * we just make sure that the bits that are clear in the local * alloc are still reserved in the global bitmap. We leave * cleaning of the local windows to recovery in the file system. */ static void mark_local_allocs(o2fsck_state *ost) { int16_t slot; uint16_t max_slots; char *buf = NULL; errcode_t ret; uint64_t blkno, start, end; struct ocfs2_dinode *di; struct ocfs2_local_alloc *la; int bit; max_slots = OCFS2_RAW_SB(ost->ost_fs->fs_super)->s_max_slots; ret = ocfs2_malloc_block(ost->ost_fs->fs_io, &buf); if (ret) { com_err(whoami, ret, "while allocating an inode buffer to " "use when verifying local alloc inode bitmaps."); goto out; } di = (struct ocfs2_dinode *)buf; for (slot = 0; slot < max_slots; slot++) { ret = ocfs2_lookup_system_inode(ost->ost_fs, LOCAL_ALLOC_SYSTEM_INODE, slot, &blkno); if (ret) { com_err(whoami, ret, "while looking up local alloc " "inode %"PRIu64" to verify its bitmap", blkno); goto out; } ret = ocfs2_read_inode(ost->ost_fs, blkno, buf); if (ret) { com_err(whoami, ret, "while reading local alloc " "inode %"PRIu64" to verify its bitmap", blkno); goto out; } la = &di->id2.i_lab; if (di->id1.bitmap1.i_total == 0) continue; /* make sure we don't try to work with a crazy bitmap. It * can only be this crazy if the user wouldn't let us fix * it up.. just ignore it */ if (la->la_size > ocfs2_local_alloc_size(ost->ost_fs->fs_blocksize) || di->id1.bitmap1.i_used > di->id1.bitmap1.i_total || di->id1.bitmap1.i_total > la->la_size * 8) continue; start = la->la_bm_off; end = la->la_bm_off + di->id1.bitmap1.i_total; if (start >= ost->ost_fs->fs_clusters || end < start || end > ost->ost_fs->fs_clusters) continue; /* bits that are clear in the local alloc haven't been * used by the slot yet, they must still be set in the * main bitmap. bits that are set might have been used * and already freed in the main bitmap. */ for(bit = 0; bit < di->id1.bitmap1.i_total; bit++) { bit = ocfs2_find_next_bit_clear(la->la_bitmap, di->id1.bitmap1.i_total, bit); if (bit < di->id1.bitmap1.i_total) { verbosef("bit %u is clear, reserving " "cluster %u\n", bit, bit + la->la_bm_off); o2fsck_mark_cluster_allocated(ost, bit + la->la_bm_off); } } } out: if (buf) ocfs2_free(&buf); return; } /* * Clusters that are in the truncate logs should still be allocated. We just * make sure our accounting realizes this and let the kernel replay the logs * and free them. This will change someday when fsck learns to fully practice * recovery isntead of just making sure that the system is in a coherent * recoverable state. */ static void mark_truncate_logs(o2fsck_state *ost) { int16_t slot; uint16_t max_slots, i, max; struct ocfs2_truncate_log *tl; uint64_t blkno; struct ocfs2_dinode *di; char *buf = NULL; errcode_t ret; max_slots = OCFS2_RAW_SB(ost->ost_fs->fs_super)->s_max_slots; max = ocfs2_truncate_recs_per_inode(ost->ost_fs->fs_blocksize); ret = ocfs2_malloc_block(ost->ost_fs->fs_io, &buf); if (ret) { com_err(whoami, ret, "while allocating an inode buffer to " "use accounting for records in truncate logs"); goto out; } di = (struct ocfs2_dinode *)buf; for (slot = 0; slot < max_slots; slot++) { ret = ocfs2_lookup_system_inode(ost->ost_fs, TRUNCATE_LOG_SYSTEM_INODE, slot, &blkno); if (ret) { com_err(whoami, ret, "while looking up truncate log " "inode %"PRIu64" to account for its records", blkno); goto out; } ret = ocfs2_read_inode(ost->ost_fs, blkno, buf); if (ret) { com_err(whoami, ret, "while reading truncate log " "inode %"PRIu64" to account for its records", blkno); goto out; } tl = &di->id2.i_dealloc; for (i = 0; i < ocfs2_min(tl->tl_used, max); i++) { struct ocfs2_truncate_rec *tr = &tl->tl_recs[i]; if (tr->t_start == 0) continue; verbosef("rec [%u, %u] at off %u\n", tr->t_start, tr->t_clusters, i); o2fsck_mark_clusters_allocated(ost, tr->t_start, tr->t_clusters); } } out: if (buf) ocfs2_free(&buf); return; } /* XXX we really need to get the latch stuff straight */ static errcode_t force_cluster_bit(o2fsck_state *ost, ocfs2_cached_inode *ci, uint64_t bit, int val) { errcode_t ret; char *reason; if (!val) { reason = "marked in the global cluster bitmap but it isn't in " "use. Clear its bit in the bitmap?"; } else { reason = "in use but isn't set in the global cluster bitmap. " "Set its bit in the bitmap?"; } if (!prompt(ost, PY, PR_CLUSTER_ALLOC_BIT, "Cluster %"PRIu64" is %s", bit, reason)) return 0; ret = ocfs2_chain_force_val(ost->ost_fs, ci, bit, !!val, NULL); if (ret) com_err(whoami, ret, "while trying to %s bit %"PRIu64" in the " "cluster bitmap", val ? "set" : "clear", bit); return ret; } static inline int bit_in_backup_super(uint64_t bit, uint32_t *clusters, int len) { int i; for (i = 0; i < len; i++) { if (bit == clusters[i]) return 1; } return 0; } /* once we've iterated all the inodes we should have the current working * set of which blocks we think are in use. we use this to derive the set * of clusters that should be allocated in the cluster chain allocators. we * don't iterate over all clusters like we do inodes.. */ static void write_cluster_alloc(o2fsck_state *ost) { ocfs2_cached_inode *ci = NULL; errcode_t ret; uint64_t blkno, last_cbit, cbit, cbit_found; struct ocfs2_cluster_group_sizes cgs; uint64_t blocks[OCFS2_MAX_BACKUP_SUPERBLOCKS]; uint32_t clusters[OCFS2_MAX_BACKUP_SUPERBLOCKS]; int backup_super = 0, num = 0, i; ocfs2_calc_cluster_groups(ost->ost_fs->fs_clusters, ost->ost_fs->fs_blocksize, &cgs); /* first load the cluster chain alloc so we can compare */ ret = ocfs2_lookup_system_inode(ost->ost_fs, GLOBAL_BITMAP_SYSTEM_INODE, 0, &blkno); if (ret) { com_err(whoami, ret, "while looking up the cluster bitmap " "allocator inode"); goto out; } /* load in the cluster chain allocator */ ret = ocfs2_read_cached_inode(ost->ost_fs, blkno, &ci); if (ret) { com_err(whoami, ret, "while reading the cluster bitmap " "allocator inode from block %"PRIu64, blkno); goto out; } ret = ocfs2_load_chain_allocator(ost->ost_fs, ci); if (ret) { com_err(whoami, ret, "while loading the cluster bitmap " "allocator from block %"PRIu64, blkno); goto out; } /* handle the condition of backup superblock. */ memset(&blocks, 0, sizeof(blocks)); memset(&clusters, 0, sizeof(clusters)); if (OCFS2_HAS_COMPAT_FEATURE(OCFS2_RAW_SB(ost->ost_fs->fs_super), OCFS2_FEATURE_COMPAT_BACKUP_SB)) { num = ocfs2_get_backup_super_offsets(ost->ost_fs, blocks, ARRAY_SIZE(blocks)); if (num) { backup_super = 1; for (i = 0; i < num; i++) clusters[i] = ocfs2_blocks_to_clusters(ost->ost_fs, blocks[i]); } } /* we walk our found blocks bitmap to find clusters that we think * are in use. each time we find a block in a cluster we skip ahead * to the first block of the next cluster when looking for the next. * * once we have a cluster we think is allocated we walk the cluster * chain alloc bitmaps from the last cluster we thought was allocated * to make sure that all the bits are cleared on the way. * * we special case the number of clusters as the cluster offset which * indicates that the rest of the bits to the end of the bitmap * should be clear. * * we should take backup superblock as a special case since it doesn't * belong to any inode. So it shouldn't be exist in * ost->ost_allocated_clusters. */ for (last_cbit = 0, cbit = 0; cbit < ost->ost_fs->fs_clusters; cbit++, last_cbit = cbit) { ret = ocfs2_bitmap_find_next_set(ost->ost_allocated_clusters, cbit, &cbit); /* clear to the end */ if (ret == OCFS2_ET_BIT_NOT_FOUND) cbit = ost->ost_fs->fs_clusters; ret = ocfs2_bitmap_find_next_set(ci->ci_chains, last_cbit, &cbit_found); if (ret == OCFS2_ET_BIT_NOT_FOUND) cbit_found = ost->ost_fs->fs_clusters; verbosef("cbit %"PRIu64" last_cbit %"PRIu64" cbit_found " "%"PRIu64"\n", cbit, last_cbit, cbit_found); if (cbit_found == cbit) continue; /* clear set bits that should have been clear up to cbit */ while (cbit_found < cbit) { /* check whether the volume has backup blocks * and if yes, check whether the cluster contains * one of the backup blocks. */ if (!backup_super || !bit_in_backup_super(cbit_found, clusters, num)) force_cluster_bit(ost, ci, cbit_found, 0); cbit_found++; ret = ocfs2_bitmap_find_next_set(ci->ci_chains, cbit_found, &cbit_found); if (ret == OCFS2_ET_BIT_NOT_FOUND) cbit_found = ost->ost_fs->fs_clusters; } /* make sure cbit is set before moving on */ if (cbit_found != cbit && cbit != ost->ost_fs->fs_clusters) force_cluster_bit(ost, ci, cbit, 1); } ret = ocfs2_write_chain_allocator(ost->ost_fs, ci); if (ret) com_err(whoami, ret, "while trying to write back the cluster " "bitmap allocator"); out: if (ci) ocfs2_free_cached_inode(ost->ost_fs, ci); } static void write_inode_alloc(o2fsck_state *ost) { int max_slots = OCFS2_RAW_SB(ost->ost_fs->fs_super)->s_max_slots; ocfs2_cached_inode **ci; errcode_t ret; int i; if (!ost->ost_write_inode_alloc) return; for (i = -1; i < max_slots; i++) { if (i == OCFS2_INVALID_SLOT) ci = &ost->ost_global_inode_alloc; else ci = &ost->ost_inode_allocs[i]; if (*ci == NULL) continue; verbosef("writing slot %d's allocator\n", i); ret = ocfs2_write_chain_allocator(ost->ost_fs, *ci); if (ret) com_err(whoami, ret, "while trying to write back slot " "%d's inode allocator", i); } } errcode_t o2fsck_pass1(o2fsck_state *ost) { errcode_t ret; uint64_t blkno; char *buf; struct ocfs2_dinode *di; ocfs2_inode_scan *scan; ocfs2_filesys *fs = ost->ost_fs; int valid; struct o2fsck_resource_track rt; uint64_t numinodes; printf("Pass 1: Checking inodes and blocks\n"); o2fsck_init_resource_track(&rt, fs->fs_io); ret = ocfs2_malloc_block(fs->fs_io, &buf); if (ret) { com_err(whoami, ret, "while allocating inode buffer"); goto out; } di = (struct ocfs2_dinode *)buf; ret = ocfs2_open_inode_scan(fs, &scan); if (ret) { com_err(whoami, ret, "while opening inode scan"); goto out_free; } if (tools_progress_enabled()) { numinodes = ocfs2_get_max_inode_count(scan); if (numinodes) ost->ost_prog = tools_progress_start("Scanning inodes", "inodes", numinodes); if (ost->ost_prog) setbuf(stdout, NULL); } for(;;) { ret = ocfs2_get_next_inode(scan, &blkno, buf); if (ret) { /* we don't deal with corrupt inode allocation * files yet. They won't be files for much longer. * In the future the intent is to clean up inode * allocation if scanning returns an error. */ com_err(whoami, ret, "while getting next inode"); goto out_close_scan; } if (blkno == 0) break; valid = 0; /* we never consider inodes who don't have a signature */ if (!memcmp(di->i_signature, OCFS2_INODE_SIGNATURE, strlen(OCFS2_INODE_SIGNATURE))) { ocfs2_swap_inode_to_cpu(fs, di); /* We only consider inodes whose generations don't * match if the user has asked us to */ if ((ost->ost_fix_fs_gen || (di->i_fs_generation == ost->ost_fs_generation))) { if (di->i_flags & OCFS2_VALID_FL) o2fsck_verify_inode_fields(fs, ost, blkno, di); if (di->i_flags & OCFS2_VALID_FL) { ret = o2fsck_check_refcount_tree(ost, di); if (ret) goto out; ret = o2fsck_check_blocks(fs, ost, blkno, di); if (ret) goto out; ret = o2fsck_check_xattr(ost, di); if (ret) goto out; } valid = di->i_flags & OCFS2_VALID_FL; } } update_inode_alloc(ost, di, blkno, valid); if (ost->ost_prog) tools_progress_step(ost->ost_prog, 1); } mark_local_allocs(ost); mark_truncate_logs(ost); ret = o2fsck_check_mark_refcounted_clusters(ost); if (ret) com_err(whoami, ret, "while checking refcounted clusters"); write_cluster_alloc(ost); write_inode_alloc(ost); out_close_scan: ocfs2_close_inode_scan(scan); out_free: ocfs2_free(&buf); if (!ret && ost->ost_duplicate_clusters) ret = ocfs2_pass1_dups(ost); o2fsck_compute_resource_track(&rt, fs->fs_io); o2fsck_print_resource_track("Pass 1", ost, &rt, fs->fs_io); o2fsck_add_resource_track(&ost->ost_rt, &rt); out: o2fsck_free_inode_allocs(ost); if (ost->ost_prog) { tools_progress_stop(ost->ost_prog); setlinebuf(stdout); } return ret; } ocfs2-tools-ocfs2-tools-1.8.6/fsck.ocfs2/pass1b.c000066400000000000000000001150021347147137200213560ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * Copyright (C) 2009 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License version 2 as published by the Free Software Foundation. * * 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. * * The scheme of the passes is based on e2fsck pass1b.c, * Copyright (C) 1993, 1994, 1995, 1996, 1997 Theodore Ts'o. * * -- * * Pass 1B-D are extra passes invoked only if Pass 1 discovered clusters * in use by more than one inode. They are very expensive, as they cannot * make easy use of the I/O cache. * * Pass 1 has already built us a bitmap of duplicated clusters. For * efficiency, it doesn't try to track who owns them. Now we need to know. * Because Pass 1 has already repaired and verified the allocators, we can * trust them to be consistent. * * Pass 1B rescans the inodes and builds two rbtrees. The first rbtree * maps a duplicate cluster to the inodes that share it. The second rbtree * keeps track of all inodes with duplicates. If an inode has more than * one duplicate cluster, it will get cloned or deleted when the first one * is evaluated in Pass 1D. The second rbtree prevents us from re-examining * this inode for each addition cluster it used to share. * * Pass 1C walks the directory tree and gives names to each inode. This * is so the user can see the name of the file they are fixing. The pass * does a depth-first traversal of the tree. For every inode in the * rbtree of duplicates it finds, it stores the path for it. It will * ignore errors in the directory tree, because we haven't fixed it yet. * When reporting to the user, inodes without names will just get their * inode number printed. * * Pass 1D does the actual fixing. Each inode with duplicate clusters can * cloned to an entirely new file or deleted. Regardless of the choice, * an inode that is fixed no longer has duplicate clusters. Cloning is * done by creating a new inode and copying the data to it. Then the * extent trees are swapped between the original and clone inode. This * leaves the original inode with a good extent tree. Finally, the clone * inode is removed and its extent tree released. If deletion is chosen * instead of cloning, the original inode is removed. Either way, we end * up over-freeing the clusters in the main bitmap. At the end, we run * the list of multi-claimed clusters again. If the cluster still has * claimers, it is forced on in the bitmap. If it does not, it is forced * clear in the bitmap. If we crash in the middle, we're still safe. A * re-run of fsck will determine whether the over-freed clusters are * actually in use. * * Once Pass1D is complete, the ost_duplicate_clusters bitmap can be * freed. */ #include #include #include #include #include "ocfs2/ocfs2.h" #include "ocfs2/bitops.h" #include "dirblocks.h" #include "dirparents.h" #include "extent.h" #include "icount.h" #include "fsck.h" #include "pass1.h" #include "pass1b.h" #include "problem.h" #include "util.h" #include "xattr.h" static const char *whoami = "UNSET!"; /* states for dup_inode.di_state */ #define DUP_INODE_CLONED 0x01 #define DUP_INODE_REMOVED 0x02 /* A simple test to see if we should care about this dup inode anymore */ #define DUP_INODE_HANDLED (DUP_INODE_CLONED | DUP_INODE_REMOVED) /* * Keep track of an inode that claims clusters shared by other objects. */ struct dup_inode { struct rb_node di_node; /* The block number of this inode */ uint64_t di_ino; /* The path to this inode */ char *di_path; /* * i_flags from the inode. We need to refuse deletion of * system files, and chain allocators are even worse. */ uint32_t di_flags; /* What we've done to it. */ unsigned int di_state; /* the refcount tree it has. */ uint64_t di_refcount_loc; }; /* * Keep track of clusters that are claimed by multiple objects. */ struct dup_cluster_owner { struct list_head dco_list; /* * The block number of the owning inode. This is the lookup key * for the dup inode rbtree. */ uint64_t dco_ino; /* * virtual offset in the extent tree. * Only valid for an extent tree, 0 for a chain file. */ uint32_t dco_cpos; }; struct dup_cluster { struct rb_node dc_node; /* The physical cluster that is multiply-claimed */ uint32_t dc_cluster; /* List of owning inodes */ struct list_head dc_owners; }; /* * Context for Passes 1B-D. */ struct dup_context { /* Tree of multiply-claimed clusters */ struct rb_root dup_clusters; /* Inodes that own them */ struct rb_root dup_inodes; /* How many there are */ uint64_t dup_inode_count; }; /* See if the cluster rbtree has the given cluster. */ static struct dup_cluster *dup_cluster_lookup(struct dup_context *dct, uint32_t cluster) { struct rb_node *p = dct->dup_clusters.rb_node; struct dup_cluster *dc; while (p) { dc = rb_entry(p, struct dup_cluster, dc_node); if (cluster < dc->dc_cluster) { p = p->rb_left; } else if (cluster > dc->dc_cluster) { p = p->rb_right; } else return dc; } return NULL; } static void dup_cluster_insert(struct dup_context *dct, struct dup_cluster *insert_dc) { struct rb_node **p = &dct->dup_clusters.rb_node; struct rb_node *parent = NULL; struct dup_cluster *dc = NULL; while (*p) { parent = *p; dc = rb_entry(parent, struct dup_cluster, dc_node); if (insert_dc->dc_cluster < dc->dc_cluster) { p = &(*p)->rb_left; dc = NULL; } else if (insert_dc->dc_cluster > dc->dc_cluster) { p = &(*p)->rb_right; dc = NULL; } else assert(0); /* Caller checked */ } rb_link_node(&insert_dc->dc_node, parent, p); rb_insert_color(&insert_dc->dc_node, &dct->dup_clusters); } /* See if the inode rbtree has the given cluster. */ static struct dup_inode *dup_inode_lookup(struct dup_context *dct, uint64_t ino) { struct rb_node *p = dct->dup_inodes.rb_node; struct dup_inode *di; while (p) { di = rb_entry(p, struct dup_inode, di_node); if (ino < di->di_ino) { p = p->rb_left; } else if (ino > di->di_ino) { p = p->rb_right; } else return di; } return NULL; } static void dup_inode_insert(struct dup_context *dct, struct dup_inode *insert_di) { struct rb_node **p = &dct->dup_inodes.rb_node; struct rb_node *parent = NULL; struct dup_inode *di = NULL; while (*p) { parent = *p; di = rb_entry(parent, struct dup_inode, di_node); if (insert_di->di_ino < di->di_ino) { p = &(*p)->rb_left; di = NULL; } else if (insert_di->di_ino > di->di_ino) { p = &(*p)->rb_right; di = NULL; } else assert(0); /* Caller checked */ } rb_link_node(&insert_di->di_node, parent, p); rb_insert_color(&insert_di->di_node, &dct->dup_inodes); dct->dup_inode_count++; } /* * Given a (cluster,inode) tuple, insert the appropriate metadata * into the context. */ static errcode_t dup_insert(struct dup_context *dct, uint32_t cluster, struct ocfs2_dinode *dinode, uint32_t v_cpos) { errcode_t ret; struct list_head *p; struct dup_cluster *dc, *new_dc = NULL; struct dup_inode *di, *new_di = NULL; struct dup_cluster_owner *dco, *new_dco = NULL; ret = ocfs2_malloc0(sizeof(struct dup_cluster), &new_dc); if (ret) { com_err(whoami, ret, "while allocating duplicate cluster tracking " "structures"); goto out; } INIT_LIST_HEAD(&new_dc->dc_owners); new_dc->dc_cluster = cluster; ret = ocfs2_malloc0(sizeof(struct dup_inode), &new_di); if (ret) { com_err(whoami, ret, "while allocating duplicate cluster tracking " "structures"); goto out; } new_di->di_ino = dinode->i_blkno; new_di->di_flags = dinode->i_flags; new_di->di_refcount_loc = dinode->i_refcount_loc; ret = ocfs2_malloc0(sizeof(struct dup_cluster_owner), &new_dco); if (ret) { com_err(whoami, ret, "while allocating duplicate cluster tracking " "structures"); goto out; } new_dco->dco_ino = dinode->i_blkno; new_dco->dco_cpos = v_cpos; dc = dup_cluster_lookup(dct, cluster); if (!dc) { dup_cluster_insert(dct, new_dc); dc = new_dc; new_dc = NULL; } di = dup_inode_lookup(dct, dinode->i_blkno); if (!di) { dup_inode_insert(dct, new_di); new_di = NULL; } dco = NULL; list_for_each(p, &dc->dc_owners) { dco = list_entry(p, struct dup_cluster_owner, dco_list); if (dco->dco_ino == dinode->i_blkno) break; dco = NULL; } if (!dco) { list_add_tail(&new_dco->dco_list, &dc->dc_owners); new_dco = NULL; } out: if (new_dc) ocfs2_free(&new_dc); if (new_di) ocfs2_free(&new_di); if (new_dco) ocfs2_free(&new_dco); return ret; } static void o2fsck_empty_dup_context(struct dup_context *dct) { struct dup_cluster *dc; struct dup_inode *di; struct dup_cluster_owner *dco; struct rb_node *node; struct list_head *p, *next; while ((node = rb_first(&dct->dup_clusters)) != NULL) { dc = rb_entry(node, struct dup_cluster, dc_node); list_for_each_safe(p, next, &dc->dc_owners) { dco = list_entry(p, struct dup_cluster_owner, dco_list); list_del(&dco->dco_list); ocfs2_free(&dco); } rb_erase(&dc->dc_node, &dct->dup_clusters); ocfs2_free(&dc); } while ((node = rb_first(&dct->dup_inodes)) != NULL) { di = rb_entry(node, struct dup_inode, di_node); rb_erase(&di->di_node, &dct->dup_inodes); ocfs2_free(&di); } } /* * Pass 1B */ struct process_extents_context { o2fsck_state *ost; struct dup_context *dct; struct ocfs2_dinode *di; errcode_t ret; uint64_t global_bitmap_blkno; char *extra_buf; }; static errcode_t process_dup_clusters(struct process_extents_context *pc, uint32_t p_cpos, uint32_t clusters, uint32_t v_cpos) { int was_set; errcode_t ret = 0; while (clusters) { ret = ocfs2_bitmap_test(pc->ost->ost_duplicate_clusters, p_cpos, &was_set); if (ret) { com_err(whoami, ret, "while testing cluster %"PRIu32" of inode " "%"PRIu64" in the duplicate cluster map", p_cpos, (uint64_t)pc->di->i_blkno); break; } if (was_set) { verbosef("Marking multiply-claimed cluster %"PRIu32 " as claimed by inode %"PRIu64"\n", p_cpos, (uint64_t)pc->di->i_blkno); ret = dup_insert(pc->dct, p_cpos, pc->di, v_cpos); if (ret) { com_err(whoami, ret, "while marking duplicate cluster " "%"PRIu32" as owned by inode " "%"PRIu64, p_cpos, (uint64_t)pc->di->i_blkno); break; } } p_cpos++; v_cpos++; clusters--; } return ret; } static int process_inode_chains(ocfs2_filesys *fs, uint64_t gd_blkno, int chain_num, void *priv_data) { struct process_extents_context *pc = priv_data; uint32_t clusters = pc->di->id2.i_chain.cl_cpg; struct ocfs2_group_desc *gd; int i; if (pc->di->i_blkno == pc->global_bitmap_blkno) clusters = 1; if (!ocfs2_supports_discontig_bg(OCFS2_RAW_SB(fs->fs_super)) || (pc->di->i_blkno == pc->global_bitmap_blkno)) { pc->ret = process_dup_clusters(pc, ocfs2_blocks_to_clusters(fs, gd_blkno), clusters, 0); goto out; } pc->ret = ocfs2_read_group_desc(fs, gd_blkno, pc->extra_buf); if (pc->ret) goto out; gd = (struct ocfs2_group_desc *)pc->extra_buf; if (!ocfs2_gd_is_discontig(gd)) { pc->ret = process_dup_clusters(pc, ocfs2_blocks_to_clusters(fs, gd_blkno), clusters, 0); goto out; } /* Now check the discontiguous group case. */ for (i = 0; i < gd->bg_list.l_next_free_rec; i++) { struct ocfs2_extent_rec *rec = &gd->bg_list.l_recs[i]; pc->ret = process_dup_clusters(pc, ocfs2_blocks_to_clusters(fs, rec->e_blkno), rec->e_leaf_clusters, rec->e_cpos); } out: return pc->ret ? OCFS2_CHAIN_ERROR | OCFS2_CHAIN_ABORT : 0; } static int process_inode_extents(ocfs2_filesys *fs, struct ocfs2_extent_rec *rec, int tree_depth, uint32_t ccount, uint64_t ref_blkno, int ref_recno, void *priv_data) { struct process_extents_context *pc = priv_data; assert(!tree_depth); pc->ret = process_dup_clusters(pc, ocfs2_blocks_to_clusters(fs, rec->e_blkno), rec->e_leaf_clusters, rec->e_cpos); return pc->ret ? OCFS2_EXTENT_ERROR | OCFS2_EXTENT_ABORT : 0; } static errcode_t process_xattr_header(struct process_extents_context *pc, struct ocfs2_xattr_header *xh) { int i; errcode_t ret = 0; struct ocfs2_xattr_entry *xe; struct ocfs2_xattr_value_root *xv; for (i = 0 ; i < xh->xh_count; i++) { xe = &xh->xh_entries[i]; if (ocfs2_xattr_is_local(xe)) continue; xv = (struct ocfs2_xattr_value_root *) ((void *)xh + xe->xe_name_offset + OCFS2_XATTR_SIZE(xe->xe_name_len)); ret = ocfs2_extent_iterate_xattr(pc->ost->ost_fs, &xv->xr_list, xv->xr_last_eb_blk, OCFS2_EXTENT_FLAG_DATA_ONLY, process_inode_extents, pc, NULL); if (ret) com_err(whoami, ret, "while processing xattrs on inode %"PRIu64, (uint64_t)pc->di->i_blkno); if (!ret) ret = pc->ret; if (ret) break; } return ret; } static errcode_t process_one_bucket_list(struct process_extents_context *pc, struct ocfs2_extent_rec *rec) { int i; errcode_t ret; char *bucket; struct ocfs2_xattr_header *xh; uint64_t blkno = ocfs2_clusters_to_blocks(pc->ost->ost_fs, rec->e_cpos); int bucket_count = rec->e_leaf_clusters * ocfs2_xattr_buckets_per_cluster(pc->ost->ost_fs); ret = ocfs2_malloc_blocks(pc->ost->ost_fs->fs_io, ocfs2_blocks_per_xattr_bucket(pc->ost->ost_fs), &bucket); if (ret) { com_err(whoami, ret, "while allocating an xattr bucket buffer"); return ret; } xh = (struct ocfs2_xattr_header *)bucket; for (i = 0; i < bucket_count; i++) { ret = ocfs2_read_xattr_bucket(pc->ost->ost_fs, blkno, bucket); if (ret) { com_err(whoami, ret, "while reading the xattr bucket at " "%"PRIu64" on inode %"PRIu64, blkno, (uint64_t)pc->di->i_blkno); break; } if (!i) bucket_count = xh->xh_num_buckets; ret = process_xattr_header(pc, xh); if (ret) break; blkno += ocfs2_blocks_per_xattr_bucket(pc->ost->ost_fs); } ocfs2_free(&bucket); return ret; } static int process_xattr_buckets(ocfs2_filesys *fs, struct ocfs2_extent_rec *rec, int tree_depth, uint32_t ccount, uint64_t ref_blkno, int ref_recno, void *priv_data) { errcode_t ret = 0; struct process_extents_context *pc = priv_data; assert(!tree_depth); pc->ret = process_dup_clusters(pc, ocfs2_blocks_to_clusters(fs, rec->e_blkno), rec->e_leaf_clusters, rec->e_cpos); if (pc->ret) goto out; ret = process_one_bucket_list(pc, rec); out: if (!ret) ret = pc->ret; return pc->ret ? OCFS2_EXTENT_ERROR | OCFS2_EXTENT_ABORT : 0; } static errcode_t process_xattr_tree(struct process_extents_context *pc, struct ocfs2_xattr_block *xb) { errcode_t ret; ret = ocfs2_extent_iterate_xattr(pc->ost->ost_fs, &xb->xb_attrs.xb_root.xt_list, xb->xb_attrs.xb_root.xt_last_eb_blk, OCFS2_EXTENT_FLAG_DATA_ONLY, process_xattr_buckets, pc, NULL); if (ret) com_err(whoami, ret, "while processing xattrs on inode %"PRIu64, (uint64_t)pc->di->i_blkno); if (!ret) ret = pc->ret; return ret; } static errcode_t process_xattr_block(struct process_extents_context *pc) { errcode_t ret; char *blk = NULL; struct ocfs2_xattr_block *xb; ret = ocfs2_malloc_block(pc->ost->ost_fs->fs_io, &blk); if (ret) { com_err(whoami, ret, "while allocating a buffer to read the xattr block " "on inode %"PRIu64, (uint64_t)pc->di->i_xattr_loc); goto out; } ret = ocfs2_read_xattr_block(pc->ost->ost_fs, pc->di->i_xattr_loc, blk); if (ret) { com_err(whoami, ret, "while reading externel block of" " extended attributes "); goto out; } xb = (struct ocfs2_xattr_block *)blk; if (!(xb->xb_flags & OCFS2_XATTR_INDEXED)) ret = process_xattr_header(pc, &xb->xb_attrs.xb_header); else ret = process_xattr_tree(pc, xb); out: if (blk) ocfs2_free(&blk); return ret; } static errcode_t process_inode_xattrs(struct process_extents_context *pc) { errcode_t ret = 0; struct ocfs2_xattr_header *xh; if (pc->di->i_dyn_features & OCFS2_INLINE_XATTR_FL) { xh = (struct ocfs2_xattr_header *) ((void *)pc->di + pc->ost->ost_fs->fs_blocksize - pc->di->i_xattr_inline_size); ret = process_xattr_header(pc, xh); if (ret) goto out; } if (pc->di->i_xattr_loc) ret = process_xattr_block(pc); out: return ret; } static errcode_t pass1b_process_inode(o2fsck_state *ost, struct dup_context *dct, uint64_t ino, struct ocfs2_dinode *di, char *extra_buf) { errcode_t ret = 0; static uint64_t global_bitmap_blkno = 0; struct process_extents_context pc = { .ost = ost, .dct = dct, .di = di, .extra_buf = extra_buf, }; /* * The global bitmap needs magic in process_inode_chains. Let's * look it up here. It's static, so this only happens once. */ if (!global_bitmap_blkno) { ret = ocfs2_lookup_system_inode(ost->ost_fs, GLOBAL_BITMAP_SYSTEM_INODE, 0, &global_bitmap_blkno); if (ret) { com_err(whoami, ret, "while looking up the cluster bitmap " "allocator inode"); goto out; } } pc.global_bitmap_blkno = global_bitmap_blkno; /* * Skip extent processing for for inodes that don't have i_list or * i_chains. Like Pass 1, we have to trust i_mode/i_clusters to * tell us that a symlink has put target data in the union instead * of i_list */ if ((di->i_flags & (OCFS2_SUPER_BLOCK_FL | OCFS2_LOCAL_ALLOC_FL | OCFS2_DEALLOC_FL)) || (di->i_dyn_features & OCFS2_INLINE_DATA_FL) || (S_ISLNK(di->i_mode) && di->i_clusters == 0)) goto xattrs; if (di->i_flags & OCFS2_CHAIN_FL) ret = ocfs2_chain_iterate(ost->ost_fs, pc.di->i_blkno, process_inode_chains, &pc); else ret = ocfs2_extent_iterate_inode(ost->ost_fs, di, OCFS2_EXTENT_FLAG_DATA_ONLY, NULL, process_inode_extents, &pc); if (ret) com_err(whoami, ret, "while processing inode %"PRIu64, ino); else ret = pc.ret; if (ret) goto out; xattrs: if (di->i_dyn_features & OCFS2_HAS_XATTR_FL) ret = process_inode_xattrs(&pc); out: return ret; } static errcode_t o2fsck_pass1b(o2fsck_state *ost, struct dup_context *dct) { errcode_t ret; uint64_t blkno; char *buf = NULL, *extra_buf = NULL; struct ocfs2_dinode *di; ocfs2_inode_scan *scan; ocfs2_filesys *fs = ost->ost_fs; whoami = "pass1b"; printf("Running additional passes to resolve clusters claimed by " "more than one inode...\n" "Pass 1b: Determining ownership of multiply-claimed clusters\n"); ret = ocfs2_malloc_block(fs->fs_io, &buf); if (ret) { com_err(whoami, ret, "while allocating inode buffer"); goto out; } ret = ocfs2_malloc_block(fs->fs_io, &extra_buf); if (ret) { com_err(whoami, ret, "while allocating extra buffer"); goto out; } di = (struct ocfs2_dinode *)buf; ret = ocfs2_open_inode_scan(fs, &scan); if (ret) { com_err(whoami, ret, "while opening inode scan"); goto out; } /* * The inode allocators should be good after Pass 1. * Valid inodes should really be valid. Errors are real errors. */ for(;;) { ret = ocfs2_get_next_inode(scan, &blkno, buf); if (ret) { com_err(whoami, ret, "while getting next inode"); break; } if (blkno == 0) break; if (memcmp(di->i_signature, OCFS2_INODE_SIGNATURE, strlen(OCFS2_INODE_SIGNATURE))) continue; ocfs2_swap_inode_to_cpu(fs, di); if (di->i_fs_generation != ost->ost_fs_generation) continue; if (!(di->i_flags & OCFS2_VALID_FL)) continue; ret = pass1b_process_inode(ost, dct, blkno, di, extra_buf); if (ret) break; } ocfs2_close_inode_scan(scan); out: if (buf) ocfs2_free(&buf); if (extra_buf) ocfs2_free(&extra_buf); return ret; } /* * Pass 1C */ struct dir_to_scan { struct list_head ts_list; uint64_t ts_ino; char *ts_path; }; struct dir_scan_context { o2fsck_state *ds_ost; struct dup_context *ds_dct; /* Inodes we still have to find */ int64_t ds_inodes_left; /* Subdirs that are pending */ struct list_head ds_paths; /* The cwd's path and ino */ uint64_t ds_ino; char *ds_cwd; int ds_cwdlen; }; static void pass1c_warn(errcode_t ret) { static int warned = 0; if (warned) return; warned = 1; com_err(whoami, ret, "while finding path names in Pass 1c. The pass will " "continue, but some inodes may be described by " "inode number instead of name."); } static char *de_to_path(struct dir_scan_context *scan, struct ocfs2_dir_entry *de) { /* The 2 is for the path separator and the null */ int copied, pathlen = scan->ds_cwdlen + de->name_len + 2; char *path = NULL; /* We start with an empty cwd as we add '/' or '//' */ const char *cwdstr = scan->ds_cwdlen ? scan->ds_cwd : ""; const char *sep = "/"; /* Don't repeat '/' */ if (scan->ds_cwdlen && (scan->ds_cwd[scan->ds_cwdlen - 1] == '/')) sep = ""; if (de->name_len && (de->name[0] == '/')) sep = ""; if (!ocfs2_malloc0(sizeof(char) * pathlen, &path)) { copied = snprintf(path, pathlen, "%s%s%.*s", cwdstr, sep, de->name_len, de->name); assert(copied < pathlen); } return path; } static void push_dir(struct dir_scan_context *scan, struct ocfs2_dir_entry *de) { errcode_t ret; struct dir_to_scan *ts = NULL; ret = ocfs2_malloc0(sizeof(struct dir_to_scan), &ts); if (ret) goto warn; ts->ts_ino = de->inode; ts->ts_path = de_to_path(scan, de); if (!ts->ts_path) { ret = OCFS2_ET_NO_MEMORY; goto warn; } list_add(&ts->ts_list, &scan->ds_paths); return; warn: if (ts) ocfs2_free(&ts); pass1c_warn(ret); } static void set_next_cwd(struct dir_scan_context *scan) { struct dir_to_scan *ts; if (scan->ds_cwd) ocfs2_free(&scan->ds_cwd); ts = list_entry(scan->ds_paths.next, struct dir_to_scan, ts_list); list_del(&ts->ts_list); /* Steal the string from ts */ scan->ds_cwd = ts->ts_path; scan->ds_cwdlen = strlen(scan->ds_cwd); scan->ds_ino = ts->ts_ino; ocfs2_free(&ts); } static void name_inode(struct dir_scan_context *scan, struct ocfs2_dir_entry *de) { struct dup_inode *di = dup_inode_lookup(scan->ds_dct, de->inode); if (!di || di->di_path) return; scan->ds_inodes_left--; di->di_path = de_to_path(scan, de); if (!di->di_path) pass1c_warn(OCFS2_ET_NO_MEMORY); } static int walk_iterate(struct ocfs2_dir_entry *de, uint64_t blocknr, int offset, int blocksize, char *buf, void *priv_data) { struct dir_scan_context *scan = priv_data; /* Directories are checked when they're traversed */ if (de->file_type == OCFS2_FT_DIR) push_dir(scan, de); else name_inode(scan, de); return scan->ds_inodes_left ? 0 : OCFS2_DIRENT_ABORT; } static void walk_cwd(struct dir_scan_context *scan) { errcode_t ret; struct ocfs2_dir_entry *de; int len = sizeof(struct ocfs2_dir_entry); if (scan->ds_cwdlen > OCFS2_MAX_FILENAME_LEN) len = sizeof(struct ocfs2_dir_entry) + scan->ds_cwdlen - OCFS2_MAX_FILENAME_LEN; ret = ocfs2_malloc(len, &de); if (ret) { pass1c_warn(ret); return; } memcpy(de->name, scan->ds_cwd, scan->ds_cwdlen); de->name_len = scan->ds_cwdlen; name_inode(scan, de); free(de); ret = ocfs2_dir_iterate(scan->ds_ost->ost_fs, scan->ds_ino, OCFS2_DIRENT_FLAG_EXCLUDE_DOTS, NULL, walk_iterate, scan); if (ret) pass1c_warn(ret); } static void o2fsck_pass1c(o2fsck_state *ost, struct dup_context *dct) { struct dir_scan_context scan = { .ds_ost = ost, .ds_dct = dct, .ds_inodes_left = dct->dup_inode_count, }; whoami = "pass1c"; printf("Pass 1c: Determining the names of inodes owning " "multiply-claimed clusters\n"); INIT_LIST_HEAD(&scan.ds_paths); push_dir(&scan, &(struct ocfs2_dir_entry){ .name = "/", .name_len = 1, .file_type = OCFS2_FT_DIR, .inode = ost->ost_fs->fs_root_blkno, }); push_dir(&scan, &(struct ocfs2_dir_entry){ .name = "//", .name_len = 2, .file_type = OCFS2_FT_DIR, .inode = ost->ost_fs->fs_sysdir_blkno, }); while (scan.ds_inodes_left && !list_empty(&scan.ds_paths)) { set_next_cwd(&scan); walk_cwd(&scan); } } /* * Pass 1D */ static void print_inode_path(struct dup_inode *di) { if (di->di_path) fprintf(stdout, "%s\n", di->di_path); else fprintf(stdout, "<%"PRIu64">\n", di->di_ino); } /* * Walk the owning inodes of a dup_cluster, calling func(). func() may * return non-zero to abort the walk. */ static void for_each_owner(struct dup_context *dct, struct dup_cluster *dc, int (*func)(struct dup_cluster *dc, struct dup_inode *di, struct dup_cluster_owner *dco, void *priv_data), void *priv_data) { struct list_head *p, *next; struct dup_cluster_owner *dco; struct dup_inode *di; assert(!list_empty(&dc->dc_owners)); list_for_each_safe(p, next, &dc->dc_owners) { dco = list_entry(p, struct dup_cluster_owner, dco_list); di = dup_inode_lookup(dct, dco->dco_ino); assert(di); if (func(dc, di, dco, priv_data)) break; } } static int count_func(struct dup_cluster *dc, struct dup_inode *di, struct dup_cluster_owner *dco, void *priv_data) { uint64_t *count = priv_data; if (!(di->di_state & DUP_INODE_HANDLED)) (*count)++; return 0; } static int print_func(struct dup_cluster *dc, struct dup_inode *di, struct dup_cluster_owner *dco, void *priv_data) { printf(" "); print_inode_path(di); return 0; } /* * Check whether we can create refcount for the file. * So a file is valid only if: * 1. It isn't a system file. * 2. It has no refcount tree. * 3. It has the same tree as others. * Store refcount_loc if we find one. * * if there is other file that doesn't have the same tree, set refcount_loc * to UINT64_MAX and stop the search. */ static int find_refcount_func(struct dup_cluster *dc, struct dup_inode *di, struct dup_cluster_owner *dco, void *priv_data) { uint64_t *refcount_loc = priv_data; if (di->di_flags & OCFS2_SYSTEM_FL) { *refcount_loc = UINT64_MAX; return 1; } if (!di->di_refcount_loc) return 0; if (!*refcount_loc) { *refcount_loc = di->di_refcount_loc; return 0; } if (di->di_refcount_loc != *refcount_loc) { *refcount_loc = UINT64_MAX; return 1; } return 0; } /* Context for fix_dups_func() */ struct fix_dup_context { o2fsck_state *fd_ost; struct dup_context *fd_dct; errcode_t fd_err; }; static void print_chain_warning(void) { static int chain_warning = 0; if (chain_warning) return; printf("The filesystem is safe to read. You may wish to mount " "it read-only and copy data to a new filesystem.\n"); chain_warning = 1; } static errcode_t new_clone(ocfs2_filesys *fs, ocfs2_cached_inode *orig_ci, ocfs2_cached_inode **clone_ci) { errcode_t ret, ret2; uint64_t clone_blkno = 0; uint64_t bytes = orig_ci->ci_inode->i_size; uint32_t clusters = ocfs2_clusters_in_bytes(fs, bytes); ret = ocfs2_new_inode(fs, &clone_blkno, orig_ci->ci_inode->i_mode); if (ret) { com_err(whoami, ret, "while allocating a clone inode"); return ret; } /* * Let's get the clusters in the best way we can. We make sure * i_size is updated so that ocfs2_file_write() is happy. */ if (ocfs2_writes_unwritten_extents(OCFS2_RAW_SB(fs->fs_super)) && !(orig_ci->ci_inode->i_flags & OCFS2_SYSTEM_FL)) ret = ocfs2_allocate_unwritten_extents(fs, clone_blkno, 0, bytes); else { ret = ocfs2_extend_allocation(fs, clone_blkno, clusters); if (!ret) ret = ocfs2_extend_file(fs, clone_blkno, bytes); } if (ret) { com_err(whoami, ret, "while allocating data clusters for a clone inode"); goto out; } ret = ocfs2_read_cached_inode(fs, clone_blkno, clone_ci); if (ret) com_err(whoami, ret, "while reading temporary clone inode"); /* * It is so tempting to link the temporary clone inode into * the orphan directory here. But we can't, because later in * the clone process it will point to multiply-claimed clusters. * Orphan cleanup would free them, which is even worse than * leaving the temporary clone inode around. */ out: if (ret && clone_blkno) { ret2 = ocfs2_delete_inode(fs, clone_blkno); if (ret2) com_err(whoami, ret2, "while removing temporary clone inode"); } return ret; } static errcode_t copy_clone(ocfs2_filesys *fs, ocfs2_cached_inode *orig_ci, ocfs2_cached_inode *clone_ci) { char *buf; errcode_t ret; uint64_t offset = 0; uint64_t filesize = orig_ci->ci_inode->i_size; unsigned int iosize = 1024 * 1024; /* Let's read in 1MB hunks */ unsigned int got, wrote, write_len; ret = ocfs2_malloc_blocks(fs->fs_io, iosize / fs->fs_blocksize, &buf); if (ret) { com_err(whoami, ret, "while allocating clone buffer"); return ret; } while (offset < filesize) { ret = ocfs2_file_read(orig_ci, buf, iosize, offset, &got); if (ret) { com_err(whoami, ret, "while reading inode to clone"); break; } write_len = ocfs2_align_bytes_to_blocks(fs, got); ret = ocfs2_file_write(clone_ci, buf, write_len, offset, &wrote); if (ret) { com_err(whoami, ret, "while writing clone data"); break; } assert(got == wrote); offset += wrote; } ocfs2_free(&buf); return ret; } static errcode_t swap_clone(ocfs2_filesys *fs, ocfs2_cached_inode *orig_ci, ocfs2_cached_inode *clone_ci) { uint32_t clusters; errcode_t ret; struct ocfs2_extent_list *tmp_el = NULL; struct ocfs2_extent_list *orig_el = &orig_ci->ci_inode->id2.i_list; struct ocfs2_extent_list *clone_el = &clone_ci->ci_inode->id2.i_list; int el_size = offsetof(struct ocfs2_extent_list, l_recs) + sizeof(struct ocfs2_extent_rec) * orig_el->l_count; ret = ocfs2_malloc0(el_size, &tmp_el); if (ret) { com_err(whoami, ret, "while allocating temporary memory to swap a " "cloned inode"); goto out; } memcpy(tmp_el, orig_el, el_size); memcpy(orig_el, clone_el, el_size); memcpy(clone_el, tmp_el, el_size); /* * In new_clone, we allocate all the clusters disregard whether * there are holes. So here we need to update the i_clusters also. */ clusters = orig_ci->ci_inode->i_clusters; orig_ci->ci_inode->i_clusters = clone_ci->ci_inode->i_clusters; clone_ci->ci_inode->i_clusters = clusters; /* * We write the cloned inode with the original extent list first. * If we crash between writing the cloned inode and the original * one, the cloned inode will appear to share the same extents * as the original and the extents we just allocated to the clone * will look unused to a subsequent fsck run. They'll be reusable * for recovery. */ ret = ocfs2_write_cached_inode(fs, clone_ci); if (ret) { com_err(whoami, ret, "while writing out clone inode %"PRIu64, clone_ci->ci_blkno); goto out; } ret = ocfs2_write_cached_inode(fs, orig_ci); if (ret) com_err(whoami, ret, "while writing out inode %"PRIu64, orig_ci->ci_blkno); out: if (tmp_el) ocfs2_free(&tmp_el); return ret; } static int can_free(struct dup_context *dct, uint32_t cpos) { struct dup_cluster *dc; int unhandled = 0; dc = dup_cluster_lookup(dct, cpos); /* We don't call can_free unless it's in the dup bitmap */ assert(dc); /* * See how many inodes still point to it. It can't be zero, * because we're working on an inode that points to it RIGHT * NOW. */ for_each_owner(dct, dc, count_func, &unhandled); assert(unhandled > 0); if (unhandled > 1) return 0; return 1; } static errcode_t pass1d_free_clusters(ocfs2_filesys *fs, uint32_t len, uint64_t start, void *free_data) { errcode_t ret = 0; int was_set; struct fix_dup_context *fd = free_data; uint32_t p_cpos, p_start = ocfs2_blocks_to_clusters(fs, start); for (p_cpos = p_start; p_cpos < (p_start + len); p_cpos++) { verbosef("checking cpos %"PRIu32"\n", p_cpos); ret = ocfs2_bitmap_test(fd->fd_ost->ost_duplicate_clusters, p_cpos, &was_set); if (ret) { com_err(whoami, ret, "while testing cluster %"PRIu32" in " "the duplicate cluster map", p_cpos); break; } verbosef("cpos %"PRIu32" was_set == %d\n", p_cpos, was_set); if (was_set) { if (can_free(fd->fd_dct, p_cpos)) verbosef("Freeing multiply-claimed cluster " "%"PRIu32", as it is no longer used\n", p_cpos); else continue; } verbosef("Freeing cluster %"PRIu32"\n", p_cpos); ret = ocfs2_free_clusters(fd->fd_ost->ost_fs, 1, ocfs2_clusters_to_blocks(fs, p_cpos)); if (ret) { com_err(whoami, ret, "while freeing duplicate cluster " "%"PRIu32, p_cpos); break; } } return ret; } static int delete_one_inode(struct fix_dup_context *fd, uint64_t ino) { errcode_t ret; o2fsck_state *ost = fd->fd_ost; verbosef("Truncating inode %"PRIu64"\n", ino); ret = ocfs2_truncate_full(ost->ost_fs, ino, 0, pass1d_free_clusters, fd); if (ret) { com_err(whoami, ret, "while truncating inode %"PRIu64" to remove it", ino); goto out; } verbosef("Deleting inode %"PRIu64"\n", ino); ret = ocfs2_delete_inode(ost->ost_fs, ino); if (ret) com_err(whoami, ret, "while removing inode %"PRIu64, ino); else o2fsck_icount_set(ost->ost_icount_in_inodes, ino, 0); out: return ret ? 1 : 0; } static int clone_one_inode(struct fix_dup_context *fd, struct dup_inode *di) { errcode_t ret, tmpret; ocfs2_filesys *fs = fd->fd_ost->ost_fs; ocfs2_cached_inode *orig_ci = NULL, *clone_ci = NULL; ret = ocfs2_read_cached_inode(fs, di->di_ino, &orig_ci); if (ret) { com_err(whoami, ret, "while reading inode \"%s\" to clone it", di->di_path); goto out; } ret = new_clone(fs, orig_ci, &clone_ci); if (ret) goto out; verbosef("Copying inode \"%s\" to clone %"PRIu64"\n", di->di_path, clone_ci->ci_blkno); ret = copy_clone(fs, orig_ci, clone_ci); if (ret) goto out; ret = swap_clone(fs, orig_ci, clone_ci); out: if (orig_ci) ocfs2_free_cached_inode(fs, orig_ci); if (clone_ci) { tmpret = delete_one_inode(fd, clone_ci->ci_blkno); if (!ret) ret = tmpret; ocfs2_free_cached_inode(fs, clone_ci); } return ret ? 1 : 0; } static int fix_dups_func(struct dup_cluster *dc, struct dup_inode *di, struct dup_cluster_owner *dco, void *priv_data) { int ret = 0; struct fix_dup_context *fd = priv_data; if (di->di_flags & OCFS2_CHAIN_FL) { printf("Inode \"%s\" is a chain allocator and cannot " "be cloned or deleted.\n", di->di_path); print_chain_warning(); return 0; } if (di->di_flags & OCFS2_SYSTEM_FL) { if (prompt(fd->fd_ost, PY, PR_DUP_CLUSTERS_SYSFILE_CLONE, "Inode \"%s\" is a system file. It may be " "cloned but not deleted. Clone inode \"%s\" to " "break claims on clusters it shares with other " "inodes?", di->di_path, di->di_path)) { ret = clone_one_inode(fd, di); if (!ret) di->di_state |= DUP_INODE_CLONED; } } else { if (prompt(fd->fd_ost, PY, PR_DUP_CLUSTERS_CLONE, "Inode \"%s\" may be cloned or deleted to " "break the claim it has on its clusters. " "Clone inode \"%s\" to break claims on " "clusters it shares with other inodes?", di->di_path, di->di_path)) { ret = clone_one_inode(fd, di); if (!ret) di->di_state |= DUP_INODE_CLONED; } else if (prompt(fd->fd_ost, PN, PR_DUP_CLUSTERS_DELETE, "Delete inode \"%s\" to break claims on " "clusters it shares with other inodes?", di->di_path)) { ret = delete_one_inode(fd, di->di_ino); if (!ret) di->di_state |= DUP_INODE_REMOVED; } } return ret; } struct create_refcount { o2fsck_state *cr_ost; uint64_t cr_refcount_loc; errcode_t cr_err; }; static int create_refcount_func(struct dup_cluster *dc, struct dup_inode *di, struct dup_cluster_owner *dco, void *priv_data) { errcode_t ret; struct create_refcount *cr = priv_data; ocfs2_filesys *fs = cr->cr_ost->ost_fs; if (!di->di_refcount_loc) { ret = ocfs2_attach_refcount_tree(fs, di->di_ino, cr->cr_refcount_loc); if (ret) { com_err(whoami, ret, "while attaching file %"PRIu64" to" "refcount tree %"PRIu64, di->di_ino, cr->cr_refcount_loc); goto out; } di->di_refcount_loc = cr->cr_refcount_loc; } ret = ocfs2_change_refcount_flag(fs, di->di_ino, dco->dco_cpos, 1, dc->dc_cluster, OCFS2_EXT_REFCOUNTED, 0); if (ret) { com_err(whoami, ret, "while mark extent refcounted at %u in file %"PRIu64, dco->dco_cpos, di->di_ino); goto out; } ret = ocfs2_increase_refcount(fs, di->di_ino, dc->dc_cluster, 1); if (ret) com_err(whoami, ret, "while increasing refcount at %u for file %"PRIu64, dc->dc_cluster, di->di_ino); out: cr->cr_err = ret; return ret; } /* * Create refcount record for all the files sharing the same clusters. * Create a new refcount tree if all the files don't have it(refcount_loc = 0). * If a file don't have refcount tree, attach it to that tree. */ static errcode_t o2fsck_create_refcount(o2fsck_state *ost, struct dup_context *dct, struct dup_cluster *dc, uint64_t refcount_loc) { errcode_t ret = 0; struct create_refcount cr = { .cr_ost = ost, .cr_refcount_loc = refcount_loc, }; if (!refcount_loc) { ret = ocfs2_create_refcount_tree(ost->ost_fs, &refcount_loc); if (ret) { com_err(whoami, ret, "while allocating a new refcount block"); goto out; } cr.cr_refcount_loc = refcount_loc; } for_each_owner(dct, dc, create_refcount_func, &cr); ret = cr.cr_err; out: return ret; } static errcode_t o2fsck_pass1d(o2fsck_state *ost, struct dup_context *dct) { errcode_t ret = 0; struct dup_cluster *dc; struct rb_node *node = rb_first(&dct->dup_clusters); uint64_t dups, refcount_loc; struct fix_dup_context fd = { .fd_ost = ost, .fd_dct = dct, }; whoami = "pass1d"; printf("Pass 1d: Reconciling multiply-claimed clusters\n"); for (node = rb_first(&dct->dup_clusters); node; node = rb_next(node)) { dc = rb_entry(node, struct dup_cluster, dc_node); dups = 0; for_each_owner(dct, dc, count_func, &dups); if (dups < 2) continue; printf("Cluster %"PRIu32" is claimed by the following " "inodes:\n", dc->dc_cluster); for_each_owner(dct, dc, print_func, NULL); /* We try create refcount tree first. */ if (ocfs2_refcount_tree(OCFS2_RAW_SB(ost->ost_fs->fs_super))) { refcount_loc = 0; for_each_owner(dct, dc, find_refcount_func, &refcount_loc); if (refcount_loc != UINT64_MAX && prompt(ost, PY, PR_DUP_CLUSTERS_ADD_REFCOUNT, "Create refcount record for it?")) { ret = o2fsck_create_refcount(ost, dct, dc, refcount_loc); if (ret) break; continue; } } for_each_owner(dct, dc, fix_dups_func, &fd); if (fd.fd_err) { ret = fd.fd_err; break; } } return ret; } /* * Exported call */ errcode_t ocfs2_pass1_dups(o2fsck_state *ost) { errcode_t ret; struct dup_context dct = { .dup_clusters = RB_ROOT, .dup_inodes = RB_ROOT, }; ret = o2fsck_pass1b(ost, &dct); if (!ret) { o2fsck_pass1c(ost, &dct); ret = o2fsck_pass1d(ost, &dct); } o2fsck_empty_dup_context(&dct); return ret; } ocfs2-tools-ocfs2-tools-1.8.6/fsck.ocfs2/pass2.c000066400000000000000000000756251347147137200212350ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * Copyright (C) 1993-2004 by Theodore Ts'o. * Copyright (C) 2004 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License, version 2, as published by the Free Software Foundation. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * * -- * Pass 2 iterates through the directory blocks that pass 1 found under * directory inodes. The basic dirent structures are made consistent * in each block. Directory entries much point to active inodes. "dot dot" * must be in the first blocks of the dir and nowhere else. Duplicate entries * are detected but little more. Slashes and nulls in names are replaced * with dots. The file type in the entry is synced with the type found in * the inode it points to. Throughout this invalid entries are cleared * by simply setting their inode field to 0 so that the fs will reuse them. * * Pass 2 builds up the parent dir linkage as it scans the directory entries * so that pass 3 can walk the directory trees to find disconnected inodes. */ #include #include #include #include "ocfs2/ocfs2.h" #include "ocfs2/kernel-rbtree.h" #include "dirparents.h" #include "icount.h" #include "fsck.h" #include "pass2.h" #include "problem.h" #include "o2fsck_strings.h" #include "util.h" static const char *whoami = "pass2"; int o2fsck_test_inode_allocated(o2fsck_state *ost, uint64_t blkno) { errcode_t ret; int was_set; ret = ocfs2_test_inode_allocated(ost->ost_fs, blkno, &was_set); /* XXX this should stop fsck from marking the fs clean */ if (ret) { com_err(whoami, ret, "while testing if inode %"PRIu64" is " "allocated. Continuing as though it is.", blkno); was_set = 1; } return was_set; } struct dirblock_data { o2fsck_state *ost; ocfs2_filesys *fs; char *dirblock_buf; char *inoblock_buf; errcode_t ret; o2fsck_strings strings; uint64_t last_ino; struct rb_root re_idx_dirs; }; static int dirent_has_dots(struct ocfs2_dir_entry *dirent, int num_dots) { if (num_dots < 1 || num_dots > 2 || num_dots != dirent->name_len) return 0; if (num_dots == 2 && dirent->name[1] != '.') return 0; return dirent->name[0] == '.'; } static int expected_dots(o2fsck_state *ost, o2fsck_dirblock_entry *dbe, int offset) { int inline_off = offsetof(struct ocfs2_dinode, id2.i_data.id_data); if (dbe->e_blkcount == 0) { if (offset == 0 || (dbe->e_ino == dbe->e_blkno && offset == inline_off)) return 1; if (offset == OCFS2_DIR_REC_LEN(1) || (dbe->e_ino == dbe->e_blkno && offset == inline_off + OCFS2_DIR_REC_LEN(1))) return 2; } return 0; } static errcode_t fix_dirent_dots(o2fsck_state *ost, o2fsck_dirblock_entry *dbe, struct ocfs2_dir_entry *dirent, int offset, int left, unsigned int *flags) { int expect_dots = expected_dots(ost, dbe, offset); int changed_len = 0; struct ocfs2_dir_entry *next; uint16_t new_len; errcode_t ret = 0; if (!expect_dots) { if (!dirent->inode || (!dirent_has_dots(dirent, 1) && !dirent_has_dots(dirent, 2))) goto out; if (prompt(ost, PY, PR_DIRENT_DOTTY_DUP, "Duplicate '%.*s' directory entry found, remove " "it?", dirent->name_len, dirent->name)) { dirent->inode = 0; *flags |= OCFS2_DIRENT_CHANGED; goto out; } } if (!dirent_has_dots(dirent, expect_dots) && prompt(ost, PY, PR_DIRENT_NOT_DOTTY, "The %s directory entry in directory inode " "%"PRIu64" is '%.*s' instead of '%.*s'. Clobber the " "current name with the expected dot name?", expect_dots == 1 ? "first" : "second", dbe->e_ino, dirent->name_len, dirent->name, expect_dots, "..")) { dirent->name_len = expect_dots; memset(dirent->name, '.', expect_dots); dirent->file_type = OCFS2_FT_DIR; changed_len = 1; *flags |= OCFS2_DIRENT_CHANGED; } /* we only record where .. points for now and that ends the * checks for .. */ if (expect_dots == 2) { o2fsck_dir_parent *dp; dp = o2fsck_dir_parent_lookup(&ost->ost_dir_parents, dbe->e_ino); if (dp == NULL) { ret = OCFS2_ET_INTERNAL_FAILURE; com_err(whoami, ret, "no dir parents for '..' entry " "for inode %"PRIu64, dbe->e_ino); } else dp->dp_dot_dot = dirent->inode; goto out; } if ((dirent->inode != dbe->e_ino) && prompt(ost, PY, PR_DIRENT_DOT_INODE, "The '.' entry in directory inode %"PRIu64" " "points to inode %"PRIu64" instead of itself. Fix " "the '.' entry?", dbe->e_ino, (uint64_t)dirent->inode)) { dirent->inode = dbe->e_ino; *flags |= OCFS2_DIRENT_CHANGED; } /* * we might have slop at the end of this "." dirent. split * it into another seperate dirent if there is enough room and * we've just updated it's name_len or the user says we should. */ new_len = OCFS2_DIR_REC_LEN(dirent->name_len) - dirent->rec_len; if (new_len && (changed_len || prompt(ost, PY, PR_DIRENT_DOT_EXCESS, "The '.' entry in directory inode " "%"PRIu64" is too long. Try to create another " "directory entry from the excess?", dbe->e_ino))) { dirent->rec_len = OCFS2_DIR_REC_LEN(dirent->name_len); next = (struct ocfs2_dir_entry *)((char *)dirent + dirent->rec_len); next->inode = 0; next->name_len = 0; next->rec_len = OCFS2_DIR_REC_LEN(next->rec_len); *flags |= OCFS2_DIRENT_CHANGED; } out: return ret; } /* * The directory trailer has compatibility fields so it can be treated * as an empty (deleted) dirent. We need to make sure those are correct. */ static void fix_dir_trailer(o2fsck_state *ost, o2fsck_dirblock_entry *dbe, struct ocfs2_dir_block_trailer *trailer, unsigned int *flags) { if (trailer->db_compat_inode && prompt(ost, PY, PR_DIR_TRAILER_INODE, "Directory block trailer for logical block %"PRIu64" " "physcal block %"PRIu64" in directory inode %"PRIu64" " "has a non-zero inode number. Clear it?", dbe->e_blkcount, dbe->e_blkno, dbe->e_ino)) { trailer->db_compat_inode = 0; *flags |= OCFS2_DIRENT_CHANGED; } if (trailer->db_compat_name_len && prompt(ost, PY, PR_DIR_TRAILER_NAME_LEN, "Directory block trailer for logical block %"PRIu64" " "physcal block %"PRIu64" in directory inode %"PRIu64" " "has a non-zero name_len. Clear it?", dbe->e_blkcount, dbe->e_blkno, dbe->e_ino)) { trailer->db_compat_name_len = 0; *flags |= OCFS2_DIRENT_CHANGED; } if ((trailer->db_compat_rec_len != sizeof(struct ocfs2_dir_block_trailer)) && prompt(ost, PY, PR_DIR_TRAILER_REC_LEN, "Directory block trailer for logical block %"PRIu64" " "physcal block %"PRIu64" in directory inode %"PRIu64" " "has an invalid rec_len. Fix it?", dbe->e_blkcount, dbe->e_blkno, dbe->e_ino)) { trailer->db_compat_rec_len = sizeof(struct ocfs2_dir_block_trailer); *flags |= OCFS2_DIRENT_CHANGED; } if ((trailer->db_blkno != dbe->e_blkno) && prompt(ost, PY, PR_DIR_TRAILER_BLKNO, "Directory block trailer for logical block %"PRIu64" " "physcal block %"PRIu64" in directory inode %"PRIu64" " "has an invalid db_blkno of %"PRIu64". Fix it?", dbe->e_blkcount, dbe->e_blkno, dbe->e_ino, (uint64_t)trailer->db_blkno)) { trailer->db_blkno = dbe->e_blkno; *flags |= OCFS2_DIRENT_CHANGED; } if ((trailer->db_parent_dinode != dbe->e_ino) && prompt(ost, PY, PR_DIR_TRAILER_PARENT_INODE, "Directory block trailer for logical block %"PRIu64" " "physcal block %"PRIu64" in directory inode %"PRIu64" " "claims it belongs to inoe %"PRIu64". Fix it?", dbe->e_blkcount, dbe->e_blkno, dbe->e_ino, (uint64_t)trailer->db_parent_dinode)) { trailer->db_parent_dinode = dbe->e_ino; *flags |= OCFS2_DIRENT_CHANGED; } } static int dirent_leaves_partial(struct ocfs2_dir_entry *dirent, int left) { left -= dirent->rec_len; return (left > 0 && left < OCFS2_DIR_MEMBER_LEN); } /* * The caller has found that either of rec_len or name_len are garbage. The * caller trusts us to fix them up in place and will be checking them again * before proceeding. We have to update the lengths to make forward progress. * 'left' is the number of bytes from the start of this dirent struct that * remain in the block. * * We're called for invalid dirents, and having a dirent * that leaves a partial dirent at the end of the block is considered invalid, * and we pad out partials at the end of this call so we can't be called here * with left < OCFS2_DIR_MEMBER_LEN. * * we're pretty limited in the repairs we can make: * * - We can't just set name_len if rec_len looks valid, we might guess * name_len wrong and create a bogus file name. * - we can't just set rec_len based on name_len. rec_len could have * included an arbitrary part of the name from a previously freed dirent. */ static void fix_dirent_lengths(struct ocfs2_dir_entry *dirent, int left, struct ocfs2_dir_entry *prev, unsigned int *flags) { /* * as described above we can't reconstruct either value if it is * complete nonsense. We can only proceed if we can work off of * one that is kind of valid looking. * name_len could well be 0 from the dirent being cleared. */ if (dirent->rec_len < OCFS2_DIR_MEMBER_LEN || (dirent->rec_len > left || dirent->name_len > left)) goto wipe; /* if we see a dirent with no file name then we remove it by * shifting the remaining dirents forward */ if ((dirent->rec_len == OCFS2_DIR_MEMBER_LEN)) { char *cp = (char *)dirent; left -= dirent->rec_len; memmove(cp, cp + dirent->rec_len, left); memset(cp + left, 0, dirent->rec_len); goto out; } /* if rec_len just appears to be mis-rounded in a way that doesn't * affect following dirents then we can probably save this dirent */ if (OCFS2_DIR_REC_LEN(dirent->name_len) != dirent->rec_len && OCFS2_DIR_REC_LEN(dirent->name_len) == OCFS2_DIR_REC_LEN(dirent->rec_len)) { dirent->rec_len = OCFS2_DIR_REC_LEN(dirent->name_len); left -= dirent->rec_len; goto out; } /* if name_len is too far off, however, we're going to lose this * dirent.. we might be able to just lose this one dirent if rec_len * appears to be intact. */ if ((dirent->rec_len & OCFS2_DIR_ROUND) == 0 && !dirent_leaves_partial(dirent, left)) { left -= dirent->rec_len; dirent->name_len = 0; dirent->inode = 0; dirent->file_type = OCFS2_FT_UNKNOWN; goto out; } /* * if we can't trust rec_len, however, then we don't know where the * next dirent might begin. We've lost the trail of dirents created by * the file system and run the risk of parsing file names as dirents. * So we're forced to wipe the block and leave the rest to lost+found. */ wipe: dirent->rec_len = left; dirent->name_len = 0; dirent->inode = 0; dirent->file_type = OCFS2_FT_UNKNOWN; left = 0; out: /* * rec_len must be valid and left must reflect the space *after* the * current dirent by this point. if there isn't enough room for * another dirent after the one we've just repaired then we tack the * remaining space onto the current dirent. */ if (dirent_leaves_partial(dirent, left)) dirent->rec_len += left; *flags |= OCFS2_DIRENT_CHANGED; } static void fix_dirent_name(o2fsck_state *ost, o2fsck_dirblock_entry *dbe, struct ocfs2_dir_entry *dirent, int offset, unsigned int *flags) { char *chr = dirent->name; int len = dirent->name_len, fix = 0; if (len == 0) { if (prompt(ost, PY, PR_DIRENT_ZERO, "Directory entry has a zero-length name, " "clear it?")) { dirent->inode = 0; *flags |= OCFS2_DIRENT_CHANGED; } } for(; len-- > 0 && (*chr == '/' || *chr == '\0'); chr++) { /* XXX in %s parent name */ if (!fix) { fix = prompt(ost, PY, PR_DIRENT_NAME_CHARS, "Directory entry '%.*s' " "contains invalid characters, replace " "them with dots?", dirent->name_len, dirent->name); if (!fix) break; } *chr = '.'; *flags |= OCFS2_DIRENT_CHANGED; } } static void fix_dirent_inode(o2fsck_state *ost, o2fsck_dirblock_entry *dbe, struct ocfs2_dir_entry *dirent, int offset, unsigned int *flags) { if (ocfs2_block_out_of_range(ost->ost_fs, dirent->inode) && prompt(ost, PY, PR_DIRENT_INODE_RANGE, "Directory entry '%.*s' refers to inode " "number %"PRIu64" which is out of range, clear the entry?", dirent->name_len, dirent->name, (uint64_t)dirent->inode)) { dirent->inode = 0; *flags |= OCFS2_DIRENT_CHANGED; goto out; } if (!o2fsck_test_inode_allocated(ost, dirent->inode) && prompt(ost, PY, PR_DIRENT_INODE_FREE, "Directory entry '%.*s' refers to inode number " "%"PRIu64" which isn't allocated, clear the entry?", dirent->name_len, dirent->name, (uint64_t)dirent->inode)) { dirent->inode = 0; *flags |= OCFS2_DIRENT_CHANGED; } out: return; } #define type_entry(type) [type] = #type static char *file_types[] = { type_entry(OCFS2_FT_UNKNOWN), type_entry(OCFS2_FT_REG_FILE), type_entry(OCFS2_FT_DIR), type_entry(OCFS2_FT_CHRDEV), type_entry(OCFS2_FT_BLKDEV), type_entry(OCFS2_FT_FIFO), type_entry(OCFS2_FT_SOCK), type_entry(OCFS2_FT_SYMLINK), }; #undef type_entry static char *file_type_string(uint8_t type) { if (type >= OCFS2_FT_MAX) return "(unknown)"; return file_types[type]; } static errcode_t fix_dirent_filetype(o2fsck_state *ost, o2fsck_dirblock_entry *dbe, struct ocfs2_dir_entry *dirent, int offset, unsigned int *flags) { uint8_t expected_type; errcode_t ret; int was_set; ret = ocfs2_bitmap_test(ost->ost_dir_inodes, dirent->inode, &was_set); if (ret) goto out; if (was_set) { expected_type = OCFS2_FT_DIR; goto check; } ret = ocfs2_bitmap_test(ost->ost_reg_inodes, dirent->inode, &was_set); if (ret) goto out; if (was_set) { expected_type = OCFS2_FT_REG_FILE; goto check; } ret = o2fsck_type_from_dinode(ost, dirent->inode, &expected_type); if (ret) goto out; check: if ((dirent->file_type != expected_type) && prompt(ost, PY, PR_DIRENT_TYPE, "Directory entry %.*s contains file type %s (%u) " "but its inode %"PRIu64" leads to type %s (%u). Reset the " "entry's type to match the inode's?", dirent->name_len, dirent->name, file_type_string(dirent->file_type), dirent->file_type, (uint64_t)dirent->inode, file_type_string(expected_type), expected_type)) { dirent->file_type = expected_type; *flags |= OCFS2_DIRENT_CHANGED; } out: if (ret) com_err(whoami, ret, "while trying to verify the file type " "of directory entry %.*s", dirent->name_len, dirent->name); return ret; } static errcode_t fix_dirent_linkage(o2fsck_state *ost, o2fsck_dirblock_entry *dbe, struct ocfs2_dir_entry *dirent, int offset, unsigned int *flags) { int expect_dots = expected_dots(ost, dbe, offset); o2fsck_dir_parent *dp; errcode_t ret = 0; int is_dir; /* we already took care of special-casing the dots */ if (expect_dots) goto out; /* we're only checking the linkage if we already found the dir * this inode claims to be pointing to */ ret = ocfs2_bitmap_test(ost->ost_dir_inodes, dirent->inode, &is_dir); if (ret) com_err(whoami, ret, "while checking for inode %"PRIu64" in " "the dir bitmap", (uint64_t)dirent->inode); if (!is_dir) goto out; dp = o2fsck_dir_parent_lookup(&ost->ost_dir_parents, dirent->inode); if (dp == NULL) { ret = OCFS2_ET_INTERNAL_FAILURE; com_err(whoami, ret, "no dir parents recorded for inode " "%"PRIu64, (uint64_t)dirent->inode); goto out; } /* if no dirents have pointed to this inode yet we record ours * as the first and move on */ if (dp->dp_dirent == 0) { dp->dp_dirent = dbe->e_ino; goto out; } if (prompt(ost, 0, PR_DIR_PARENT_DUP, "Directory inode %"PRIu64" is not the first to " "claim to be the parent of subdir '%.*s' (inode %"PRIu64"). " "Clear this directory entry and leave the previous parent of " "the subdir's inode intact?", dbe->e_ino, dirent->name_len, dirent->name, (uint64_t)dirent->inode)) { dirent->inode = 0; *flags |= OCFS2_DIRENT_CHANGED; } out: return ret; } /* detecting dups is irritating because of the storage requirements of * detecting duplicates. e2fsck avoids the storage burden for a regular fsck * pass by only detecting duplicate entries that occur in the same directory * block. its repair pass then suffers under enormous directories because it * reads the whole thing into memory to detect duplicates. * * we'll take a compromise which expands the reach of a regular fsck pass by * using a slightly larger block size but which repairs in place rather than * reading the dir into memory. * * if we ever truly care to invest in duplicate detection and repair we could * either explicitly use some external sort and merge algo or perhaps just * combine mmap and some internal sort that has strong enough locality of * reference to work well with the vm. */ static errcode_t fix_dirent_dups(o2fsck_state *ost, o2fsck_dirblock_entry *dbe, struct ocfs2_dir_entry *dirent, o2fsck_strings *strings, unsigned int *flags) { errcode_t ret = 0; char *new_name = NULL; int was_set, i; /* start over every N bytes of dirent */ if (o2fsck_strings_bytes_allocated(strings) > (4 * 1024 * 1024)) o2fsck_strings_free(strings); ret = o2fsck_strings_insert(strings, dirent->name, dirent->name_len, &was_set); if (ret) { com_err(whoami, ret, "while allocating space to find " "duplicate directory entries"); goto out; } if (!was_set) goto out; new_name = calloc(1, dirent->rec_len + 1); if (new_name == NULL) { ret = OCFS2_ET_NO_MEMORY; com_err(whoami, ret, "while trying to generate a new name " "for duplicate file name '%.*s' in dir inode " "%"PRIu64, dirent->name_len, dirent->name, dbe->e_ino); goto out; } /* just simple mangling for now */ memcpy(new_name, dirent->name, dirent->name_len); was_set = 1; /* append '_' to free space in the dirent until its unique */ for (i = dirent->name_len ; was_set && i < dirent->rec_len; i++){ new_name[i] = '_'; if (!o2fsck_strings_exists(strings, new_name, strlen(new_name))) was_set = 0; } /* rename characters at the end to '_' until its unique */ for (i = dirent->name_len - 1 ; was_set && i >= 0; i--) { new_name[i] = '_'; if (!o2fsck_strings_exists(strings, new_name, strlen(new_name))) was_set = 0; } if (was_set) { printf("Directory inode %"PRIu64" contains a duplicate " "occurrence " "of the file name '%.*s' but fsck was " "unable to come up with a unique name so this duplicate " "name will not be dealt with.\n.", dbe->e_ino, dirent->name_len, dirent->name); goto out; } if (!prompt(ost, PY, PR_DIRENT_DUPLICATE, "Directory inode %"PRIu64" contains a duplicate occurrence " "of the file name '%.*s'. Replace this duplicate name " "with '%s'?", dbe->e_ino, dirent->name_len, dirent->name, new_name)) { /* we don't really care that we leak new_name's recording * in strings, it'll be freed later */ goto out; } ret = o2fsck_strings_insert(strings, new_name, strlen(new_name), NULL); if (ret) { com_err(whoami, ret, "while allocating space to track " "duplicates of a newly renamed dirent"); goto out; } dirent->name_len = strlen(new_name); memcpy(dirent->name, new_name, dirent->name_len); *flags |= OCFS2_DIRENT_CHANGED; out: if (new_name != NULL) free(new_name); return ret; } static errcode_t fix_dirent_index(o2fsck_dirblock_entry *dbe, struct dirblock_data *dd, struct ocfs2_dir_entry *dirent, unsigned int *flags) { errcode_t ret = 0; struct ocfs2_dinode *di = (struct ocfs2_dinode *)dd->inoblock_buf; uint64_t ino; if (!ocfs2_supports_indexed_dirs(OCFS2_RAW_SB(dd->fs->fs_super))) goto out; if (di->i_dyn_features & OCFS2_INDEXED_DIR_FL) { ret = ocfs2_lookup(dd->fs, dbe->e_ino, dirent->name, dirent->name_len, NULL, &ino); if (ret) { if ((ret == OCFS2_ET_DIR_CORRUPTED) && prompt(dd->ost, PY, PR_DX_LOOKUP_FAILED, "Directory inode %"PRIu64" has invalid index. " "Rebuild index tree?", dbe->e_ino)) { *flags |= OCFS2_DIRENT_CHANGED; ret = 0; goto out; } if (ret != OCFS2_ET_FILE_NOT_FOUND) goto out; ret = 0; if (prompt(dd->ost, PY, PR_DX_LOOKUP_FAILED, "Directory inode %"PRIu64" is missing " "an index entry for the file \"%.*s\"" " (inode # %"PRIu64")\n. Repair this by " "rebuilding the directory index?", dbe->e_ino, dirent->name_len, dirent->name, ino)) *flags |= OCFS2_DIRENT_CHANGED; goto out; } } out: return ret; } static int corrupt_dirent_lengths(struct ocfs2_dir_entry *dirent, int left) { if ((dirent->rec_len >= OCFS2_DIR_REC_LEN(1)) && ((dirent->rec_len & OCFS2_DIR_ROUND) == 0) && (dirent->rec_len <= left) && (OCFS2_DIR_REC_LEN(dirent->name_len) <= dirent->rec_len) && !dirent_leaves_partial(dirent, left)) return 0; verbosef("corrupt dirent: %"PRIu64" rec_len %u name_len %u\n", (uint64_t)dirent->inode, dirent->rec_len, dirent->name_len); return 1; } /* this could certainly be more clever to issue reads in groups */ static unsigned pass2_dir_block_iterate(o2fsck_dirblock_entry *dbe, void *priv_data) { struct dirblock_data *dd = priv_data; struct ocfs2_dir_entry *dirent, *prev = NULL; unsigned int offset = 0, ret_flags = 0, end = dd->fs->fs_blocksize; unsigned int write_off, saved_reclen; struct ocfs2_dinode *di = (struct ocfs2_dinode *)dd->inoblock_buf; errcode_t ret = 0; if (!o2fsck_test_inode_allocated(dd->ost, dbe->e_ino)) { printf("Directory block %"PRIu64" belongs to directory inode " "%"PRIu64" which isn't allocated. Ignoring this " "block.", dbe->e_blkno, dbe->e_ino); goto out; } if (dbe->e_ino != dd->last_ino) { o2fsck_strings_free(&dd->strings); dd->last_ino = dbe->e_ino; ret = ocfs2_read_inode(dd->ost->ost_fs, dbe->e_ino, dd->inoblock_buf); if (ret == OCFS2_ET_BAD_CRC32) { if (prompt(dd->ost, PY, PR_BAD_CRC32, "Directory inode %"PRIu64" " "has bad CRC32. Recalculate CRC32 " "and write inode block?", dbe->e_ino)) { ocfs2_write_inode(dd->ost->ost_fs, dbe->e_ino, dd->inoblock_buf); } } else if (ret) { com_err(whoami, ret, "while reading dir inode %"PRIu64, dbe->e_ino); ret_flags |= OCFS2_DIRENT_ABORT; goto out; } verbosef("dir inode %"PRIu64" i_size %"PRIu64"\n", dbe->e_ino, (uint64_t)di->i_size); /* Set the flag for index rebuilding */ if (ocfs2_supports_indexed_dirs(OCFS2_RAW_SB(dd->fs->fs_super)) && !(di->i_dyn_features & OCFS2_INLINE_DATA_FL) && !(di->i_dyn_features & OCFS2_INDEXED_DIR_FL) && prompt(dd->ost, PY, PR_DX_TREE_MISSING, "Directory %"PRIu64" is missing index. " "Rebuild?", dbe->e_ino)) ret_flags |= OCFS2_DIRENT_CHANGED; } verbosef("dir block %"PRIu64" block offs %"PRIu64" in ino\n", dbe->e_blkno, dbe->e_blkcount); if (di->i_dyn_features & OCFS2_INLINE_DATA_FL) { if (dbe->e_ino != dbe->e_blkno) goto out; memcpy(dd->dirblock_buf, dd->inoblock_buf, dd->fs->fs_blocksize); offset = offsetof(struct ocfs2_dinode, id2.i_data.id_data); } else { if (dbe->e_blkcount >= ocfs2_blocks_in_bytes(dd->fs, di->i_size)) goto out; ret = ocfs2_read_dir_block(dd->fs, di, dbe->e_blkno, dd->dirblock_buf); if (ret && ret != OCFS2_ET_DIR_CORRUPTED) { com_err(whoami, ret, "while reading dir block %"PRIu64, dbe->e_blkno); goto out; } if (ocfs2_dir_has_trailer(dd->fs, di)) end = ocfs2_dir_trailer_blk_off(dd->fs); } write_off = offset; while (offset < end) { dirent = (struct ocfs2_dir_entry *)(dd->dirblock_buf + offset); verbosef("checking dirent offset %d, rec_len %"PRIu16" " "name_len %"PRIu8" file_type %"PRIu8"\n", offset, dirent->rec_len, dirent->name_len, dirent->file_type); /* XXX I wonder if we should be checking that the padding * is 0 */ /* if we can't trust this dirent then fix it up or skip * the whole block */ if (corrupt_dirent_lengths(dirent, end - offset)) { if (!prompt(dd->ost, PY, PR_DIRENT_LENGTH, "Directory inode %"PRIu64" " "corrupted in logical block %"PRIu64" " "physical block %"PRIu64" offset %d. " "Attempt to repair this block's directory " "entries?", dbe->e_ino, dbe->e_blkcount, dbe->e_blkno, offset)) break; /* we edit the dirent in place so we try to parse * it again after fixing it */ fix_dirent_lengths(dirent, end - offset, prev, &ret_flags); continue; } /* * In general, these calls mark ->inode as 0 when they want it * to be seen as deleted; ignored by fsck and reclaimed by the * kernel. The dots are a special case, of course. This * pass makes sure that they are the first two entries in * the directory and pass3 fixes ".."'s ->inode. * * XXX should verify that ocfs2 reclaims entries like that. */ ret = fix_dirent_dots(dd->ost, dbe, dirent, offset, end - offset, &ret_flags); if (ret) goto out; if (dirent->inode == 0) goto next; fix_dirent_name(dd->ost, dbe, dirent, offset, &ret_flags); if (dirent->inode == 0) goto next; fix_dirent_inode(dd->ost, dbe, dirent, offset, &ret_flags); if (dirent->inode == 0) goto next; ret = fix_dirent_filetype(dd->ost, dbe, dirent, offset, &ret_flags); if (ret) goto out; if (dirent->inode == 0) goto next; ret = fix_dirent_linkage(dd->ost, dbe, dirent, offset, &ret_flags); if (ret) goto out; if (dirent->inode == 0) goto next; ret = fix_dirent_dups(dd->ost, dbe, dirent, &dd->strings, &ret_flags); if (ret) goto out; if (dirent->inode == 0) goto next; ret = fix_dirent_index(dbe, dd, dirent, &ret_flags); if (ret) goto out; verbosef("dirent %.*s refs ino %"PRIu64"\n", dirent->name_len, dirent->name, (uint64_t)dirent->inode); o2fsck_icount_delta(dd->ost->ost_icount_refs, dirent->inode, 1); next: saved_reclen = dirent->rec_len; if (dd->ost->ost_compress_dirs) { if (prev && prev->inode) { /*Bring previous rec_len to required space */ prev->rec_len = OCFS2_DIR_REC_LEN(prev->name_len); write_off += prev->rec_len; } if (write_off < offset) { verbosef("ino: %llu woff: %u off: %u\n", dirent->inode, write_off, offset); memmove(dd->dirblock_buf + write_off, dd->dirblock_buf + offset, OCFS2_DIR_REC_LEN(dirent->name_len)); dirent = (struct ocfs2_dir_entry *)(dd->dirblock_buf + write_off); /* Cover space from our new location to * the next dirent */ dirent->rec_len = saved_reclen + offset - write_off; ret_flags |= OCFS2_DIRENT_CHANGED; } } prev = dirent; offset += saved_reclen; } if (ocfs2_dir_has_trailer(dd->fs, di)) fix_dir_trailer(dd->ost, dbe, ocfs2_dir_trailer_from_block(dd->fs, dd->dirblock_buf), &ret_flags); if (ret_flags & OCFS2_DIRENT_CHANGED) { if (di->i_dyn_features & OCFS2_INLINE_DATA_FL) { memcpy(dd->inoblock_buf, dd->dirblock_buf, dd->fs->fs_blocksize); ret = ocfs2_write_inode(dd->fs, dbe->e_ino, dd->dirblock_buf); } else ret = ocfs2_write_dir_block(dd->fs, di, dbe->e_blkno, dd->dirblock_buf); if (ret) { com_err(whoami, ret, "while writing dir block %"PRIu64, dbe->e_blkno); dd->ost->ost_write_error = 1; goto out; } if (ocfs2_supports_indexed_dirs(OCFS2_RAW_SB(dd->fs->fs_super)) && !(di->i_dyn_features & OCFS2_INLINE_DATA_FL)) { di->i_dyn_features |= OCFS2_INDEXED_DIR_FL; ret = o2fsck_try_add_reidx_dir(&dd->re_idx_dirs, dbe->e_ino); if (ret) { com_err(whoami, ret, "while adding block for " "directory inode %"PRIu64" to rebuild " "dir index", dbe->e_ino); goto out; } } } /* truncate invalid indexed tree */ if ((!ocfs2_supports_indexed_dirs(OCFS2_RAW_SB(dd->fs->fs_super)))&& di->i_dyn_features & OCFS2_INDEXED_DIR_FL ) { /* ignore the return value */ if (prompt(dd->ost, PY, PR_IV_DX_TREE, "A directory index was " "found on inode %"PRIu64" but this filesystem does" "not support directory indexes. Truncate the invalid index?", dbe->e_ino)) ocfs2_dx_dir_truncate(dd->fs, dbe->e_ino); } out: if (ret) dd->ret = ret; return ret_flags; } static void release_re_idx_dirs_rbtree(struct rb_root * root) { struct rb_node *node; o2fsck_dirblock_entry *dp; while ((node = rb_first(root)) != NULL) { dp = rb_entry(node, o2fsck_dirblock_entry, e_node); rb_erase(&dp->e_node, root); ocfs2_free(&dp); } } errcode_t o2fsck_pass2(o2fsck_state *ost) { o2fsck_dir_parent *dp; errcode_t ret; struct dirblock_data dd = { .ost = ost, .fs = ost->ost_fs, .last_ino = 0, .re_idx_dirs = RB_ROOT, }; ocfs2_filesys *fs = ost->ost_fs; struct o2fsck_resource_track rt; printf("Pass 2: Checking directory entries\n"); o2fsck_init_resource_track(&rt, fs->fs_io); if (tools_progress_enabled() && ost->ost_dirblocks.db_numblocks) { ost->ost_prog = tools_progress_start("Scanning directories", "dirs", ost->ost_dirblocks.db_numblocks); if (ost->ost_prog) setbuf(stdout, NULL); } o2fsck_strings_init(&dd.strings); ret = ocfs2_malloc_block(ost->ost_fs->fs_io, &dd.dirblock_buf); if (ret) { com_err(whoami, ret, "while allocating a block buffer to " "store directory blocks."); goto out; } ret = ocfs2_malloc_block(ost->ost_fs->fs_io, &dd.inoblock_buf); if (ret) { com_err(whoami, ret, "while allocating a block buffer to " "store a directory inode."); goto out; } /* * Mark the root directory's dirent parent as itself if we found the * inode during inode scanning. The dir will be created in pass3 * if it didn't exist already. XXX we should do this for all our other * magical directories. */ dp = o2fsck_dir_parent_lookup(&ost->ost_dir_parents, ost->ost_fs->fs_root_blkno); if (dp) dp->dp_dirent = ost->ost_fs->fs_root_blkno; dp = o2fsck_dir_parent_lookup(&ost->ost_dir_parents, ost->ost_fs->fs_sysdir_blkno); if (dp) dp->dp_dirent = ost->ost_fs->fs_sysdir_blkno; o2fsck_dir_block_iterate(ost, pass2_dir_block_iterate, &dd); if (dd.re_idx_dirs.rb_node) { ret = o2fsck_rebuild_indexed_dirs(ost->ost_fs, &dd.re_idx_dirs); if (ret) com_err(whoami, ret, "while rebuild indexed dirs."); } release_re_idx_dirs_rbtree(&dd.re_idx_dirs); o2fsck_strings_free(&dd.strings); o2fsck_compute_resource_track(&rt, fs->fs_io); o2fsck_print_resource_track("Pass 2", ost, &rt, fs->fs_io); o2fsck_add_resource_track(&ost->ost_rt, &rt); out: if (ost->ost_prog) { tools_progress_stop(ost->ost_prog); setlinebuf(stdout); } tools_progress_disable(); if (dd.dirblock_buf) ocfs2_free(&dd.dirblock_buf); if (dd.inoblock_buf) ocfs2_free(&dd.inoblock_buf); return ret; } ocfs2-tools-ocfs2-tools-1.8.6/fsck.ocfs2/pass3.c000066400000000000000000000305561347147137200212300ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * Copyright (C) 1993-2004 by Theodore Ts'o. * Copyright (C) 2004 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License, version 2, as published by the Free Software Foundation. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * * -- * Pass 3 makes sure that all directories are connected to the file system * tree and that their are no cycles in the tree. It starts by marking * the root and system directories in the filesystem as connected. It then * iterates through the directories found in pass 1. For each directory * it ascends to the root of the file system via the chain of parent dir * entries as built up by pass 2. If a directory is found which doesn't have * a parent it is connected to lost+found. connect_directory() is careful * to stop before following a parent that it has already seen. This lets it * connect to lost+found instead and break cycles. */ #include #include #include #include "ocfs2/ocfs2.h" #include "dirparents.h" #include "fsck.h" #include "pass2.h" #include "pass3.h" #include "problem.h" #include "util.h" static const char *whoami = "pass3"; static void check_root(o2fsck_state *ost) { struct ocfs2_super_block *sb = OCFS2_RAW_SB(ost->ost_fs->fs_super); errcode_t ret; uint64_t blkno, old_root; int was_set; if (o2fsck_test_inode_allocated(ost, ost->ost_fs->fs_root_blkno)) { ocfs2_bitmap_test(ost->ost_dir_inodes, ost->ost_fs->fs_root_blkno, &was_set); if (!was_set) printf("The root inode exists but isn't a " "directory.\n"); return; } if (!prompt(ost, PY, PR_ROOT_DIR_MISSING, "The super block claims that inode %"PRIu64" is the root " "directory but it isn't allocated. Create a new root " "directory and update the super block?", ost->ost_fs->fs_root_blkno)) return; ret = ocfs2_new_inode(ost->ost_fs, &blkno, 0755 | S_IFDIR); if (ret) { com_err(whoami, ret, "while trying to allocate a new inode " "for the root directory\n"); return; } ret = ocfs2_init_dir(ost->ost_fs, blkno, blkno); if (ret) { com_err(whoami, ret, "while trying to expand a new root " "directory"); goto out; } o2fsck_icount_set(ost->ost_icount_in_inodes, blkno, 1); o2fsck_icount_set(ost->ost_icount_refs, blkno, 1); ret = o2fsck_add_dir_parent(&ost->ost_dir_parents, blkno, ost->ost_fs->fs_root_blkno, ost->ost_fs->fs_root_blkno, 0); if (ret) { com_err(whoami, ret, "while recording a new root directory"); goto out; } old_root = sb->s_root_blkno; ost->ost_fs->fs_root_blkno = blkno; sb->s_root_blkno = blkno; ret = ocfs2_write_primary_super(ost->ost_fs); if (ret) { com_err(whoami, ret, "while writing the super block with a " "new root directory inode"); ost->ost_fs->fs_root_blkno = old_root; sb->s_root_blkno = old_root; goto out; } blkno = 0; out: if (blkno) { ret = ocfs2_delete_inode(ost->ost_fs, blkno); if (ret) { com_err(whoami, ret, "while trying to clean up an " "an allocated inode after linking /lost+found " "failed"); } } } static void check_lostfound(o2fsck_state *ost) { char name[] = "lost+found"; int namelen = sizeof(name) - 1; uint64_t blkno; errcode_t ret; ret = ocfs2_lookup(ost->ost_fs, ost->ost_fs->fs_root_blkno, name, namelen, NULL, &ost->ost_lostfound_ino); if (ret == 0) return; if (!prompt(ost, PY, PR_LOSTFOUND_MISSING, "/lost+found does not exist. Create it so " "that we can possibly fill it with orphaned inodes?")) return; ret = ocfs2_new_inode(ost->ost_fs, &blkno, 0755 | S_IFDIR); if (ret) { com_err(whoami, ret, "while trying to allocate a new inode " "for /lost+found"); return; } ret = ocfs2_init_dir(ost->ost_fs, blkno, ost->ost_fs->fs_root_blkno); if (ret) { com_err(whoami, ret, "while trying to expand a new " "/lost+found directory"); goto out; } ret = ocfs2_link(ost->ost_fs, ost->ost_fs->fs_root_blkno, name, blkno, OCFS2_FT_DIR); if (ret) { com_err(whoami, ret, "while linking inode %"PRIu64" as " "/lost+found", blkno); goto out; } /* XXX maybe this should be a helper to clean up the dir tracking * for any new dir. "2" for both the l+f dirent pointing to the * inode and the "." dirent in its dirblock */ o2fsck_icount_set(ost->ost_icount_in_inodes, blkno, 2); o2fsck_icount_set(ost->ost_icount_refs, blkno, 2); ret = o2fsck_add_dir_parent(&ost->ost_dir_parents, blkno, ost->ost_fs->fs_root_blkno, ost->ost_fs->fs_root_blkno, 0); if (ret) { com_err(whoami, ret, "while recording a new /lost+found " "directory"); goto out; } /* we've already iterated through the dirblocks in pass2 so there * is no need to register l+f's new dir block */ ost->ost_lostfound_ino = blkno; blkno = 0; out: if (blkno) { ret = ocfs2_delete_inode(ost->ost_fs, blkno); if (ret) { com_err(whoami, ret, "while trying to clean up an " "an allocated inode after linking /lost+found " "failed"); } } } struct fix_dot_dot_args { o2fsck_state *ost; uint64_t parent; int fixed; }; static int fix_dot_dot_dirent(struct ocfs2_dir_entry *dirent, uint64_t blocknr, int offset, int blocksize, char *buf, void *priv_data) { struct fix_dot_dot_args *args = priv_data; if (dirent->name_len != 2 || strncmp(dirent->name, "..", 2)) return 0; verbosef("fixing '..' entry to point to %"PRIu64"\n", args->parent); if (dirent->inode != 0) o2fsck_icount_delta(args->ost->ost_icount_refs, dirent->inode, -1); o2fsck_icount_delta(args->ost->ost_icount_refs, args->parent, 1); dirent->inode = args->parent; args->fixed = 1; return OCFS2_DIRENT_ABORT | OCFS2_DIRENT_CHANGED; } static void fix_dot_dot(o2fsck_state *ost, o2fsck_dir_parent *dir) { errcode_t ret; struct fix_dot_dot_args args = { .ost = ost, .parent = dir->dp_dirent, .fixed = 0, }; ret = ocfs2_dir_iterate(ost->ost_fs, dir->dp_ino, OCFS2_DIRENT_FLAG_INCLUDE_EMPTY, NULL, fix_dot_dot_dirent, &args); if (ret) { com_err("fix_dot_dot", ret, "while iterating through dir " "inode %"PRIu64"'s directory entries.", dir->dp_dirent); /* XXX mark fs invalid */ return; } if (!args.fixed) { fprintf(stderr, "Didn't find a '..' entry to fix.\n"); /* XXX mark fs invalid */ return; } dir->dp_dot_dot = dir->dp_dirent; } /* add a directory entry that points to a given inode in lost+found. */ void o2fsck_reconnect_file(o2fsck_state *ost, uint64_t inode) { static char iname[NAME_MAX + 1]; char name[] = "lost+found"; int namelen = sizeof(name) - 1; o2fsck_dir_parent *dp; errcode_t ret; uint8_t type; int len; if (ost->ost_lostfound_ino == 0) { ret = ocfs2_lookup(ost->ost_fs, ost->ost_fs->fs_root_blkno, name, namelen, NULL, &ost->ost_lostfound_ino); if (ret) { com_err(whoami, ret, "while trying to find the " "/lost+found directory so that inode " "%"PRIu64" could be moved there.", inode); goto out; } } len = snprintf(iname, sizeof(iname), "#%"PRIu64, inode); if (len <= 0) { ret = OCFS2_ET_NO_MEMORY; com_err(whoami, ret, "while trying to build a new file name " "for inode %"PRIu64" to use in /lost+found", inode); goto out; } ret = o2fsck_type_from_dinode(ost, inode, &type); if (ret) goto out; ret = ocfs2_link(ost->ost_fs, ost->ost_lostfound_ino, iname, inode, type); if (ret) { com_err(whoami, ret, "while trying to link inode %"PRIu64" " "into /lost+found", inode); goto out; } /* add another ref to account for this new dirent */ o2fsck_icount_delta(ost->ost_icount_refs, inode, 1); /* if we just added a directory to l+f we need to track that * the new dirent points to the dir. we leave the dot_dot tracking * intact because we didn't change that in the dirblock.. */ if (type == OCFS2_FT_DIR) { dp = o2fsck_dir_parent_lookup(&ost->ost_dir_parents, inode); if (dp == NULL) { ret = OCFS2_ET_INTERNAL_FAILURE; com_err(whoami, ret, "while looking up the directory " "parent structure for inode %"PRIu64, inode); goto out; } dp->dp_dirent = ost->ost_lostfound_ino; } out: return; } static uint64_t loop_no = 0; static errcode_t connect_directory(o2fsck_state *ost, o2fsck_dir_parent *dir) { o2fsck_dir_parent *dp = dir, *par; errcode_t ret = 0; int fix; verbosef("checking dir inode %"PRIu64" parent %"PRIu64" dot_dot " "%"PRIu64"\n", dir->dp_ino, dp->dp_dirent, dp->dp_dot_dot); loop_no++; while(!dp->dp_connected) { /* we either will ascend to a parent that is connected or * we'll graft the subtree with this directory on to lost * and found. */ dp->dp_connected = 1; /* move on to the parent dir only if it exists and we haven't * already traversed it in this instance of parent walking */ if (dp->dp_dirent) { par = o2fsck_dir_parent_lookup(&ost->ost_dir_parents, dp->dp_dirent); if (par == NULL) { ret = OCFS2_ET_INTERNAL_FAILURE; com_err(whoami, ret, "no dir info for parent " "%"PRIu64, dp->dp_dirent); goto out; } if (par->dp_loop_no != loop_no) { par->dp_loop_no = loop_no; dp = par; continue; } } /* ok, we hit an orphan subtree with no parent or are at * the dir in a subtree that is the first to try to reference * a dir in its children */ fix = prompt(ost, PY, PR_DIR_NOT_CONNECTED, "Directory inode %"PRIu64" isn't " "connected to the filesystem. Move it to " "lost+found?", dp->dp_ino); if (fix) o2fsck_reconnect_file(ost, dp->dp_ino); break; } /* * orphan dirs are a magically awesome special case. they have * their i_link_count increased when subdirs are added but * the subdirs '..' entry isn't updated to point to the orphan * dir. we alter our book-keeping to it look like the '..' * was reasonable on disk. */ if (dir->dp_in_orphan_dir) { /* previous '..' entry is garbage */ if (dir->dp_dot_dot) o2fsck_icount_delta(ost->ost_icount_refs, dir->dp_dot_dot, -1); /* pretend '..' pointed to the orphan dir */ dir->dp_dot_dot = dir->dp_dirent; o2fsck_icount_delta(ost->ost_icount_refs, dir->dp_dot_dot, 1); } if (dir->dp_dirent != dir->dp_dot_dot) { fix = prompt(ost, PY, PR_DIR_DOTDOT, "Directory inode %"PRIu64" is " "referenced by a dirent in directory %"PRIu64" " "but its '..' entry points to inode %"PRIu64". " "Fix the '..' entry to reference %"PRIu64"?", dir->dp_ino, dir->dp_dirent, dir->dp_dot_dot, dir->dp_dirent); if (fix) fix_dot_dot(ost, dir); } out: return ret; } errcode_t o2fsck_pass3(o2fsck_state *ost) { o2fsck_dir_parent *dp; errcode_t ret = 0; ocfs2_filesys *fs = ost->ost_fs; struct o2fsck_resource_track rt; printf("Pass 3: Checking directory connectivity\n"); o2fsck_init_resource_track(&rt, fs->fs_io); /* these could probably share more code. We might need to treat the * other required directories like root here */ check_root(ost); check_lostfound(ost); dp = o2fsck_dir_parent_lookup(&ost->ost_dir_parents, ost->ost_fs->fs_root_blkno); if (dp == NULL) { ret = OCFS2_ET_INTERNAL_FAILURE; com_err(whoami, ret, "root inode %"PRIu64" wasn't marked as " "a directory in pass1", ost->ost_fs->fs_root_blkno); goto out; } dp->dp_connected = 1; dp = o2fsck_dir_parent_lookup(&ost->ost_dir_parents, ost->ost_fs->fs_sysdir_blkno); if (dp == NULL) { ret = OCFS2_ET_INTERNAL_FAILURE; com_err(whoami, ret, "system dir inode %"PRIu64" wasn't " "marked as a directory in pass1", ost->ost_fs->fs_sysdir_blkno); goto out; } dp->dp_connected = 1; for(dp = o2fsck_dir_parent_first(&ost->ost_dir_parents) ; dp; dp = o2fsck_dir_parent_next(dp)) { /* XXX hmm, make sure dir->ino is in the dir map? */ ret = connect_directory(ost, dp); if (ret) goto out; } o2fsck_compute_resource_track(&rt, fs->fs_io); o2fsck_print_resource_track("Pass 3", ost, &rt, fs->fs_io); o2fsck_add_resource_track(&ost->ost_rt, &rt); out: return ret; } ocfs2-tools-ocfs2-tools-1.8.6/fsck.ocfs2/pass4.c000066400000000000000000000240251347147137200212230ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * Copyright (C) 1993-2004 by Theodore Ts'o. * Copyright (C) 2004 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License, version 2, as published by the Free Software Foundation. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * * -- * * Pass 4 walks all the active inodes and makes sure that they are reachable * via directory entries, just like pass 3 did for directories. It also * makes sure each inode's link_count reflects the number of entries that * refer to it. Inodes that aren't referred to by any entries are moved * to lost+found. */ #include #include #include "ocfs2/ocfs2.h" #include "fsck.h" #include "icount.h" #include "pass3.h" #include "pass4.h" #include "problem.h" #include "util.h" struct orphan_dir_ctxt { o2fsck_state *ost; uint64_t orphan_dir; }; static const char *whoami = "pass4"; static void check_link_counts(o2fsck_state *ost, struct ocfs2_dinode *di, uint64_t blkno) { uint16_t refs, in_inode; errcode_t ret; refs = o2fsck_icount_get(ost->ost_icount_refs, blkno); in_inode = o2fsck_icount_get(ost->ost_icount_in_inodes, blkno); verbosef("ino %"PRIu64", refs %u in %u\n", blkno, refs, in_inode); /* XXX offer to remove files/dirs with no data? */ if (refs == 0 && prompt(ost, PY, PR_INODE_NOT_CONNECTED, "Inode %"PRIu64" isn't referenced by any " "directory entries. Move it to lost+found?", blkno)) { o2fsck_reconnect_file(ost, blkno); refs = o2fsck_icount_get(ost->ost_icount_refs, blkno); } if (refs == in_inode) goto out; ret = ocfs2_read_inode(ost->ost_fs, blkno, (char *)di); if (ret) { com_err(whoami, ret, "reading inode %"PRIu64" to update its " "i_links_count. Could this be because a directory " "entry referenced an invalid inode but wasn't fixed?", blkno); goto out; } if (in_inode != di->i_links_count) com_err(whoami, OCFS2_ET_INTERNAL_FAILURE, "fsck's thinks " "inode %"PRIu64" has a link count of %"PRIu16" but on " "disk it is %"PRIu16, (uint64_t)di->i_blkno, in_inode, di->i_links_count); if (prompt(ost, PY, PR_INODE_COUNT, "Inode %"PRIu64" has a link count of %"PRIu16" on " "disk but directory entry references come to %"PRIu16". " "Update the count on disk to match?", (uint64_t)di->i_blkno, in_inode, refs)) { di->i_links_count = refs; o2fsck_icount_set(ost->ost_icount_in_inodes, di->i_blkno, refs); o2fsck_write_inode(ost, di->i_blkno, di); } out: return; } #define OCFS2_DIO_ORPHAN_PREFIX "dio-" #define OCFS2_DIO_ORPHAN_PREFIX_LEN 4 static int replay_orphan_iterate(struct ocfs2_dir_entry *dirent, uint64_t blocknr, int offset, int blocksize, char *buf, void *priv_data) { struct orphan_dir_ctxt *ctxt = priv_data; o2fsck_state *ost = ctxt->ost; int ret_flags = 0; errcode_t ret = 0; if (!(ost->ost_fs->fs_flags & OCFS2_FLAG_RW)) { printf("** Skipping orphan dir replay because -n was " "given.\n"); ret_flags |= OCFS2_DIRENT_ABORT; goto out; } ost->ost_orphan_count++; /* Only ask for confirmation in force check. */ if (ost->ost_force) { if (!prompt(ost, PY, PR_INODE_ORPHANED, "Inode %"PRIu64" was found in the orphan directory. " "Delete its contents and unlink it?", (uint64_t)dirent->inode)) goto out; } ret = ocfs2_truncate(ost->ost_fs, dirent->inode, 0); if (ret) { com_err(whoami, ret, "while truncating orphan inode %"PRIu64, (uint64_t)dirent->inode); ret_flags |= OCFS2_DIRENT_ABORT; goto out; } /* do not delete inode in case of dio orphan entry */ if (!strncmp(dirent->name, OCFS2_DIO_ORPHAN_PREFIX, OCFS2_DIO_ORPHAN_PREFIX_LEN)) goto out_check; ret = ocfs2_delete_inode(ost->ost_fs, dirent->inode); if (ret) { com_err(whoami, ret, "while deleting orphan inode %"PRIu64 "after truncating it", (uint64_t)dirent->inode); ret_flags |= OCFS2_DIRENT_ABORT; goto out; } ost->ost_orphan_deleted_count++; out_check: /* Only calculate icount in force check. */ if (ost->ost_force) { /* * this matches a special case in o2fsck_verify_inode_fields() * where orphan dir members are recorded as having 1 link count, * even though they have 0 on disk */ o2fsck_icount_delta(ost->ost_icount_in_inodes, dirent->inode, -1); /* * dirs have this dirent ref and their '.' dirent and we also * need to handle '..' dirent for their parents. */ if (dirent->file_type == OCFS2_FT_DIR) { o2fsck_icount_delta(ost->ost_icount_refs, dirent->inode, -2); o2fsck_icount_delta(ost->ost_icount_refs, ctxt->orphan_dir, -1); } else o2fsck_icount_delta(ost->ost_icount_refs, dirent->inode, -1); } dirent->inode = 0; ret_flags |= OCFS2_DIRENT_CHANGED; out: ost->ost_err = ret; return ret_flags; } static errcode_t create_orphan_dir(o2fsck_state *ost, char *fname) { errcode_t ret; uint64_t blkno; ocfs2_filesys *fs = ost->ost_fs; /* create inode for system file */ ret = ocfs2_new_system_inode(fs, &blkno, ocfs2_system_inodes[ORPHAN_DIR_SYSTEM_INODE].si_mode, ocfs2_system_inodes[ORPHAN_DIR_SYSTEM_INODE].si_iflags); if (ret) goto bail; ret = ocfs2_init_dir(fs, blkno, fs->fs_sysdir_blkno); if (ret) goto bail; /* Add the inode to the system dir */ ret = ocfs2_link(fs, fs->fs_sysdir_blkno, fname, blkno, OCFS2_FT_DIR); if (ret) goto bail; /* we have created an orphan dir under system dir and updated the disk, * so we have to update the refs in ost accordingly. */ o2fsck_icount_delta(ost->ost_icount_refs, fs->fs_sysdir_blkno, 1); o2fsck_icount_delta(ost->ost_icount_in_inodes, fs->fs_sysdir_blkno, 1); bail: return ret; } /* * replay_orphan_dir could happen in 2 places and we handle it diffrently. * 1. In slot recovery, we will return any error which lead to a force check. * 2. in o2fsck_pass4, all other errors should be fixed in pass0,1,2 and 3, so * we try to fix some errors by ourselves. */ errcode_t replay_orphan_dir(o2fsck_state *ost, int slot_recovery) { errcode_t ret = OCFS2_ET_CORRUPT_SUPERBLOCK; char name[PATH_MAX]; uint64_t ino; int bytes; int i; int num_slots = OCFS2_RAW_SB(ost->ost_fs->fs_super)->s_max_slots; struct orphan_dir_ctxt ctxt; ctxt.ost = ost; for (i = 0; i < num_slots; ++i) { bytes = ocfs2_sprintf_system_inode_name(name, PATH_MAX, ORPHAN_DIR_SYSTEM_INODE, i); if (bytes < 1) { ret = OCFS2_ET_INTERNAL_FAILURE; goto out; } ret = ocfs2_lookup(ost->ost_fs, ost->ost_fs->fs_sysdir_blkno, name, bytes, NULL, &ino); if (ret) { if (slot_recovery) goto out; if (ret != OCFS2_ET_FILE_NOT_FOUND) goto out; /* orphan dir is missing, it may be caused by an * unsuccessful removing slots in tunefs.ocfs2. * so create it. */ if (prompt(ost, PY, PR_ORPHAN_DIR_MISSING, "%s is missing in system directory. " "Create it?", name)) { ret = create_orphan_dir(ost, name); if (ret) { com_err(whoami, ret, "while creating" "orphan directory %s", name); continue; } } } ctxt.orphan_dir = ino; ost->ost_err = 0; ret = ocfs2_dir_iterate(ost->ost_fs, ino, OCFS2_DIRENT_FLAG_EXCLUDE_DOTS, NULL, replay_orphan_iterate, &ctxt); if (!ret) ret = ost->ost_err; if (ret && slot_recovery) break; } out: return ret; } /* return the next inode that has either directory entries pointing to it or * that was valid and had a non-zero i_links_count. OCFS2_ET_BIT_NOT_FOUND * will be bubbled up from the next_blkno() calls when there is no such next * inode. It is expected that sometimes these won't match. If a directory * has been lost there can be inodes with i_links_count and no directory * entries at all. If an inode was lost but the user chose not to erase * the directory entries then there may be references to inodes that * we never saw the i_links_count for */ static errcode_t next_inode_any_ref(o2fsck_state *ost, uint64_t start, uint64_t *blkno_ret) { errcode_t tmp, ret = OCFS2_ET_BIT_NOT_FOUND; uint64_t blkno; tmp = o2fsck_icount_next_blkno(ost->ost_icount_refs, start, &blkno); if (tmp == 0) { *blkno_ret = blkno; ret = 0; } tmp = o2fsck_icount_next_blkno(ost->ost_icount_in_inodes, start, &blkno); /* use this if we didn't have one yet or this one's lesser */ if (tmp == 0 && (ret != 0 || (blkno < *blkno_ret))) { ret = 0; *blkno_ret = blkno; } return ret; } errcode_t o2fsck_pass4(o2fsck_state *ost) { struct ocfs2_dinode *di; char *buf = NULL; errcode_t ret; uint64_t blkno = 0, start; ocfs2_filesys *fs = ost->ost_fs; struct o2fsck_resource_track rt; printf("Pass 4a: Checking for orphaned inodes\n"); o2fsck_init_resource_track(&rt, fs->fs_io); ret = replay_orphan_dir(ost, 0); if (ret) { com_err(whoami, ret, "while trying to replay the orphan " "directory"); goto out; } o2fsck_compute_resource_track(&rt, fs->fs_io); o2fsck_print_resource_track("Pass 4a", ost, &rt, fs->fs_io); o2fsck_add_resource_track(&ost->ost_rt, &rt); printf("Pass 4b: Checking inodes link counts\n"); o2fsck_init_resource_track(&rt, fs->fs_io); ret = ocfs2_malloc_block(ost->ost_fs->fs_io, &buf); if (ret) { com_err(whoami, ret, "while allocating space to read inodes"); goto out; } di = (struct ocfs2_dinode *)buf; start = 0; while (next_inode_any_ref(ost, start, &blkno) == 0) { check_link_counts(ost, di, blkno); start = blkno + 1; } o2fsck_compute_resource_track(&rt, fs->fs_io); o2fsck_print_resource_track("Pass 4b", ost, &rt, fs->fs_io); o2fsck_add_resource_track(&ost->ost_rt, &rt); out: if (buf) ocfs2_free(&buf); return ret; } ocfs2-tools-ocfs2-tools-1.8.6/fsck.ocfs2/pass5.c000066400000000000000000000367101347147137200212300ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * Copyright (C) 2009 Novell. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License, version 2, as published by the Free Software Foundation. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * * -- * Pass 5 tries to read as much data as possible from the global quota file. * (we are interested mainly in limits for users and groups). After that we * scan the filesystem and recompute quota usage for each user / group and * finally we dump all the information into freshly created quota files. * * At this pass, filesystem should be already sound, so we use libocfs2 * functions for low-level operations. * * FIXME: We could also check node-local quota files and use limits there. * For now we just discard them. */ #include #include #include #include "ocfs2/ocfs2.h" #include "ocfs2/bitops.h" #include "ocfs2/byteorder.h" #include "fsck.h" #include "pass5.h" #include "problem.h" #include "o2fsck_strings.h" #include "util.h" static const char *whoami = "pass5"; static char *qbmp[MAXQUOTAS]; static ocfs2_quota_hash *qhash[MAXQUOTAS]; static char *type2name(int type) { if (type == USRQUOTA) return "user"; return "group"; } static errcode_t o2fsck_release_dquot(ocfs2_cached_dquot *dquot, void *p) { ocfs2_quota_hash *hash = p; ocfs2_remove_quota_hash(hash, dquot); ocfs2_free(&dquot); return 0; } static int check_blkref(uint32_t block, uint32_t maxblocks) { if (block < QT_TREEOFF || block >= maxblocks) return 0; return 1; } static errcode_t o2fsck_validate_blk(ocfs2_filesys *fs, char *buf) { struct ocfs2_disk_dqtrailer *dqt = ocfs2_block_dqtrailer(fs->fs_blocksize, buf); return ocfs2_validate_meta_ecc(fs, buf, &dqt->dq_check); } static int o2fsck_valid_quota_info(ocfs2_filesys *fs, int type, struct ocfs2_disk_dqheader *header, struct ocfs2_global_disk_dqinfo *info) { uint32_t magics[MAXQUOTAS] = OCFS2_GLOBAL_QMAGICS; int versions[MAXQUOTAS] = OCFS2_GLOBAL_QVERSIONS; if (header->dqh_magic != magics[type] || header->dqh_version > versions[type]) return 0; if (info->dqi_blocks != fs->qinfo[type].qi_inode->ci_inode->i_size / fs->fs_blocksize) return 0; if ((info->dqi_free_blk && !check_blkref(info->dqi_free_blk, info->dqi_blocks)) || (info->dqi_free_entry && !check_blkref(info->dqi_free_entry, info->dqi_blocks))) return 0; return 1; } static errcode_t o2fsck_read_blk(ocfs2_filesys *fs, int type, char *buf, uint32_t blk) { uint32_t got; errcode_t ret; ret = ocfs2_file_read(fs->qinfo[type].qi_inode, buf, fs->fs_blocksize, blk * fs->fs_blocksize, &got); if (ret) return ret; if (got != fs->fs_blocksize) return OCFS2_ET_SHORT_READ; return 0; } static errcode_t o2fsck_check_info(o2fsck_state *ost, int type) { errcode_t ret; ocfs2_filesys *fs = ost->ost_fs; char *buf; struct ocfs2_disk_dqheader *header; struct ocfs2_global_disk_dqinfo *info; uint64_t blocks; int checksum_valid; ret = ocfs2_malloc_block(fs->fs_io, &buf); if (ret) { com_err(whoami, ret, "while allocating block buffer"); goto set_default; } ret = o2fsck_read_blk(fs, type, buf, 0); if (ret) { com_err(whoami, ret, "while reading global %s quota info " "block", type2name(type)); goto set_default; } checksum_valid = !o2fsck_validate_blk(fs, buf); header = (struct ocfs2_disk_dqheader *)buf; info = (struct ocfs2_global_disk_dqinfo *)(buf + OCFS2_GLOBAL_INFO_OFF); ocfs2_swap_quota_header(header); ocfs2_swap_quota_global_info(info); if ((!checksum_valid || !o2fsck_valid_quota_info(fs, type, header, info)) && !prompt(ost, PN, PR_QMAGIC_INVALID, "%s quota info looks corrupt." " Use its content:\nBlock grace time: %"PRIu32" sec\n" "Inode grace time: %"PRIu32" sec\n" "Cluster quota sync time: %"PRIu32" ms\n", type2name(type), info->dqi_bgrace, info->dqi_igrace, info->dqi_syncms)) { goto set_default; } fs->qinfo[type].qi_info.dqi_bgrace = info->dqi_bgrace; fs->qinfo[type].qi_info.dqi_igrace = info->dqi_igrace; fs->qinfo[type].qi_info.dqi_syncms = info->dqi_syncms; goto set_blocks; set_default: fs->qinfo[type].qi_info.dqi_bgrace = OCFS2_DEF_BLOCK_GRACE; fs->qinfo[type].qi_info.dqi_igrace = OCFS2_DEF_INODE_GRACE; fs->qinfo[type].qi_info.dqi_syncms = OCFS2_DEF_QUOTA_SYNC; set_blocks: blocks = fs->qinfo[type].qi_inode->ci_inode->i_size / fs->fs_blocksize; if (blocks > (1ULL << 32) - 1) fs->qinfo[type].qi_info.dqi_blocks = (1ULL << 32) - 1; else fs->qinfo[type].qi_info.dqi_blocks = blocks; return ret; } /* Check whether a reference to a tree block is sane */ static int o2fsck_check_tree_ref(o2fsck_state *ost, int type, uint32_t blk, int depth) { ocfs2_filesys *fs = ost->ost_fs; uint32_t blocks = fs->qinfo[type].qi_info.dqi_blocks; /* Bogus block number? */ if (!check_blkref(blk, blocks)) { verbosef("ignoring invalid %s quota block reference %"PRIu32, type2name(type), blk); return 0; } /* Already scanned block? */ if (depth < ocfs2_qtree_depth(fs->fs_blocksize) && ocfs2_test_bit(blk, qbmp[type])) { verbosef("ignoring duplicate %s quota block reference %"PRIu32, type2name(type), blk); return 0; } return 1; } /* Read the block, check dquot structures in it */ static errcode_t o2fsck_check_data_blk(o2fsck_state *ost, int type, uint32_t blk, char *buf) { ocfs2_filesys *fs = ost->ost_fs; errcode_t ret; struct qt_disk_dqdbheader *dh = (struct qt_disk_dqdbheader *)buf; int str_in_blk = ocfs2_global_dqstr_in_blk(fs->fs_blocksize); int i; struct ocfs2_global_disk_dqblk *ddquot; ocfs2_cached_dquot *dquot; uint32_t blocks = fs->qinfo[type].qi_info.dqi_blocks; int valid = 1; ocfs2_set_bit(blk, qbmp[type]); ret = o2fsck_read_blk(fs, type, buf, blk); if (ret) { com_err(whoami, ret, "while reading %s quota file block %"PRIu32, type2name(type), blk); return ret; } ret = o2fsck_validate_blk(fs, buf); if (ret) { verbosef("%s: invalid checksum in %s quota leaf block (block %" PRIu32")", error_message(ret), type2name(type), blk); valid = 0; } ocfs2_swap_quota_leaf_block_header(dh); if ((dh->dqdh_next_free && !check_blkref(dh->dqdh_next_free, blocks)) || (dh->dqdh_prev_free && !check_blkref(dh->dqdh_prev_free, blocks)) || dh->dqdh_entries > str_in_blk) { verbosef("corrupt %s quota leaf block header (block %"PRIu32")", type2name(type), blk); valid = 0; } ddquot = (struct ocfs2_global_disk_dqblk *)(buf + sizeof(struct qt_disk_dqdbheader)); for (i = 0; i < str_in_blk; i++, ddquot++) { if (ocfs2_qtree_entry_unused(ddquot)) continue; ocfs2_swap_quota_global_dqblk(ddquot); ret = ocfs2_find_quota_hash(qhash[type], ddquot->dqb_id, &dquot); if (ret) { com_err(whoami, ret, "while searching in %s quota hash", type2name(type)); return ret; } if (dquot && valid) { if (!prompt(ost, PY, PR_DUP_DQBLK_VALID, "Duplicate %s quota structure for id %" PRIu32":\nCurrent quota limits: Inode: %" PRIu64" %"PRIu64" Space: %"PRIu64" %" PRIu64"\nFound quota limits: Inode: %" PRIu64" %"PRIu64" Space: %"PRIu64" %" PRIu64"\nUse found limits?", type2name(type), ddquot->dqb_id, (uint64_t)dquot->d_ddquot.dqb_isoftlimit, (uint64_t)dquot->d_ddquot.dqb_ihardlimit, (uint64_t)dquot->d_ddquot.dqb_bsoftlimit, (uint64_t)dquot->d_ddquot.dqb_bhardlimit, (uint64_t)ddquot->dqb_isoftlimit, (uint64_t)ddquot->dqb_ihardlimit, (uint64_t)ddquot->dqb_bsoftlimit, (uint64_t)ddquot->dqb_bhardlimit)) continue; } else if (dquot && !valid) { if (!prompt(ost, PN, PR_DUP_DQBLK_INVALID, "Found %s quota structure for id %"PRIu32 " in a corrupted block and already have " "values for this id:\nCurrent quota " "limits: Inode: %"PRIu64" %"PRIu64" Space:" " %"PRIu64" %"PRIu64"\nFound quota limits:" " Inode: %"PRIu64" %"PRIu64" Space: %" PRIu64" %"PRIu64"\nUse found limits?", type2name(type), ddquot->dqb_id, (uint64_t)dquot->d_ddquot.dqb_isoftlimit, (uint64_t)dquot->d_ddquot.dqb_ihardlimit, (uint64_t)dquot->d_ddquot.dqb_bsoftlimit, (uint64_t)dquot->d_ddquot.dqb_bhardlimit, (uint64_t)ddquot->dqb_isoftlimit, (uint64_t)ddquot->dqb_ihardlimit, (uint64_t)ddquot->dqb_bsoftlimit, (uint64_t)ddquot->dqb_bhardlimit)) continue; } else if (!dquot && !valid) { if (!prompt(ost, PN, PR_DQBLK_INVALID, "Found corrupted %s quota structure for id" " %"PRIu32":\nFound quota limits: Inode: %" PRIu64" %"PRIu64" Space: %"PRIu64" %" PRIu64"\nUse found limits?", type2name(type), ddquot->dqb_id, (uint64_t)ddquot->dqb_isoftlimit, (uint64_t)ddquot->dqb_ihardlimit, (uint64_t)ddquot->dqb_bsoftlimit, (uint64_t)ddquot->dqb_bhardlimit)) continue; } if (!dquot) { ret = ocfs2_find_create_quota_hash(qhash[type], ddquot->dqb_id, &dquot); if (ret) { com_err(whoami, ret, "while inserting quota" " structure into hash"); return ret; } } memcpy(&dquot->d_ddquot, ddquot, sizeof(struct ocfs2_global_disk_dqblk)); dquot->d_ddquot.dqb_use_count = 0; dquot->d_ddquot.dqb_curinodes = 0; dquot->d_ddquot.dqb_curspace = 0; } return 0; } /* Read the block, check references in it */ static errcode_t o2fsck_check_tree_blk(o2fsck_state *ost, int type, uint32_t blk, int depth, char *buf) { ocfs2_filesys *fs = ost->ost_fs; errcode_t ret; int epb = (fs->fs_blocksize - OCFS2_QBLK_RESERVED_SPACE) >> 2; int tree_depth = ocfs2_qtree_depth(fs->fs_blocksize); int i; uint32_t *refs = (uint32_t *)buf, actref; ocfs2_set_bit(blk, qbmp[type]); ret = o2fsck_read_blk(fs, type, buf, blk); if (ret) { com_err(whoami, ret, "while reading %s quota file block %"PRIu32, type2name(type), blk); goto out; } ret = o2fsck_validate_blk(fs, buf); if (ret && !prompt(ost, PN, PR_QTREE_BLK_INVALID, "Corrupted %s quota tree " "block %"PRIu32" (checksum error: %s). Scan referenced " "blocks anyway?", type2name(type), blk, error_message(ret))) { goto out; } for (i = 0; i < epb; i++) { actref = le32_to_cpu(refs[i]); if (!actref) continue; /* Valid block reference? */ if (o2fsck_check_tree_ref(ost, type, actref, depth + 1)) { if (depth + 1 < tree_depth) { ret = o2fsck_check_tree_blk(ost, type, actref, depth + 1, buf + fs->fs_blocksize); } else if (!ocfs2_test_bit(actref, qbmp[type])) { ret = o2fsck_check_data_blk(ost, type, actref, buf + fs->fs_blocksize); } if (ret) goto out; } } out: return ret; } static errcode_t load_quota_file(o2fsck_state *ost, int type) { ocfs2_filesys *fs = ost->ost_fs; char *buf = NULL; errcode_t ret; ret = ocfs2_init_fs_quota_info(fs, type); if (ret) { com_err(whoami, ret, "while looking up global %s quota file", type2name(type)); goto out; } ret = o2fsck_check_info(ost, type); /* Some fatal error happened? */ if (ret) goto out; ret = ocfs2_malloc0((fs->qinfo[type].qi_info.dqi_blocks + 7) / 8, qbmp + type); if (ret) { com_err(whoami, ret, "while allocating %s quota file block " "bitmap", type2name(type)); goto out; } ret = ocfs2_malloc_blocks(fs->fs_io, ocfs2_qtree_depth(fs->fs_blocksize) + 1, &buf); if (ret) { com_err(whoami, ret, "while allocating buffer for quota blocks"); goto out; } if (!o2fsck_check_tree_ref(ost, type, QT_TREEOFF, 0)) goto out; ret = o2fsck_check_tree_blk(ost, type, QT_TREEOFF, 0, buf); out: if (qbmp[type]) ocfs2_free(qbmp + type); if (buf) ocfs2_free(&buf); return ret; } /* You have to write the inode yourself after calling this function! */ static errcode_t truncate_cached_inode(ocfs2_filesys *fs, ocfs2_cached_inode *ci) { uint32_t new_clusters; errcode_t ret; ret = ocfs2_zero_tail_and_truncate(fs, ci, 0, &new_clusters); if (ret) return ret; ci->ci_inode->i_clusters = new_clusters; if (new_clusters == 0) ci->ci_inode->id2.i_list.l_tree_depth = 0; ci->ci_inode->i_size = 0; return 0; } static errcode_t recreate_quota_files(ocfs2_filesys *fs, int type) { ocfs2_cached_inode *ci = fs->qinfo[type].qi_inode; errcode_t ret; ret = truncate_cached_inode(fs, ci); if (ret) { com_err(whoami, ret, "while truncating global %s quota file", type2name(type)); return ret; } ret = ocfs2_init_global_quota_file(fs, type); if (ret) { com_err(whoami, ret, "while reinitializing global %s quota file", type2name(type)); return ret; } ret = ocfs2_write_release_dquots(fs, type, qhash[type]); if (ret) { com_err(whoami, ret, "while writing %s quota usage", type2name(type)); return ret; } ret = ocfs2_init_local_quota_files(fs, type); if (ret) { com_err(whoami, ret, "while initializing local quota files"); return ret; } return 0; } errcode_t o2fsck_pass5(o2fsck_state *ost) { errcode_t ret; ocfs2_filesys *fs = ost->ost_fs; struct ocfs2_super_block *super = OCFS2_RAW_SB(fs->fs_super); int has_usrquota, has_grpquota; struct o2fsck_resource_track rt; has_usrquota = OCFS2_HAS_RO_COMPAT_FEATURE(super, OCFS2_FEATURE_RO_COMPAT_USRQUOTA); has_grpquota = OCFS2_HAS_RO_COMPAT_FEATURE(super, OCFS2_FEATURE_RO_COMPAT_GRPQUOTA); /* Nothing to check? */ if (!has_usrquota && !has_grpquota) return 0; printf("Pass 5: Checking quota information\n"); o2fsck_init_resource_track(&rt, fs->fs_io); if (has_usrquota) { ret = ocfs2_new_quota_hash(qhash + USRQUOTA); if (ret) { com_err(whoami, ret, "while allocating user quota hash"); goto out; } ret = load_quota_file(ost, USRQUOTA); if (ret) goto out; } if (has_grpquota) { ret = ocfs2_new_quota_hash(qhash + GRPQUOTA); if (ret) { com_err(whoami, ret, "while allocating group quota hash"); goto out; } ret = load_quota_file(ost, GRPQUOTA); if (ret) goto out; } ret = ocfs2_compute_quota_usage(fs, qhash[USRQUOTA], qhash[GRPQUOTA]); if (ret) { com_err(whoami, ret, "while computing quota usage"); goto out; } if (has_usrquota && ost->ost_fs->fs_flags & OCFS2_FLAG_RW) { ret = recreate_quota_files(fs, USRQUOTA); if (ret) goto out; ret = ocfs2_free_quota_hash(qhash[USRQUOTA]); if (ret) { com_err(whoami, ret, "while release user quota hash"); goto out; } } if (has_grpquota && ost->ost_fs->fs_flags & OCFS2_FLAG_RW) { ret = recreate_quota_files(fs, GRPQUOTA); if (ret) goto out; ret = ocfs2_free_quota_hash(qhash[GRPQUOTA]); if (ret) { com_err(whoami, ret, "while release group quota hash"); goto out; } } o2fsck_compute_resource_track(&rt, fs->fs_io); o2fsck_print_resource_track("Pass 5", ost, &rt, fs->fs_io); o2fsck_add_resource_track(&ost->ost_rt, &rt); return 0; out: if (qhash[USRQUOTA]) { ocfs2_iterate_quota_hash(qhash[USRQUOTA], o2fsck_release_dquot, qhash[USRQUOTA]); ocfs2_free_quota_hash(qhash[USRQUOTA]); } if (qhash[GRPQUOTA]) { ocfs2_iterate_quota_hash(qhash[GRPQUOTA], o2fsck_release_dquot, qhash[GRPQUOTA]); ocfs2_free_quota_hash(qhash[GRPQUOTA]); } return ret; } ocfs2-tools-ocfs2-tools-1.8.6/fsck.ocfs2/problem.c000066400000000000000000000073341347147137200216350ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * Copyright (C) 1993-2004 by Theodore Ts'o. * Copyright (C) 2004 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License, version 2, as published by the Free Software Foundation. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * * -- * * prompt() asks the user whether a given problem should be fixed or not. * "problem.c" is derived from the baroque e2fsck origins of this concept. * * XXX * The significant gap here is in persistent answers. Often one wants * to tell fsck to stop asking the same freaking question over and over * until a different question is asked. */ #include #include #include #include #include #include #include #include "ocfs2/ocfs2.h" #include "problem.h" #include "util.h" /* XXX more of fsck will want this.. */ static sig_atomic_t interrupted = 0; static void handle_sigint(int sig) { interrupted = 1; } /* * when a caller cares why read() failed we can bother to communicate * the error. */ static int read_a_char(int fd) { struct termios orig, new; char c; ssize_t ret; struct sigaction sa; memset(&sa, 0, sizeof(sa)); sa.sa_handler = handle_sigint; sa.sa_flags = SA_ONESHOT; /* !SA_RESTART */ sigaction(SIGINT, &sa, NULL); /* turn off buffering and echoing and encourage single character * reads */ tcgetattr(0, &orig); new = orig; new.c_lflag &= ~(ICANON | ECHO); new.c_cc[VMIN] = 1; new.c_cc[VTIME] = 0; tcsetattr (0, TCSANOW, &new); ret = read(fd, &c, sizeof(c)); tcsetattr (0, TCSANOW, &orig); if (interrupted) return 3; if (ret != sizeof(c)) return EOF; return c; } /* * this checks the user's intent. someday soon it will check command line flags * and have a notion of grouping, as well. The caller is expected to provide * a fully formed question that isn't terminated with a newline. */ int prompt_input(o2fsck_state *ost, unsigned flags, struct prompt_code code, const char *fmt, ...) { va_list ap; int c, ans = 0; static char yes[] = " ", no[] = " "; /* paranoia for jokers that claim to default to both */ if((flags & PY) && (flags & PN)) flags &= ~PY; printf("[%s] ", code.str); va_start(ap, fmt); vprintf(fmt, ap); va_end(ap); if (!ost->ost_ask) { ans = ost->ost_answer ? 'y' : 'n'; } else { if (flags & PY) printf("%s", yes); else if (flags & PN) printf("%s", no); } fflush(stdout); /* no curses, no nothin. overly regressive? */ while (!ans && (c = read_a_char(fileno(stdin))) != EOF) { if (c == 3) { printf("ctl-c pressed, aborting.\n"); exit(FSCK_ERROR|FSCK_CANCELED); } if (c == 27) { printf("ESC pressed, aborting.\n"); exit(FSCK_ERROR|FSCK_CANCELED); } c = tolower(c); /* space or CR lead to applying the optional default */ if (c == ' ' || c == '\n') { if (flags & PY) c = 'y'; else if (flags & PN) c = 'n'; } if (c == 'y' || c == 'n') { ans = c; break; } /* otherwise keep asking */ } if (!ans) { printf("input failed, aborting.\n"); exit(FSCK_ERROR); } /* this is totally silly. */ if (!ost->ost_ask) printf(" %c\n", ans); else printf("%c\n", ans); return ans == 'y'; } ocfs2-tools-ocfs2-tools-1.8.6/fsck.ocfs2/refcount.c000066400000000000000000000702621347147137200220220ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * refcount.c * * Copyright (C) 2009 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License version 2 as published by the Free Software Foundation. * * 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. */ #include #include #include "ocfs2/kernel-rbtree.h" #include "ocfs2-kernel/kernel-list.h" #include "ocfs2/ocfs2.h" #include "problem.h" #include "fsck.h" #include "extent.h" #include "util.h" #include "refcount.h" static const char *whoami = "refcount.c"; struct check_refcount_rec { uint64_t root_blkno; uint64_t c_end; }; /* every REFCOUNTED ocfs2_extent_rec will become one. */ struct refcount_extent { struct rb_node ext_node; uint32_t v_cpos; uint32_t clusters; uint64_t p_cpos; }; struct refcount_file { struct list_head list; uint64_t i_blkno; struct rb_root ref_extents; /* store every refcounted extent rec * in this file. */ }; struct refcount_tree { struct rb_node ref_node; uint64_t rf_blkno; uint64_t rf_end; struct list_head files_list; int files_count; int is_valid; char *root_buf; char *leaf_buf; /* the cluster offset we have checked against this tree. */ uint64_t p_cend; }; static errcode_t check_rb(o2fsck_state *ost, uint64_t blkno, uint64_t root_blkno, uint64_t *c_end, uint32_t offset, int no_holes, int *is_valid); static void check_rl(o2fsck_state *ost, uint64_t rb_blkno, uint64_t root_blkno, struct ocfs2_refcount_list *rl, uint64_t *c_end, int *changed) { struct ocfs2_refcount_rec *rec; uint16_t i; size_t cpy; int trust_used = 1; int max_recs = ocfs2_refcount_recs_per_rb(ost->ost_fs->fs_blocksize); verbosef("count %u used %u\n", rl->rl_count, rl->rl_used); if (rl->rl_count > max_recs && prompt(ost, PY, PR_REFCOUNT_LIST_COUNT, "Refcount list in refcount tree %"PRIu64" claims to have %u " "records, but the maximum is %u. Fix the list's count?", root_blkno, rl->rl_count, max_recs)) { rl->rl_count = max_recs; *changed = 1; } if (max_recs > rl->rl_count) max_recs = rl->rl_count; if (rl->rl_used > max_recs) { if (prompt(ost, PY, PR_REFCOUNT_LIST_USED, "Refcount list in refcount tree %"PRIu64" claims %u " "as the used record, but fsck believes " "the largest valid value is %u. Clamp the used " "record value?", root_blkno, rl->rl_used, max_recs)) { rl->rl_used = rl->rl_count; *changed = 1; } else trust_used = 0; } if (trust_used) max_recs = rl->rl_used; for (i = 0; i < max_recs; i++) { rec = &rl->rl_recs[i]; /* offer to remove records that point to nowhere */ if (ocfs2_block_out_of_range(ost->ost_fs, ocfs2_clusters_to_blocks(ost->ost_fs, rec->r_cpos + rec->r_clusters - 1)) && prompt(ost, PY, PR_REFCOUNT_CLUSTER_RANGE, "Refcount record %u in refcount block %"PRIu64" " "of refcount tree %"PRIu64" refers to a cluster " "that is out of range. Remove " "this record from the refcount list?", i, rb_blkno, root_blkno)) { if (!trust_used) { printf("Can't remove the record because " "rl_used hasn't been fixed\n"); continue; } goto remove_rec; } if (rec->r_cpos < *c_end && prompt(ost, PY, PR_REFCOUNT_CLUSTER_COLLISION, "Refcount record %u in refcount block %"PRIu64" " "of refcount tree %"PRIu64" refers to a cluster " "that is collided with the previous record. Remove " "this record from the refcount list?", i, rb_blkno, root_blkno)) { if (!trust_used) { printf("Can't remove the record because " "rl_used hasn't been fixed\n"); continue; } goto remove_rec; } *c_end = rec->r_cpos + rec->r_clusters; continue; remove_rec: cpy = (max_recs - i - 1) * sizeof(*rec); /* shift the remaining recs into this ones place */ if (cpy != 0) { memcpy(rec, rec + 1, cpy); memset(&rl->rl_recs[max_recs - 1], 0, sizeof(*rec)); i--; } rl->rl_used--; max_recs--; *changed = 1; continue; } } static errcode_t refcount_check_leaf_extent_rec(o2fsck_state *ost, uint64_t owner, struct ocfs2_extent_list *el, struct ocfs2_extent_rec *er, int *changed, uint32_t offset, int no_holes, void *para) { errcode_t ret; int is_valid = 1; struct check_refcount_rec *check = para; ret = check_rb(ost, er->e_blkno, check->root_blkno, &check->c_end, offset, no_holes, &is_valid); if (!is_valid && prompt(ost, PY, PR_REFCOUNT_BLOCK_INVALID, "Refcount block %"PRIu64 " for tree %"PRIu64" is invalid. " "Remove it from the tree?", (uint64_t)er->e_blkno, check->root_blkno)) { er->e_blkno = 0; *changed = 1; } return ret; } static errcode_t check_rb(o2fsck_state *ost, uint64_t blkno, uint64_t root_blkno, uint64_t *c_end, uint32_t offset, int no_holes, int *is_valid) { int changed = 0; char *buf = NULL; struct ocfs2_refcount_block *rb; errcode_t ret; /* XXX test that the block isn't already used */ /* we only consider a refcount block invalid if we were able to read * it and it didn't have a refcount block signature */ *is_valid = 1; ret = ocfs2_malloc_block(ost->ost_fs->fs_io, &buf); if (ret) { com_err(whoami, ret, "while allocating a block-sized buffer " "for a refcount block"); goto out; } ret = ocfs2_read_refcount_block_nocheck(ost->ost_fs, blkno, buf); if (ret) { com_err(whoami, ret, "reading refcount block at %"PRIu64" in " "refcount tree %"PRIu64" for verification", blkno, root_blkno); if (ret == OCFS2_ET_BAD_EXTENT_BLOCK_MAGIC) *is_valid = 0; goto out; } rb = (struct ocfs2_refcount_block *)buf; if (rb->rf_blkno != blkno && prompt(ost, PY, PR_RB_BLKNO, "A refcount block at %"PRIu64" in refcount tree %"PRIu64" " "claims to be located at block %"PRIu64". Update the " "refcount block's location?", blkno, root_blkno, (uint64_t)rb->rf_blkno)) { rb->rf_blkno = blkno; changed = 1; } if (rb->rf_fs_generation != ost->ost_fs_generation) { if (prompt(ost, PY, PR_RB_GEN, "A refcount block at %"PRIu64" in refcount tree " "%"PRIu64" has a generation of %x which doesn't " "match the volume's generation of %x. Consider " "this refcount block invalid?", blkno, root_blkno, rb->rf_fs_generation, ost->ost_fs_generation)) { *is_valid = 0; goto out; } if (prompt(ost, PY, PR_RB_GEN_FIX, "Update the refcount block's generation to match " "the volume?")) { rb->rf_fs_generation = ost->ost_fs_generation; changed = 1; } } if (rb->rf_blkno != root_blkno && rb->rf_parent != root_blkno && prompt(ost, PY, PR_RB_PARENT, "A refcount block at %"PRIu64" in refcount tree %"PRIu64" " "claims to belong to tree %"PRIu64". Update the " "parent's information?", blkno, root_blkno, (uint64_t)rb->rf_parent)) { rb->rf_parent = root_blkno; changed = 1; } /* XXX worry about suballoc node/bit */ if (rb->rf_flags & OCFS2_REFCOUNT_TREE_FL) { struct check_refcount_rec check = {root_blkno, *c_end}; struct extent_info ei = {0, }; uint16_t max_recs = ocfs2_extent_recs_per_rb(ost->ost_fs->fs_blocksize); ei.para = ✓ ei.chk_rec_func = refcount_check_leaf_extent_rec; /* * leaf extent rec for a refcount tree is allocated from * extent_alloc, so we don't need to set mark_rec_alloc_func * here. */ check_el(ost, &ei, rb->rf_blkno, &rb->rf_list, max_recs, offset, no_holes, &changed); *c_end = check.c_end; if (ei.ei_clusters != rb->rf_clusters && prompt(ost, PY, PR_REFCOUNT_CLUSTERS, "Refcount tree %"PRIu64" claims to have %u " "clusters, but we only found %u. " "Fix it?", root_blkno, rb->rf_clusters, (uint32_t)ei.ei_clusters)) { rb->rf_clusters = ei.ei_clusters; changed = 1; } } else { assert(c_end); check_rl(ost, root_blkno, blkno, &rb->rf_records, c_end, &changed); /* We allow the root block to be empty. */ if (root_blkno != blkno && !rb->rf_records.rl_used && prompt(ost, PY, PR_REFCOUNT_LIST_EMPTY, "Refcount block %"PRIu64" claims to have no " "refcount record in it. Consider it as invalid " "and Remove from tree?", (uint64_t)rb->rf_blkno)) { *is_valid = 0; changed = 1; } } if (changed) { ret = ocfs2_write_refcount_block(ost->ost_fs, blkno, buf); if (ret) { com_err(whoami, ret, "while writing an updated " "refcount block at %"PRIu64" for refcount " "tree %"PRIu64, blkno, root_blkno); goto out; } } out: if (buf) ocfs2_free(&buf); return 0; } /* See if the recount_tree rbtree has the given ref_blkno. */ static struct refcount_tree* refcount_tree_lookup(o2fsck_state *ost, uint64_t ref_blkno) { struct rb_node *p = ost->ost_refcount_trees.rb_node; struct refcount_tree *ref_tree; while (p) { ref_tree = rb_entry(p, struct refcount_tree, ref_node); if (ref_blkno < ref_tree->rf_blkno) p = p->rb_left; else if (ref_blkno > ref_tree->rf_blkno) p = p->rb_right; else return ref_tree; } return NULL; } static void refcount_tree_insert(o2fsck_state *ost, struct refcount_tree *insert_rb) { struct rb_node **p = &ost->ost_refcount_trees.rb_node; struct rb_node *parent = NULL; struct refcount_tree *ref_tree = NULL; while (*p) { parent = *p; ref_tree = rb_entry(parent, struct refcount_tree, ref_node); if (insert_rb->rf_blkno < ref_tree->rf_blkno) p = &(*p)->rb_left; else if (insert_rb->rf_blkno > ref_tree->rf_blkno) p = &(*p)->rb_right; else assert(0); /* Caller checked */ } rb_link_node(&insert_rb->ref_node, parent, p); rb_insert_color(&insert_rb->ref_node, &ost->ost_refcount_trees); } errcode_t o2fsck_check_refcount_tree(o2fsck_state *ost, struct ocfs2_dinode *di) { errcode_t ret = 0; uint64_t c_end = 0; int is_valid = 1; struct refcount_tree *tree; struct refcount_file *file; if (!(di->i_dyn_features & OCFS2_HAS_REFCOUNT_FL)) return 0; tree = refcount_tree_lookup(ost, di->i_refcount_loc); if (tree) goto check_valid; ret = ocfs2_malloc0(sizeof(struct refcount_tree), &tree); if (ret) return ret; ret = check_rb(ost, di->i_refcount_loc, di->i_refcount_loc, &c_end, 0, 0, &is_valid); /* * Add refcount tree to the rb-tree. * rf_end records the end of the refcount record we have. * It will be used later. */ tree->rf_blkno = di->i_refcount_loc; tree->is_valid = is_valid; tree->rf_end = c_end; INIT_LIST_HEAD(&tree->files_list); refcount_tree_insert(ost, tree); check_valid: if (!tree->is_valid && prompt(ost, PY, PR_REFCOUNT_ROOT_BLOCK_INVALID, "Refcount tree %"PRIu64 " for inode %"PRIu64" is invalid. " "Remove it and clear the flag for the inode?", (uint64_t)di->i_refcount_loc, (uint64_t)di->i_blkno)) { di->i_refcount_loc = 0; di->i_dyn_features &= ~OCFS2_HAS_REFCOUNT_FL; o2fsck_write_inode(ost, di->i_blkno, di); } else { ret = ocfs2_malloc0(sizeof(struct refcount_file), &file); if (!ret) { file->i_blkno = di->i_blkno; INIT_LIST_HEAD(&file->list); list_add_tail(&file->list, &tree->files_list); tree->files_count++; } } return ret; } static void refcount_extent_insert(struct refcount_file *file, struct refcount_extent *insert) { struct rb_node **p = &file->ref_extents.rb_node; struct rb_node *parent = NULL; struct refcount_extent *extent = NULL; while (*p) { parent = *p; extent = rb_entry(parent, struct refcount_extent, ext_node); if (insert->p_cpos < extent->p_cpos) p = &(*p)->rb_left; else if (insert->p_cpos > extent->p_cpos) p = &(*p)->rb_right; else assert(0); /* Caller checked */ } rb_link_node(&insert->ext_node, parent, p); rb_insert_color(&insert->ext_node, &file->ref_extents); } errcode_t o2fsck_mark_clusters_refcounted(o2fsck_state *ost, uint64_t rf_blkno, uint64_t i_blkno, uint64_t p_cpos, uint32_t clusters, uint32_t v_cpos) { errcode_t ret; struct refcount_tree *tree; struct refcount_file *file = ost->ost_latest_file; struct list_head *p, *next; struct refcount_extent *extent; if (file && file->i_blkno == i_blkno) goto add_clusters; tree = refcount_tree_lookup(ost, rf_blkno); /* We should already insert the tree during refcount tree check. */ assert(tree); list_for_each_safe(p, next, &tree->files_list) { file = list_entry(p, struct refcount_file, list); if (file->i_blkno == i_blkno) goto add_clusters; } /* We should already insert the file during refcount tree check. */ assert(0); add_clusters: ost->ost_latest_file = file; ret = ocfs2_malloc0(sizeof(struct refcount_extent), &extent); if (ret) return ret; extent->v_cpos = v_cpos; extent->clusters = clusters; extent->p_cpos = p_cpos; refcount_extent_insert(file, extent); return 0; } /* * Given a refcount tree, find the lowest p_cpos of all * the files sharing the tree. */ static int get_refcounted_extent(struct refcount_tree *tree, uint64_t *p_cpos, uint32_t *p_clusters, uint32_t *p_refcount) { struct refcount_extent *extent; struct refcount_file *file; struct list_head *p, *next; struct rb_node *node; uint64_t cpos = UINT64_MAX; uint32_t clusters = 0, refcount = 0; int found = 0; list_for_each_safe(p, next, &tree->files_list) { file = list_entry(p, struct refcount_file, list); node = rb_first(&file->ref_extents); /* * If the file has no extent, go to next file. * XXX: We can improve it here by removing the empty file. */ if (!node) continue; found = 1; extent = rb_entry(node, struct refcount_extent, ext_node); if (extent->p_cpos < cpos) { /* We meet with a new start. */ clusters = cpos - extent->p_cpos < extent->clusters ? cpos - extent->p_cpos : extent->clusters; cpos = extent->p_cpos; refcount = 1; } else if (extent->p_cpos == cpos) { clusters = clusters < extent->clusters ? clusters : extent->clusters; refcount++; } else if (extent->p_cpos < cpos + clusters) { /* * extent->p_cpos > cpos, change clusters accordingly. */ clusters = extent->p_cpos - cpos; } } if (!found) return 0; *p_cpos = cpos; *p_clusters = clusters; *p_refcount = refcount; return 1; } /* * Remove pair(cpos, clusters) from the all the files sharing the tree. * The pair is actually got by get_refcounted_extent. */ static void remove_refcounted_extent(struct refcount_tree *tree, uint64_t cpos, uint32_t clusters) { struct refcount_extent *extent; struct refcount_file *file; struct list_head *p, *next; struct rb_node *node; /* Remove the tuple from the refcounted file. */ list_for_each_safe(p, next, &tree->files_list) { file = list_entry(p, struct refcount_file, list); node = rb_first(&file->ref_extents); /* If the file has no extent, go to next file. */ if (!node) continue; extent = rb_entry(node, struct refcount_extent, ext_node); assert(extent->p_cpos >= cpos); if (cpos + clusters <= extent->p_cpos) continue; assert(extent->p_cpos + extent->clusters >= cpos + clusters); if (cpos + clusters == extent->p_cpos + extent->clusters) { rb_erase(&extent->ext_node, &file->ref_extents); ocfs2_free(&extent); } else { extent->clusters = (extent->p_cpos + extent->clusters) - (cpos + clusters); extent->p_cpos = cpos + clusters; } } } /* * Check all the files sharing the tree and if there is a file contains * the (p_cpos, len) with refcounted flag, we clear it. * Note: * This function is only called when checking a continuous clusters. * The pair (p_cpos, len) is a part of the original tuple we get from * get_refcounted_extent, so it can't be in 2 different refcount_extent. */ static errcode_t o2fsck_clear_refcount(o2fsck_state *ost, struct refcount_tree *tree, uint64_t p_cpos, uint32_t len) { errcode_t ret = 0; struct refcount_extent *extent; struct refcount_file *file; struct list_head *p, *next; struct rb_node *node; uint32_t v_start; list_for_each_safe(p, next, &tree->files_list) { file = list_entry(p, struct refcount_file, list); node = file->ref_extents.rb_node; /* If the file has no extent, go to next file. */ if (!node) continue; while (node) { extent = rb_entry(node, struct refcount_extent, ext_node); if (extent->p_cpos > p_cpos + len) node = node->rb_left; else if (extent->p_cpos + extent->clusters <= p_cpos) node = node->rb_right; else break; } if (node && (extent->p_cpos <= p_cpos && extent->p_cpos + extent->clusters >= p_cpos + len)) { v_start = p_cpos - extent->p_cpos + extent->v_cpos; ret = ocfs2_change_refcount_flag(ost->ost_fs, file->i_blkno, v_start, len, p_cpos, 0, OCFS2_EXT_REFCOUNTED); if (ret) { com_err(whoami, ret, "while clearing refcount flag at " "%u in file %"PRIu64, v_start, file->i_blkno); goto out; } } } out: return ret; } /* * o2fsck_refcount_punch_hole and o2fsck_change_refcount are just wrappers * for the corresponding libocfs2 functions with one addition: re-read * root refcount block since we may have changed the tree during the operation. */ static errcode_t o2fsck_refcount_punch_hole(o2fsck_state *ost, struct refcount_tree *tree, uint64_t p_cpos, uint32_t len) { errcode_t ret; ret = ocfs2_refcount_punch_hole(ost->ost_fs, tree->rf_blkno, p_cpos, len); if (ret) { com_err(whoami, ret, "while punching hole in " "(%"PRIu64", %u) in refcount tree %"PRIu64, p_cpos, len, tree->rf_blkno); goto out; } /* re-read the root blkno since we may have changed it somehow. */ ret = ocfs2_read_refcount_block(ost->ost_fs, tree->rf_blkno, tree->root_buf); out: return ret; } static errcode_t o2fsck_change_refcount(o2fsck_state *ost, struct refcount_tree *tree, uint64_t p_cpos, uint32_t len, uint32_t refcount) { errcode_t ret; ret = ocfs2_change_refcount(ost->ost_fs, tree->rf_blkno, p_cpos, len, refcount); if (ret) { com_err(whoami, ret, "while changing refcount in " "(%"PRIu64", %u) in refcount tree %"PRIu64" to %u", p_cpos, len, tree->rf_blkno, refcount); goto out; } /* re-read the root blkno since we may have changed it somehow. */ ret = ocfs2_read_refcount_block(ost->ost_fs, tree->rf_blkno, tree->root_buf); out: return ret; } /* * Given [cpos, end), remove all the refcount records in this range from * the refcount tree. */ static errcode_t o2fsck_remove_refcount_range(o2fsck_state *ost, struct refcount_tree *tree, uint64_t cpos, uint64_t end) { errcode_t ret = 0; int index; unsigned int len; struct ocfs2_refcount_rec rec; uint64_t range = end - cpos; while (range) { len = range > UINT_MAX ? UINT_MAX : range; ret = ocfs2_get_refcount_rec(ost->ost_fs, tree->root_buf, cpos, len, &rec, &index, tree->leaf_buf); if (ret) { com_err(whoami, ret, "while getting refcount rec at " "%"PRIu64" in tree %"PRIu64, cpos, tree->rf_blkno); goto out; } if (!rec.r_refcount) { cpos += rec.r_clusters; range -= rec.r_clusters; continue; } /* * In case we found some refcount rec, just ask for * punching hole for the whole range (cpos, len), and * o2fsck_refcount_punch_hole will handle the complex * issue for us. */ if (prompt(ost, PY, PR_REFCOUNT_REC_REDUNDANT, "refcount records among clusters (%"PRIu64 ", %u) are found with no physical clusters " "corresponding to them. Remove them?", cpos, len)) { ret = o2fsck_refcount_punch_hole(ost, tree, cpos, len); if (ret) { com_err(whoami, ret, "while punching " "hole in (%"PRIu64", %u) in refcount " "tree %"PRIu64, cpos, len, tree->rf_blkno); goto out; } } cpos += len; range -= len; } out: return ret; } /* * Given tuple(p_cpos, clusters, refcount), check whether the refcount * tree has the corresponding refcount record. If not, add/update them. * If the user don't allow us to change the refcount tree, add them to * to duplicate_clusters and let it handle them. */ static errcode_t o2fsck_check_clusters_in_refcount(o2fsck_state *ost, struct refcount_tree *tree, uint64_t p_cpos, uint32_t clusters, uint32_t refcount) { errcode_t ret = 0; uint32_t rec_len; int index; struct ocfs2_refcount_rec rec; if (!clusters) return 0; /* * the previous check ended at tree->p_cend, and now we get * p_cpos, so any refcount record between p_cend and p_cpos * should be considered as redundant. */ ret = o2fsck_remove_refcount_range(ost, tree, tree->p_cend, p_cpos); if (ret) { com_err(whoami, ret, "while removing refcount rec from " "%"PRIu64" to %"PRIu64" in tree %"PRIu64, tree->p_cend, p_cpos, tree->rf_blkno); goto out; } tree->p_cend = p_cpos + clusters; again: ret = ocfs2_get_refcount_rec(ost->ost_fs, tree->root_buf, p_cpos, clusters, &rec, &index, tree->leaf_buf); if (ret) { com_err(whoami, ret, "while getting refcount rec at " "%"PRIu64" in tree %"PRIu64, p_cpos, tree->rf_blkno); goto out; } /* * Actually ocfs2_get_refcount_rec will fake some refcount record * in case it can't find p_cpos in the refcount tree. So we really * shouldn't meet with a case rec->r_cpos > p_cpos. */ assert(rec.r_cpos <= p_cpos); rec_len = ocfs2_min(p_cpos + clusters, (uint64_t)rec.r_cpos + rec.r_clusters) - p_cpos; if (rec.r_refcount != refcount) { if (prompt(ost, PY, PR_REFCOUNT_COUNT_INVALID, "clusters %"PRIu64 " with len %u have %u refcount " "while there are %u files point to them. " "Correct the refcount value?", p_cpos, rec_len, rec.r_refcount, refcount)) { ret = o2fsck_change_refcount(ost, tree, p_cpos, rec_len, refcount); if (ret) { com_err(whoami, ret, "while updating refcount " "%u at %"PRIu64" len %u in tree " "%"PRIu64, refcount, p_cpos, rec_len, tree->rf_blkno); goto out; } } else { /* * XXX: * Do we need to ask user for adding them to dup? * * Call o2fsck_mark_clusters_allocated will add them * them to duplicate_clusters automatically. */ o2fsck_mark_clusters_allocated(ost, p_cpos, rec_len); ret = o2fsck_refcount_punch_hole(ost, tree, p_cpos, rec_len); if (ret) { com_err(whoami, ret, "while punching " "hole at %"PRIu64"in refcount " "tree %"PRIu64, p_cpos, tree->rf_blkno); goto out; } ret = o2fsck_clear_refcount(ost, tree, p_cpos, rec_len); if (ret) { com_err(whoami, ret, "while clearing refcount for " "cluster %"PRIu64" len %u in %"PRIu64, p_cpos, rec_len, tree->rf_blkno); goto out; } } } /* we have finished checking (p_cpos, clusters). */ if (p_cpos + clusters <= rec.r_cpos + rec.r_clusters) goto out; /* * now we have finished checking current refcount_rec, * p_cpos + clusters > rec.r_cpos + rec.r_clusters, * need to read next refcount_rec. */ clusters += p_cpos; p_cpos = rec.r_cpos + rec.r_clusters; clusters -= p_cpos; goto again; out: return ret; } static errcode_t o2fsck_check_refcount_clusters(o2fsck_state *ost, struct refcount_tree *tree, uint64_t start, uint32_t len, uint32_t refcount) { int val; errcode_t ret = 0; uint64_t p_cend; uint32_t clusters; o2fsck_mark_clusters_allocated(ost, start, len); while (len) { if (ost->ost_duplicate_clusters) { /* * Check whether the clusters can be found in * duplicated cluster list. */ p_cend = start; clusters = len; while (clusters) { ocfs2_bitmap_test( ost->ost_duplicate_clusters, p_cend, &val); if (val) break; clusters--; p_cend++; } } else p_cend = start + len; /* * p_cend points to the end cluster we will check in this loop. * * If there is a cluster which is already setted by other * owner(find in duplicate_clusters), p_end now points to it. * * So we check the refcounted clusters [start, p_cend) and then * punch a hole in refcount tree at p_cend in case. */ clusters = p_cend - start; ret = o2fsck_check_clusters_in_refcount(ost, tree, start, clusters, refcount); if (ret) { com_err(whoami, ret, "while checking " "refcounted clusters"); goto out; } if (len > clusters) { /* * We haven't finished our check and the reason * is that p_cend is setted in dup_clusters, so * punch a hole, clear the refcount flag for * p_cend and continue our check. */ ret = o2fsck_refcount_punch_hole(ost, tree, p_cend, 1); if (ret) { com_err(whoami, ret, "while punching " "hole at %"PRIu64"in refcount " "tree %"PRIu64, p_cend, tree->rf_blkno); goto out; } ret = o2fsck_clear_refcount(ost, tree, p_cend, 1); if (ret) { com_err(whoami, ret, "while clearing refcount for " "cluster %"PRIu64" in %"PRIu64, p_cend, tree->rf_blkno); goto out; } /* start check from next cluster. */ p_cend++; } len = start + len - p_cend; start = p_cend; } out: return ret; } /* * Given a refcount tree, check the refcounted clusters and their refcount. */ static errcode_t o2fsck_check_refcount(o2fsck_state *ost, struct refcount_tree *tree) { errcode_t ret; uint64_t p_cpos = 0; uint32_t clusters = 0, refcount = 0; struct ocfs2_refcount_block *root_rb; ret = ocfs2_malloc_block(ost->ost_fs->fs_io, &tree->root_buf); if (ret) { com_err(whoami, ret, "while allocating a block-sized buffer " "for a refcount block"); goto out; } ret = ocfs2_malloc_block(ost->ost_fs->fs_io, &tree->leaf_buf); if (ret) { com_err(whoami, ret, "while allocating a block-sized buffer " "for a refcount block"); goto out; } ret = ocfs2_read_refcount_block(ost->ost_fs, tree->rf_blkno, tree->root_buf); if (ret) { com_err(whoami, ret, "while reading root refcount block at" " %"PRIu64, tree->rf_blkno); goto out; } root_rb = (struct ocfs2_refcount_block *)tree->root_buf; if (tree->files_count != root_rb->rf_count && prompt(ost, PY, PR_REFCOUNT_COUNT, "Refcount tree at %"PRIu64" claims to have %u " "files associated with it, but we only found %u." "Update the count number?", tree->rf_blkno, root_rb->rf_count, tree->files_count)) { root_rb->rf_count = tree->files_count; ret = ocfs2_write_refcount_block(ost->ost_fs, tree->rf_blkno, tree->root_buf); if (ret) { com_err(whoami, ret, "while updati rb_count for tree " "%"PRIu64, tree->rf_blkno); goto out; } } while (get_refcounted_extent(tree, &p_cpos, &clusters, &refcount)) { ret = o2fsck_check_refcount_clusters(ost, tree, p_cpos, clusters, refcount); if (ret) { com_err(whoami, ret, "while checking refcount clusters " "(%"PRIu64", %u, %u) in tree %"PRIu64, p_cpos, clusters, refcount, tree->rf_blkno); goto out; } remove_refcounted_extent(tree, p_cpos, clusters); } /* * Remove all the refcount rec passed p_cpos + clusters from the tree * since there is no corresponding refcounted clusters. */ if (tree->rf_end > p_cpos + clusters) { ret = o2fsck_remove_refcount_range(ost, tree, p_cpos + clusters, tree->rf_end); if (ret) com_err(whoami, ret, "while deleting redundant refcount rec"); } out: if (tree->root_buf) ocfs2_free(&tree->root_buf); if (tree->leaf_buf) ocfs2_free(&tree->leaf_buf); return ret; } errcode_t o2fsck_check_mark_refcounted_clusters(o2fsck_state *ost) { errcode_t ret = 0; struct refcount_tree *tree; struct rb_node *node; struct list_head *p, *next; struct refcount_file *file; if (!ocfs2_refcount_tree(OCFS2_RAW_SB(ost->ost_fs->fs_super))) return 0; while ((node = rb_first(&ost->ost_refcount_trees)) != NULL) { tree = rb_entry(node, struct refcount_tree, ref_node); if (tree->is_valid) { ret = o2fsck_check_refcount(ost, tree); if (ret) goto out; } list_for_each_safe(p, next, &tree->files_list) { file = list_entry(p, struct refcount_file, list); node = rb_first(&file->ref_extents); assert(!node); list_del(&file->list); ocfs2_free(&file); } rb_erase(&tree->ref_node, &ost->ost_refcount_trees); ocfs2_free(&tree); } out: return ret; } ocfs2-tools-ocfs2-tools-1.8.6/fsck.ocfs2/slot_recovery.c000066400000000000000000000111271347147137200230670ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * slot_recovery.c * * Slot recovery handler. * * Copyright (C) 2008 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License version 2 as published by the Free Software Foundation. * * 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. */ #include #include "util.h" #include "slot_recovery.h" #include "pass4.h" static errcode_t ocfs2_clear_truncate_log(ocfs2_filesys *fs, struct ocfs2_dinode *di, int slot) { errcode_t ret = 0; struct ocfs2_truncate_log *tl; struct ocfs2_truncate_rec *tr; int i, was_set = 0, cleared = 0; int max = ocfs2_truncate_recs_per_inode(fs->fs_blocksize); uint64_t blkno; if (!(di->i_flags & OCFS2_VALID_FL) || !(di->i_flags & OCFS2_SYSTEM_FL) || !(di->i_flags & OCFS2_DEALLOC_FL)) return OCFS2_ET_INVALID_ARGUMENT; tl = &di->id2.i_dealloc; if (tl->tl_used > max) return OCFS2_ET_INTERNAL_FAILURE; for (i = 0; i < tl->tl_used; i++) { tr = &tl->tl_recs[i]; if (tr->t_start == 0) continue; blkno = ocfs2_clusters_to_blocks(fs, tr->t_start); ret = ocfs2_test_clusters(fs, tr->t_clusters, blkno, 1, &was_set); if (ret) goto bail; if (!was_set) { ret = OCFS2_ET_INVALID_BIT; goto bail; } ret = ocfs2_free_clusters(fs, tr->t_clusters, blkno); if (ret) goto bail; cleared = 1; } tl->tl_used = 0; memset(tl->tl_recs, 0, fs->fs_blocksize - offsetof(struct ocfs2_dinode, id2.i_dealloc.tl_recs)); ret = ocfs2_write_inode(fs, di->i_blkno, (char *)di); if (!ret && cleared) printf("Slot %d's truncate log replayed successfully\n", slot); bail: return ret; } errcode_t o2fsck_replay_truncate_logs(ocfs2_filesys *fs) { return handle_slots_system_file(fs, TRUNCATE_LOG_SYSTEM_INODE, ocfs2_clear_truncate_log); } static errcode_t ocfs2_clear_local_alloc(ocfs2_filesys *fs, struct ocfs2_dinode *di, int slot) { errcode_t ret = 0; int bit_off, left, count, start, was_set = 0, cleared = 0; uint64_t la_start_blk; uint64_t blkno; void *bitmap; struct ocfs2_local_alloc *la; if (!(di->i_flags & OCFS2_VALID_FL) || !(di->i_flags & OCFS2_SYSTEM_FL) || !(di->i_flags & OCFS2_BITMAP_FL)) return OCFS2_ET_INVALID_ARGUMENT; if (!di->id1.bitmap1.i_total) goto bail; la = &di->id2.i_lab; if (di->id1.bitmap1.i_used == di->id1.bitmap1.i_total) goto clear_inode; la_start_blk = ocfs2_clusters_to_blocks(fs, la->la_bm_off); bitmap = la->la_bitmap; start = count = bit_off = 0; left = di->id1.bitmap1.i_total; while ((bit_off = ocfs2_find_next_bit_clear(bitmap, left, start)) != -1) { if ((bit_off < left) && (bit_off == start)) { count++; start++; continue; } if (count) { blkno = la_start_blk + ocfs2_clusters_to_blocks(fs, start - count); ret = ocfs2_test_clusters(fs, count, blkno, 1, &was_set); if (ret) goto bail; if (!was_set) { ret = OCFS2_ET_INVALID_BIT; goto bail; } ret = ocfs2_free_clusters(fs, count, blkno); if (ret) goto bail; cleared = 1; } if (bit_off >= left) break; count = 1; start = bit_off + 1; } clear_inode: di->id1.bitmap1.i_total = 0; di->id1.bitmap1.i_used = 0; la->la_bm_off = 0; memset(la->la_bitmap, 0, ocfs2_local_alloc_size(fs->fs_blocksize)); ret = ocfs2_write_inode(fs, di->i_blkno, (char *)di); if (!ret && cleared) printf("Slot %d's local alloc replayed successfully\n", slot); bail: return ret; } errcode_t o2fsck_replay_local_allocs(ocfs2_filesys *fs) { return handle_slots_system_file(fs, LOCAL_ALLOC_SYSTEM_INODE, ocfs2_clear_local_alloc); } static errcode_t ocfs2_clear_link_count(ocfs2_filesys *fs, struct ocfs2_dinode *di, int slot) { errcode_t ret = 0; if (!(di->i_flags & OCFS2_VALID_FL) || !(di->i_flags & OCFS2_SYSTEM_FL) || !S_ISDIR(di->i_mode)) return OCFS2_ET_INVALID_ARGUMENT; if (di->i_links_count == 2) goto bail; di->i_links_count = 2; ret = ocfs2_write_inode(fs, di->i_blkno, (char *)di); if (!ret) printf("Slot %d's orphan dir replayed successfully\n", slot); bail: return ret; } errcode_t o2fsck_replay_orphan_dirs(o2fsck_state *ost) { errcode_t ret; ret = replay_orphan_dir(ost, 1); if (ret) return ret; return handle_slots_system_file(ost->ost_fs, ORPHAN_DIR_SYSTEM_INODE, ocfs2_clear_link_count); } ocfs2-tools-ocfs2-tools-1.8.6/fsck.ocfs2/strings.c000066400000000000000000000065221347147137200216640ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * Copyright (C) 2004 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License, version 2, as published by the Free Software Foundation. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * * -- * * A light wrapper around rbtree to store strings with the sole purpose of * detecting dupliates. * */ #include #include #include #include #include "ocfs2/ocfs2.h" #include "fsck.h" #include "o2fsck_strings.h" #include "util.h" struct string_entry { struct rb_node s_node; size_t s_strlen; char s_string[0]; /* null terminated */ }; /* I'm too lazy to share code with _insert right now */ int o2fsck_strings_exists(o2fsck_strings *strings, char *string, size_t strlen) { struct rb_node ** p = &strings->s_root.rb_node; struct rb_node * parent = NULL; struct string_entry *se; int cmp; while (*p) { parent = *p; se = rb_entry(parent, struct string_entry, s_node); /* we don't actually care about lexographical sorting */ cmp = strlen - se->s_strlen; if (cmp == 0) cmp = memcmp(string, se->s_string, strlen); if (cmp < 0) p = &(*p)->rb_left; else if (cmp > 0) p = &(*p)->rb_right; else { return 1; } } return 0; } errcode_t o2fsck_strings_insert(o2fsck_strings *strings, char *string, size_t strlen, int *is_dup) { struct rb_node ** p = &strings->s_root.rb_node; struct rb_node * parent = NULL; struct string_entry *se; size_t bytes; int cmp; if (is_dup) *is_dup = 0; while (*p) { parent = *p; se = rb_entry(parent, struct string_entry, s_node); /* we don't actually care about lexographical sorting */ cmp = strlen - se->s_strlen; if (cmp == 0) cmp = memcmp(string, se->s_string, strlen); #if 0 printf("%.*s %s %.*s\n", (int)strlen, string, cmp < 0 ? "<" : (cmp > 0 ? ">" : "==" ), (int)se->s_strlen, se->s_string); #endif if (cmp < 0) p = &(*p)->rb_left; else if (cmp > 0) p = &(*p)->rb_right; else { if (is_dup) *is_dup = 1; return 0; } } bytes = offsetof(struct string_entry, s_string[strlen]); se = malloc(bytes); if (se == NULL) return OCFS2_ET_NO_MEMORY; strings->s_allocated += bytes; se->s_strlen = strlen; memcpy(se->s_string, string, strlen); rb_link_node(&se->s_node, parent, p); rb_insert_color(&se->s_node, &strings->s_root); return 0; } void o2fsck_strings_init(o2fsck_strings *strings) { strings->s_root = RB_ROOT; } void o2fsck_strings_free(o2fsck_strings *strings) { struct string_entry *se; struct rb_node *node; while((node = rb_first(&strings->s_root)) != NULL) { se = rb_entry(node, struct string_entry, s_node); rb_erase(node, &strings->s_root); free(se); } strings->s_allocated = 0; } size_t o2fsck_strings_bytes_allocated(o2fsck_strings *strings) { return strings->s_allocated; } ocfs2-tools-ocfs2-tools-1.8.6/fsck.ocfs2/util.c000066400000000000000000000303661347147137200211530ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * Copyright (C) 2004 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License, version 2, as published by the Free Software Foundation. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * * -- * * Little helpers that are used by all passes. * * XXX * pull more in here.. look in include/pass?.h for incongruities * */ #include #include #include #include #include #include #include #include #include "ocfs2/ocfs2.h" #include "util.h" void o2fsck_write_inode(o2fsck_state *ost, uint64_t blkno, struct ocfs2_dinode *di) { errcode_t ret; const char *whoami = __FUNCTION__; if (blkno != di->i_blkno) { com_err(whoami, OCFS2_ET_INTERNAL_FAILURE, "when asked to " "write an inode with an i_blkno of %"PRIu64" to block " "%"PRIu64, (uint64_t)di->i_blkno, blkno); return; } ret = ocfs2_write_inode(ost->ost_fs, blkno, (char *)di); if (ret) { com_err(whoami, ret, "while writing inode %"PRIu64, (uint64_t)di->i_blkno); ost->ost_saw_error = 1; } } void o2fsck_mark_cluster_allocated(o2fsck_state *ost, uint32_t cluster) { int was_set = 0; errcode_t ret; const char *whoami = __FUNCTION__; o2fsck_bitmap_set(ost->ost_allocated_clusters, cluster, &was_set); if (!was_set) return; if (!ost->ost_duplicate_clusters) { fprintf(stderr, "Duplicate clusters detected. Pass 1b will be run\n"); ret = ocfs2_cluster_bitmap_new(ost->ost_fs, "duplicate clusters", &ost->ost_duplicate_clusters); if (ret) { com_err(whoami, ret, "while allocating duplicate cluster bitmap"); o2fsck_abort(); } } verbosef("Cluster %"PRIu32" is allocated to more than one object\n", cluster); ocfs2_bitmap_set(ost->ost_duplicate_clusters, cluster, NULL); } void o2fsck_mark_clusters_allocated(o2fsck_state *ost, uint32_t cluster, uint32_t num) { while(num--) o2fsck_mark_cluster_allocated(ost, cluster++); } void o2fsck_mark_cluster_unallocated(o2fsck_state *ost, uint32_t cluster) { int was_set; o2fsck_bitmap_clear(ost->ost_allocated_clusters, cluster, &was_set); } errcode_t o2fsck_type_from_dinode(o2fsck_state *ost, uint64_t ino, uint8_t *type) { char *buf = NULL; errcode_t ret; struct ocfs2_dinode *dinode; const char *whoami = __FUNCTION__; *type = 0; ret = ocfs2_malloc_block(ost->ost_fs->fs_io, &buf); if (ret) { com_err(whoami, ret, "while allocating an inode buffer to " "read and discover the type of inode %"PRIu64, ino); goto out; } ret = ocfs2_read_inode(ost->ost_fs, ino, buf); if (ret) { com_err(whoami, ret, "while reading inode %"PRIu64" to " "discover its file type", ino); goto out; } dinode = (struct ocfs2_dinode *)buf; *type = ocfs2_type_by_mode[(dinode->i_mode & S_IFMT)>>S_SHIFT]; out: if (buf) ocfs2_free(&buf); return ret; } size_t o2fsck_bitcount(unsigned char *bytes, size_t len) { static unsigned char nibble_count[16] = { 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4 }; size_t count = 0; for (; len--; bytes++) { count += nibble_count[*bytes >> 4]; count += nibble_count[*bytes & 0xf]; } return count; } errcode_t handle_slots_system_file(ocfs2_filesys *fs, int type, errcode_t (*func)(ocfs2_filesys *fs, struct ocfs2_dinode *di, int slot)) { errcode_t ret; uint64_t blkno; int slot, max_slots; char *buf = NULL; struct ocfs2_dinode *di; ret = ocfs2_malloc_block(fs->fs_io, &buf); if (ret) goto bail; di = (struct ocfs2_dinode *)buf; max_slots = OCFS2_RAW_SB(fs->fs_super)->s_max_slots; for (slot = 0; slot < max_slots; slot++) { ret = ocfs2_lookup_system_inode(fs, type, slot, &blkno); if (ret) goto bail; ret = ocfs2_read_inode(fs, blkno, buf); if (ret) goto bail; if (func) { ret = func(fs, di, slot); if (ret) goto bail; } } bail: if (buf) ocfs2_free(&buf); return ret; } void o2fsck_init_resource_track(struct o2fsck_resource_track *rt, io_channel *channel) { struct rusage r; gettimeofday(&rt->rt_real_time, 0); io_get_stats(channel, &rt->rt_io_stats); memset(&r, 0, sizeof(struct rusage)); getrusage(RUSAGE_SELF, &r); rt->rt_user_time = r.ru_utime; rt->rt_sys_time = r.ru_stime; } static inline float timeval_in_secs(struct timeval *tv) { return tv->tv_sec + ((float)(tv->tv_usec) / 1000000); } static inline void diff_timeval(struct timeval *tv1, struct timeval *tv2) { tv1->tv_sec -= tv2->tv_sec; if (tv1->tv_usec < tv2->tv_usec) { tv1->tv_usec = 1000000 - tv2->tv_usec + tv1->tv_usec; tv1->tv_sec--; } else tv1->tv_usec -= tv2->tv_usec; } static inline void add_timeval(struct timeval *tv1, struct timeval *tv2) { tv1->tv_sec += tv2->tv_sec; tv1->tv_usec += tv2->tv_usec; if (tv1->tv_usec > 1000000) { tv1->tv_sec++; tv1->tv_usec -= 1000000; } } void o2fsck_add_resource_track(struct o2fsck_resource_track *rt1, struct o2fsck_resource_track *rt2) { struct ocfs2_io_stats *io1 = &rt1->rt_io_stats; struct ocfs2_io_stats *io2 = &rt2->rt_io_stats; add_timeval(&rt1->rt_real_time, &rt2->rt_real_time); add_timeval(&rt1->rt_user_time, &rt2->rt_user_time); add_timeval(&rt1->rt_sys_time, &rt2->rt_sys_time); io1->is_bytes_read += io2->is_bytes_read; io1->is_bytes_written += io2->is_bytes_written; io1->is_cache_hits += io2->is_cache_hits; io1->is_cache_misses += io2->is_cache_misses; io1->is_cache_inserts += io2->is_cache_inserts; io1->is_cache_removes += io2->is_cache_removes; } void o2fsck_compute_resource_track(struct o2fsck_resource_track *rt, io_channel *channel) { struct rusage r; struct timeval time_end; struct ocfs2_io_stats _ios, *ios = &_ios; struct ocfs2_io_stats *rtio = &rt->rt_io_stats; getrusage(RUSAGE_SELF, &r); gettimeofday(&time_end, 0); diff_timeval(&r.ru_utime, &rt->rt_user_time); diff_timeval(&r.ru_stime, &rt->rt_sys_time); diff_timeval(&time_end, &rt->rt_real_time); memcpy(&rt->rt_user_time, &r.ru_utime, sizeof(struct timeval)); memcpy(&rt->rt_sys_time, &r.ru_stime, sizeof(struct timeval)); memcpy(&rt->rt_real_time, &time_end, sizeof(struct timeval)); io_get_stats(channel, ios); rtio->is_bytes_read = ios->is_bytes_read - rtio->is_bytes_read; rtio->is_bytes_written = ios->is_bytes_written - rtio->is_bytes_written; rtio->is_cache_hits = ios->is_cache_hits - rtio->is_cache_hits; rtio->is_cache_misses = ios->is_cache_misses - rtio->is_cache_misses; rtio->is_cache_inserts = ios->is_cache_inserts - rtio->is_cache_inserts; rtio->is_cache_removes = ios->is_cache_removes - rtio->is_cache_removes; } void o2fsck_print_resource_track(char *pass, o2fsck_state *ost, struct o2fsck_resource_track *rt, io_channel *channel) { struct ocfs2_io_stats *rtio = &rt->rt_io_stats; uint64_t total_io, cache_read; float rtime_s, utime_s, stime_s, walltime; uint32_t rtime_m, utime_m, stime_m; if (!ost->ost_show_stats) return ; if (pass && !ost->ost_show_extended_stats) return; #define split_time(_t, _m, _s) \ do { \ (_s) = timeval_in_secs(&_t); \ (_m) = (_s) / 60; \ (_s) -= ((_m) * 60); \ } while (0); split_time(rt->rt_real_time, rtime_m, rtime_s); split_time(rt->rt_user_time, utime_m, utime_s); split_time(rt->rt_sys_time, stime_m, stime_s); walltime = timeval_in_secs(&rt->rt_real_time) - timeval_in_secs(&rt->rt_user_time); /* TODO: Investigate why user time is sometimes > wall time*/ if (walltime < 0) walltime = 0; cache_read = (uint64_t)rtio->is_cache_hits * io_get_blksize(channel); total_io = rtio->is_bytes_read + rtio->is_bytes_written; if (!pass) printf(" Cache size: %luMB\n", mbytes(io_get_cache_size(channel))); printf(" I/O read disk/cache: %"PRIu64"MB / %"PRIu64"MB, " "write: %"PRIu64"MB, rate: %.2fMB/s\n", mbytes(rtio->is_bytes_read), mbytes(cache_read), mbytes(rtio->is_bytes_written), (double)(mbytes(total_io) / walltime)); printf(" Times real: %dm%.3fs, user: %dm%.3fs, sys: %dm%.3fs\n", rtime_m, rtime_s, utime_m, utime_s, stime_m, stime_s); } /* Number of blocks available in the I/O cache */ static int cache_blocks; /* * Number of blocks we've currently cached. This is an imperfect guess * designed for pre-caching. Code can keep slurping blocks until * o2fsck_worth_caching() returns 0. */ static int blocks_cached; void o2fsck_init_cache(o2fsck_state *ost, enum o2fsck_cache_hint hint) { errcode_t ret; uint64_t blocks_wanted, av_blocks; int leave_room; ocfs2_filesys *fs = ost->ost_fs; int max_slots = OCFS2_RAW_SB(fs->fs_super)->s_max_slots; uint64_t pages_wanted, avpages; switch (hint) { case O2FSCK_CACHE_MODE_FULL: leave_room = 1; blocks_wanted = fs->fs_blocks; break; case O2FSCK_CACHE_MODE_JOURNAL: /* * We need enough blocks for all the journal * data. Let's guess at 256M journals. */ leave_room = 0; blocks_wanted = (uint64_t)max_slots * 1024 * 1024 * 256; blocks_wanted = ocfs2_bytes_to_blocks(fs, blocks_wanted); break; case O2FSCK_CACHE_MODE_NONE: return; default: assert(0); } verbosef("Want %"PRIu64" blocks for the I/O cache\n", blocks_wanted); /* * leave_room means that we don't want our cache to be taking * all available memory. So we try to get twice as much as we * want; if that works, we know that getting exactly as much as * we want is going to be safe. */ if (leave_room) blocks_wanted <<= 1; if (blocks_wanted > INT_MAX) blocks_wanted = INT_MAX; av_blocks = blocks_wanted; avpages = sysconf(_SC_AVPHYS_PAGES); pages_wanted = blocks_wanted * fs->fs_blocksize / getpagesize(); if (pages_wanted > avpages) av_blocks = avpages * getpagesize() / fs->fs_blocksize; while (blocks_wanted > 0) { io_destroy_cache(fs->fs_io); verbosef("Asking for %"PRIu64" blocks of I/O cache\n", blocks_wanted); if (blocks_wanted > av_blocks) blocks_wanted = av_blocks; ret = io_init_cache(fs->fs_io, blocks_wanted); if (!ret) { /* * We want to pin our cache; there's no point in * having a large cache if half of it is in swap. * However, some callers may not be privileged * enough, so once we get down to a small enough * number (512 blocks), we'll stop caring. */ ret = io_mlock_cache(fs->fs_io); if (ret && (blocks_wanted <= 512)) ret = 0; } if (!ret) { verbosef("Got %"PRIu64" blocks\n", blocks_wanted); /* * We've found an allocation that works. If * we're not leaving room, we're done. But if * we're leaving room, we clear leave_room and go * around again. We expect to succeed there. */ if (!leave_room) { cache_blocks = blocks_wanted; break; } verbosef("Leaving room for other %s\n", "allocations"); leave_room = 0; } blocks_wanted >>= 1; } } int o2fsck_worth_caching(int blocks_to_read) { if ((blocks_to_read + blocks_cached) > cache_blocks) return 0; blocks_cached += blocks_to_read; return 1; } void o2fsck_reset_blocks_cached(void) { blocks_cached = 0; } void __o2fsck_bitmap_set(ocfs2_bitmap *bitmap, uint64_t bitno, int *oldval, const char *where) { errcode_t ret; ret = ocfs2_bitmap_set(bitmap, bitno, oldval); if (ret) { com_err(where, ret, "while trying to set bit %"PRIu64, bitno); o2fsck_abort(); } } void __o2fsck_bitmap_clear(ocfs2_bitmap *bitmap, uint64_t bitno, int *oldval, const char *where) { errcode_t ret; ret = ocfs2_bitmap_clear(bitmap, bitno, oldval); if (ret) { com_err(where, ret, "while trying to clear bit %"PRIu64, bitno); o2fsck_abort(); } } /* * What if we're somewhere we can't set an error and we need to abort fsck? * We don't want to just exit(1), as we may have some cluster locks, etc. * If we SIGTERM ourselves, our signal handler should do the right thing. */ void o2fsck_abort(void) { fprintf(stderr, "Aborting\n"); kill(getpid(), SIGTERM); } ocfs2-tools-ocfs2-tools-1.8.6/fsck.ocfs2/xattr.c000066400000000000000000000466171347147137200213460ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * xattr.c * * Copyright (C) 2004, 2008 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License version 2 as published by the Free Software Foundation. * * 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. */ #include #include #include "ocfs2/byteorder.h" #include "ocfs2/ocfs2.h" #include "xattr.h" #include "extent.h" #include "fsck.h" #include "problem.h" #include "util.h" static const char *whoami = "xattr.c"; #define IS_LAST_ENTRY(entry) (*(uint32_t *)(entry) == 0) #define HEADER_SIZE (sizeof(struct ocfs2_xattr_header)) #define ENTRY_SIZE (sizeof(struct ocfs2_xattr_entry)) #define MIN_VALUE 4 #define XE_OFFSET(xh, xe) ((char *)(xe) - (char *)(xh)) enum xattr_location { IN_INODE = 0, IN_BLOCK, IN_BUCKET }; /* These must be kept in sync with xattr_location */ static const char *xattr_object[] = { [IN_INODE] = "inode", [IN_BLOCK] = "block", [IN_BUCKET] = "bucket", }; struct xattr_info { enum xattr_location location; uint32_t max_offset; uint64_t blkno; }; /* * This use to describe the used area of xattr in inode, block and bucket. * The used area include all xattr structs, such as header, entry, name+value. */ struct used_area { struct list_head ua_list; /* list of used area */ uint16_t ua_offset; /* the offset of the area */ uint16_t ua_length; /* the length of the area */ struct ocfs2_xattr_entry ua_xe; /* store the valid xattr entry */ uint16_t ua_xe_valid; /* whether it is a valid xattr entry */ }; struct used_map { uint16_t um_size; /* the size of the map */ struct list_head um_areas; /* list of used area */ }; static int check_xattr_count(o2fsck_state *ost, struct ocfs2_dinode *di, struct ocfs2_xattr_header *xh, int *changed, struct xattr_info *xi) { struct ocfs2_xattr_entry *entry = xh->xh_entries; struct ocfs2_xattr_entry *pre_xe = entry; uint16_t det_count = 0; uint16_t max_count = (xi->max_offset - HEADER_SIZE) / (ENTRY_SIZE + MIN_VALUE); while (!IS_LAST_ENTRY(entry)) { /* * xattr entries in bucket had sorted by name_hash, * so this can help us to detect count. */ if (xi->location == IN_BUCKET && entry->xe_name_hash < pre_xe->xe_name_hash) break; if (det_count >= max_count) break; det_count++; pre_xe = entry; entry++; } if (xh->xh_count > det_count) { if (prompt(ost, PY, PR_XATTR_COUNT_INVALID, "Extended attributes in %s #%"PRIu64" claims to" " have %u entries, but fsck believes it is %u," " Fix the entries count?", xattr_object[xi->location], xi->blkno, xh->xh_count, det_count)) { xh->xh_count = det_count; if (!det_count && xi->location == IN_BUCKET) { xh->xh_free_start = OCFS2_XATTR_BUCKET_SIZE; xh->xh_name_value_len = 0; } *changed = 1; } else return -1; } return 0; } static struct used_area *new_used_area(uint16_t off, uint16_t len, struct ocfs2_xattr_entry *xe) { struct used_area *ua = NULL; ua = malloc(sizeof(struct used_area)); if (!ua) return NULL; memset(ua, 0 , sizeof(struct used_area)); ua->ua_offset = off; ua->ua_length = len; if (xe) { memcpy(&ua->ua_xe, xe, ENTRY_SIZE); ua->ua_xe_valid = 1; } return ua; } static errcode_t set_used_area(struct used_map *um, uint16_t off, uint16_t len, struct ocfs2_xattr_entry *xe) { struct used_area *new_area = NULL; if (!um) return OCFS2_ET_INVALID_ARGUMENT; new_area = new_used_area(off, len, xe); if (!new_area) { com_err(whoami, OCFS2_ET_NO_MEMORY, "Unable to allocate" " buffer for extended attribute "); return OCFS2_ET_NO_MEMORY; } INIT_LIST_HEAD(&new_area->ua_list); list_add_tail(&new_area->ua_list, &um->um_areas); return 0; } static void clear_used_area(struct used_map *um, uint16_t off, uint16_t len) { struct used_area *area = NULL; struct list_head *ua, *ua2; if (list_empty(&um->um_areas)) return; list_for_each_safe(ua, ua2, &um->um_areas) { area = list_entry(ua, struct used_area, ua_list); if (off == area->ua_offset && len == area->ua_length) { list_del(ua); free(area); return; } } return; } static int check_area_fits(struct used_map *um, uint16_t off, uint16_t len) { struct used_area *area = NULL; struct list_head *ua, *ua2; if (!um || (off + len) > um->um_size) return -1; if (list_empty(&um->um_areas)) return 0; list_for_each_safe(ua, ua2, &um->um_areas) { area = list_entry(ua, struct used_area, ua_list); if ((off + len) <= area->ua_offset) continue; if ((area->ua_offset + area->ua_length) <= off) continue; return -1; } return 0; } static void free_used_map(struct used_map *um) { struct used_area *area = NULL; struct list_head *ua, *ua2; if (list_empty(&um->um_areas)) return; list_for_each_safe(ua, ua2, &um->um_areas) { area = list_entry(ua, struct used_area, ua_list); list_del(ua); free(area); } return; } static errcode_t check_xattr_entry(o2fsck_state *ost, struct ocfs2_dinode *di, struct ocfs2_xattr_header *xh, int *changed, struct xattr_info *xi) { int i, ret = 0; uint16_t count; struct used_map *umap; count = xh->xh_count; umap = malloc(sizeof(struct used_map)); if (!umap) { com_err(whoami, OCFS2_ET_NO_MEMORY, "Unable to allocate" " buffer for extended attribute "); return OCFS2_ET_NO_MEMORY; } umap->um_size = xi->max_offset; INIT_LIST_HEAD(&umap->um_areas); /* set xattr header as used area */ set_used_area(umap, 0, sizeof(struct ocfs2_xattr_header), NULL); for (i = 0 ; i < xh->xh_count; i++) { struct ocfs2_xattr_entry *xe = &xh->xh_entries[i]; uint16_t value_len; uint32_t hash; if (check_area_fits(umap, XE_OFFSET(xh, xe), ENTRY_SIZE)) { if (!prompt(ost, PY, PR_XATTR_ENTRY_INVALID, "Extended attribute entry in %s #%" PRIu64" refers to a used area at %u," " clear this entry?", xattr_object[xi->location], xi->blkno, (uint32_t)XE_OFFSET(xh, xe))) { ret = -1; break; } else goto wipe_entry; } /* check and fix name_offset */ if (xe->xe_name_offset >= xi->max_offset) { if (!prompt(ost, PY, PR_XATTR_NAME_OFFSET_INVALID, "Extended attribute entry in %s #%"PRIu64 " refers to an invalid name offset %u," " clear this entry?", xattr_object[xi->location], xi->blkno, xe->xe_name_offset)) { ret = -1; break; } else goto wipe_entry; } /* check type and value size */ if ((ocfs2_xattr_is_local(xe) && xe->xe_value_size > OCFS2_XATTR_INLINE_SIZE) || (!ocfs2_xattr_is_local(xe) && xe->xe_value_size <= OCFS2_XATTR_INLINE_SIZE)) { char *local; if (ocfs2_xattr_is_local(xe)) local = ""; else local = "not "; if (!prompt(ost, PY, PR_XATTR_LOCATION_INVALID, "Extended attribute entry in %s #%"PRIu64 " claims to have value %sin local, but the" " value size is %"PRIu64 ", clear this entry?", xattr_object[xi->location], xi->blkno, local, (uint64_t)xe->xe_value_size)) { ret = -1; break; } else goto wipe_entry; } /* mark the entry area as used*/ set_used_area(umap, XE_OFFSET(xh, xe), ENTRY_SIZE, xe); /* get the value's real size in inode, block or bucket */ value_len = ocfs2_xattr_value_real_size(xe->xe_name_len, xe->xe_value_size); if (check_area_fits(umap, xe->xe_name_offset, value_len)) { if (!prompt(ost, PY, PR_XATTR_VALUE_INVALID, "Extended attribute entry in %s #%"PRIu64 " refers to a used area at %u," " clear this entry?", xattr_object[xi->location], xi->blkno, xe->xe_name_offset)) { ret = -1; break; } else { clear_used_area(umap, XE_OFFSET(xh, xe), ENTRY_SIZE); goto wipe_entry; } } /* mark the value area as used */ set_used_area(umap, xe->xe_name_offset, value_len, NULL); /* check and fix name hash */ hash = ocfs2_xattr_name_hash( ost->ost_fs->fs_super->id2.i_super.s_uuid_hash, (void *)xh + xe->xe_name_offset, xe->xe_name_len); if (xe->xe_name_hash != hash && prompt(ost, PY, PR_XATTR_HASH_INVALID, "Extended attribute entry in %s #%"PRIu64 " refers to an invalid name hash %u," " Fix the name hash?", xattr_object[xi->location], xi->blkno, xe->xe_name_hash)) { xe->xe_name_hash = hash; *changed = 1; } continue; wipe_entry: /* * we don't wipe entry at here, just reduce the count, * we will wipe them when we finish the check. */ count -= 1; *changed = 1; } if (*changed && xh->xh_count != count) { struct used_area *area = NULL; struct list_head *ua, *ua2; /* * according to used map, remove bad entries from entry area, * and left the name+value in the object. */ i = 0; list_for_each_safe(ua, ua2, &umap->um_areas) { area = list_entry(ua, struct used_area, ua_list); if (!area->ua_xe_valid) continue; memcpy(&xh->xh_entries[i], &area->ua_xe, ENTRY_SIZE); i++; } xh->xh_count = i; } free_used_map(umap); free(umap); return ret; } static errcode_t check_xattr_value(o2fsck_state *ost, struct ocfs2_dinode *di, struct ocfs2_xattr_header *xh, uint64_t start, int *changed) { int i; struct extent_info ei = {0, }; errcode_t ret = 0; uint64_t owner; ei.chk_rec_func = o2fsck_check_extent_rec; ei.mark_rec_alloc_func = o2fsck_mark_tree_clusters_allocated; ei.para = di; for (i = 0 ; i < xh->xh_count; i++) { int change = 0; struct ocfs2_xattr_entry *xe = &xh->xh_entries[i]; if (!ocfs2_xattr_is_local(xe)) { int offset = xe->xe_name_offset + OCFS2_XATTR_SIZE(xe->xe_name_len); struct ocfs2_xattr_value_root *xv = (struct ocfs2_xattr_value_root *) ((void *)xh + offset); struct ocfs2_extent_list *el = &xv->xr_list; owner = start + offset / ost->ost_fs->fs_blocksize; ret = check_el(ost, &ei, owner, el, 1, 0, 0, &change); if (ret) return ret; if (change) *changed = 1; } } return ret; } static errcode_t check_xattr(o2fsck_state *ost, struct ocfs2_dinode *di, struct ocfs2_xattr_header *xh, int *changed, struct xattr_info *xi) { errcode_t ret; uint16_t min_offs, total_len; /* At first we check and fix the total xattr entry count */ if (check_xattr_count(ost, di, xh, changed, xi)) return 0; /* then check and fix the xattr entry */ if (check_xattr_entry(ost, di, xh, changed, xi)) return 0; ret = check_xattr_value(ost, di, xh, xi->blkno, changed); if (ret) return ret; if (xi->location == IN_BUCKET) { /* check and fix xh_free_start */ min_offs = ocfs2_xattr_min_offset(xh, xi->max_offset); if (xh->xh_free_start != min_offs && prompt(ost, PY, PR_XATTR_FREE_START_INVALID, "Extended attribute in %s #%"PRIu64" claims to" " have free space start at %u , but fsck believes" " it is %u, Fix the value of free start?", xattr_object[xi->location], xi->blkno, xh->xh_free_start, min_offs)) { xh->xh_free_start = min_offs; *changed = 1; } /* check and fix xh_name_value_len */ total_len = ocfs2_xattr_name_value_len(xh); if (xh->xh_name_value_len != total_len && prompt(ost, PY, PR_XATTR_VALUE_LEN_INVALID, "Extended attribute in %s #%"PRIu64" claims to have" " the total length %u of all EAs name and value" " in this object, but fsck believes it is %u," " Fix the value of the total length?", xattr_object[xi->location], xi->blkno, xh->xh_name_value_len, total_len)) { xh->xh_name_value_len = total_len; *changed = 1; } } return 0; } static uint16_t detect_xattr_bucket_count(char *bucket, uint32_t max_buckets) { int i; char *bucket_buf = NULL; struct ocfs2_xattr_header *xh; uint16_t max_count, max_offset; max_offset = OCFS2_XATTR_BUCKET_SIZE; max_count = (OCFS2_XATTR_BUCKET_SIZE - HEADER_SIZE) / (ENTRY_SIZE + MIN_VALUE); bucket_buf = bucket; for (i = 0; i < max_buckets; i++) { xh = (struct ocfs2_xattr_header *)bucket_buf; if (xh->xh_count < max_count && xh->xh_free_start > xh->xh_count * ENTRY_SIZE && xh->xh_free_start <= max_offset && xh->xh_name_value_len <= max_offset - xh->xh_free_start) { bucket_buf += OCFS2_XATTR_BUCKET_SIZE; continue; } else return i; } return i; } static errcode_t ocfs2_check_xattr_buckets(o2fsck_state *ost, struct ocfs2_dinode *di, uint64_t blkno, uint32_t clusters) { int i; errcode_t ret = 0; char *bucket = NULL; char *bucket_buf = NULL; struct ocfs2_xattr_header *xh; int blk_per_bucket = ocfs2_blocks_per_xattr_bucket(ost->ost_fs); uint32_t bpc = ocfs2_xattr_buckets_per_cluster(ost->ost_fs); uint32_t max_buckets = clusters * bpc; uint32_t max_blocks = max_buckets * blk_per_bucket; uint32_t num_buckets = 0; uint64_t blk = 0; /* malloc space for all buckets */ ret = ocfs2_malloc_blocks(ost->ost_fs->fs_io, max_blocks, &bucket); if (ret) { com_err(whoami, ret, "while allocating room to read" " extended attributes bucket"); goto out; } /* read all buckets for detect (some of them may not be used) */ bucket_buf = bucket; blk = blkno; for (i = 0; i < max_buckets; i++) { ret = ocfs2_read_xattr_bucket(ost->ost_fs, blk, bucket_buf); if (ret) { max_buckets = i; break; } blk += blk_per_bucket; bucket_buf += OCFS2_XATTR_BUCKET_SIZE; } /* * The real bucket num in this series of blocks is stored * in the 1st bucket. */ xh = (struct ocfs2_xattr_header *)bucket; if (xh->xh_num_buckets == 0 || xh->xh_num_buckets > max_buckets) { num_buckets = detect_xattr_bucket_count(bucket, max_buckets); if (prompt(ost, PY, PR_XATTR_BUCKET_COUNT_INVALID, "Extended attribute buckets start at %"PRIu64 " claims to have %u buckets, but fsck believes" " it is %u, Fix the bucket count?", blkno, xh->xh_num_buckets, num_buckets ? num_buckets : 1)) { if (num_buckets == 0) { /* * If buckets count is 0, we need clean * xh_count and set xh_num_buckets to 1. */ xh->xh_count = 0; xh->xh_free_start = OCFS2_XATTR_BUCKET_SIZE; xh->xh_num_buckets = 1; } else xh->xh_num_buckets = num_buckets; /* only update first bucket */ ret = ocfs2_write_xattr_bucket(ost->ost_fs, blkno, bucket); if (ret) { com_err(whoami, ret, "while writing bucket of" " extended attributes "); goto out; } if (num_buckets == 0) goto out; } else goto out; } else num_buckets = xh->xh_num_buckets; bucket_buf = bucket; for (i = 0; i < num_buckets; i++) { int changed = 0; struct xattr_info xi = { .location = IN_BUCKET, .max_offset = OCFS2_XATTR_BUCKET_SIZE, .blkno = blkno, }; xh = (struct ocfs2_xattr_header *)bucket_buf; ret = check_xattr(ost, di, xh, &changed, &xi); if (ret) break; if (changed) { ret = ocfs2_write_xattr_bucket(ost->ost_fs, blkno, bucket_buf); if (ret) { com_err(whoami, ret, "while writing bucket of" " extended attributes "); goto out; } } blkno += blk_per_bucket; bucket_buf += OCFS2_XATTR_BUCKET_SIZE; } out: if (bucket) ocfs2_free(&bucket); return ret; } static errcode_t o2fsck_check_xattr_index_block(o2fsck_state *ost, struct ocfs2_dinode *di, struct ocfs2_xattr_block *xb, int *changed) { struct ocfs2_extent_list *el = &xb->xb_attrs.xb_root.xt_list; errcode_t ret = 0; uint32_t name_hash = UINT_MAX, e_cpos = 0, num_clusters = 0; uint64_t p_blkno = 0; struct extent_info ei = {0, }; if (!el->l_next_free_rec) return 0; ei.chk_rec_func = o2fsck_check_extent_rec; ei.mark_rec_alloc_func = o2fsck_mark_tree_clusters_allocated; ei.para = di; ret = check_el(ost, &ei, xb->xb_blkno, el, ocfs2_xattr_recs_per_xb(ost->ost_fs->fs_blocksize), 0, 0, changed); if (ret) return ret; /* * We need to write the changed xattr tree first so that the following * ocfs2_xattr_get_rec can get the updated information. */ if (*changed) { ret = ocfs2_write_xattr_block(ost->ost_fs, di->i_xattr_loc, (char *)xb); if (ret) { com_err(whoami, ret, "while writing root block of" " extended attributes "); return ret; } } while (name_hash > 0) { ret = ocfs2_xattr_get_rec(ost->ost_fs, xb, name_hash, &p_blkno, &e_cpos, &num_clusters); if (ret) { com_err(whoami, ret, "while getting bucket record" " of extended attributes "); goto out; } ret = ocfs2_check_xattr_buckets(ost, di, p_blkno, num_clusters); if (ret) { com_err(whoami, ret, "while iterating bucket" " of extended attributes "); goto out; } if (e_cpos == 0) break; name_hash = e_cpos - 1; } out: return ret; } static errcode_t o2fsck_check_xattr_block(o2fsck_state *ost, struct ocfs2_dinode *di, int *i_changed) { errcode_t ret; char *blk = NULL; struct ocfs2_xattr_block *xb = NULL; int b_changed = 0; ret = ocfs2_malloc_block(ost->ost_fs->fs_io, &blk); if (ret) { com_err(whoami, ret, "while allocating room to read block" "of extended attribute "); return ret; } ret = ocfs2_read_xattr_block(ost->ost_fs, di->i_xattr_loc, blk); if (ret) { com_err(whoami, ret, "while reading externel block of" " extended attributes "); goto out; } xb = (struct ocfs2_xattr_block *)blk; if (strcmp((char *)xb->xb_signature, OCFS2_XATTR_BLOCK_SIGNATURE)) { if (prompt(ost, PY, PR_XATTR_BLOCK_INVALID, "Extended attributes block %"PRIu64" has bad signature" " %.*s, remove this block?", (uint64_t)di->i_xattr_loc, 7, xb->xb_signature)) { di->i_xattr_loc = 0; *i_changed = 1; } goto out; } if (!(xb->xb_flags & OCFS2_XATTR_INDEXED)) { struct ocfs2_xattr_header *xh = &xb->xb_attrs.xb_header; struct xattr_info xi = { .location = IN_BLOCK, .max_offset = ost->ost_fs->fs_blocksize - offsetof(struct ocfs2_xattr_block, xb_attrs.xb_header), .blkno = di->i_xattr_loc, }; ret = check_xattr(ost, di, xh, &b_changed, &xi); } else ret = o2fsck_check_xattr_index_block(ost, di, xb, &b_changed); if (!ret && b_changed) { ret = ocfs2_write_xattr_block(ost->ost_fs, di->i_xattr_loc, blk); if (ret) com_err(whoami, ret, "while writing externel block of" " extended attributes "); } out: if (blk) ocfs2_free(&blk); return ret; } static errcode_t o2fsck_check_xattr_ibody(o2fsck_state *ost, struct ocfs2_dinode *di, int *i_changed) { struct ocfs2_xattr_header *xh = NULL; struct xattr_info xi = { .location = IN_INODE, .max_offset = di->i_xattr_inline_size, .blkno = di->i_blkno, }; xh = (struct ocfs2_xattr_header *) ((void *)di + ost->ost_fs->fs_blocksize - di->i_xattr_inline_size); return check_xattr(ost, di, xh, i_changed, &xi); } /* * o2fsck_check_xattr * * Check extended attribute in inode block or external block. */ errcode_t o2fsck_check_xattr(o2fsck_state *ost, struct ocfs2_dinode *di) { errcode_t ret = 0; int i_changed = 0; if (!(di->i_dyn_features & OCFS2_HAS_XATTR_FL)) return 0; if (di->i_dyn_features & OCFS2_INLINE_XATTR_FL) { ret = o2fsck_check_xattr_ibody(ost, di, &i_changed); if (ret) return ret; if (i_changed) { o2fsck_write_inode(ost, di->i_blkno, di); i_changed = 0; } } if (di->i_xattr_loc) ret = o2fsck_check_xattr_block(ost, di, &i_changed); if (!ret && i_changed) o2fsck_write_inode(ost, di->i_blkno, di); return ret; } ocfs2-tools-ocfs2-tools-1.8.6/fswreck/000077500000000000000000000000001347147137200175255ustar00rootroot00000000000000ocfs2-tools-ocfs2-tools-1.8.6/fswreck/.gitignore000066400000000000000000000000331347147137200215110ustar00rootroot00000000000000cscope.* fswreck *.d *.sw? ocfs2-tools-ocfs2-tools-1.8.6/fswreck/Cscope.make000066400000000000000000000005321347147137200216000ustar00rootroot00000000000000.PHONY: cscope cscope: rm -f cscope.* echo "-k" >> cscope.files echo "-I inc" >> cscope.files find . -maxdepth 2 -name '*.c' -print >>cscope.files find . -maxdepth 2 -name '*.h' -print >>cscope.files find ../libocfs2/ -maxdepth 2 -name '*.h' -print >>cscope.files find ../libocfs2/ -maxdepth 2 -name '*.c' -print >>cscope.files cscope -b ocfs2-tools-ocfs2-tools-1.8.6/fswreck/Makefile000066400000000000000000000027021347147137200211660ustar00rootroot00000000000000TOPDIR = .. include $(TOPDIR)/Preamble.make DEFINES = -DG_DISABLE_DEPRECATED -DLINUX DEFINES += -DVERSION=\"$(VERSION)\" INCLUDES = -I$(TOPDIR)/include -Iinclude INCLUDES += $(GLIB_CFLAGS) UNINST_PROGRAMS = fswreck CFILES = main.c corrupt.c chain.c extent.c group.c inode.c local_alloc.c truncate_log.c special.c symlink.c dir.c journal.c quota.c refcount.c discontig_bg.c HFILES = \ include/chain.h \ include/dir.h \ include/fsck_type.h \ include/inode.h \ include/local_alloc.h \ include/special.h \ include/truncate_log.h \ include/corrupt.h \ include/extent.h \ include/group.h \ include/journal.h \ include/main.h \ include/symlink.h \ include/quota.h \ include/refcount.h \ include/discontig_bg.h DIST_FILES = $(CFILES) $(HFILES) DIST_RULES = dist-subdircreate OBJS = $(subst .c,.o,$(CFILES)) LIBOCFS2_LIBS = -L$(TOPDIR)/libocfs2 -locfs2 LIBOCFS2_DEPS = $(TOPDIR)/libocfs2/libocfs2.a LIBO2DLM_LIBS = -L$(TOPDIR)/libo2dlm -lo2dlm $(DL_LIBS) LIBO2DLM_DEPS = $(TOPDIR)/libo2dlm/libo2dlm.a ifneq ($(BUILD_FSDLM_SUPPORT),) LIBO2CB_LIBS = -L$(TOPDIR)/libo2cb -lo2cb -ldlm_lt else LIBO2CB_LIBS = -L$(TOPDIR)/libo2cb -lo2cb endif LIBO2CB_DEPS = $(TOPDIR)/libo2cb/libo2cb.a dist-subdircreate: $(TOPDIR)/mkinstalldirs $(DIST_DIR)/include fswreck: $(OBJS) $(LIBOCFS2_DEPS) $(LIBO2DLM_DEPS) $(LIBO2CB_DEPS) $(LINK) $(LIBOCFS2_LIBS) $(LIBO2DLM_LIBS) $(LIBO2CB_LIBS) $(GLIB_LIBS) $(COM_ERR_LIBS) $(AIO_LIBS) include $(TOPDIR)/Postamble.make ocfs2-tools-ocfs2-tools-1.8.6/fswreck/chain.c000066400000000000000000000210111347147137200207460ustar00rootroot00000000000000/* * chain.c * * chain group corruptions * * Copyright (C) 2006 Oracle. All rights reserved. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * */ #include "main.h" extern char *progname; static void mess_up_sys_file(ocfs2_filesys *fs, uint64_t blkno, enum fsck_type type) { errcode_t ret; char *buf = NULL, *bufgroup = NULL; struct ocfs2_dinode *di; struct ocfs2_chain_list *cl; struct ocfs2_chain_rec *cr; uint64_t oldblkno; struct ocfs2_group_desc *bg = NULL; ret = ocfs2_malloc_block(fs->fs_io, &buf); if (ret) FSWRK_COM_FATAL(progname, ret); ret = ocfs2_read_inode(fs, blkno, buf); if (ret) FSWRK_COM_FATAL(progname, ret); di = (struct ocfs2_dinode *)buf; if (!(di->i_flags & OCFS2_BITMAP_FL)) FSWRK_COM_FATAL(progname, ret); if (!(di->i_flags & OCFS2_CHAIN_FL)) FSWRK_COM_FATAL(progname, ret); cl = &(di->id2.i_chain); /* for CHAIN_EMPTY, CHAIN_HEAD_LINK_RANGE, CHAIN_LINK_RANGE, * CHAIN_BITS, CHAIN_LINK_GEN, CHAIN_LINK_MAGIC, * we need to corrupt some chain rec, so check it first. */ if (type == CHAIN_EMPTY || type == CHAIN_HEAD_LINK_RANGE || type == CHAIN_LINK_RANGE || type == CHAIN_BITS || type == CHAIN_LINK_GEN || type == CHAIN_LINK_MAGIC) if (!cl->cl_next_free_rec) { FSWRK_WARN("No chain record found at block#%"PRIu64 ",so can't corrupt it for type[%d].\n", blkno, type); goto bail; } switch (type) { case CHAIN_COUNT: fprintf(stdout, "Corrupt CHAIN_COUNT: " "Modified cl_count " "in block#%"PRIu64" from %u to %u\n", blkno, cl->cl_count, (cl->cl_count + 100)); cl->cl_count += 100; break; case CHAIN_NEXT_FREE: fprintf(stdout, "Corrupt CHAIN_NEXT_FREE:" " Modified cl_next_free_rec " "in block#%"PRIu64" from %u to %u\n", blkno, cl->cl_next_free_rec, (cl->cl_count + 10)); cl->cl_next_free_rec = cl->cl_count + 10; break; case CHAIN_EMPTY: cr = cl->cl_recs; fprintf(stdout, "Corrupt CHAIN_EMPTY:" " Modified e_blkno " "in block#%"PRIu64" from %"PRIu64" to 0\n", blkno, (uint64_t)cr->c_blkno); cr->c_blkno = 0; break; case CHAIN_I_CLUSTERS: fprintf(stdout, "Corrupt CHAIN_I_CLUSTERS:" "change i_clusters in block#%"PRIu64" from %u to %u\n", blkno, di->i_clusters, (di->i_clusters + 10)); di->i_clusters += 10; break; case CHAIN_I_SIZE: fprintf(stdout, "Corrupt CHAIN_I_SIZE:" "change i_size " "in block#%"PRIu64" from %"PRIu64" to %"PRIu64"\n", blkno, (uint64_t)di->i_size, ((uint64_t)di->i_size + 10)); di->i_size += 10; break; case CHAIN_GROUP_BITS: fprintf(stdout, "Corrupt CHAIN_GROUP_BITS:" "change i_used of bitmap " "in block#%"PRIu64" from %u to %u\n", blkno, di->id1.bitmap1.i_used, (di->id1.bitmap1.i_used + 10)); di->id1.bitmap1.i_used += 10; break; case CHAIN_HEAD_LINK_RANGE: cr = cl->cl_recs; oldblkno = cr->c_blkno; cr->c_blkno = ocfs2_clusters_to_blocks(fs, fs->fs_clusters) + 10; fprintf(stdout, "Corrupt CHAIN_HEAD_LINK_RANGE:" "change " "in block#%"PRIu64" from %"PRIu64" to %"PRIu64"\n", blkno, oldblkno, (uint64_t)cr->c_blkno); break; case CHAIN_LINK_GEN: case CHAIN_LINK_MAGIC: case CHAIN_LINK_RANGE: ret = ocfs2_malloc_block(fs->fs_io, &bufgroup); if (ret) FSWRK_COM_FATAL(progname, ret); bg = (struct ocfs2_group_desc *)bufgroup; cr = cl->cl_recs; ret = ocfs2_read_group_desc(fs, cr->c_blkno, (char *)bg); if (ret) FSWRK_COM_FATAL(progname, ret); if (type == CHAIN_LINK_GEN) { fprintf(stdout, "Corrupt CHAIN_LINK_GEN: " "change generation num from %u to 0x1234\n", bg->bg_generation); bg->bg_generation = 0x1234; } else if (type == CHAIN_LINK_MAGIC) { fprintf(stdout, "Corrupt CHAIN_LINK_MAGIC: " "change signature to '1234'\n"); sprintf((char *)bg->bg_signature,"1234"); } else { oldblkno = bg->bg_next_group; bg->bg_next_group = ocfs2_clusters_to_blocks(fs, fs->fs_clusters) + 10; fprintf(stdout, "Corrupt CHAIN_LINK_RANGE: " "change next group from %"PRIu64" to %"PRIu64 " \n", oldblkno, (uint64_t)bg->bg_next_group); } ret = ocfs2_write_group_desc(fs, cr->c_blkno, (char *)bg); if (ret) FSWRK_COM_FATAL(progname, ret); break; case CHAIN_BITS: cr = cl->cl_recs; fprintf(stdout, "Corrupt CHAIN_BITS:" "change inode#%"PRIu64" c_total from %u to %u\n", blkno, cr->c_total, (cr->c_total + 10)); cr->c_total += 10; break; case CHAIN_CPG: fprintf(stdout, "Corrupt CHAIN_CPG: " "change cl_cpg of global_bitmap from %u to %u.\n", cl->cl_cpg, (cl->cl_cpg + 16)); cl->cl_cpg += 16; cl->cl_next_free_rec = 1; break; default: FSWRK_FATAL("Unknown fsck_type[%d]\n", type); } ret = ocfs2_write_inode(fs, blkno, buf); if (ret) FSWRK_COM_FATAL(progname, ret); bail: if (bufgroup) ocfs2_free(&bufgroup); if (buf) ocfs2_free(&buf); return ; } static void mess_up_sys_chains(ocfs2_filesys *fs, uint16_t slotnum, enum fsck_type type) { errcode_t ret; char sysfile[OCFS2_MAX_FILENAME_LEN]; uint64_t blkno; struct ocfs2_super_block *sb = OCFS2_RAW_SB(fs->fs_super); if (slotnum == UINT16_MAX) snprintf(sysfile, sizeof(sysfile), "%s", ocfs2_system_inodes[GLOBAL_BITMAP_SYSTEM_INODE].si_name); else snprintf(sysfile, sizeof(sysfile), ocfs2_system_inodes[INODE_ALLOC_SYSTEM_INODE].si_name, slotnum); ret = ocfs2_lookup(fs, sb->s_system_dir_blkno, sysfile, strlen(sysfile), NULL, &blkno); if (ret) FSWRK_COM_FATAL(progname, ret); mess_up_sys_file(fs, blkno, type); return ; } void mess_up_chains_list(ocfs2_filesys *fs, enum fsck_type type, uint16_t slotnum) { mess_up_sys_chains(fs, slotnum, type); } void mess_up_chains_rec(ocfs2_filesys *fs, enum fsck_type type, uint16_t slotnum) { mess_up_sys_chains(fs, slotnum, type); } void mess_up_chains_inode(ocfs2_filesys *fs, enum fsck_type type, uint16_t slotnum) { mess_up_sys_chains(fs, slotnum, type); } void mess_up_chains_group(ocfs2_filesys *fs, enum fsck_type type, uint16_t slotnum) { mess_up_sys_chains(fs, slotnum, type); } void mess_up_chains_group_magic(ocfs2_filesys *fs, enum fsck_type type, uint16_t slotnum) { mess_up_sys_chains(fs, slotnum, type); } void mess_up_chains_cpg(ocfs2_filesys *fs, enum fsck_type type, uint16_t slotnum) { errcode_t ret; char sysfile[OCFS2_MAX_FILENAME_LEN]; uint64_t blkno; struct ocfs2_super_block *sb = OCFS2_RAW_SB(fs->fs_super); snprintf(sysfile, sizeof(sysfile), "%s", ocfs2_system_inodes[GLOBAL_BITMAP_SYSTEM_INODE].si_name); ret = ocfs2_lookup(fs, sb->s_system_dir_blkno, sysfile, strlen(sysfile), NULL, &blkno); if (ret) FSWRK_COM_FATAL(progname, ret); mess_up_sys_file(fs, blkno, CHAIN_CPG); return; } static void mess_up_superblock_clusters(ocfs2_filesys *fs, int excess) { errcode_t ret; char *buf = NULL; struct ocfs2_dinode *di = fs->fs_super; uint32_t new_clusters, cpg, wrong; /* corrupt superblock, just copy the * superblock, change it and * write it back to disk. */ ret = ocfs2_malloc_block(fs->fs_io, &buf); if (ret) FSWRK_COM_FATAL(progname, ret); memcpy(buf, (char *)di, fs->fs_blocksize); di = (struct ocfs2_dinode *)buf; cpg = 8 * ocfs2_group_bitmap_size(fs->fs_blocksize, 0, OCFS2_RAW_SB(fs->fs_super)->s_feature_incompat); /* make the wrong value to 2.5 times of cluster_per_group. */ wrong = cpg * 2 + cpg / 2; if (excess) new_clusters = di->i_clusters + wrong; else new_clusters = di->i_clusters - wrong; fprintf(stdout, "Corrupt SUPERBLOCK_CLUSTERS: " "change superblock i_clusters from %u to %u.\n", di->i_clusters, new_clusters); di->i_clusters = new_clusters; ret = io_write_block(fs->fs_io, di->i_blkno, 1, buf); if (ret) FSWRK_COM_FATAL(progname, ret); if(buf) ocfs2_free(&buf); return; } void mess_up_superblock_clusters_excess(ocfs2_filesys *fs, enum fsck_type type, uint16_t slotnum) { mess_up_superblock_clusters(fs, 1); } void mess_up_superblock_clusters_lack(ocfs2_filesys *fs, enum fsck_type type, uint16_t slotnum) { mess_up_superblock_clusters(fs, 0); } ocfs2-tools-ocfs2-tools-1.8.6/fswreck/corrupt.c000066400000000000000000000243241347147137200213740ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * corrupt.c * * corruption routines * * Copyright (C) 2006 Oracle. All rights reserved. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * */ #include "main.h" extern char *progname; void create_named_directory(ocfs2_filesys *fs, char *dirname, uint64_t *blkno) { errcode_t ret; struct ocfs2_super_block *sb = OCFS2_RAW_SB(fs->fs_super); ret = ocfs2_lookup(fs, sb->s_root_blkno, dirname, strlen(dirname), NULL, blkno); if (!ret) return; else if (ret != OCFS2_ET_FILE_NOT_FOUND) FSWRK_COM_FATAL(progname, ret); ret = ocfs2_new_inode(fs, blkno, S_IFDIR | 0755); if (ret) FSWRK_COM_FATAL(progname, ret); ret = ocfs2_init_dir(fs, *blkno, fs->fs_root_blkno); if (ret) FSWRK_COM_FATAL(progname, ret); ret = ocfs2_link(fs, fs->fs_root_blkno, dirname, *blkno, OCFS2_FT_DIR); if (ret) FSWRK_COM_FATAL(progname, ret); return; } void corrupt_file(ocfs2_filesys *fs, enum fsck_type type, uint16_t slotnum) { void (*func)(ocfs2_filesys *fs, enum fsck_type type, uint64_t blkno) = NULL; uint64_t blkno; switch (type) { case EB_BLKNO: func = mess_up_extent_block; break; case EB_GEN: func = mess_up_extent_block; break; case EB_GEN_FIX: func = mess_up_extent_block; break; case EXTENT_EB_INVALID: func = mess_up_extent_block; break; case EXTENT_MARKED_UNWRITTEN: case EXTENT_MARKED_REFCOUNTED: case EXTENT_BLKNO_UNALIGNED: func = mess_up_extent_record; break; case EXTENT_CLUSTERS_OVERRUN: func = mess_up_extent_record; break; case EXTENT_BLKNO_RANGE: func = mess_up_extent_record; break; case EXTENT_OVERLAP: func = mess_up_extent_record; break; case EXTENT_HOLE: func = mess_up_extent_record; break; case EXTENT_LIST_DEPTH: func = mess_up_extent_list; break; case EXTENT_LIST_COUNT: func = mess_up_extent_list; break; case EXTENT_LIST_FREE: func = mess_up_extent_list; break; case INODE_SUBALLOC: func = mess_up_inode_field; break; case INODE_GEN: func = mess_up_inode_field; break; case INODE_GEN_FIX: func = mess_up_inode_field; break; case INODE_BLKNO: func = mess_up_inode_field; break; case INODE_NZ_DTIME: func = mess_up_inode_field; break; case INODE_SIZE: func = mess_up_inode_field; break; case INODE_SPARSE_SIZE: func = mess_up_inode_field; break; case INODE_CLUSTERS: func = mess_up_inode_field; break; case INODE_SPARSE_CLUSTERS: func = mess_up_inode_field; break; case INODE_COUNT: func = mess_up_inode_field; break; case INODE_NOT_CONNECTED: func = mess_up_inode_not_connected; break; case LINK_FAST_DATA: func = mess_up_symlink; break; case LINK_NULLTERM: func = mess_up_symlink; break; case LINK_SIZE: func = mess_up_symlink; break; case LINK_BLOCKS: func = mess_up_symlink; break; case ROOT_NOTDIR: func = mess_up_root; break; case ROOT_DIR_MISSING: func = mess_up_root; break; case LOSTFOUND_MISSING: func = mess_up_root; break; case DIR_ZERO: func = mess_up_dir_inode; break; case DIR_HOLE: func = mess_up_dir_inode; break; case DIRENT_DOTTY_DUP: func = mess_up_dir_ent; break; case DIRENT_NOT_DOTTY: func = mess_up_dir_ent; break; case DIRENT_DOT_INODE: func = mess_up_dir_ent; break; case DIRENT_DOT_EXCESS: func = mess_up_dir_ent; break; case DIR_DOTDOT: func = mess_up_dir_ent; break; case DIRENT_ZERO: func = mess_up_dir_ent; break; case DIRENT_NAME_CHARS: func = mess_up_dir_ent; break; case DIRENT_INODE_RANGE: func = mess_up_dir_ent; break; case DIRENT_INODE_FREE: func = mess_up_dir_ent; break; case DIRENT_TYPE: func = mess_up_dir_ent; break; case DIRENT_DUPLICATE: func = mess_up_dir_ent; break; case DIRENT_LENGTH: func = mess_up_dir_ent; break; case DIR_PARENT_DUP: func = mess_up_dir_parent_dup; break; case DIR_NOT_CONNECTED: func = mess_up_dir_not_connected; break; case INLINE_DATA_FLAG_INVALID: func = mess_up_inline_flag; break; case INLINE_DATA_COUNT_INVALID: func = mess_up_inline_inode; break; case INODE_INLINE_SIZE: func = mess_up_inline_inode; break; case INODE_INLINE_CLUSTERS: func = mess_up_inline_inode; break; case DUP_CLUSTERS_CLONE: case DUP_CLUSTERS_DELETE: case DUP_CLUSTERS_SYSFILE_CLONE: func = mess_up_dup_clusters; break; case REFCOUNT_FLAG_INVALID: func = mess_up_inode_field; break; case REFCOUNT_LOC_INVALID: func = mess_up_inode_field; break; case INODE_BLOCK_ECC: func = mess_up_inode_field; break; case INODE_VALID_FLAG: func = mess_up_inode_field; break; default: FSWRK_FATAL("Invalid code=%d", type); } create_named_directory(fs, "tmp", &blkno); if (func) func(fs, type, blkno); return; } void corrupt_sys_file(ocfs2_filesys *fs, enum fsck_type type, uint16_t slotnum) { void (*func)(ocfs2_filesys *fs, enum fsck_type type, uint16_t slotnum) = NULL; switch (type) { case CHAIN_COUNT: func = mess_up_chains_list; break; case CHAIN_NEXT_FREE: func = mess_up_chains_list; break; case CHAIN_EMPTY: func = mess_up_chains_rec; break; case CHAIN_HEAD_LINK_RANGE: func = mess_up_chains_rec; break; case CHAIN_BITS: func = mess_up_chains_rec; break; case CHAIN_I_CLUSTERS: func = mess_up_chains_inode; break; case CHAIN_I_SIZE: func = mess_up_chains_inode; break; case CHAIN_GROUP_BITS: func = mess_up_chains_inode; break; case CHAIN_LINK_GEN: func = mess_up_chains_group; break; case CHAIN_LINK_RANGE: func = mess_up_chains_group; break; case CHAIN_LINK_MAGIC: func = mess_up_chains_group_magic; break; case CHAIN_CPG: func = mess_up_chains_cpg; break; case SUPERBLOCK_CLUSTERS_EXCESS: func = mess_up_superblock_clusters_excess; break; case SUPERBLOCK_CLUSTERS_LACK: func = mess_up_superblock_clusters_lack; break; case INODE_ORPHANED: func = mess_up_inode_orphaned; break; case INODE_ALLOC_REPAIR: func = mess_up_inode_alloc; break; case JOURNAL_FILE_INVALID: case JOURNAL_UNKNOWN_FEATURE: case JOURNAL_MISSING_FEATURE: case JOURNAL_TOO_SMALL: func = mess_up_journal; break; case QMAGIC_INVALID: case QTREE_BLK_INVALID: case DQBLK_INVALID: case DUP_DQBLK_INVALID: case DUP_DQBLK_VALID: func = mess_up_quota; break; default: FSWRK_FATAL("Invalid code=%d", type); } if (func) func(fs, type, slotnum); return; } void corrupt_group_desc(ocfs2_filesys *fs, enum fsck_type type, uint16_t slotnum) { void (*func)(ocfs2_filesys *fs, enum fsck_type type, uint16_t slotnum) = NULL; switch (type) { case GROUP_PARENT: func = mess_up_group_minor; break; case GROUP_BLKNO: func = mess_up_group_minor; break; case GROUP_CHAIN: func = mess_up_group_minor; break; case GROUP_FREE_BITS: func = mess_up_group_minor; break; case GROUP_CHAIN_LOOP: func = mess_up_group_minor; break; case GROUP_GEN: func = mess_up_group_gen; break; case GROUP_UNEXPECTED_DESC: func = mess_up_group_list; break; case GROUP_EXPECTED_DESC: func = mess_up_group_list; break; case CLUSTER_GROUP_DESC: func = mess_up_cluster_group_desc; break; case CLUSTER_ALLOC_BIT: func = mess_up_cluster_alloc_bits; break; default: FSWRK_FATAL("Invalid code=%d", type); } if (func) func(fs, type, slotnum); return; } void corrupt_local_alloc(ocfs2_filesys *fs, enum fsck_type type, uint16_t slotnum) { void (*func)(ocfs2_filesys *fs, enum fsck_type type, uint16_t slotnum) = NULL; switch (type) { case LALLOC_SIZE: func = mess_up_local_alloc_empty; break; case LALLOC_NZ_USED: func = mess_up_local_alloc_empty; break; case LALLOC_NZ_BM: func = mess_up_local_alloc_empty; break; case LALLOC_BM_OVERRUN: func = mess_up_local_alloc_bitmap; break; case LALLOC_BM_STRADDLE: func = mess_up_local_alloc_bitmap; break; case LALLOC_BM_SIZE: func = mess_up_local_alloc_bitmap; break; case LALLOC_USED_OVERRUN: func = mess_up_local_alloc_used; break; case LALLOC_CLEAR: func = mess_up_local_alloc_used; break; default: FSWRK_FATAL("Invalid code = %d", type); } if (func) func(fs, type, slotnum); return; } void corrupt_truncate_log(ocfs2_filesys *fs, enum fsck_type type, uint16_t slotnum) { void (*func)(ocfs2_filesys *fs, enum fsck_type type, uint16_t slotnum) = NULL; switch (type) { case DEALLOC_COUNT: func = mess_up_truncate_log_list; break; case DEALLOC_USED: func = mess_up_truncate_log_list; break; case TRUNCATE_REC_START_RANGE: func = mess_up_truncate_log_rec; break; case TRUNCATE_REC_WRAP: func = mess_up_truncate_log_rec; break; case TRUNCATE_REC_RANGE: func = mess_up_truncate_log_rec; break; default: FSWRK_FATAL("Invalid code = %d", type); } if (func) func(fs, type, slotnum); return; } void corrupt_refcount(ocfs2_filesys *fs, enum fsck_type type, uint16_t slotnum) { uint64_t blkno; void (*func)(ocfs2_filesys *fs, enum fsck_type type, uint64_t blkno) = NULL; switch (type) { case RB_BLKNO: case RB_GEN: case RB_GEN_FIX: case RB_PARENT: case REFCOUNT_BLOCK_INVALID: case REFCOUNT_ROOT_BLOCK_INVALID: case REFCOUNT_LIST_COUNT: case REFCOUNT_LIST_USED: case REFCOUNT_CLUSTER_RANGE: case REFCOUNT_CLUSTER_COLLISION: case REFCOUNT_LIST_EMPTY: case REFCOUNT_REC_REDUNDANT: case REFCOUNT_COUNT_INVALID: case DUP_CLUSTERS_ADD_REFCOUNT: func = mess_up_refcount_tree_block; break; case REFCOUNT_CLUSTERS: case REFCOUNT_COUNT: func = mess_up_refcount_tree; break; default: FSWRK_FATAL("Invalid code = %d", type); } create_named_directory(fs, "tmp", &blkno); if (func) func(fs, type, blkno); return; } void corrupt_discontig_bg(ocfs2_filesys *fs, enum fsck_type type, uint16_t slotnum) { mess_up_discontig_bg(fs, type, slotnum); } ocfs2-tools-ocfs2-tools-1.8.6/fswreck/dir.c000066400000000000000000000340311347147137200204500ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * dir.c * * directory corruptions * * Copyright (C) 2006 Oracle. All rights reserved. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * */ /* This file will create corruption for directory. * * Directory inode error: DIR_ZERO * * Dirent dot error: DIRENT_DOTTY_DUP, DIRENT_NOT_DOTTY, DIRENT_DOT_INODE, * DIRENT_DOT_EXCESS * * Dirent field error: DIRENT_ZERO, DIRENT_NAME_CHARS,DIRENT_INODE_RANGE, * DIRENT_INODE_FREE, DIRENT_TYPE, DIRENT_DUPLICATE, * DIRENT_LENGTH * * Directory parent duplicate error: DIR_PARENT_DUP * * Directory not connected error: DIR_NOT_CONNECTED * */ #include "main.h" extern char *progname; void create_directory(ocfs2_filesys *fs, uint64_t parentblk, uint64_t *blkno) { errcode_t ret; char random_name[OCFS2_MAX_FILENAME_LEN]; memset(random_name, 0, sizeof(random_name)); sprintf(random_name, "testXXXXXX"); /* Don't use mkstemp since it will create a file * in the working directory which is no use. * Use mktemp instead Although there is a compiling warning. * mktemp fails to work in some implementations follow BSD 4.3, * but currently ocfs2 will only support linux, * so it will not affect us. */ if (!mktemp(random_name)) FSWRK_COM_FATAL(progname, errno); ret = ocfs2_lookup(fs, parentblk, random_name, strlen(random_name), NULL, blkno); if (!ret) return; else if (ret != OCFS2_ET_FILE_NOT_FOUND) FSWRK_COM_FATAL(progname, ret); ret = ocfs2_new_inode(fs, blkno, S_IFDIR | 0755); if (ret) FSWRK_COM_FATAL(progname, ret); ret = ocfs2_init_dir(fs, *blkno, parentblk); if (ret) FSWRK_COM_FATAL(progname, ret); ret = ocfs2_link(fs, parentblk, random_name, *blkno, OCFS2_FT_DIR); if (ret) FSWRK_COM_FATAL(progname, ret); return; } struct dirent_corrupt_struct { const char *oldname; const char *name; int namelen; int oldnamelen; int done; int reserved; }; static int corrupt_match_dirent(struct dirent_corrupt_struct *dcs, struct ocfs2_dir_entry *dirent) { if (!dcs->oldname) return 1; if (((dirent->name_len & 0xFF) != dcs->oldnamelen)) return 0; if (strncmp(dcs->oldname, dirent->name, dirent->name_len & 0xFF)) return 0; return 1; } static int rename_dirent_proc(struct ocfs2_dir_entry *dirent, uint64_t blocknr, int offset, int blocksize, char *buf, void *priv_data) { struct dirent_corrupt_struct *dcs = (struct dirent_corrupt_struct *) priv_data; if (!corrupt_match_dirent(dcs, dirent)) return 0; if (dcs->namelen <= (dirent->rec_len - offsetof(struct ocfs2_dir_entry, name))) { strcpy(dirent->name, dcs->name); dirent->name_len = dcs->namelen; } else FSWRK_FATAL("The lenght of new name for target dirent you" "want to rename didn't fit the old one.\n"); dcs->done++; return OCFS2_DIRENT_ABORT|OCFS2_DIRENT_CHANGED; } static int rename_dirent(ocfs2_filesys *fs, uint64_t dir, const char *name, const char *oldname) { errcode_t rc; struct dirent_corrupt_struct dcs; if (!(fs->fs_flags & OCFS2_FLAG_RW)) return OCFS2_ET_RO_FILESYS; dcs.name = name; dcs.oldname = oldname; dcs.namelen = name ? strlen(name) : 0; dcs.oldnamelen = oldname ? strlen(oldname) : 0; dcs.done = 0; rc = ocfs2_dir_iterate(fs, dir, 0, 0, rename_dirent_proc, &dcs); if (rc) return rc; return (dcs.done) ? 0 : OCFS2_ET_DIR_NO_SPACE; } static int corrupt_dirent_ino_proc(struct ocfs2_dir_entry *dirent, uint64_t blocknr, int offset, int blocksize, char *buf, void *priv_data) { struct dirent_corrupt_struct *dcs = (struct dirent_corrupt_struct*) priv_data; if (!corrupt_match_dirent(dcs, dirent)) return 0; dirent->inode += dcs->reserved; dcs->reserved = dirent->inode; dcs->done++; return OCFS2_DIRENT_ABORT|OCFS2_DIRENT_CHANGED; } static int corrupt_dirent_ino(ocfs2_filesys *fs, uint64_t dir, const char *name, uint64_t *new_ino, int inc) { errcode_t rc; struct dirent_corrupt_struct dcs; if (!(fs->fs_flags & OCFS2_FLAG_RW)) return OCFS2_ET_RO_FILESYS; dcs.oldname = name; dcs.oldnamelen = name ? strlen(name) : 0; dcs.done = 0; dcs.reserved = inc; rc = ocfs2_dir_iterate(fs, dir, 0, 0, corrupt_dirent_ino_proc, &dcs); if (rc) return rc; *new_ino = dcs.reserved; return (dcs.done) ? 0 : OCFS2_ET_DIR_NO_SPACE; } static int corrupt_dirent_reclen_proc(struct ocfs2_dir_entry *dirent, uint64_t blocknr, int offset, int blocksize, char *buf, void *priv_data) { struct dirent_corrupt_struct *dcs = (struct dirent_corrupt_struct*) priv_data; if (!corrupt_match_dirent(dcs, dirent)) return 0; dirent->rec_len += dcs->reserved; dcs->done++; dcs->reserved = dirent->rec_len; return OCFS2_DIRENT_ABORT|OCFS2_DIRENT_CHANGED; } static int corrupt_dirent_reclen(ocfs2_filesys *fs, uint64_t dir, const char *name, uint64_t *new_reclen, int inc) { errcode_t rc; struct dirent_corrupt_struct dcs; if (!(fs->fs_flags & OCFS2_FLAG_RW)) return OCFS2_ET_RO_FILESYS; dcs.oldname = name; dcs.oldnamelen = name ? strlen(name) : 0; dcs.done = 0; dcs.reserved = inc; rc = ocfs2_dir_iterate(fs, dir, 0, 0, corrupt_dirent_reclen_proc, &dcs); if (rc) return rc; *new_reclen = dcs.reserved; return (dcs.done) ? 0 : OCFS2_ET_DIR_NO_SPACE; } static void damage_dir_content(ocfs2_filesys *fs, uint64_t dir, enum fsck_type type) { errcode_t ret; uint64_t tmp_blkno, tmp_no; char name[OCFS2_MAX_FILENAME_LEN]; mode_t mode; memset(name, 0, sizeof(name)); sprintf(name, "testXXXXXX"); if (!mktemp(name)) FSWRK_COM_FATAL(progname, errno); switch (type) { case DIRENT_DOTTY_DUP: /* add another "." at the end of the directory */ sprintf(name, "."); ret = ocfs2_link(fs, dir, name, dir, OCFS2_FT_DIR); if (ret) FSWRK_COM_FATAL(progname, ret); fprintf(stdout, "DIRENT_DOTTY_DUP: " "Corrupt directory#%"PRIu64 ", add another '.' to it.\n", dir); break; case DIRENT_NOT_DOTTY: /* rename the first ent from "." to "a". */ sprintf(name, "a"); rename_dirent(fs, dir, name, "."); fprintf(stdout, "DIRENT_NOT_DOTTY: " "Corrupt directory#%"PRIu64 ", change '.' to %s.\n", dir, name); break; case DIRENT_DOT_INODE: fprintf(stdout, "DIRENT_DOT_INODE: " "Corrupt directory#%"PRIu64 ", change dot inode to #%"PRIu64".\n", dir, (dir+10)); corrupt_dirent_ino(fs, dir, ".", &tmp_no, 10); break; case DIRENT_DOT_EXCESS: corrupt_dirent_reclen(fs, dir, ".", &tmp_no, OCFS2_DIR_PAD); fprintf(stdout, "DIR_DOT_EXCESS: " "Corrupt directory#%"PRIu64"," "change dot's dirent length from %"PRIu64" " "to %"PRIu64"\n", dir, tmp_no - OCFS2_DIR_PAD, tmp_no); break; case DIR_DOTDOT: corrupt_dirent_ino(fs, dir, "..", &tmp_no, 10); fprintf(stdout, "DIR_DOTDOT: " "Corrupt directory#%"PRIu64 ", change dotdot inode from %"PRIu64" to %"PRIu64".\n", dir, tmp_no - 10, tmp_no); break; case DIRENT_ZERO: memset(name, 0, 1); ret = ocfs2_link(fs, dir, name, dir + 100, OCFS2_FT_DIR); if (ret) FSWRK_COM_FATAL(progname, ret); fprintf(stdout, "DIRENT_ZERO: " "Corrupt directory#%"PRIu64 ", add an zero entry to it.\n", dir); break; case DIRENT_NAME_CHARS: name[0] = 47; mode = S_IFREG | 0755; ret = ocfs2_new_inode(fs, &tmp_blkno, mode); if (ret) FSWRK_COM_FATAL(progname, ret); ret = ocfs2_link(fs, dir, name, tmp_blkno, OCFS2_FT_REG_FILE); if (ret) FSWRK_COM_FATAL(progname, ret); fprintf(stdout, "DIRENT_NAME_CHARS: " "Corrupt directory#%"PRIu64 ", add an invalid entry to it.\n", dir); break; case DIRENT_INODE_RANGE: tmp_blkno = fs->fs_blocks; ret = ocfs2_link(fs, dir, name, tmp_blkno, OCFS2_FT_REG_FILE); if (ret) FSWRK_COM_FATAL(progname, ret); corrupt_dirent_ino(fs, dir, name, &tmp_no, 1); fprintf(stdout, "DIRENT_INODE_RANGE: " "Corrupt directory#%"PRIu64 ", add an entry whose inode exceeds" " the limits.\n", dir); break; case DIRENT_INODE_FREE: tmp_blkno = dir + 1000; ret = ocfs2_link(fs, dir, name, tmp_blkno, OCFS2_FT_REG_FILE); if (ret) FSWRK_COM_FATAL(progname, ret); fprintf(stdout, "DIRENT_INODE_FREE: " "Corrupt directory#%"PRIu64 ", add an entry's inode#%"PRIu64 " whose inode isn't used.\n", dir, tmp_blkno); break; case DIRENT_TYPE: mode = S_IFREG | 0755; ret = ocfs2_new_inode(fs, &tmp_blkno, mode); if (ret) FSWRK_COM_FATAL(progname, ret); ret = ocfs2_link(fs, dir, name, tmp_blkno, OCFS2_FT_SYMLINK); if (ret) FSWRK_COM_FATAL(progname, ret); fprintf(stdout, "DIRENT_TYPE: " "Corrupt directory#%"PRIu64 ", change an entry's mode from %u to %u.\n", dir, mode, S_IFLNK | 0755); break; case DIRENT_DUPLICATE: mode = S_IFREG | 0755; ret = ocfs2_new_inode(fs, &tmp_blkno, mode); if (ret) FSWRK_COM_FATAL(progname, ret); ret = ocfs2_link(fs, dir, name, tmp_blkno, OCFS2_FT_REG_FILE); if (ret) FSWRK_COM_FATAL(progname, ret); ret = ocfs2_link(fs, dir, name, tmp_blkno, OCFS2_FT_REG_FILE); if (ret) FSWRK_COM_FATAL(progname, ret); fprintf(stdout, "DIRENT_DUPLICATE: " "Corrupt directory#%"PRIu64 ", add two entries with the same name '%s'.\n", dir, name); break; case DIRENT_LENGTH: mode = S_IFREG | 0755; ret = ocfs2_new_inode(fs, &tmp_blkno, mode); if (ret) FSWRK_COM_FATAL(progname, ret); ret = ocfs2_link(fs, dir, name, tmp_blkno, OCFS2_FT_REG_FILE); if (ret) FSWRK_COM_FATAL(progname, ret); corrupt_dirent_reclen(fs, dir, name, &tmp_no, 1); fprintf(stdout, "DIRENT_LENGTH: " "Corrupt directory#%"PRIu64 ", modify entry#%"PRIu64" from %"PRIu64" " "to %"PRIu64".\n", dir, tmp_blkno, tmp_no - 1, tmp_no); break; default: FSWRK_FATAL("Invalid type = %d\n", type); } return; } void mess_up_dir_ent(ocfs2_filesys *fs, enum fsck_type type, uint64_t blkno) { uint64_t tmp_blkno; create_directory(fs, blkno, &tmp_blkno); damage_dir_content(fs, tmp_blkno, type); return; } void mess_up_dir_parent_dup(ocfs2_filesys *fs, enum fsck_type type, uint64_t blkno) { errcode_t ret; uint64_t parent1, parent2, tmp_blkno; char random_name[OCFS2_MAX_FILENAME_LEN]; /* create 2 direcotories */ create_directory(fs, blkno, &parent1); create_directory(fs, blkno, &parent2); /* create a directory under parent1, tmp_blkno indicates its inode. */ create_directory(fs, parent1, &tmp_blkno); memset(random_name, 0, sizeof(random_name)); sprintf(random_name, "testXXXXXX"); /* Don't use mkstemp since it will create a file * in the working directory which is no use. * Use mktemp instead Although there is a compiling warning. * mktemp fails to work in some implementations follow BSD 4.3, * but currently ocfs2 will only support linux, * so it will not affect us. */ if (!mktemp(random_name)) FSWRK_COM_FATAL(progname, errno); ret = ocfs2_link(fs, parent2, random_name, tmp_blkno, OCFS2_FT_DIR); if (ret) FSWRK_COM_FATAL(progname, ret); fprintf(stdout, "DIR_PARENT_DUP: " "Create a directory #%"PRIu64 " which has two parents: #%"PRIu64" and #%"PRIu64".\n", tmp_blkno, parent1, parent2); return; } void mess_up_dir_inode(ocfs2_filesys *fs, enum fsck_type type, uint64_t blkno) { errcode_t ret; char *buf = NULL, *buf2 = NULL; struct ocfs2_dinode *di, *di2; struct ocfs2_extent_list *el; uint64_t tmp_blkno, file_ino, dir2; int i; struct ocfs2_extent_rec *er; create_directory(fs, blkno, &tmp_blkno); ret = ocfs2_malloc_block(fs->fs_io, &buf); if (ret) FSWRK_COM_FATAL(progname, ret); ret = ocfs2_malloc_block(fs->fs_io, &buf2); if (ret) FSWRK_COM_FATAL(progname, ret); ret = ocfs2_read_inode(fs, tmp_blkno, buf); if (ret) FSWRK_COM_FATAL(progname, ret); di = (struct ocfs2_dinode *)buf; if (!(di->i_flags & OCFS2_VALID_FL)) FSWRK_FATAL("not a valid file"); switch (type) { case DIR_HOLE: create_named_directory(fs, "tmp1", &dir2); for (i = 0; i < fs->fs_blocksize; i++) { if (i%2) create_file(fs, blkno, &file_ino); else create_file(fs, dir2, &file_ino); } ret = ocfs2_read_inode(fs, dir2, buf2); di2 = (struct ocfs2_dinode *)buf2; el = &(di2->id2.i_list); er = &(el->l_recs[0]); er->e_cpos += 2; er = &(el->l_recs[1]); er->e_cpos += 3; di2->i_size += 23 * fs->fs_clustersize; ret = ocfs2_write_inode(fs, dir2, buf2); fprintf(stdout, "DIR_HOLE: Messed up extent records of %" PRIu64" to show a hole.\n", dir2); break; case DIR_ZERO: if (di->i_dyn_features & OCFS2_INLINE_DATA_FL) { FSWRK_FATAL("Inlined directory"); } else { el = &(di->id2.i_list); if (el->l_next_free_rec == 0) FSWRK_FATAL("directory empty"); el->l_next_free_rec = 0; } fprintf(stdout, "DIR_ZERO: Corrupt directory#%"PRIu64 ", empty its content.\n", tmp_blkno); break; default: fprintf(stdout, "Invalid code passed for dir\n"); } ret = ocfs2_write_inode(fs, tmp_blkno, buf); if (ret) FSWRK_COM_FATAL(progname, ret); if (buf) ocfs2_free(&buf); if (buf2) ocfs2_free(&buf2); return; } void mess_up_dir_not_connected(ocfs2_filesys *fs, enum fsck_type type, uint64_t blkno) { errcode_t ret; uint64_t tmp_blkno; ret = ocfs2_new_inode(fs, &tmp_blkno, S_IFDIR | 0755); if (ret) FSWRK_COM_FATAL(progname, ret); ret = ocfs2_init_dir(fs, tmp_blkno, blkno); if (ret) FSWRK_COM_FATAL(progname, ret); fprintf(stdout, "DIR_NOT_CONNECTED: " "create a directory#%"PRIu64" which has no connections.\n", tmp_blkno); return; } ocfs2-tools-ocfs2-tools-1.8.6/fswreck/discontig_bg.c000066400000000000000000000212461347147137200223310ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * discontig_bg.c * * Copyright (C) 2010 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License version 2 as published by the Free Software Foundation. * * 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. */ #include #include "main.h" extern char *progname; static void create_discontig_bg_list(ocfs2_filesys *fs, struct ocfs2_group_desc *gd, uint64_t blkno, uint32_t clusters) { uint16_t recs = ocfs2_extent_recs_per_gd(fs->fs_blocksize) / 2; int i, clusters_per_rec, cpos = 0; struct ocfs2_extent_rec *rec; /* Calculate out how much clusters one rec have and how many * recs we have. */ if (clusters > recs) clusters_per_rec = clusters / recs; else { recs = clusters; clusters_per_rec = 1; } for (i = 0; i < recs - 1; i++) { rec = &gd->bg_list.l_recs[i]; rec->e_blkno = blkno; rec->e_cpos = cpos; rec->e_leaf_clusters = clusters_per_rec; blkno += ocfs2_clusters_to_blocks(fs, clusters_per_rec); cpos += clusters_per_rec; } /* Set the last rec according to all the clusters left. */ rec = &gd->bg_list.l_recs[recs - 1]; rec->e_blkno = blkno; rec->e_cpos = cpos; rec->e_leaf_clusters = clusters - (recs - 1) * clusters_per_rec; gd->bg_list.l_count = ocfs2_extent_recs_per_gd(fs->fs_blocksize); gd->bg_list.l_tree_depth = 0; gd->bg_list.l_next_free_rec = recs; } /* * Add a new discontig inode alloc group to the given slot. * the buf will contain the new created alloc group. */ static void create_discontig_bg(ocfs2_filesys *fs, uint16_t slotnum, char *gd_buf, uint16_t *cpg) { char *buf = NULL; errcode_t ret; char sysfile[OCFS2_MAX_FILENAME_LEN]; struct ocfs2_super_block *sb = OCFS2_RAW_SB(fs->fs_super); struct ocfs2_chain_list *cl; struct ocfs2_dinode *di; struct ocfs2_group_desc *gd; struct ocfs2_chain_rec *rec; uint32_t clusters; uint64_t gd_blkno, di_blkno, old_blkno; uint16_t chain; ret = ocfs2_malloc_block(fs->fs_io, &buf); if (ret) FSWRK_COM_FATAL(progname, ret); if (slotnum == UINT16_MAX) slotnum = 0; snprintf(sysfile, sizeof(sysfile), ocfs2_system_inodes[INODE_ALLOC_SYSTEM_INODE].si_name, slotnum); ret = ocfs2_lookup(fs, sb->s_system_dir_blkno, sysfile, strlen(sysfile), NULL, &di_blkno); if (ret) FSWRK_COM_FATAL(progname, ret); ret = ocfs2_read_inode(fs, di_blkno, buf); if (ret) FSWRK_COM_FATAL(progname, ret); di = (struct ocfs2_dinode *)buf; cl = &di->id2.i_chain; chain = cl->cl_next_free_rec; if (chain == cl->cl_count) chain = 0; ret = ocfs2_new_clusters(fs, cl->cl_cpg, cl->cl_cpg, &gd_blkno, &clusters); if (ret || (clusters != cl->cl_cpg)) FSWRK_COM_FATAL(progname, ret); gd = (struct ocfs2_group_desc *)gd_buf; ocfs2_init_group_desc(fs, gd, gd_blkno, fs->fs_super->i_fs_generation, di->i_blkno, di->id2.i_chain.cl_cpg * di->id2.i_chain.cl_bpc, chain, 1); create_discontig_bg_list(fs, gd, gd_blkno, clusters); rec = &di->id2.i_chain.cl_recs[chain]; old_blkno = rec->c_blkno; gd->bg_next_group = old_blkno; ret = ocfs2_write_group_desc(fs, gd_blkno, gd_buf); if (ret) FSWRK_COM_FATAL(progname, ret); rec->c_free += gd->bg_free_bits_count; rec->c_total += gd->bg_bits; rec->c_blkno = gd_blkno; di->i_clusters += di->id2.i_chain.cl_cpg; di->i_size = (uint64_t)di->i_clusters * fs->fs_clustersize; di->id1.bitmap1.i_total += gd->bg_bits; di->id1.bitmap1.i_used += gd->bg_bits - gd->bg_free_bits_count; if (di->id2.i_chain.cl_next_free_rec == chain) di->id2.i_chain.cl_next_free_rec = chain + 1; *cpg = cl->cl_cpg; ret = ocfs2_write_inode(fs, di_blkno, buf); if (ret) FSWRK_COM_FATAL(progname, ret); ocfs2_free(&buf); } void mess_up_discontig_bg(ocfs2_filesys *fs, enum fsck_type type, uint16_t slotnum) { errcode_t ret; char *buf = NULL; struct ocfs2_group_desc *gd; uint16_t old, cpg; uint32_t old_clusters, old_clusters1; uint64_t old_blkno; if (!ocfs2_supports_discontig_bg(OCFS2_RAW_SB(fs->fs_super))) FSWRK_FATAL("Should specify a discontig-bg supported " "volume to do this corruption\n"); ret = ocfs2_malloc_block(fs->fs_io, &buf); if (ret) FSWRK_COM_FATAL(progname, ret); create_discontig_bg(fs, slotnum, buf, &cpg); gd = (struct ocfs2_group_desc *)buf; switch (type) { case DISCONTIG_BG_DEPTH: old = gd->bg_list.l_tree_depth; gd->bg_list.l_tree_depth++; fprintf(stdout, "DISCONTIG_BG_DEPTH: Corrupt discontig bg #" "%"PRIu64", change l_tree_depth from %u to %u\n", (uint64_t)gd->bg_blkno, old, gd->bg_list.l_tree_depth); break; case DISCONTIG_BG_COUNT: old = gd->bg_list.l_count; gd->bg_list.l_count += 10;; fprintf(stdout, "DISCONTIG_BG_COUNT: Corrupt discontig bg #" "%"PRIu64", change l_count from %u to %u\n", (uint64_t)gd->bg_blkno, old, gd->bg_list.l_count); break; case DISCONTIG_BG_REC_RANGE: old_blkno = gd->bg_list.l_recs[0].e_blkno; gd->bg_list.l_recs[0].e_blkno = fs->fs_blocks + 10; fprintf(stdout, "DISCONTIG_BG_REC_RANGE: Corrupt discontig bg #" "%"PRIu64", change recs[0].e_blkno from %"PRIu64 " to %"PRIu64"\n", (uint64_t)gd->bg_blkno, old_blkno, (uint64_t)gd->bg_list.l_recs[0].e_blkno); break; case DISCONTIG_BG_CORRUPT_LEAVES: old_clusters = gd->bg_list.l_recs[0].e_leaf_clusters; old_clusters1 = gd->bg_list.l_recs[1].e_leaf_clusters; gd->bg_list.l_recs[0].e_leaf_clusters = cpg + 1; gd->bg_list.l_recs[1].e_leaf_clusters = cpg + 1; fprintf(stdout, "DISCONTIG_BG_CORRUPT_LEAVES: Corrupt discontig" " bg #%"PRIu64", change recs[0] clusters from %u to %u," " change recs1[1] clusters from %u to %u\n", (uint64_t)gd->bg_blkno, old_clusters, gd->bg_list.l_recs[0].e_leaf_clusters, old_clusters1, gd->bg_list.l_recs[1].e_leaf_clusters); break; case DISCONTIG_BG_CLUSTERS: old = gd->bg_list.l_next_free_rec - 1; old_clusters = gd->bg_list.l_recs[old].e_leaf_clusters; gd->bg_list.l_recs[old].e_leaf_clusters += 1; fprintf(stdout, "DISCONTIG_BG_CLUSTERS: Corrupt " "discontig bg #%"PRIu64", change recs[%u] clusters " "from %u to %u\n", (uint64_t)gd->bg_blkno, old, old_clusters, gd->bg_list.l_recs[old].e_leaf_clusters); break; case DISCONTIG_BG_LESS_CLUSTERS: old = gd->bg_list.l_next_free_rec; gd->bg_list.l_next_free_rec -= 1; fprintf(stdout, "DISCONTIG_BG_LESS_CLUSTERS: Corrupt discontig" " bg #%"PRIu64", change l_next_free_rec from %u to " "%u\n", (uint64_t)gd->bg_blkno, old, gd->bg_list.l_next_free_rec); break; case DISCONTIG_BG_NEXT_FREE_REC: old = gd->bg_list.l_next_free_rec; gd->bg_list.l_next_free_rec += 1;; fprintf(stdout, "DISCONTIG_BG_NEXT_FREE_REC: Corrupt discontig " "bg #%"PRIu64", change l_next_free_rec from %u to %u\n", (uint64_t)gd->bg_blkno, old, gd->bg_list.l_next_free_rec); break; case DISCONTIG_BG_LIST_CORRUPT: old = gd->bg_list.l_next_free_rec; old_clusters = gd->bg_list.l_recs[0].e_leaf_clusters; gd->bg_list.l_recs[0].e_leaf_clusters = cpg+1; old_clusters1 = gd->bg_list.l_recs[old-2].e_leaf_clusters; gd->bg_list.l_recs[old-2].e_leaf_clusters += 2; fprintf(stdout, "DISCONTIG_BG_LIST_CORRUPT: Corrupt discontig " "bg #%"PRIu64", change recs[0] clusters from %u to %u, " "change recs[%u] cluster from %u to %u\n", (uint64_t)gd->bg_blkno, old_clusters, gd->bg_list.l_recs[0].e_leaf_clusters, old - 2, old_clusters1, gd->bg_list.l_recs[old-2].e_leaf_clusters); break; case DISCONTIG_BG_REC_CORRUPT: old_clusters = gd->bg_list.l_recs[0].e_leaf_clusters; old_clusters1 = gd->bg_list.l_recs[1].e_leaf_clusters; gd->bg_list.l_recs[0].e_leaf_clusters = cpg + 1; gd->bg_list.l_recs[1].e_leaf_clusters += 1; fprintf(stdout, "DISCONTIG_BG_REC_CORRUPT: Corrupt discontig" " bg #%"PRIu64", change recs[0] clusters from %u to " "%u, recs[1] clusters from %u to %u\n", (uint64_t)gd->bg_blkno, old_clusters, gd->bg_list.l_recs[0].e_leaf_clusters, old_clusters1, gd->bg_list.l_recs[1].e_leaf_clusters); break; case DISCONTIG_BG_LEAF_CLUSTERS: old_clusters = gd->bg_list.l_recs[0].e_leaf_clusters; gd->bg_list.l_recs[0].e_leaf_clusters = cpg + 1; fprintf(stdout, "DISCONTIG_BG_LEAF_CLUSTERS: Corrupt discontig" " bg #%"PRIu64", change recs[0] clusters from %u to " "%u\n", (uint64_t)gd->bg_blkno, old_clusters, gd->bg_list.l_recs[0].e_leaf_clusters); break; default: FSWRK_FATAL("Invalid type[%d]\n", type); } ret = ocfs2_write_group_desc(fs, gd->bg_blkno, buf); if (ret) FSWRK_COM_FATAL(progname, ret); ocfs2_free(&buf); return; } ocfs2-tools-ocfs2-tools-1.8.6/fswreck/extent.c000066400000000000000000000252351347147137200212070ustar00rootroot00000000000000/* * extent.c * * extent corruptions * * Copyright (C) 2006 Oracle. All rights reserved. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * */ /* This file will create the following errors for extent rec and blocks . * * Extent block error: EB_BLKNO, EB_GEN, EB_GEN_FIX, EXTENT_EB_INVALID * * Extent list error: EB_LIST_DEPTH, EXTENT_LIST_COUNT, EXTENT_LIST_FREE * * Extent record error: EXTENT_BLKNO_UNALIGNED, EXTENT_CLUSTERS_OVERRUN, * EXTENT_BLKNO_RANGE */ #include #include "main.h" extern char *progname; void create_file(ocfs2_filesys *fs, uint64_t blkno, uint64_t *retblkno) { errcode_t ret; uint64_t tmp_blkno = 0; char random_name[OCFS2_MAX_FILENAME_LEN]; memset(random_name, 0, sizeof(random_name)); sprintf(random_name, "testXXXXXX"); /* Don't use mkstemp since it will create a file * in the working directory which is no use. * Use mktemp instead Although there is a compiling warning. * mktemp fails to work in some implementations follow BSD 4.3, * but currently ocfs2 will only support linux, * so it will not affect us. */ if (!mktemp(random_name)) FSWRK_COM_FATAL(progname, errno); ret = ocfs2_check_directory(fs, blkno); if (ret) FSWRK_COM_FATAL(progname, ret); ret = ocfs2_new_inode(fs, &tmp_blkno, S_IFREG | 0755); if (ret) FSWRK_COM_FATAL(progname, ret); ret = ocfs2_link(fs, blkno, random_name, tmp_blkno, OCFS2_FT_REG_FILE); if (ret) FSWRK_COM_FATAL(progname, ret); *retblkno = tmp_blkno; return; } /* * This function is similar to ocfs2_extend_allocation() as both extend files. * However, this one ensures that the extent record tree also grows. */ static void custom_extend_allocation(ocfs2_filesys *fs, uint64_t ino, uint32_t new_clusters) { errcode_t ret; uint32_t n_clusters; uint32_t i, offset = 0; uint64_t blkno; uint64_t tmpblk; if (!(fs->fs_flags & OCFS2_FLAG_RW)) FSWRK_FATAL("read-only filesystem"); while (new_clusters) { ret = ocfs2_new_clusters(fs, 1, new_clusters, &blkno, &n_clusters); if (ret) FSWRK_COM_FATAL(progname, ret); if (!n_clusters) FSWRK_FATAL("ENOSPC"); /* In order to ensure the extent records are not coalesced, * we insert each cluster in reverse. */ for(i = n_clusters; i; --i) { tmpblk = blkno + ocfs2_clusters_to_blocks(fs, i - 1); ret = ocfs2_inode_insert_extent(fs, ino, offset++, tmpblk, 1, 0); if (ret) FSWRK_COM_FATAL(progname, ret); } new_clusters -= n_clusters; } return; } /* Damage the file's extent block according to the given fsck_type */ static void damage_extent_block(ocfs2_filesys *fs, uint64_t blkno, enum fsck_type type) { errcode_t ret; char *inobuf = NULL; char *extbuf = NULL; struct ocfs2_dinode *di; struct ocfs2_extent_list *el; struct ocfs2_extent_block *eb; uint32_t oldno; uint64_t oldblkno; ret = ocfs2_malloc_block(fs->fs_io, &inobuf); if (ret) FSWRK_COM_FATAL(progname, ret); ret = ocfs2_read_inode(fs, blkno, inobuf); if (ret) FSWRK_COM_FATAL(progname, ret); di = (struct ocfs2_dinode *)inobuf; if (!(di->i_flags & OCFS2_VALID_FL)) FSWRK_FATAL("not a file"); el = &(di->id2.i_list); if (el->l_next_free_rec > 0 && el->l_tree_depth > 0) { ret = ocfs2_malloc_block(fs->fs_io, &extbuf); if (ret) FSWRK_COM_FATAL(progname, ret); ret = ocfs2_read_extent_block(fs, el->l_recs[0].e_blkno, extbuf); if (ret) FSWRK_COM_FATAL(progname, ret); eb = (struct ocfs2_extent_block *)extbuf; switch (type) { case EB_BLKNO: oldblkno = eb->h_blkno; eb->h_blkno += 1; fprintf(stdout, "EB_BLKNO: Corrupt inode#%"PRIu64", " "change extent block's number from %"PRIu64" to " "%"PRIu64"\n", blkno, oldblkno, (uint64_t)eb->h_blkno); break; case EB_GEN: case EB_GEN_FIX: oldno = eb->h_fs_generation; eb->h_fs_generation = 0x1234; if (type == EB_GEN) fprintf(stdout, "EB_GEN: "); else if (type == EB_GEN_FIX) fprintf(stdout, "EB_GEN_FIX: "); else fprintf(stdout, "EXTENT_EB_INVALID: "); fprintf(stdout, "Corrupt inode#%"PRIu64", change " "generation number from 0x%x to 0x%x\n", blkno, oldno, eb->h_fs_generation); break; case EXTENT_EB_INVALID: memset(eb->h_signature, 'a', sizeof(eb->h_signature)); fprintf(stdout, "Corrupt the signature of extent block " "%"PRIu64"\n", (uint64_t)eb->h_blkno); break; case EXTENT_LIST_DEPTH: oldno = eb->h_list.l_tree_depth; eb->h_list.l_tree_depth += 1; fprintf(stdout, "EXTENT_LIST_DEPTH: Corrupt inode#" "%"PRIu64", change first block's list depth " "from %d to %d\n", blkno, oldno, eb->h_list.l_tree_depth); break; case EXTENT_LIST_COUNT: oldno = eb->h_list.l_count; eb->h_list.l_count = 2 * ocfs2_extent_recs_per_eb(fs->fs_blocksize); fprintf(stdout, "EXTENT_LIST_COUNT: Corrupt inode#" "%"PRIu64", change cluster from %d to %d\n", blkno, oldno, eb->h_list.l_count); break; case EXTENT_LIST_FREE: oldno = eb->h_list.l_next_free_rec; eb->h_list.l_next_free_rec = 2 * ocfs2_extent_recs_per_eb(fs->fs_blocksize); fprintf(stdout, "EXTENT_LIST_FREE: Corrupt inode#%"PRIu64", " "change blkno from %d to %d\n", blkno, oldno, eb->h_list.l_next_free_rec); break; default: FSWRK_FATAL("Invalid type=%d", type); } ret = ocfs2_write_extent_block(fs, el->l_recs[0].e_blkno, extbuf); if (ret) FSWRK_COM_FATAL(progname, ret); ret = ocfs2_write_inode(fs, blkno, inobuf); if (ret) FSWRK_COM_FATAL(progname, ret); } else FSWRK_WARN("File inode#%"PRIu64" does not have an extent " "block to corrupt.", blkno); if (extbuf) ocfs2_free(&extbuf); if (inobuf) ocfs2_free(&inobuf); return; } static void damage_extent_block_by_type(ocfs2_filesys *fs, uint64_t blkno, enum fsck_type type) { uint64_t tmpblkno; uint32_t clusters; /* Extend enough clusters to assure that we end up with a file * with atleast an extent block */ clusters = 2 * ocfs2_extent_recs_per_inode(fs->fs_blocksize); create_file(fs, blkno, &tmpblkno); custom_extend_allocation(fs, tmpblkno, clusters); damage_extent_block(fs, tmpblkno, type); return; } void mess_up_extent_list(ocfs2_filesys *fs, enum fsck_type type, uint64_t blkno) { damage_extent_block_by_type(fs, blkno, type); return; } void mess_up_extent_block(ocfs2_filesys *fs, enum fsck_type type, uint64_t blkno) { damage_extent_block_by_type(fs, blkno, type); return; } static void mess_up_record(ocfs2_filesys *fs, uint64_t blkno, enum fsck_type type) { errcode_t ret; char *buf = NULL; struct ocfs2_dinode *di; struct ocfs2_extent_list *el; struct ocfs2_extent_rec *er; uint64_t oldno; ret = ocfs2_malloc_block(fs->fs_io, &buf); if (ret) FSWRK_COM_FATAL(progname, ret); ret = ocfs2_read_inode(fs, blkno, buf); if (ret) FSWRK_COM_FATAL(progname, ret); di = (struct ocfs2_dinode *)buf; if (!(di->i_flags & OCFS2_VALID_FL)) FSWRK_COM_FATAL(progname, ret); /* We don't want fsck complaining about i_size */ if (!di->i_size) di->i_size = 1; el = &(di->id2.i_list); if (el->l_next_free_rec > 0) { er = el->l_recs; oldno = er->e_blkno; switch (type) { case EXTENT_MARKED_UNWRITTEN: if (ocfs2_writes_unwritten_extents(OCFS2_RAW_SB(fs->fs_super))) FSWRK_FATAL("Cannot exercise " "EXTENT_MARKED_UNWRITTEN on a " "filesystem with unwritten " "extents supported (obviously)"); er->e_flags |= OCFS2_EXT_UNWRITTEN; fprintf(stdout, "EXTENT_MARKED_UNWRITTEN: " "Corrupt inode#%"PRIu64", mark extent " "at cpos %"PRIu32" unwritten\n", blkno, er->e_cpos); break; case EXTENT_MARKED_REFCOUNTED: if (ocfs2_refcount_tree(OCFS2_RAW_SB(fs->fs_super))) FSWRK_FATAL("Cannot exercise " "EXTENT_MARKED_REFCOUNTED on a " "filesystem with refcounted " "extents supported (obviously)"); er->e_flags |= OCFS2_EXT_REFCOUNTED; fprintf(stdout, "EXTENT_MARKED_REFCOUNTED: " "Corrupt inode#%"PRIu64", mark extent " "at cpos %"PRIu32" refcounted\n", blkno, er->e_cpos); break; case EXTENT_BLKNO_UNALIGNED: er->e_blkno += 1; fprintf(stdout, "EXTENT_BLKNO_UNALIGNED: " "Corrupt inode#%"PRIu64", change blkno " "from %"PRIu64 " to %"PRIu64"\n", blkno, oldno, (uint64_t)er->e_blkno); break; case EXTENT_CLUSTERS_OVERRUN: oldno = er->e_leaf_clusters; er->e_leaf_clusters = 2; er->e_blkno = ocfs2_clusters_to_blocks(fs, fs->fs_clusters - 1); fprintf(stdout, "EXTENT_CLUSTERS_OVERRUN: " "Corrupt inode#%"PRIu64", " "change cluster from %"PRIu64 " to %d\n", blkno, oldno, er->e_leaf_clusters); break; case EXTENT_BLKNO_RANGE: er->e_blkno = 1; fprintf(stdout, "EXTENT_BLKNO_RANGE: " "Corrupt inode#%"PRIu64", change blkno " " from %"PRIu64 " to %"PRIu64"\n", blkno, oldno, (uint64_t)er->e_blkno); break; case EXTENT_OVERLAP: ret = ocfs2_extend_allocation(fs, blkno, 2); if (ret) FSWRK_COM_FATAL(progname, ret); ret = ocfs2_read_inode(fs, blkno, buf); if (ret) FSWRK_COM_FATAL(progname, ret); er = &(el->l_recs[1]); er->e_cpos -= 1; fprintf(stdout, "EXTENT_OVERLAP: " "Corrupt inode#%"PRIu64", change cpos " " to overlap\n", blkno); break; case EXTENT_HOLE: ret = ocfs2_extend_allocation(fs, blkno, 2); if (ret) FSWRK_COM_FATAL(progname, ret); ret = ocfs2_read_inode(fs, blkno, buf); if (ret) FSWRK_COM_FATAL(progname, ret); er = &(el->l_recs[1]); er->e_cpos += 11; fprintf(stdout, "EXTENT_HOLE: " "Corrupt inode#%"PRIu64", change cpos " " to increase hole\n", blkno); break; default: goto bail; } ret = ocfs2_write_inode(fs, blkno, buf); if (ret) FSWRK_COM_FATAL(progname, ret); } else FSWRK_WARN("Test file inode#%"PRIu64" has no content." "Can't damage it.\n", blkno); bail: if (buf) ocfs2_free(&buf); return; } void mess_up_extent_record(ocfs2_filesys *fs, enum fsck_type type, uint64_t blkno) { uint64_t tmpblkno; errcode_t ret; create_file(fs, blkno, &tmpblkno); ret = ocfs2_extend_allocation(fs, tmpblkno, 1); if (ret) FSWRK_COM_FATAL(progname, ret); mess_up_record(fs, tmpblkno, type); return; } ocfs2-tools-ocfs2-tools-1.8.6/fswreck/group.c000066400000000000000000000210021347147137200210200ustar00rootroot00000000000000/* * group.c * * group descriptor corruptions * * Copyright (C) 2006 Oracle. All rights reserved. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * */ /* This file will create the following errors for a group descriptor. * * Group minor field error: GROUP_PARENT, GROUP_BLKNO, GROUP_CHAIN, * GROUP_FREE_BITS * * Group generation error: GROUP_GEN * * Group list error: GROUP_UNEXPECTED_DESC, GROUP_EXPECTED_DESC * */ #include "main.h" extern char *progname; static void create_test_group_desc(ocfs2_filesys *fs, uint64_t *newblk, struct ocfs2_group_desc *clone) { errcode_t ret; uint32_t n_clusters; char *buf = NULL; struct ocfs2_group_desc *bg = NULL; if (!clone) FSWRK_FATAL("Can't fake the null group descriptor"); ret = ocfs2_new_clusters(fs, 1, 1, newblk, &n_clusters); if (ret) FSWRK_COM_FATAL(progname, ret); ret = ocfs2_malloc_block(fs->fs_io, &buf); if (ret) FSWRK_COM_FATAL(progname, ret); bg = (struct ocfs2_group_desc *)buf; *bg = *clone; bg->bg_blkno = cpu_to_le64(*newblk); bg->bg_next_group = 0; ret = io_write_block(fs->fs_io, *newblk, 1, buf); if(ret) FSWRK_COM_FATAL(progname, ret); return; } static void damage_group_desc(ocfs2_filesys *fs, uint64_t blkno, enum fsck_type type) { errcode_t ret; char *buf = NULL, *bufgroup = NULL, *bufgroup1 = NULL; struct ocfs2_dinode *di; struct ocfs2_chain_list *cl; struct ocfs2_chain_rec *cr; struct ocfs2_group_desc *bg = NULL, *bg1 = NULL; uint64_t newblk; ret = ocfs2_malloc_block(fs->fs_io, &buf); if (ret) FSWRK_COM_FATAL(progname, ret); ret = ocfs2_read_inode(fs, blkno, buf); if (ret) FSWRK_COM_FATAL(progname, ret); di = (struct ocfs2_dinode *)buf; if (!(di->i_flags & OCFS2_BITMAP_FL)) FSWRK_FATAL("not a bitmap"); if (!(di->i_flags & OCFS2_CHAIN_FL)) FSWRK_FATAL("not a chain group"); cl = &(di->id2.i_chain); if (!cl->cl_next_free_rec) { FSWRK_WARN("No chain record found at inode#%"PRIu64 ",so can't corrupt it for type[%d].\n", blkno, type); goto bail; } ret = ocfs2_malloc_block(fs->fs_io, &bufgroup); if (ret) FSWRK_COM_FATAL(progname, ret); cr = cl->cl_recs; ret = ocfs2_read_group_desc(fs, cr->c_blkno, bufgroup); if (ret) FSWRK_COM_FATAL(progname, ret); bg = (struct ocfs2_group_desc *)bufgroup; switch (type) { case GROUP_EXPECTED_DESC: fprintf(stdout, "Corrput GROUP_EXPECED_DESC: " "delete the group desciptor#%"PRIu64" from the chain " "#%d\n", (uint64_t)bg->bg_next_group, bg->bg_chain); bg->bg_next_group = 0; break; case GROUP_UNEXPECTED_DESC: create_test_group_desc(fs, &newblk, bg); fprintf(stdout, "Corrput GROUP_UNEXPECED_DESC: " "Add a fake descriptor#%"PRIu64 " in the chain#%d of inode#%"PRIu64"\n", newblk, bg->bg_chain, blkno); bg->bg_next_group = cpu_to_le64(newblk); break; case GROUP_GEN: fprintf(stdout, "Corrput GROUP_GEN: " "change group generation from %x to %x\n", bg->bg_generation, (bg->bg_generation + 10)); bg->bg_generation += 10; /* crash next group correspondingly to verify fsck.ocfs2. */ if (bg->bg_next_group) { ret = ocfs2_malloc_block(fs->fs_io, &bufgroup1); if (ret) FSWRK_COM_FATAL(progname, ret); ret = ocfs2_read_group_desc(fs, bg->bg_next_group, bufgroup1); if (ret) FSWRK_COM_FATAL(progname, ret); bg1 = (struct ocfs2_group_desc *)bufgroup1; bg1->bg_generation += 10; fprintf(stdout, "Corrput GROUP_GEN: " "change group generation from %x to %x\n", bg1->bg_generation, (bg1->bg_generation + 10)); ret = ocfs2_write_group_desc(fs, bg->bg_next_group, bufgroup1); if (ret) FSWRK_COM_FATAL(progname, ret); } break; case GROUP_PARENT: fprintf(stdout, "Corrput GROUP_PARENT: " "change group parent from %"PRIu64" to %"PRIu64"\n", (uint64_t)bg->bg_parent_dinode, ((uint64_t)bg->bg_parent_dinode + 10)); bg->bg_parent_dinode += 10; break; case GROUP_BLKNO: fprintf(stdout, "Corrput GROUP_BLKNO: " "change group blkno from %"PRIu64" to %"PRIu64"\n", (uint64_t)bg->bg_blkno, ((uint64_t)bg->bg_blkno + 10)); bg->bg_blkno += 10; break; case GROUP_CHAIN: fprintf(stdout, "Corrput GROUP_CHAIN: " "change group chain from %u to %u\n", bg->bg_chain, (bg->bg_chain + 10)); bg->bg_chain += 10; break; case GROUP_CHAIN_LOOP: fprintf(stdout, "Corrput GROUP_LOOP: " "change group next from %"PRIu64" to %"PRIu64"\n", (uint64_t)bg->bg_next_group, (uint64_t)cr->c_blkno); bg->bg_next_group = cpu_to_le64(cr->c_blkno); break; case GROUP_FREE_BITS: fprintf(stdout, "Corrput GROUP_FREE_BITS: " "change group free bits from %u to %u\n", bg->bg_free_bits_count, (bg->bg_bits + 10)); bg->bg_free_bits_count = bg->bg_bits + 10; break; default: FSWRK_FATAL("Invalid type[%d]\n", type); } ret = ocfs2_write_group_desc(fs, cr->c_blkno, bufgroup); if (ret) FSWRK_COM_FATAL(progname, ret); ret = ocfs2_write_inode(fs, blkno, buf); if (ret) FSWRK_COM_FATAL(progname, ret); bail: if(bufgroup1) ocfs2_free(&bufgroup1); if (bufgroup) ocfs2_free(&bufgroup); if (buf) ocfs2_free(&buf); return; } static void mess_up_group_desc(ocfs2_filesys *fs, uint16_t slotnum, enum fsck_type type) { errcode_t ret; char sysfile[OCFS2_MAX_FILENAME_LEN]; uint64_t blkno; struct ocfs2_super_block *sb = OCFS2_RAW_SB(fs->fs_super); if (slotnum == UINT16_MAX) snprintf(sysfile, sizeof(sysfile), "%s", ocfs2_system_inodes[GLOBAL_BITMAP_SYSTEM_INODE].si_name); else snprintf(sysfile, sizeof(sysfile), ocfs2_system_inodes[INODE_ALLOC_SYSTEM_INODE].si_name, slotnum); ret = ocfs2_lookup(fs, sb->s_system_dir_blkno, sysfile, strlen(sysfile), NULL, &blkno); if (ret) FSWRK_COM_FATAL(progname, ret); damage_group_desc(fs, blkno, type); return ; } void mess_up_group_minor(ocfs2_filesys *fs, enum fsck_type type, uint16_t slotnum) { mess_up_group_desc(fs, slotnum, type); } void mess_up_group_gen(ocfs2_filesys *fs, enum fsck_type type, uint16_t slotnum) { mess_up_group_desc(fs, slotnum, type); } void mess_up_group_list(ocfs2_filesys *fs, enum fsck_type type, uint16_t slotnum) { mess_up_group_desc(fs, slotnum, type); } /* We will allocate some clusters and corrupt the group descriptor * which stores the clusters and makes fsck run into error. */ void mess_up_cluster_group_desc(ocfs2_filesys *fs, enum fsck_type type, uint16_t slotnum) { errcode_t ret; uint32_t found, start_cluster, old_free_bits, request = 100; uint64_t start_blk; uint64_t bg_blk; int cpg; char *buf = NULL; struct ocfs2_group_desc *bg; ret = ocfs2_new_clusters(fs, 1, request, &start_blk, &found); if (ret) FSWRK_COM_FATAL(progname, ret); start_cluster = ocfs2_blocks_to_clusters(fs, start_blk); cpg = ocfs2_group_bitmap_size(fs->fs_blocksize, 0, OCFS2_RAW_SB(fs->fs_super)->s_feature_incompat) * 8; bg_blk = ocfs2_which_cluster_group(fs, cpg, start_cluster); ret = ocfs2_malloc_block(fs->fs_io, &buf); if (ret) FSWRK_COM_FATAL(progname, ret); ret = ocfs2_read_group_desc(fs, bg_blk, buf); if (ret) FSWRK_COM_FATAL(progname, ret); bg = (struct ocfs2_group_desc *)buf; old_free_bits = bg->bg_free_bits_count; bg->bg_free_bits_count = bg->bg_bits + 10; ret = ocfs2_write_group_desc(fs, bg_blk, buf); if (ret) FSWRK_COM_FATAL(progname, ret); fprintf(stdout, "Corrput CLUSTER and GROUP_FREE_BITS: " "Allocating %u clusters and change group[%"PRIu64"]'s free bits" " from %u to %u\n", found, bg_blk, old_free_bits, bg->bg_free_bits_count); if (buf) ocfs2_free(&buf); } /* * Simply corrupt the global bitmap by allocating a new cluster, without * actually using it later. */ void mess_up_cluster_alloc_bits(ocfs2_filesys *fs, enum fsck_type type, uint16_t slotnum) { errcode_t ret; uint32_t n_clusters; uint64_t blkno; ret = ocfs2_new_clusters(fs, 1, 1, &blkno, &n_clusters); if (ret) { FSWRK_COM_FATAL(progname, ret); ocfs2_free_clusters(fs, 1, blkno); } fprintf(stdout ,"Mark bits of global bitmap by unused " "block#%"PRIu64".\n", blkno); } ocfs2-tools-ocfs2-tools-1.8.6/fswreck/include/000077500000000000000000000000001347147137200211505ustar00rootroot00000000000000ocfs2-tools-ocfs2-tools-1.8.6/fswreck/include/chain.h000066400000000000000000000032131347147137200224020ustar00rootroot00000000000000/* * chain.h * * Function prototypes, macros, etc. for related 'C' files * * Copyright (C) 2006 Oracle. All rights reserved. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * */ #ifndef __CHAIN_H__ #define __CHAIN_H__ void mess_up_chains_list(ocfs2_filesys *fs, enum fsck_type type, uint16_t slotnum); void mess_up_chains_rec(ocfs2_filesys *fs, enum fsck_type type, uint16_t slotnum); void mess_up_chains_inode(ocfs2_filesys *fs, enum fsck_type type, uint16_t slotnum); void mess_up_chains_group(ocfs2_filesys *fs, enum fsck_type type, uint16_t slotnum); void mess_up_chains_group_magic(ocfs2_filesys *fs, enum fsck_type type, uint16_t slotnum); void mess_up_chains_cpg(ocfs2_filesys *fs, enum fsck_type type, uint16_t slotnum); void mess_up_superblock_clusters_excess(ocfs2_filesys *fs, enum fsck_type type, uint16_t slotnum); void mess_up_superblock_clusters_lack(ocfs2_filesys *fs, enum fsck_type type, uint16_t slotnum); #endif /* __CHAIN_H__ */ ocfs2-tools-ocfs2-tools-1.8.6/fswreck/include/corrupt.h000066400000000000000000000032171347147137200230220ustar00rootroot00000000000000/* * corrupt.h * * Function prototypes, macros, etc. for related 'C' files * * Copyright (C) 2006 Oracle. All rights reserved. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * */ #ifndef __CORRUPT_H #define __CORRUPT_H void corrupt_file(ocfs2_filesys *fs, enum fsck_type type, uint16_t slotnum); void corrupt_sys_file(ocfs2_filesys *fs, enum fsck_type type, uint16_t slotnum); void corrupt_group_desc(ocfs2_filesys *fs, enum fsck_type type, uint16_t slotnum); void corrupt_inode(ocfs2_filesys *fs, enum fsck_type type, uint16_t slotnum); void corrupt_local_alloc(ocfs2_filesys *fs, enum fsck_type type, uint16_t slotnum); void corrupt_truncate_log(ocfs2_filesys *fs, enum fsck_type type, uint16_t slotnum); void corrupt_refcount(ocfs2_filesys *fs, enum fsck_type type, uint16_t slotnum); void corrupt_discontig_bg(ocfs2_filesys *fs, enum fsck_type type, uint16_t slotnum); void create_named_directory(ocfs2_filesys *fs, char *name, uint64_t *blkno); #endif /* __CORRUPT_H */ ocfs2-tools-ocfs2-tools-1.8.6/fswreck/include/dir.h000066400000000000000000000027411347147137200221030ustar00rootroot00000000000000/* * dir.h * * Function prototypes, macros, etc. for related 'C' files * * Copyright (C) 2006 Oracle. All rights reserved. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * */ #ifndef __DIR_H #define __DIR_H void mess_up_dir_inode(ocfs2_filesys *fs, enum fsck_type type, uint64_t blkno); void mess_up_dir_dot(ocfs2_filesys *fs, enum fsck_type type, uint64_t blkno); void mess_up_dir_dotdot(ocfs2_filesys *fs, enum fsck_type type, uint64_t blkno); void mess_up_dir_ent(ocfs2_filesys *fs, enum fsck_type type, uint64_t blkno); void mess_up_dir_parent_dup(ocfs2_filesys *fs, enum fsck_type type, uint64_t blkno); void mess_up_dir_not_connected(ocfs2_filesys *fs, enum fsck_type type, uint64_t blkno); void create_directory(ocfs2_filesys *fs, uint64_t parentblk, uint64_t *blkno); #endif /* __DIR_H */ ocfs2-tools-ocfs2-tools-1.8.6/fswreck/include/discontig_bg.h000066400000000000000000000014121347147137200237520ustar00rootroot00000000000000/* * discontig_bg.h * * Function prototypes, macros, etc. for related 'C' files * * Copyright (C) 2010 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License version 2 as published by the Free Software Foundation. * * 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. */ #ifndef _FSWRECK_DISCONTIG_BG_H_ #define _FSWRECK_DISCONTIG_BG_H_ void mess_up_discontig_bg(ocfs2_filesys *fs, enum fsck_type type, uint16_t slotnum); #endif /* _FSWRECK_DISCONTIG_BG_H_ */ ocfs2-tools-ocfs2-tools-1.8.6/fswreck/include/extent.h000066400000000000000000000023611347147137200226320ustar00rootroot00000000000000/* * extent.h * * Function prototypes, macros, etc. for related 'C' files * * Copyright (C) 2006 Oracle. All rights reserved. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * */ #ifndef __EXTENT_H #define __EXTENT_H void mess_up_extent_list(ocfs2_filesys *fs, enum fsck_type type, uint64_t blkno); void mess_up_extent_block(ocfs2_filesys *fs, enum fsck_type type, uint64_t blkno); void mess_up_extent_record(ocfs2_filesys *fs, enum fsck_type type, uint64_t blkno); void create_file(ocfs2_filesys *fs, uint64_t blkno, uint64_t *retblkno); #endif /* __EXTENT_H */ ocfs2-tools-ocfs2-tools-1.8.6/fswreck/include/fsck_type.h000066400000000000000000000160011347147137200233060ustar00rootroot00000000000000/* * fsck_type.h * * Function prototypes, macros, etc. for related 'C' files * * Copyright (C) 2006 Oracle. All rights reserved. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * */ #ifndef _FSCK_TYPE_H #define _FSCK_TYPE_H /* * All these fsck types are copied form fsck.ocfs2/prompt-codes.h. * So if fsck.ocfs2 will implement a new fsck type, we need to copy * it here and add some verification case to test its work. */ enum fsck_type { EB_BLKNO = 0, EB_GEN, EB_GEN_FIX, EXTENT_MARKED_UNWRITTEN, EXTENT_MARKED_REFCOUNTED, EXTENT_BLKNO_UNALIGNED, EXTENT_CLUSTERS_OVERRUN, EXTENT_EB_INVALID, EXTENT_LIST_DEPTH, EXTENT_LIST_COUNT, /*10*/ EXTENT_LIST_FREE, EXTENT_BLKNO_RANGE, EXTENT_OVERLAP, EXTENT_HOLE, CHAIN_CPG, SUPERBLOCK_CLUSTERS_EXCESS, SUPERBLOCK_CLUSTERS_LACK, GROUP_UNEXPECTED_DESC, GROUP_EXPECTED_DESC, GROUP_GEN, /*20*/ GROUP_PARENT, GROUP_BLKNO, GROUP_CHAIN, GROUP_FREE_BITS, GROUP_CHAIN_LOOP, CHAIN_COUNT, CHAIN_NEXT_FREE, CHAIN_EMPTY, CHAIN_I_CLUSTERS, CHAIN_I_SIZE, CHAIN_GROUP_BITS, /*30*/ CHAIN_HEAD_LINK_RANGE, CHAIN_LINK_GEN, CHAIN_LINK_MAGIC, CHAIN_LINK_RANGE, CHAIN_BITS, DISCONTIG_BG_DEPTH, DISCONTIG_BG_COUNT, DISCONTIG_BG_REC_RANGE, DISCONTIG_BG_CORRUPT_LEAVES, DISCONTIG_BG_CLUSTERS, /*40*/ DISCONTIG_BG_LESS_CLUSTERS, DISCONTIG_BG_NEXT_FREE_REC, DISCONTIG_BG_LIST_CORRUPT, DISCONTIG_BG_REC_CORRUPT, DISCONTIG_BG_LEAF_CLUSTERS, INODE_ALLOC_REPAIR, INODE_SUBALLOC, LALLOC_SIZE, LALLOC_NZ_USED, LALLOC_NZ_BM, /*50*/ LALLOC_BM_OVERRUN, LALLOC_BM_SIZE, LALLOC_BM_STRADDLE, LALLOC_USED_OVERRUN, LALLOC_CLEAR, DEALLOC_COUNT, DEALLOC_USED, TRUNCATE_REC_START_RANGE, TRUNCATE_REC_WRAP, TRUNCATE_REC_RANGE, /*60*/ INODE_GEN, INODE_GEN_FIX, INODE_BLKNO, ROOT_NOTDIR, INODE_NZ_DTIME, LINK_FAST_DATA, LINK_NULLTERM, LINK_SIZE, LINK_BLOCKS, DIR_ZERO, /*70*/ DIR_HOLE, INODE_SIZE, INODE_SPARSE_SIZE, INODE_CLUSTERS, INODE_SPARSE_CLUSTERS, LALLOC_REPAIR, LALLOC_USED, CLUSTER_ALLOC_BIT, DIRENT_DOTTY_DUP, DIRENT_NOT_DOTTY, /*80*/ DIRENT_DOT_INODE, DIRENT_DOT_EXCESS, DIRENT_ZERO, DIRENT_NAME_CHARS, DIRENT_INODE_RANGE, DIRENT_INODE_FREE, DIRENT_TYPE, DIR_PARENT_DUP, DIRENT_DUPLICATE, DIRENT_LENGTH, /*90*/ ROOT_DIR_MISSING, LOSTFOUND_MISSING, DIR_NOT_CONNECTED, DIR_DOTDOT, INODE_NOT_CONNECTED, INODE_COUNT, INODE_ORPHANED, CLUSTER_GROUP_DESC, INLINE_DATA_FLAG_INVALID, INLINE_DATA_COUNT_INVALID, /*100*/ INODE_INLINE_SIZE, INODE_INLINE_CLUSTERS, DUP_CLUSTERS_SYSFILE_CLONE, DUP_CLUSTERS_CLONE, DUP_CLUSTERS_DELETE, JOURNAL_FILE_INVALID, JOURNAL_UNKNOWN_FEATURE, JOURNAL_MISSING_FEATURE, JOURNAL_TOO_SMALL, QMAGIC_INVALID, /*110*/ QTREE_BLK_INVALID, DQBLK_INVALID, DUP_DQBLK_INVALID, DUP_DQBLK_VALID, REFCOUNT_FLAG_INVALID, REFCOUNT_LOC_INVALID, RB_BLKNO, RB_GEN, RB_GEN_FIX, RB_PARENT, /*120*/ REFCOUNT_BLOCK_INVALID, REFCOUNT_ROOT_BLOCK_INVALID, REFCOUNT_LIST_COUNT, REFCOUNT_LIST_USED, REFCOUNT_CLUSTER_RANGE, REFCOUNT_CLUSTER_COLLISION, REFCOUNT_LIST_EMPTY, REFCOUNT_CLUSTERS, REFCOUNT_COUNT, REFCOUNT_REC_REDUNDANT, /*130*/ REFCOUNT_COUNT_INVALID, DUP_CLUSTERS_ADD_REFCOUNT, INODE_BLOCK_ECC, INODE_VALID_FLAG, /*134*/ NUM_FSCK_TYPE }; /* * All the fsck type can be divided into following groups * so that we can implement one function to implement one group. * Currently there are following groups: * * Extent block error: EB_BLKNO, EB_GEN, EB_GEN_FIX, EXTENT_EB_INVALID * * Extent list error: EB_LIST_DEPTH, EXTENT_LIST_COUNT, EXTENT_LIST_FREE * * Extent record error: EXTENT_BLKNO_UNALIGNED, EXTENT_CLUSTERS_OVERRUN, * EXTENT_BLKNO_RANGE, EXTENT_MARKED_UNWRITTEN * * Chain list error: CHAIN_COUNT, CHAIN_NEXT_FREE * * Chain record error: CHAIN_EMPTY, CHAIN_HEAD_LINK_RANGE, CHAIN_BITS, CLUSTER_ALLOC_BIT * * Chain inode error: CHAIN_I_CLUSTERS, CHAIN_I_SIZE, CHAIN_GROUP_BITS * * Chain group error: CHAIN_LINK_GEN, CHAIN_LINK_RANGE * * Chain group magic error: CHAIN_LINK_MAGIC * * Group minor field error: GROUP_PARENT, GROUP_BLKNO, GROUP_CHAIN, GROUP_FREE_BITS * * Group generation error: GROUP_GEN * * Group list error: GROUP_UNEXPECTED_DESC, GROUP_EXPECTED_DESC * * Inode field error: INODE_SUBALLOC, INODE_GEN, INODE_GEN_FIX,INODE_BLKNO, INODE_NZ_DTIME, INODE_SIZE, INODE_SPARSE_SIZE, * INODE_CLUSTERS, INODE_SPARSE_CLUSTERS, INODE_COUNT * * Inode link not connected error: INODE_NOT_CONNECTED * * Inode orphaned error: INODE_ORPHANED * * Inode alloc error: INODE_ALLOC_REPAIR * * Empty local alloc error: LALLOC_SIZE, LALLOC_NZ_USED, LALLOC_NZ_BM * * Local alloc bitmap error: LALLOC_BM_OVERRUN, LALLOC_BM_STRADDLE,LALLOC_BM_SIZE * * Local alloc used info error: LALLOC_USED_OVERRUN, LALLOC_CLEAR * LALLOC_USED, LALLOC_REPAIR is recorded in fsck.ocfs2.checks.8.in, * but never find the solution in fsck.ocfs2 source code. * * Truncate log list error: DEALLOC_COUNT, DEALLOC_USED * * Truncate log rec error: TRUNCATE_REC_START_RANGE, TRUNCATE_REC_WRAP, * TRUNCATE_REC_RANGE * * Special files error: ROOT_NOTDIR, ROOT_DIR_MISSING, LOSTFOUND_MISSING, * DIR_DOTDOT * * Journal file error: JOURNAL_FILE_INVALID, JOURNAL_UNKNOWN_FEATURE, * JOURNAL_MISSING_FEATURE, JOURNAL_TOO_SMALL. * * Quota file error: QMAGIC_INVALID, QTREE_BLK_INVALID, DQBLK_INVALID * DUP_DQBLK_INVALID, DUP_DQBLK_VALID * * Link file error: LINK_FAST_DATA, LINK_NULLTERM, LINK_SIZE, LINK_BLOCKS * * Directory inode error: DIR_ZERO * * Dirent dot error: DIRENT_DOTTY_DUP, DIRENT_NOT_DOTTY, DIRENT_DOT_INODE, * DIRENT_DOT_EXCESS * * Dirent field error: DIRENT_ZERO, DIRENT_NAME_CHARS,DIRENT_INODE_RANGE, * DIRENT_INODE_FREE, DIRENT_TYPE, DIRENT_DUPLICATE, * DIRENT_LENGTH * * Directory parent duplicate error: DIR_PARENT_DUP * * Directory not connected error: DIR_NOT_CONNECTED * * Inline file: INLINE_DATA_FLAG_INVALID, INLINE_DATA_COUNT_INVALID * INODE_INLINE_SIZE, INODE_INLINE_CLUSTERS * * Duplicate clusters error: DUP_CLUSTERS_CLONE, DUP_CLUSTERS_DELETE * DUP_CLUSTERS_SYSFILE_CLONE * * refcount tree error: EXTENT_MARKED_REFCOUNTED, REFCOUNT_FLAG_INVALID, * REFCOUNT_LOC_INVALID, RB_BLKNO, RB_GEN, RB_GEN_FIX, * RB_PARENT, REFCOUNT_BLOCK_INVALID, * REFCOUNT_ROOT_BLOCK_INVALID, REFCOUNT_LIST_COUNT, * REFCOUNT_LIST_USED, REFCOUNT_CLUSTER_RANGE, * REFCOUNT_CLUSTER_COLLISION, REFCOUNT_LIST_EMPTY * REFCOUNT_CLUSTERS, REFCOUNT_COUNT, * REFCOUNT_REC_REDUNDANT, REFCOUNT_COUNT_INVALID * DUP_CLUSTERS_ADD_REFCOUNT */ #endif ocfs2-tools-ocfs2-tools-1.8.6/fswreck/include/group.h000066400000000000000000000025421347147137200224600ustar00rootroot00000000000000/* * group.h * * Function prototypes, macros, etc. for related 'C' files * * Copyright (C) 2006 Oracle. All rights reserved. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * */ #ifndef __GROUP_H #define __GROUP_H void mess_up_group_minor(ocfs2_filesys *fs, enum fsck_type type, uint16_t slotnum); void mess_up_group_gen(ocfs2_filesys *fs, enum fsck_type type, uint16_t slotnum); void mess_up_group_list(ocfs2_filesys *fs, enum fsck_type type, uint16_t slotnum); void mess_up_cluster_group_desc(ocfs2_filesys *fs, enum fsck_type type, uint16_t slotnum); void mess_up_cluster_alloc_bits(ocfs2_filesys *fs, enum fsck_type type, uint16_t slotnum); #endif /* __GROUP_H */ ocfs2-tools-ocfs2-tools-1.8.6/fswreck/include/inode.h000066400000000000000000000030141347147137200224150ustar00rootroot00000000000000/* * inode.h * * Function prototypes, macros, etc. for related 'C' files * * Copyright (C) 2006 Oracle. All rights reserved. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * */ #ifndef __INODE_H #define __INODE_H void mess_up_inode_field(ocfs2_filesys *fs, enum fsck_type type, uint64_t blkno); void mess_up_inode_not_connected(ocfs2_filesys *fs, enum fsck_type type, uint64_t blkno); void mess_up_inode_orphaned(ocfs2_filesys *fs, enum fsck_type type, uint16_t slotnum); void mess_up_inode_alloc(ocfs2_filesys *fs, enum fsck_type type, uint16_t slotnum); void mess_up_inline_flag(ocfs2_filesys *fs, enum fsck_type type, uint64_t blkno); void mess_up_inline_inode(ocfs2_filesys *fs, enum fsck_type type, uint64_t blkno); void mess_up_dup_clusters(ocfs2_filesys *fs, enum fsck_type type, uint64_t blkno); #endif /* __INODE_H */ ocfs2-tools-ocfs2-tools-1.8.6/fswreck/include/journal.h000066400000000000000000000013511347147137200227730ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * journal.h * * Copyright (C) 2004, 2008 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License version 2 as published by the Free Software Foundation. * * 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. */ #ifndef __JOURNAL_H #define __JOURNAL_H void mess_up_journal(ocfs2_filesys *fs, enum fsck_type type, uint16_t slotnum); #endif /* __JOURNAL_H */ ocfs2-tools-ocfs2-tools-1.8.6/fswreck/include/local_alloc.h000066400000000000000000000023301347147137200235630ustar00rootroot00000000000000/* * local_alloc.h * * Function prototypes, macros, etc. for related 'C' files * * Copyright (C) 2006 Oracle. All rights reserved. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * */ #ifndef __LOCAL_ALLOC_H #define __LOCAL_ALLOC_H void mess_up_local_alloc_empty(ocfs2_filesys *fs, enum fsck_type type, uint16_t slotnum); void mess_up_local_alloc_bitmap(ocfs2_filesys *fs, enum fsck_type type, uint16_t slotnum); void mess_up_local_alloc_used(ocfs2_filesys *fs, enum fsck_type type, uint16_t slotnum); #endif /* __LOCAL_ALLOC_H */ ocfs2-tools-ocfs2-tools-1.8.6/fswreck/include/main.h000066400000000000000000000046511347147137200222530ustar00rootroot00000000000000/* * main.h * * Function prototypes, macros, etc. for related 'C' files * * Copyright (C) 2006 Oracle. All rights reserved. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * */ #ifndef __MAIN_H__ #define __MAIN_H__ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ocfs2/byteorder.h" #include "ocfs2/ocfs2.h" #define FSWRK_FATAL(fmt, arg...) ({ fprintf(stderr, "ERROR (%s,%d) " fmt , \ __FILE__, __LINE__, ##arg); \ raise (SIGTERM); \ exit(1); \ }) #define FSWRK_COM_FATAL(__p, __r) do { com_err(__p, __r, "(%s,%d)", __FILE__, __LINE__); raise (SIGTERM); exit(1); } while(0) #define FSWRK_FATAL_STR(str) FSWRK_FATAL(str, "") #define FSWRK_WARN(fmt, arg...) fprintf(stderr, "WARNING at %s, %d: " fmt ".\n", \ __FILE__, __LINE__, ##arg) #define FSWRK_WARN_STR(str) FSWRK_WARN(str, "") #undef max #define max(a,b) ((a) > (b) ? (a) : (b)) #undef min #define min(a,b) ((a) < (b) ? (a) : (b)) #define ARRAY_ELEMENTS(arr) (sizeof(arr) / sizeof(arr[0])) /* remaining headers */ #include "fsck_type.h" #include "corrupt.h" #include "chain.h" #include "extent.h" #include "group.h" #include "inode.h" #include "local_alloc.h" #include "truncate_log.h" #include "symlink.h" #include "special.h" #include "dir.h" #include "journal.h" #include "quota.h" #include "refcount.h" #include "discontig_bg.h" #endif /* __MAIN_H__ */ ocfs2-tools-ocfs2-tools-1.8.6/fswreck/include/quota.h000066400000000000000000000013371347147137200224560ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * quota.h * * Copyright (C) 2004, 2008 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License version 2 as published by the Free Software Foundation. * * 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. */ #ifndef __QUOTA_H #define __QUOTA_H void mess_up_quota(ocfs2_filesys *fs, enum fsck_type type, uint16_t slotnum); #endif /* __QUOTA_H */ ocfs2-tools-ocfs2-tools-1.8.6/fswreck/include/refcount.h000066400000000000000000000015311347147137200231460ustar00rootroot00000000000000/* * refcount.h * * Function prototypes, macros, etc. for related 'C' files * * Copyright (C) 2009 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License version 2 as published by the Free Software Foundation. * * 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. */ #ifndef _FSWRECK_REFCOUNT_H_ #define _FSWRECK_REFCOUNT_H_ void mess_up_refcount_tree_block(ocfs2_filesys *fs, enum fsck_type type, uint64_t blkno); void mess_up_refcount_tree(ocfs2_filesys *fs, enum fsck_type type, uint64_t blkno); #endif /* _FSWRECK_REFCOUNT_H_ */ ocfs2-tools-ocfs2-tools-1.8.6/fswreck/include/special.h000066400000000000000000000017561347147137200227520ustar00rootroot00000000000000/* * special.h * * Function prototypes, macros, etc. for related 'C' files * * Copyright (C) 2006 Oracle. All rights reserved. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * */ #ifndef __SPECIAL_H #define __SPECIAL_H void mess_up_root(ocfs2_filesys *fs, enum fsck_type type, uint64_t blkno); #endif /* __SPECIAL_H */ ocfs2-tools-ocfs2-tools-1.8.6/fswreck/include/symlink.h000066400000000000000000000017611347147137200230140ustar00rootroot00000000000000/* * symlink.h * * Function prototypes, macros, etc. for related 'C' files * * Copyright (C) 2006 Oracle. All rights reserved. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * */ #ifndef __SYMLINK_H #define __SYMLINK_H void mess_up_symlink(ocfs2_filesys *fs, enum fsck_type type, uint64_t blkno); #endif /* __SYMLINK_H */ ocfs2-tools-ocfs2-tools-1.8.6/fswreck/include/truncate_log.h000066400000000000000000000021751347147137200240140ustar00rootroot00000000000000/* * truncate_log.h * * Function prototypes, macros, etc. for related 'C' files * * Copyright (C) 2006 Oracle. All rights reserved. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * */ #ifndef __TRUNCATE_LOG_H #define __TRUNCATE_LOG_H void mess_up_truncate_log_list(ocfs2_filesys *fs, enum fsck_type type, uint16_t slotnum); void mess_up_truncate_log_rec(ocfs2_filesys *fs, enum fsck_type type, uint16_t slotnum); #endif /* __TRUNCATE_LOG_H */ ocfs2-tools-ocfs2-tools-1.8.6/fswreck/inode.c000066400000000000000000000345511347147137200207770ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * inode.c * * inode fields corruptions * * Copyright (C) 2006 Oracle. All rights reserved. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * */ /* This file will create the errors for the inode. * * Inode field error: INODE_SUBALLOC, INODE_GEN, INODE_GEN_FIX,INODE_BLKNO, INODE_NZ_DTIME, INODE_SIZE, INODE_CLUSTERS, INODE_COUNT, INODE_BLOCK_ECC, INODE_VALID_FLAG * * Inode link not connected error: INODE_NOT_CONNECTED * * Inode orphaned error: INODE_ORPHANED * * Inode alloc error: INODE_ALLOC_REPAIR * */ #include "main.h" extern char *progname; static void damage_inode(ocfs2_filesys *fs, uint64_t blkno, enum fsck_type type) { errcode_t ret; char *buf = NULL; struct ocfs2_dinode *di; ret = ocfs2_malloc_block(fs->fs_io, &buf); if (ret) FSWRK_COM_FATAL(progname, ret); ret = ocfs2_read_inode(fs, blkno, buf); if (ret) FSWRK_COM_FATAL(progname, ret); di = (struct ocfs2_dinode *)buf; if (!(di->i_flags & OCFS2_VALID_FL)) FSWRK_FATAL("not a file"); switch (type) { case INODE_GEN: fprintf(stdout, "INODE_GEN: " "Corrupt inode#%"PRIu64", change generation " " from %u to 0x1234\n", blkno, di->i_fs_generation); di->i_fs_generation = 0x1234; break; case INODE_GEN_FIX: fprintf(stdout, "INODE_GEN_FIX: " "Corrupt inode#%"PRIu64", change generation " " from %u to 0x1234, please answer 'n' when " "INODE_GEN error shows in fsck.ocfs2\n", blkno, di->i_fs_generation); di->i_fs_generation = 0x1234; break; case INODE_BLKNO: fprintf(stdout, "INODE_BLKNO: " "Corrupt inode#%"PRIu64", change i_blkno from %"PRIu64 " to %"PRIu64"\n", blkno, (uint64_t)di->i_blkno, ((uint64_t)di->i_blkno + 100)); di->i_blkno += 100; break; case INODE_NZ_DTIME: fprintf(stdout, "INODE_NZ_DTIME: " "Corrupt inode#%"PRIu64", change i_dtime from %"PRIu64 " to 100\n", blkno, (uint64_t)di->i_dtime); di->i_dtime = 100; break; case INODE_SUBALLOC: fprintf(stdout, "INODE_SUBALLOC: " "Corrupt inode#%"PRIu64", change i_suballoc_slot" " from %u to %u\n", blkno, di->i_suballoc_slot, (di->i_suballoc_slot + 10)); di->i_suballoc_slot += 10; break; case INODE_SIZE: fprintf(stdout, "INODE_SIZE: " "Corrupt inode#%"PRIu64", change i_size" " from %"PRIu64" to %"PRIu64"\n", blkno, (uint64_t)di->i_size, ((uint64_t)di->i_size + 100)); di->i_size += 100; break; case INODE_SPARSE_SIZE: fprintf(stdout, "INODE_SPARSE_SIZE: " "Corrupt inode#%"PRIu64", change i_size " "from %"PRIu64" to %u\n", blkno, (uint64_t)di->i_size, fs->fs_clustersize); di->i_size = fs->fs_clustersize; break; case INODE_CLUSTERS: fprintf(stdout, "INODE_CLUSTERS: " "Corrupt inode#%"PRIu64", change i_clusters" " from %u to 0\n", blkno, di->i_clusters); di->i_clusters = 0; break; case INODE_SPARSE_CLUSTERS: fprintf(stdout, "INODE_SPARSE_CLUSTERS: " "Corrupt inode#%"PRIu64", change i_clusters" " from %u to 0\n", blkno, di->i_clusters); di->i_clusters = 0; break; case INODE_COUNT: di->i_links_count = 0; fprintf(stdout, "INODE_COUNT: " "Corrupte inode#%"PRIu64", set link count to 0\n", blkno); break; case INODE_BLOCK_ECC: fprintf(stdout, "INODE_BLOCK_ECC: " "Corrupte inode#%"PRIu64", set both i_check.bc_crc32e" "=%u and i_check.bc_ecc=%u to 0x1234\n", blkno, di->i_check.bc_crc32e, di->i_check.bc_ecc); di->i_check.bc_crc32e = 0x1234; di->i_check.bc_ecc = 0x1234; break; case INODE_VALID_FLAG: fprintf(stdout, "INODE_VALID_FLAG: " "Corrupt inode#%"PRIu64", clear inode valid flag\n", blkno); di->i_flags &= ~OCFS2_VALID_FL; break; case REFCOUNT_FLAG_INVALID: di->i_dyn_features |= OCFS2_HAS_REFCOUNT_FL; fprintf(stdout, "REFCOUNT_FLAG_INVALD: " "Corrupt inode#%"PRIu64", add refcount feature\n", blkno); break; case REFCOUNT_LOC_INVALID: di->i_refcount_loc = 100; fprintf(stdout, "REFCOUNT_LOC_INVALID: " "Create an inode#%"PRIu64"," "whose i_refcount_loc has been messed up.\n", blkno); break; default: FSWRK_FATAL("Invalid type[%d]\n", type); } if (type != INODE_BLOCK_ECC) ret = ocfs2_write_inode(fs, blkno, buf); else ret = ocfs2_write_inode_without_meta_ecc(fs, blkno, buf); if (ret) FSWRK_COM_FATAL(progname, ret); if (buf) ocfs2_free(&buf); return; } void mess_up_inode_field(ocfs2_filesys *fs, enum fsck_type type, uint64_t blkno) { errcode_t ret; uint64_t tmpblkno; uint32_t clusters = 10; char *buf = NULL; struct ocfs2_dinode *di; create_file(fs, blkno, &tmpblkno); if ((type == INODE_SPARSE_SIZE) || (type == INODE_SPARSE_CLUSTERS)) { if (!ocfs2_sparse_alloc(OCFS2_RAW_SB(fs->fs_super))) FSWRK_FATAL("should specfiy a sparse file supported " "volume to do this corruption\n"); ret = ocfs2_malloc_block(fs->fs_io, &buf); if (ret) FSWRK_COM_FATAL(progname, ret); ret = ocfs2_read_inode(fs, tmpblkno, buf); if (ret) FSWRK_COM_FATAL(progname, ret); di = (struct ocfs2_dinode *)buf; di->i_size = fs->fs_clustersize * 2; ret = ocfs2_write_inode(fs, tmpblkno, buf); if (ret) FSWRK_COM_FATAL(progname, ret); if (buf) ocfs2_free(&buf); } if ((type == INODE_CLUSTERS) || (type == INODE_SPARSE_CLUSTERS) || (type == INODE_SPARSE_SIZE)) { ret = ocfs2_extend_allocation(fs, tmpblkno, clusters); if (ret) FSWRK_COM_FATAL(progname, ret); } if (type == REFCOUNT_FLAG_INVALID && ocfs2_refcount_tree(OCFS2_RAW_SB(fs->fs_super))) FSWRK_FATAL("should specfiy a norefcount volume\n"); if (type == REFCOUNT_LOC_INVALID && !ocfs2_refcount_tree(OCFS2_RAW_SB(fs->fs_super))) FSWRK_FATAL("Should specify a refcount supported volume\n"); damage_inode(fs, tmpblkno, type); return; } void mess_up_inode_not_connected(ocfs2_filesys *fs, enum fsck_type type, uint64_t blkno) { errcode_t ret; uint64_t tmpblkno; ret = ocfs2_new_inode(fs, &tmpblkno, S_IFREG | 0755); if (ret) FSWRK_COM_FATAL(progname, ret); fprintf(stdout, "INODE_NOT_CONNECTED: " "Create an inode#%"PRIu64" which has no links\n", tmpblkno); return ; } void mess_up_inode_orphaned(ocfs2_filesys *fs, enum fsck_type type, uint16_t slotnum) { errcode_t ret; uint64_t blkno, tmpblkno; char parentdir[OCFS2_MAX_FILENAME_LEN]; struct ocfs2_super_block *sb = OCFS2_RAW_SB(fs->fs_super); if (slotnum == UINT16_MAX) slotnum = 0; snprintf(parentdir, sizeof(parentdir), ocfs2_system_inodes[ORPHAN_DIR_SYSTEM_INODE].si_name, slotnum); ret = ocfs2_lookup(fs, sb->s_system_dir_blkno, parentdir, strlen(parentdir), NULL, &blkno); if (ret) FSWRK_COM_FATAL(progname, ret); create_file(fs, blkno, &tmpblkno); fprintf(stdout, "INODE_ORPHANED: " "Create an inode#%"PRIu64" under directory %s\n", tmpblkno, parentdir); return; } void mess_up_inode_alloc(ocfs2_filesys *fs, enum fsck_type type, uint16_t slotnum) { errcode_t ret; uint64_t tmpblkno; char *buf = NULL; struct ocfs2_dinode *di; ret = ocfs2_new_inode(fs, &tmpblkno, S_IFREG | 0755); if (ret) FSWRK_COM_FATAL(progname, ret); ret = ocfs2_malloc_block(fs->fs_io, &buf); if (ret) FSWRK_COM_FATAL(progname, ret); ret = ocfs2_read_inode(fs, tmpblkno, buf); if (ret) FSWRK_COM_FATAL(progname, ret); di = (struct ocfs2_dinode *)buf; di->i_flags &= ~OCFS2_VALID_FL; ret = ocfs2_write_inode(fs, tmpblkno, buf); if (ret) FSWRK_COM_FATAL(progname, ret); fprintf(stdout, "INODE_ALLOC_REPAIR: " "Create an inode#%"PRIu64" and invalidate it.\n", tmpblkno); if (buf) ocfs2_free(&buf); return; } void mess_up_inline_flag(ocfs2_filesys *fs, enum fsck_type type, uint64_t blkno) { int i; errcode_t ret; char *buf = NULL, file_type[20]; uint64_t inline_blkno; struct ocfs2_dinode *di; struct ocfs2_super_block *osb; osb = OCFS2_RAW_SB(fs->fs_super); if (ocfs2_support_inline_data(osb)) FSWRK_FATAL("should specfiy a noinline-data supported " "volume to do this corruption\n"); ret = ocfs2_malloc_block(fs->fs_io, &buf); if (ret) FSWRK_COM_FATAL(progname, ret); for (i = 0; i < 2; i++) { if (i == 0) { create_file(fs, blkno, &inline_blkno); snprintf(file_type, 20, "%s", "Regular file"); } else { create_directory(fs, blkno, &inline_blkno); snprintf(file_type, 20, "%s", "Diectory"); } ret = ocfs2_read_inode(fs, inline_blkno, buf); if (ret) FSWRK_COM_FATAL(progname, ret); di = (struct ocfs2_dinode *)buf; if (!(di->i_dyn_features & OCFS2_INLINE_DATA_FL)) { di->i_dyn_features |= OCFS2_INLINE_DATA_FL; ret = ocfs2_write_inode(fs, inline_blkno, buf); if (ret) FSWRK_COM_FATAL(progname, ret); } fprintf(stdout, "INLINE_DATA_FLAG_INVALID: " "Create an inlined inode#%"PRIu64"(%s) " "on a noinline-data supported volume\n", inline_blkno, file_type); } if (buf) ocfs2_free(&buf); return; } void mess_up_inline_inode(ocfs2_filesys *fs, enum fsck_type type, uint64_t blkno) { int i; errcode_t ret; char *buf = NULL, file_type[20]; uint64_t inline_blkno; struct ocfs2_dinode *di; uint16_t max_inline_sz; struct ocfs2_super_block *osb; osb = OCFS2_RAW_SB(fs->fs_super); if (!ocfs2_support_inline_data(osb)) FSWRK_FATAL("Should specify a inline-data supported " "volume to do this corruption\n"); ret = ocfs2_malloc_block(fs->fs_io, &buf); if (ret) FSWRK_COM_FATAL(progname, ret); for (i = 0; i < 2; i++) { if (i == 0) { create_file(fs, blkno, &inline_blkno); snprintf(file_type, 20, "%s", "Regular file"); } else { create_directory(fs, blkno, &inline_blkno); snprintf(file_type, 20, "%s", "Diectroy"); } ret = ocfs2_read_inode(fs, inline_blkno, buf); if (ret) FSWRK_COM_FATAL(progname, ret); di = (struct ocfs2_dinode *)buf; max_inline_sz = ocfs2_max_inline_data_with_xattr(fs->fs_blocksize, di); if (!(di->i_dyn_features & OCFS2_INLINE_DATA_FL)) di->i_dyn_features |= OCFS2_INLINE_DATA_FL; switch (type) { case INLINE_DATA_COUNT_INVALID: di->id2.i_data.id_count = 0; fprintf(stdout, "INLINE_DATA_COUNT_INVALID: " "Create an inlined inode#%"PRIu64"(%s)," "whose id_count has been messed up.\n", inline_blkno, file_type); break; case INODE_INLINE_SIZE: di->i_size = max_inline_sz + 1; fprintf(stdout, "INODE_INLINE_SIZE: " "Create an inlined inode#%"PRIu64"(%s)," "whose i_size has been messed up.\n", inline_blkno, file_type); break; case INODE_INLINE_CLUSTERS: di->i_clusters = 1; fprintf(stdout, "INODE_INLINE_CLUSTERS: " "Create an inlined inode#%"PRIu64"(%s)," "whose i_clusters has been messed up.\n", inline_blkno, file_type); break; default: FSWRK_FATAL("Invalid type[%d]\n", type); } ret = ocfs2_write_inode(fs, inline_blkno, buf); if (ret) FSWRK_COM_FATAL(progname, ret); } if (buf) ocfs2_free(&buf); return; } void mess_up_dup_clusters(ocfs2_filesys *fs, enum fsck_type type, uint64_t blkno) { errcode_t err; char *buf = NULL; uint64_t inode1_blkno, inode2_blkno; struct ocfs2_dinode *di1, *di2; struct ocfs2_extent_list *el1, *el2; err = ocfs2_malloc_blocks(fs->fs_io, 2, &buf); if (err) FSWRK_COM_FATAL(progname, err); create_file(fs, blkno, &inode1_blkno); di1 = (struct ocfs2_dinode *)buf; err = ocfs2_read_inode(fs, inode1_blkno, (char *)di1); if (err) FSWRK_COM_FATAL(progname, err); if (ocfs2_support_inline_data(OCFS2_RAW_SB(fs->fs_super))) { if (di1->i_dyn_features & OCFS2_INLINE_DATA_FL) { di1->i_dyn_features &= ~OCFS2_INLINE_DATA_FL; err = ocfs2_write_inode(fs, inode1_blkno, (char *)di1); if (err) FSWRK_COM_FATAL(progname, err); } } if (type != DUP_CLUSTERS_SYSFILE_CLONE) { create_file(fs, blkno, &inode2_blkno); di2 = (struct ocfs2_dinode *)(buf + fs->fs_blocksize); err = ocfs2_read_inode(fs, inode2_blkno, (char *)di2); if (err) FSWRK_COM_FATAL(progname, err); if (ocfs2_support_inline_data(OCFS2_RAW_SB(fs->fs_super))) { if (di2->i_dyn_features & OCFS2_INLINE_DATA_FL) { di2->i_dyn_features &= ~OCFS2_INLINE_DATA_FL; err = ocfs2_write_inode(fs, inode2_blkno, (char *)di2); if (err) FSWRK_COM_FATAL(progname, err); } } err = ocfs2_extend_allocation(fs, inode2_blkno, 1); if (err) FSWRK_COM_FATAL(progname, err); /* Re-read the inode with the allocation */ err = ocfs2_read_inode(fs, inode2_blkno, (char *)di2); if (err) FSWRK_COM_FATAL(progname, err); /* Set i_size to non-zero so that the allocation is valid */ di2->i_size = fs->fs_clustersize; err = ocfs2_write_inode(fs, inode2_blkno, (char *)di2); if (err) FSWRK_COM_FATAL(progname, err); if (type == DUP_CLUSTERS_CLONE) fprintf(stdout, "DUP_CLUSTERS_CLONE: " "Create two inodes #%"PRIu64" and #%"PRIu64 " by allocating same cluster to them.\n", inode1_blkno, inode2_blkno); else fprintf(stdout, "DUP_CLUSTERS_DELETE: " "Create two inodes #%"PRIu64" and #%"PRIu64 " by allocating same cluster to them.\n", inode1_blkno, inode2_blkno); } else { /* Here use journal file*/ err = ocfs2_lookup_system_inode(fs, JOURNAL_SYSTEM_INODE, 0, &inode2_blkno); if (err) FSWRK_COM_FATAL(progname, err); di2 = (struct ocfs2_dinode *)(buf + fs->fs_blocksize); err = ocfs2_read_inode(fs, inode2_blkno, (char *)di2); if (err) FSWRK_COM_FATAL(progname, err); if (di2->id2.i_list.l_tree_depth) FSWRK_FATAL("Journal inode has non-zero tree " "depth. fswreck can't use it for " "DUP_CLUSTERS_SYSFILE_CLONE\n"); fprintf(stdout, "DUP_CLUSTERS_SYSFILE_CLONE: " "Allocate same cluster to journal file " "#%"PRIu64" and regular file #%"PRIu64".\n", inode1_blkno, inode2_blkno); } el1 = &(di1->id2.i_list); el2 = &(di2->id2.i_list); el1->l_next_free_rec = 1; el1->l_recs[0] = el2->l_recs[0]; di1->i_size = ocfs2_clusters_to_bytes(fs, el1->l_recs[0].e_leaf_clusters); di1->i_clusters = di2->i_clusters; err = ocfs2_write_inode(fs, inode1_blkno, (char *)di1); if (err) FSWRK_COM_FATAL(progname, err); ocfs2_free(&buf); } ocfs2-tools-ocfs2-tools-1.8.6/fswreck/journal.c000066400000000000000000000117541347147137200213530ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * journal.c * * Copyright (C) 2004, 2008 Oracle. All rights reserved. * * Corruptions for journal system file. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License version 2 as published by the Free Software Foundation. * * 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. */ #include "main.h" extern char *progname; /* This file will corrupt jorunal system file. * * Journal Error: JOURNAL_FILE_INVALID, JOURNAL_UNKNOWN_FEATURE, * JOURNAL_MISSING_FEATURE, JOURNAL_TOO_SMALL. * */ void mess_up_journal(ocfs2_filesys *fs, enum fsck_type type, uint16_t slotnum) { errcode_t ret; uint64_t j_blkno, jsb_blkno; uint64_t contig; uint32_t old_no; uint16_t max_slots = OCFS2_RAW_SB(fs->fs_super)->s_max_slots; ocfs2_cached_inode *ci = NULL; char *buf = NULL; journal_superblock_t *jsb; uint64_t tmp_j_blkno, tmp_jsb_blkno; uint64_t tmp_contig; uint16_t adj_slotnum; ocfs2_cached_inode *tmp_ci = NULL; journal_superblock_t *tmp_jsb; char *tmp_buf = NULL; ret = ocfs2_malloc_block(fs->fs_io, &buf); if (ret) FSWRK_COM_FATAL(progname, ret); if (slotnum == UINT16_MAX) slotnum = max_slots - 1; ret = ocfs2_lookup_system_inode(fs, JOURNAL_SYSTEM_INODE, slotnum, &j_blkno); if (ret) FSWRK_COM_FATAL(progname, ret); ret = ocfs2_read_cached_inode(fs, j_blkno, &ci); if (ret) FSWRK_COM_FATAL(progname, ret); ret = ocfs2_extent_map_get_blocks(ci, 0, 1, &jsb_blkno, &contig, NULL); if (ret) FSWRK_COM_FATAL(progname, ret); ret = ocfs2_read_journal_superblock(fs, jsb_blkno, buf); if (ret) FSWRK_COM_FATAL(progname, ret); jsb = (journal_superblock_t *)buf; switch (type) { case JOURNAL_FILE_INVALID: old_no = JBD2_MAGIC_NUMBER; jsb->s_header.h_magic = ~JBD2_MAGIC_NUMBER; fprintf(stdout, "JOURNAL_FILE_INVALID: " "Corrupt journal system inode#%"PRIu64"'s " "superblock's magic number from %x to %x.\n", j_blkno, old_no, jsb->s_header.h_magic); break; case JOURNAL_UNKNOWN_FEATURE: jsb->s_feature_incompat |= ~JBD2_KNOWN_INCOMPAT_FEATURES; jsb->s_feature_ro_compat |= ~JBD2_KNOWN_ROCOMPAT_FEATURES; fprintf(stdout, "JOURNAL_UNKNOWN_FEATURE: " "Corrupt journal system inode#%"PRIu64" by " "adding unsupported features.\n", j_blkno); break; case JOURNAL_MISSING_FEATURE: /* First of all, set all supported features for * another journal file if existed, therefore, we * easily let the journal file with current slotnum * become features-missing when compared to the rest * of journal files. */ if (max_slots == 1) FSWRK_FATAL("should specfiy a volume with multiple " "slots to do this corruption\n"); else adj_slotnum = (slotnum + 1 > max_slots - 1) ? slotnum - 1 : slotnum + 1; ret = ocfs2_malloc_block(fs->fs_io, &tmp_buf); if (ret) FSWRK_COM_FATAL(progname, ret); ret = ocfs2_lookup_system_inode(fs, JOURNAL_SYSTEM_INODE, adj_slotnum, &tmp_j_blkno); if (ret) FSWRK_COM_FATAL(progname, ret); ret = ocfs2_read_cached_inode(fs, tmp_j_blkno, &tmp_ci); if (ret) FSWRK_COM_FATAL(progname, ret); ret = ocfs2_extent_map_get_blocks(tmp_ci, 0, 1, &tmp_jsb_blkno, &tmp_contig, NULL); if (ret) FSWRK_COM_FATAL(progname, ret); ret = ocfs2_read_journal_superblock(fs, tmp_jsb_blkno, tmp_buf); if (ret) FSWRK_COM_FATAL(progname, ret); tmp_jsb = (journal_superblock_t *)tmp_buf; tmp_jsb->s_feature_compat |= JBD2_KNOWN_COMPAT_FEATURES; tmp_jsb->s_feature_incompat |= JBD2_KNOWN_INCOMPAT_FEATURES; tmp_jsb->s_feature_ro_compat |= JBD2_KNOWN_ROCOMPAT_FEATURES; ret = ocfs2_write_journal_superblock(fs, tmp_jsb_blkno, tmp_buf); if (ret) FSWRK_COM_FATAL(progname, ret); if (tmp_buf) ocfs2_free(&tmp_buf); if (tmp_ci) ocfs2_free_cached_inode(fs, tmp_ci); /* * Clear features bits for journal file with * current slot number. */ jsb->s_feature_compat &= ~JBD2_KNOWN_COMPAT_FEATURES; jsb->s_feature_incompat &= ~JBD2_KNOWN_INCOMPAT_FEATURES; jsb->s_feature_ro_compat &= ~JBD2_KNOWN_ROCOMPAT_FEATURES; fprintf(stdout, "JOURNAL_MISSING_FEATURE: " "Corrupt journal system inode#%"PRIu64" by " "removing supported features.\n", j_blkno); break; case JOURNAL_TOO_SMALL: old_no = ci->ci_inode->i_clusters; ci->ci_inode->i_clusters = 0; fprintf(stdout, "JOURNAL_TOO_SMALL: " "Corrupt journal system inode#%"PRIu64"'s " "i_clusters from %u to zero.\n", j_blkno, old_no); break; default: FSWRK_FATAL("Invalid type[%d]\n", type); } ret = ocfs2_write_journal_superblock(fs, jsb_blkno, buf); if (ret) FSWRK_COM_FATAL(progname, ret); ret = ocfs2_write_cached_inode(fs, ci); if (ret) FSWRK_COM_FATAL(progname, ret); if (buf) ocfs2_free(&buf); if (ci) ocfs2_free_cached_inode(fs, ci); return; } ocfs2-tools-ocfs2-tools-1.8.6/fswreck/local_alloc.c000066400000000000000000000165361347147137200221500ustar00rootroot00000000000000/* * local_alloc.c * * local alloc corruptions * * Copyright (C) 2006 Oracle. All rights reserved. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * */ /* This file will create the errors for the inode. * * Empty local alloc error: LALLOC_SIZE, LALLOC_NZ_USED, LALLOC_NZ_BM * * Local alloc bitmap error: LALLOC_BM_OVERRUN, LALLOC_BM_STRADDLE, * LALLOC_BM_SIZE * * Local alloc used info error: LALLOC_USED_OVERRUN, LALLOC_CLEAR * * LALLOC_USED, LALLOC_REPAIR is recorded in fsck.ocfs2.checks.8.in, * but never find the solution in fsck.ocfs2 source code. * */ #include "main.h" extern char *progname; static uint32_t get_local_alloc_window_bits(void) { /* just return a specific number for test */ return 256; } static void create_local_alloc(ocfs2_filesys *fs, uint64_t blkno) { errcode_t ret; char *buf = NULL; struct ocfs2_dinode *di; struct ocfs2_local_alloc *la; uint32_t la_size, found; uint64_t la_off; ret = ocfs2_malloc_block(fs->fs_io, &buf); if (ret) FSWRK_COM_FATAL(progname, ret); ret = ocfs2_read_inode(fs, blkno, buf); if (ret) FSWRK_COM_FATAL(progname, ret); di = (struct ocfs2_dinode *)buf; if (!(di->i_flags & OCFS2_VALID_FL)) FSWRK_FATAL("not a file"); if (!(di->i_flags & OCFS2_LOCAL_ALLOC_FL)) FSWRK_FATAL("not a local alloc file"); if (di->id1.bitmap1.i_total > 0) { FSWRK_WARN("local alloc#%"PRIu64" file not empty." "Can't create a new one.\n", blkno); goto bail; } la_size = get_local_alloc_window_bits(); ret = ocfs2_new_clusters(fs, 1, la_size, &la_off, &found); if (ret) FSWRK_COM_FATAL(progname, ret); if(la_size != found) FSWRK_FATAL("can't allocate enough clusters for local alloc"); la = &(di->id2.i_lab); la->la_bm_off = la_off; di->id1.bitmap1.i_total = la_size; di->id1.bitmap1.i_used = 0; memset(la->la_bitmap, 0, la->la_size); ret = ocfs2_write_inode(fs, blkno, buf); if (ret) FSWRK_COM_FATAL(progname, ret); bail: if(buf) ocfs2_free(&buf); return; } static void damage_local_alloc(ocfs2_filesys *fs, uint64_t blkno, enum fsck_type type) { errcode_t ret; char *buf = NULL; struct ocfs2_dinode *di; struct ocfs2_local_alloc *la; ret = ocfs2_malloc_block(fs->fs_io, &buf); if (ret) FSWRK_COM_FATAL(progname, ret); ret = ocfs2_read_inode(fs, blkno, buf); if (ret) FSWRK_COM_FATAL(progname, ret); di = (struct ocfs2_dinode *)buf; if (!(di->i_flags & OCFS2_VALID_FL)) FSWRK_FATAL("not a file"); if (!(di->i_flags & OCFS2_LOCAL_ALLOC_FL)) FSWRK_FATAL("not a local alloc file"); la = &(di->id2.i_lab); /* For LALLOC_BM_OVERRUN, LALLOC_BM_STRADDLE,LALLOC_BM_SIZE, * LALLOC_USED_OVERRUN, LALLOC_CLEAR, i_total must be greater than 0. * So check it first. */ if (type == LALLOC_BM_OVERRUN || type == LALLOC_BM_STRADDLE || type == LALLOC_BM_SIZE || type == LALLOC_USED_OVERRUN) if (di->id1.bitmap1.i_total == 0) { FSWRK_WARN("local inode#%"PRIu64" is empty, so can't" "corrupt it for type[%d]\n", blkno, type); goto bail; } switch (type) { case LALLOC_SIZE: case LALLOC_CLEAR: if (type == LALLOC_SIZE) fprintf(stdout, "LALLOC_SIZE: "); else fprintf(stdout, "LALLOC_CLEAR: "); fprintf(stdout, "Corrupt local alloc inode#%"PRIu64 ", change size from %u to %u\n", blkno, la->la_size, (ocfs2_local_alloc_size(fs->fs_blocksize) + 10)); la->la_size = ocfs2_local_alloc_size(fs->fs_blocksize) + 10; break; case LALLOC_NZ_USED: di->id1.bitmap1.i_total = 0; di->id1.bitmap1.i_used = 10; fprintf(stdout, "LALLOC_NZ_USED: " "Corrupt local alloc inode#%"PRIu64", total = %d " " used = %d\n",blkno, di->id1.bitmap1.i_total, di->id1.bitmap1.i_used); break; case LALLOC_NZ_BM: di->id1.bitmap1.i_total = 0; la->la_bm_off = 100; fprintf(stdout, "LALLOC_NZ_BM: " "Corrupt local alloc inode#%"PRIu64", total = %d " " la_bm_off = %d\n",blkno, di->id1.bitmap1.i_total, la->la_bm_off); break; case LALLOC_BM_OVERRUN: case LALLOC_BM_STRADDLE: la->la_bm_off = fs->fs_clusters + 10; if (type == LALLOC_BM_OVERRUN) fprintf(stdout, "LALLOC_BM_OVERRUN: "); else fprintf(stdout, "LALLOC_BM_STRADDLE: "); fprintf(stdout, "Corrupt local alloc inode#%"PRIu64 ", la_bm_off =%u\n", blkno, la->la_bm_off); break; case LALLOC_BM_SIZE: fprintf(stdout, "LALLOC_SIZE: " "Corrupt local alloc inode#%"PRIu64", change i_total" " from %u to %u\n", blkno, di->id1.bitmap1.i_total, (la->la_size * 8 + 10)); di->id1.bitmap1.i_total = la->la_size * 8 + 10; break; case LALLOC_USED_OVERRUN: fprintf(stdout, "LALLOC_USED_OVERRUN: " "Corrupt local alloc inode#%"PRIu64", change i_used" " from %u to %u\n", blkno, di->id1.bitmap1.i_used, (di->id1.bitmap1.i_total + 10)); di->id1.bitmap1.i_used = di->id1.bitmap1.i_total + 10; break; default: FSWRK_FATAL("Unknown type = %d", type); } ret = ocfs2_write_inode(fs, blkno, buf); if (ret) FSWRK_COM_FATAL(progname, ret); bail: if (buf) ocfs2_free(&buf); return; } void mess_up_local_alloc_empty(ocfs2_filesys *fs, enum fsck_type type, uint16_t slotnum) { errcode_t ret; uint64_t blkno; char alloc_inode[OCFS2_MAX_FILENAME_LEN]; struct ocfs2_super_block *sb = OCFS2_RAW_SB(fs->fs_super); if (slotnum == UINT16_MAX) slotnum = 0; snprintf(alloc_inode, sizeof(alloc_inode), ocfs2_system_inodes[LOCAL_ALLOC_SYSTEM_INODE].si_name, slotnum); ret = ocfs2_lookup(fs, sb->s_system_dir_blkno, alloc_inode, strlen(alloc_inode), NULL, &blkno); if (ret) FSWRK_COM_FATAL(progname, ret); damage_local_alloc(fs, blkno, type); return; } void mess_up_local_alloc_bitmap(ocfs2_filesys *fs, enum fsck_type type, uint16_t slotnum) { errcode_t ret; uint64_t blkno; char alloc_inode[OCFS2_MAX_FILENAME_LEN]; struct ocfs2_super_block *sb = OCFS2_RAW_SB(fs->fs_super); if (slotnum == UINT16_MAX) slotnum = 0; snprintf(alloc_inode, sizeof(alloc_inode), ocfs2_system_inodes[LOCAL_ALLOC_SYSTEM_INODE].si_name, slotnum); ret = ocfs2_lookup(fs, sb->s_system_dir_blkno, alloc_inode, strlen(alloc_inode), NULL, &blkno); if (ret) FSWRK_COM_FATAL(progname, ret); create_local_alloc(fs, blkno); damage_local_alloc(fs, blkno, type); return; } void mess_up_local_alloc_used(ocfs2_filesys *fs, enum fsck_type type, uint16_t slotnum) { errcode_t ret; uint64_t blkno; char alloc_inode[OCFS2_MAX_FILENAME_LEN]; struct ocfs2_super_block *sb = OCFS2_RAW_SB(fs->fs_super); if (slotnum == UINT16_MAX) slotnum = 0; snprintf(alloc_inode, sizeof(alloc_inode), ocfs2_system_inodes[LOCAL_ALLOC_SYSTEM_INODE].si_name,slotnum); ret = ocfs2_lookup(fs, sb->s_system_dir_blkno, alloc_inode, strlen(alloc_inode), NULL, &blkno); if (ret) FSWRK_COM_FATAL(progname, ret); create_local_alloc(fs, blkno); damage_local_alloc(fs, blkno, type); return; } ocfs2-tools-ocfs2-tools-1.8.6/fswreck/main.c000066400000000000000000000524321347147137200206230ustar00rootroot00000000000000/* * main.c * * entry point for fswrk * * Copyright (C) 2006 Oracle. All rights reserved. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * */ #include "main.h" char *progname = NULL; static char *device = NULL; static uint16_t slotnum = UINT16_MAX; static int corrupt[NUM_FSCK_TYPE]; struct prompt_code { enum fsck_type pc_codenum; char *pc_codestr; char *pc_fsfeat; int pc_slots; void (*pc_func)(ocfs2_filesys *fs, enum fsck_type code, uint16_t slotnum); char *pc_desc; }; #define define_prompt_code(_code, _func, _feat, _slots, _desc) \ [_code] = { \ .pc_codenum = _code, \ .pc_codestr = #_code, \ .pc_fsfeat = _feat, \ .pc_slots = _slots, \ .pc_func = _func, \ .pc_desc = _desc, \ } static struct prompt_code prompt_codes[NUM_FSCK_TYPE] = { define_prompt_code(EB_BLKNO, corrupt_file, "", 1, "Corrupt an extent block's eb_blkno field"), define_prompt_code(EB_GEN, corrupt_file, "", 1, "Corrupt an extent block's generation number"), define_prompt_code(EB_GEN_FIX, corrupt_file, "", 1, "Corrupt an extent block's generation number " "so that fsck.ocfs2 can fix it"), define_prompt_code(EXTENT_MARKED_UNWRITTEN, corrupt_file, "nounwritten", 1, "Mark an extent unwritten when the filesystem " "does not support it"), define_prompt_code(EXTENT_MARKED_REFCOUNTED, corrupt_file, "norefcount", 1, "Mark an extent refcounted when the filesystem " "does not support it"), define_prompt_code(EXTENT_BLKNO_UNALIGNED, corrupt_file, "", 1, "Corrupt extent record's e_blkno"), define_prompt_code(EXTENT_CLUSTERS_OVERRUN, corrupt_file, "", 1, "Corrupt extent record's e_leaf_clusters"), define_prompt_code(EXTENT_EB_INVALID, corrupt_file, "", 1, "Corrupt an extent block's generation number"), define_prompt_code(EXTENT_LIST_DEPTH, corrupt_file, "", 1, "Corrupt first extent block's list depth of an inode"), define_prompt_code(EXTENT_LIST_COUNT, corrupt_file, "", 1, "Corrupt extent block's clusters"), define_prompt_code(EXTENT_LIST_FREE, corrupt_file, "", 1, "Corrupt extent block's l_next_free_rec"), define_prompt_code(EXTENT_BLKNO_RANGE, corrupt_file, "", 1, "Corrupt extent record's e_blkno to 1"), define_prompt_code(EXTENT_OVERLAP, corrupt_file, "", 1, "Corrupt extent record's e_cpos to overlap"), define_prompt_code(EXTENT_HOLE, corrupt_file, "", 1, "Corrupt extent record's e_cpos to create hole"), define_prompt_code(CHAIN_CPG, corrupt_sys_file, "", 1, "Corrupt chain list's cl_cpg of global_bitmap"), define_prompt_code(SUPERBLOCK_CLUSTERS_EXCESS, corrupt_sys_file, "nometaecc", 1, "Corrupt sb's i_clusters by wrong increment"), define_prompt_code(SUPERBLOCK_CLUSTERS_LACK, corrupt_sys_file, "nometaecc", 1, "Corrupt sb's i_clusters by wrong decrement"), define_prompt_code(GROUP_UNEXPECTED_DESC, corrupt_group_desc, "", 1, "Add a fake description to chain"), define_prompt_code(GROUP_EXPECTED_DESC, corrupt_group_desc, "", 1, "Delete the right description from chain"), define_prompt_code(GROUP_GEN, corrupt_group_desc, "", 1, "Corrupt chain group's generation"), define_prompt_code(GROUP_PARENT, corrupt_group_desc, "", 1, "Corrupt chain group's group parent"), define_prompt_code(GROUP_BLKNO, corrupt_group_desc, "", 1, "Corrupt chain group's blkno"), define_prompt_code(GROUP_CHAIN, corrupt_group_desc, "", 1, "Corrupt chain group's chain where it was in"), define_prompt_code(GROUP_CHAIN_LOOP, corrupt_group_desc, "", 1, "Corrupt group's chain to form a loop"), define_prompt_code(GROUP_FREE_BITS, corrupt_group_desc, "", 1, "Corrupt chain group's free bits"), define_prompt_code(CHAIN_COUNT, corrupt_sys_file, "", 1, "Corrupt chain list's cl_count"), define_prompt_code(CHAIN_NEXT_FREE, corrupt_sys_file, "", 1, "Corrupt chain list's cl_next_free_rec"), define_prompt_code(CHAIN_EMPTY, corrupt_sys_file, "", 1, "Corrupt chain list's cl_recs into zero"), define_prompt_code(CHAIN_I_CLUSTERS, corrupt_sys_file, "", 1, "Corrupt chain allocator's i_clusters"), define_prompt_code(CHAIN_I_SIZE, corrupt_sys_file, "", 1, "Corrupt chain allocator's i_size"), define_prompt_code(CHAIN_GROUP_BITS, corrupt_sys_file, "", 1, "Corrupt chain allocator's i_used of bitmap"), define_prompt_code(CHAIN_HEAD_LINK_RANGE, corrupt_sys_file, "", 1, "Corrupt chain list's header blkno"), define_prompt_code(CHAIN_LINK_GEN, corrupt_sys_file, "", 1, "Corrupt allocation group descriptor's " "bg_generation field"), define_prompt_code(CHAIN_LINK_MAGIC, corrupt_sys_file, "", 1, "Corrupt allocation group descriptor's " "bg_signature field"), define_prompt_code(CHAIN_LINK_RANGE, corrupt_sys_file, "", 1, "Corrupt allocation group descriptor's " "bg_next_group field"), define_prompt_code(CHAIN_BITS, corrupt_sys_file, "", 1, "Corrupt chain's total bits"), define_prompt_code(DISCONTIG_BG_DEPTH, corrupt_discontig_bg, "", 1, "corrupt extent tree depth for a discontig bg"), define_prompt_code(DISCONTIG_BG_COUNT, corrupt_discontig_bg, "", 1, "corrupt extent list count for a discontig bg"), define_prompt_code(DISCONTIG_BG_REC_RANGE, corrupt_discontig_bg, "", 1, "corrupt extent rec range for a discontig bg"), define_prompt_code(DISCONTIG_BG_CORRUPT_LEAVES, corrupt_discontig_bg, "", 1, "corrupt extent recs' clusters for a discontig bg"), define_prompt_code(DISCONTIG_BG_CLUSTERS, corrupt_discontig_bg, "", 1, "corrupt a discontig bg by more clusters allocated"), define_prompt_code(DISCONTIG_BG_LESS_CLUSTERS, corrupt_discontig_bg, "", 1, "corrupt a discontig bg by less clusters allocated"), define_prompt_code(DISCONTIG_BG_NEXT_FREE_REC, corrupt_discontig_bg, "", 1, "corrupt extent list's next free of a discontig bg"), define_prompt_code(DISCONTIG_BG_LIST_CORRUPT, corrupt_discontig_bg, "", 1, "corrupt extent list and rec for a discontig bg"), define_prompt_code(DISCONTIG_BG_REC_CORRUPT, corrupt_discontig_bg, "", 1, "corrupt extent rec for a discontig bg"), define_prompt_code(DISCONTIG_BG_LEAF_CLUSTERS, corrupt_discontig_bg, "", 1, "corrupt extent rec's clusters for a discontig bg"), define_prompt_code(INODE_SUBALLOC, corrupt_file, "", 1, "Corrupt inode's i_suballoc_slot field"), define_prompt_code(INODE_GEN, corrupt_file, "", 1, "Corrupt inode's i_generation field"), define_prompt_code(INODE_GEN_FIX, corrupt_file, "", 1, "Corrupt inode's i_generation field"), define_prompt_code(INODE_BLKNO, corrupt_file, "", 1, "Corrupt inode's i_blkno field"), define_prompt_code(INODE_NZ_DTIME, corrupt_file, "", 1, "Corrupt inode's i_dtime field"), define_prompt_code(INODE_SIZE, corrupt_file, "", 1, "Corrupt inode's i_size field"), define_prompt_code(INODE_SPARSE_SIZE, corrupt_file, "", 1, "Corrupt sparse inode's i_size field"), define_prompt_code(INODE_CLUSTERS, corrupt_file, "", 1, "Corrupt inode's i_clusters field"), define_prompt_code(INODE_SPARSE_CLUSTERS, corrupt_file, "", 1, "Corrupt sparse inode's i_clusters field"), define_prompt_code(INODE_COUNT, corrupt_file, "", 1, "Corrupt inode's i_links_count field"), define_prompt_code(INODE_NOT_CONNECTED, corrupt_file, "", 1, "Create an inode which has no links to dentries"), define_prompt_code(LINK_FAST_DATA, corrupt_file, "", 1, "Corrupt symlink's i_clusters to 0"), define_prompt_code(LINK_NULLTERM, corrupt_file, "", 1, "Corrupt symlink's all blocks with dummy texts"), define_prompt_code(LINK_SIZE, corrupt_file, "", 1, "Corrupt symlink's i_size field"), define_prompt_code(LINK_BLOCKS, corrupt_file, "", 1, "Corrupt symlink's e_leaf_clusters field"), define_prompt_code(ROOT_NOTDIR, corrupt_file, "", 1, "Corrupt root inode, change its i_mode to 0"), define_prompt_code(ROOT_DIR_MISSING, corrupt_file, "", 1, "Corrupt root inode, change its i_mode to 0"), define_prompt_code(LOSTFOUND_MISSING, corrupt_file, "", 1, "Corrupt root inode, change its i_mode to 0"), define_prompt_code(DIR_DOTDOT, corrupt_file, "", 1, "Corrupt dir's dotdot entry's ino it points to"), define_prompt_code(DIR_ZERO, corrupt_file, "noinline-data", 1, "Corrupt directory, empty its content"), define_prompt_code(DIR_HOLE, corrupt_file, "", 1, "Create a hole in the directory"), define_prompt_code(DIRENT_DOTTY_DUP, corrupt_file, "", 1, "Duplicate '.' dirent to a directory"), define_prompt_code(DIRENT_NOT_DOTTY, corrupt_file, "", 1, "Corrupt directory's '.' dirent to a dummy one"), define_prompt_code(DIRENT_DOT_INODE, corrupt_file, "", 1, "Corrupt dot's inode no"), define_prompt_code(DIRENT_DOT_EXCESS, corrupt_file, "", 1, "Corrupt dot's dirent length"), define_prompt_code(DIRENT_ZERO, corrupt_file, "", 1, "Corrupt directory, add a zero dirent"), define_prompt_code(DIRENT_NAME_CHARS, corrupt_file, "", 1, "Corrupt directory, add a invalid dirent"), define_prompt_code(DIRENT_INODE_RANGE, corrupt_file, "", 1, "Corrupt directory, add an entry whose inode " "exceeds the limits"), define_prompt_code(DIRENT_INODE_FREE, corrupt_file, "", 1, "Corrupt directory, add an entry whose inode " "isn't used"), define_prompt_code(DIRENT_TYPE, corrupt_file, "", 1, "Corrupt dirent's mode"), define_prompt_code(DIRENT_DUPLICATE, corrupt_file, "", 1, "Add two duplicated dirents to dir"), define_prompt_code(DIRENT_LENGTH, corrupt_file, "", 1, "Corrupt dirent's length"), define_prompt_code(DIR_PARENT_DUP, corrupt_file, "", 1, "Create a dir with two '..' dirent"), define_prompt_code(DIR_NOT_CONNECTED, corrupt_file, "", 1, "Create a dir which has no connections"), define_prompt_code(INLINE_DATA_FLAG_INVALID, corrupt_file, "noinline-data", 1, "Create an inlined inode on a unsupported volume"), define_prompt_code(INLINE_DATA_COUNT_INVALID, corrupt_file, "", 1, "Corrupt inlined inode's id_count"), define_prompt_code(INODE_INLINE_SIZE, corrupt_file, "", 1, "Corrupt inlined inode's i_size"), define_prompt_code(INODE_INLINE_CLUSTERS, corrupt_file, "", 1, "Corrupt inlined inode's i_clusters"), define_prompt_code(DUP_CLUSTERS_CLONE, corrupt_file, "", 1, "Allocate same cluster to different files"), define_prompt_code(DUP_CLUSTERS_DELETE, corrupt_file, "", 1, "Allocate same cluster to different files"), define_prompt_code(DUP_CLUSTERS_SYSFILE_CLONE, corrupt_file, "", 1, "Allocate same cluster to different system files"), define_prompt_code(CLUSTER_ALLOC_BIT, corrupt_group_desc, "", 1, "Mark bits of global bitmap by unused clusters"), define_prompt_code(INODE_ORPHANED, corrupt_sys_file, "", 1, "Create an inode under orphan dir"), define_prompt_code(INODE_ALLOC_REPAIR, corrupt_sys_file, "", 1, "Create an invalid inode"), define_prompt_code(CLUSTER_GROUP_DESC, corrupt_group_desc, "", 1, "Corrupt chain group's clusters and free bits"), define_prompt_code(LALLOC_SIZE, corrupt_local_alloc, "", 1, "Corrupt local alloc's size"), define_prompt_code(LALLOC_NZ_USED, corrupt_local_alloc, "", 1, "Corrupt local alloc's used and total clusters"), define_prompt_code(LALLOC_NZ_BM, corrupt_local_alloc, "", 1, "Corrupt local alloc's starting bit offset"), define_prompt_code(LALLOC_BM_OVERRUN, corrupt_local_alloc, "", 1, "Overrun local alloc's starting bit offset"), define_prompt_code(LALLOC_BM_STRADDLE, corrupt_local_alloc, "", 1, "Straddle local alloc's starting bit offset"), define_prompt_code(LALLOC_BM_SIZE, corrupt_local_alloc, "", 1, "Corrupt local alloc bitmap's i_total"), define_prompt_code(LALLOC_USED_OVERRUN, corrupt_local_alloc, "", 1, "Corrupt local alloc bitmap's i_used"), define_prompt_code(LALLOC_CLEAR, corrupt_local_alloc, "", 1, "Corrupt local alloc's size"), define_prompt_code(LALLOC_REPAIR, NULL, "", 1, "Unimplemented corrupt code"), define_prompt_code(LALLOC_USED, NULL, "", 1, "Unimplemented corrupt code"), define_prompt_code(DEALLOC_COUNT, corrupt_truncate_log, "", 1, "Corrupt truncate log's tl_count"), define_prompt_code(DEALLOC_USED, corrupt_truncate_log, "", 1, "Corrupt truncate log's tl_used"), define_prompt_code(TRUNCATE_REC_START_RANGE, corrupt_truncate_log, "", 1, "Corrupt truncate log's t_start"), define_prompt_code(TRUNCATE_REC_WRAP, corrupt_truncate_log, "", 1, "Corrupt truncate log's tl_recs"), define_prompt_code(TRUNCATE_REC_RANGE, corrupt_truncate_log, "", 1, "Corrupt truncate log's t_clusters"), define_prompt_code(JOURNAL_FILE_INVALID, corrupt_sys_file, "", 1, "Corrupt journal file as an invalid one."), define_prompt_code(JOURNAL_UNKNOWN_FEATURE, corrupt_sys_file, "", 1, "Corrupt journal file with unknown feature ."), define_prompt_code(JOURNAL_MISSING_FEATURE, corrupt_sys_file, "", 4, "Corrupt journal file by missing features."), define_prompt_code(JOURNAL_TOO_SMALL, corrupt_sys_file, "", 1, "Corrupt journal file as a too small one."), define_prompt_code(QMAGIC_INVALID, corrupt_sys_file, "", 1, "Corrupt quota system file's header."), define_prompt_code(QTREE_BLK_INVALID, corrupt_sys_file, "", 1, "Corrupt quota tree block."), define_prompt_code(DQBLK_INVALID, corrupt_sys_file, "", 1, "Corrupt quota data blok."), define_prompt_code(DUP_DQBLK_INVALID, corrupt_sys_file, "", 1, "Duplicate a invalid quota limits."), define_prompt_code(DUP_DQBLK_VALID, corrupt_sys_file, "", 1, "Duplicate a valid quota limits."), define_prompt_code(REFCOUNT_FLAG_INVALID, corrupt_file, "", 1, "Create a refcounted inode on a unsupported volume"), define_prompt_code(REFCOUNT_LOC_INVALID, corrupt_file, "", 1, "Corrupt a refcounted file's refcount location"), define_prompt_code(RB_BLKNO, corrupt_refcount, "", 1, "Corrupt a refcount block's rf_blkno"), define_prompt_code(RB_GEN, corrupt_refcount, "", 1, "Corrupt a refcount block's generation"), define_prompt_code(RB_GEN_FIX, corrupt_refcount, "", 1, "Corrupt a refcount block's generation"), define_prompt_code(RB_PARENT, corrupt_refcount, "", 1, "Corrupt a refcount block's rf_parent"), define_prompt_code(REFCOUNT_BLOCK_INVALID, corrupt_refcount, "", 1, "Corrupt a refcount block's rf_parent"), define_prompt_code(REFCOUNT_ROOT_BLOCK_INVALID, corrupt_refcount, "", 1, "Corrupt a refcount block's rf_parent"), define_prompt_code(REFCOUNT_LIST_COUNT, corrupt_refcount, "", 1, "corrupt the refcount list in a refcount block"), define_prompt_code(REFCOUNT_LIST_USED, corrupt_refcount, "", 1, "corrupt the refcount list in a refcount block"), define_prompt_code(REFCOUNT_CLUSTER_RANGE, corrupt_refcount, "", 1, "corrupt the refcount list in a refcount block"), define_prompt_code(REFCOUNT_CLUSTER_COLLISION, corrupt_refcount, "", 1, "corrupt the refcount list in a refcount block"), define_prompt_code(REFCOUNT_LIST_EMPTY, corrupt_refcount, "", 1, "corrupt the refcount list in a refcount block"), define_prompt_code(REFCOUNT_CLUSTERS, corrupt_refcount, "", 1, "corrupt the rf_clusters for a refcount tree"), define_prompt_code(REFCOUNT_COUNT, corrupt_refcount, "", 1, "corrupt the rf_count for a refcount tree"), define_prompt_code(REFCOUNT_REC_REDUNDANT, corrupt_refcount, "", 1, "corrupt the refcount record in a refcount block"), define_prompt_code(REFCOUNT_COUNT_INVALID, corrupt_refcount, "", 1, "corrupt the refcount record in a refcount block"), define_prompt_code(DUP_CLUSTERS_ADD_REFCOUNT, corrupt_refcount, "", 1, "corrupt refcount record and handle them in dup"), define_prompt_code(INODE_BLOCK_ECC, corrupt_file, "", 1, "corrupt inode's i_check filed"), define_prompt_code(INODE_VALID_FLAG, corrupt_file, "", 1, "corrupt inode's i_flags filed"), }; #undef define_prompt_code /* * usage() * */ static void usage(void) { g_print("%s is a program to corrupt a filesystem\n", progname); g_print("***** THIS WILL DAMAGE YOUR FILESYSTEM. USE AT YOUR OWN RISK. *****\n"); g_print("Usage: %s [-c corrupt-string] [-C corrupt-number] [-L corrupt-number] " \ "[-N slot-number] [-nlM] [DEVICE]\n", progname); g_print(" -c, -C, Corrupt the file system\n"); g_print(" -L, Prints the corresponsing corrupt-string\n"); g_print(" -l, Lists all the corrupt codes\n"); g_print(" -n, Prints the total number of corrupt codes\n"); g_print(" -M, Prints the mkfs options\n"); exit(0); } static void print_codes(void) { int i, len; g_print("Corrupt codes:\n"); for (len = 0, i = 0; i < NUM_FSCK_TYPE; ++i) len = ocfs2_max(len, (int)strlen(prompt_codes[i].pc_codestr)); for (i = 0; i < NUM_FSCK_TYPE; i++) fprintf(stdout, "%3d %-*s %s\n", prompt_codes[i].pc_codenum, len, prompt_codes[i].pc_codestr, prompt_codes[i].pc_desc); exit(0); } /* * print_version() * */ static void print_version(void) { fprintf(stderr, "%s %s\n", progname, VERSION); } /* * handle_signal() * */ static void handle_signal(int sig) { switch (sig) { case SIGTERM: case SIGINT: exit(1); } return ; } /* handle_signal */ static int corrupt_code_match(const char *corrupt_code) { int i; for (i = 0; i < NUM_FSCK_TYPE; i++) if (strcmp(corrupt_code, prompt_codes[i].pc_codestr) == 0) return i; return -1; } static int parse_corrupt_codes(const char *corrupt_codes) { int ret = 0, i = -2; char *p; char *token = NULL; p = (char *)corrupt_codes; while (p) { token = p; p = strchr(p, ','); if (p) *p = 0; if (strcmp(token, "") != 0) { i = corrupt_code_match(token); if (i >= 0) corrupt[i] = 1; else { fprintf(stderr, "Corrupt code \"%s\" was not " "supported.\n", token); ret = -1; break; } } if (!p) continue; p++; } if (i == -2) { fprintf(stderr, "At least one corrupt code needed.\n"); ret = -1; } return ret; } /* * read_options() * */ static int read_options(int argc, char **argv) { int i, c, listcode = 0, showmkfs = 0; int ret = 0; progname = basename(argv[0]); if (argc < 2) usage(); while (1) { c = getopt(argc, argv, "lnc:C:N:L:M"); if (c == -1) break; switch (c) { case 'c': /* corrupt code string */ ret = parse_corrupt_codes(optarg); break; case 'l': print_codes(); break; case 'L': listcode = 1; case 'C': /* corrupt code number */ ret = atoi(optarg); if (ret < NUM_FSCK_TYPE) corrupt[ret] = 1; else { fprintf(stderr, "Corrupt code \"%d\" is not " "supported.\n", ret); exit(-1); } break; case 'M': /* mkfs features */ showmkfs = 1; break; case 'n': fprintf(stdout, "%d\n", NUM_FSCK_TYPE); exit(NUM_FSCK_TYPE); case 'N': /* slotnum */ slotnum = strtoul(optarg, NULL, 0); break; default: ret = -1; goto out; } } if (listcode) { for (i = 0; i < NUM_FSCK_TYPE; i++) { if (corrupt[i]) { fprintf(stdout, "%s\n", prompt_codes[i].pc_codestr); listcode = 0; break; } } exit(listcode); } #define MKFS_PARAMS_FIX \ "-b 4096 -C 4096 --fs-feature-level=max-features -J size=16M " \ "-L fswreck -M local" if (showmkfs) { for (i = 0; i < NUM_FSCK_TYPE; i++) { if (corrupt[i]) { fprintf(stdout, MKFS_PARAMS_FIX); fprintf(stdout, " -N %d", prompt_codes[i].pc_slots); if (strlen(prompt_codes[i].pc_fsfeat)) fprintf(stdout, " --fs-features=%s", prompt_codes[i].pc_fsfeat); fprintf(stdout, "\n"); showmkfs = 0; break; } } exit(showmkfs); } if (optind >= argc || !argv[optind]) { ret = -1; goto out; } device = argv[optind]; out: if (ret < 0) usage(); return ret; } /* * main() * */ int main(int argc, char **argv) { ocfs2_filesys *fs = NULL; errcode_t ret = 0; int i; initialize_ocfs_error_table(); #define INSTALL_SIGNAL(sig) \ do { \ if (signal(sig, handle_signal) == SIG_ERR) { \ fprintf(stderr, "Could not set " #sig "\n");\ goto bail; \ } \ } while (0) INSTALL_SIGNAL(SIGTERM); INSTALL_SIGNAL(SIGINT); memset(corrupt, 0, sizeof(corrupt)); ret = read_options(argc, argv); if (ret < 0) goto bail; print_version(); ret = ocfs2_open(device, OCFS2_FLAG_RW, 0, 0, &fs); if (ret) { com_err(progname, ret, "while opening \"%s\"", device); goto bail; } for (i = 0; i < NUM_FSCK_TYPE; ++i) { if (corrupt[i]) { if (!prompt_codes[i].pc_func) { fprintf(stderr, "Unimplemented corrupt code " "= %s\n", prompt_codes[i].pc_codestr); continue; } fprintf(stdout, "%s: Corrupting %s with code %s (%d)\n", progname, device, prompt_codes[i].pc_codestr, prompt_codes[i].pc_codenum); prompt_codes[i].pc_func(fs, prompt_codes[i].pc_codenum, slotnum); } } bail: if (fs) { ret = ocfs2_close(fs); if (ret) com_err(progname, ret, "while closing \"%s\"", device); } return ret; } /* main */ ocfs2-tools-ocfs2-tools-1.8.6/fswreck/quota.c000066400000000000000000000163221347147137200210260ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * quota.c * * Copyright (C) 2004, 2008 Oracle. All rights reserved. * * Corruptions for quota system file. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License version 2 as published by the Free Software Foundation. * * 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. */ #include "main.h" extern char *progname; static int g_actref; /* This file will corrupt quota system file. * * Quota Error: QMAGIC_INVALID, QTREE_BLK_INVALID, DQBLK_INVALID * DUP_DQBLK_INVALID, DUP_DQBLK_VALID * */ static char *type2name(int type) { if (type == USRQUOTA) return "user"; return "group"; } static errcode_t o2fswreck_read_blk(ocfs2_filesys *fs, int type, char *buf, uint32_t blk) { uint32_t got; errcode_t ret; ret = ocfs2_file_read(fs->qinfo[type].qi_inode, buf, fs->fs_blocksize, blk * fs->fs_blocksize, &got); if (ret) return ret; if (got != fs->fs_blocksize) return OCFS2_ET_SHORT_READ; return 0; } static errcode_t o2fswreck_write_blk(ocfs2_filesys *fs, int type, char *buf, uint32_t blk) { errcode_t err; uint32_t written; err = ocfs2_file_write(fs->qinfo[type].qi_inode, buf, fs->fs_blocksize, blk * fs->fs_blocksize, &written); if (err) return err; if (written != fs->fs_blocksize) return OCFS2_ET_SHORT_WRITE; return 0; } static errcode_t o2fswreck_get_data_blk(ocfs2_filesys *fs, int type, uint32_t blk, int depth, char *buf) { errcode_t ret; int epb = (fs->fs_blocksize - OCFS2_QBLK_RESERVED_SPACE) >> 2; int tree_depth = ocfs2_qtree_depth(fs->fs_blocksize); uint32_t *refs = (uint32_t *)buf, actref; int i; ret = o2fswreck_read_blk(fs, type, buf, blk); if (ret) FSWRK_COM_FATAL(progname, ret); for (i = 0; i < epb; i++) { actref = le32_to_cpu(refs[i]); if (!actref) continue; if (depth + 1 < tree_depth) { ret = o2fswreck_get_data_blk(fs, type, actref, depth + 1, buf + fs->fs_blocksize); } else { ret = o2fswreck_read_blk(fs, type, buf + fs->fs_blocksize, actref); if (ret) FSWRK_COM_FATAL(progname, ret); g_actref = actref; return 0; } if (ret) goto out; } out: return ret; } void mess_up_quota(ocfs2_filesys *fs, enum fsck_type type, uint16_t slotnum) { errcode_t ret; int qtype; char *buf; int tree_depth = ocfs2_qtree_depth(fs->fs_blocksize); struct ocfs2_disk_dqheader *header; struct ocfs2_global_disk_dqblk *ddquot; struct qt_disk_dqdbheader *dh; if ((!OCFS2_HAS_RO_COMPAT_FEATURE(OCFS2_RAW_SB(fs->fs_super), OCFS2_FEATURE_RO_COMPAT_USRQUOTA)) || (!OCFS2_HAS_RO_COMPAT_FEATURE(OCFS2_RAW_SB(fs->fs_super), OCFS2_FEATURE_RO_COMPAT_GRPQUOTA))) FSWRK_FATAL("Should specify a volume with both usrquota and " "grpquota enabled to do this corruption.\n"); ret = ocfs2_init_fs_quota_info(fs, USRQUOTA); if (ret) FSWRK_COM_FATAL(progname, ret); ret = ocfs2_init_fs_quota_info(fs, GRPQUOTA); if (ret) FSWRK_COM_FATAL(progname, ret); ret = ocfs2_malloc_blocks(fs->fs_io, tree_depth + 1, &buf); if (ret) FSWRK_COM_FATAL(progname, ret); switch (type) { case QMAGIC_INVALID: qtype = USRQUOTA; ret = o2fswreck_read_blk(fs, qtype, buf, 0); if (ret) FSWRK_COM_FATAL(progname, ret); header = (struct ocfs2_disk_dqheader *)buf; ocfs2_swap_quota_header(header); header->dqh_magic = ~header->dqh_magic; ocfs2_swap_quota_header(header); ret = o2fswreck_write_blk(fs, qtype, buf, 0); if (ret) FSWRK_COM_FATAL(progname, ret); fprintf(stdout, "QMAGIC_INVALID: " "Corrupt global %s quota file's magic number " "in its header.\n", type2name(qtype)); break; case QTREE_BLK_INVALID: qtype = GRPQUOTA; ret = o2fswreck_read_blk(fs, qtype, buf, QT_TREEOFF); if (ret) FSWRK_COM_FATAL(progname, ret); struct ocfs2_disk_dqtrailer *dqt = ocfs2_block_dqtrailer(fs->fs_blocksize, buf); dqt->dq_check.bc_crc32e = ~dqt->dq_check.bc_crc32e; dqt->dq_check.bc_ecc = ~dqt->dq_check.bc_ecc; ret = o2fswreck_write_blk(fs, qtype, buf, QT_TREEOFF); if (ret) FSWRK_COM_FATAL(progname, ret); fprintf(stdout, "QTREE_BLK_INVALID: " "Corrupt global %s quota tree block.\n", type2name(qtype)); break; case DQBLK_INVALID: qtype = USRQUOTA; ret = o2fswreck_get_data_blk(fs, qtype, QT_TREEOFF, 0, buf); ddquot = (struct ocfs2_global_disk_dqblk *)(buf + fs->fs_blocksize * tree_depth + sizeof(struct qt_disk_dqdbheader)); ocfs2_swap_quota_global_dqblk(ddquot); ddquot->dqb_id = 0xFFFFFFF6; ddquot->dqb_isoftlimit += 1; ddquot->dqb_ihardlimit += 2; ddquot->dqb_bsoftlimit += 3; ddquot->dqb_bhardlimit += 4; ocfs2_swap_quota_global_dqblk(ddquot); dh = (struct qt_disk_dqdbheader *)(buf + fs->fs_blocksize * tree_depth); ocfs2_swap_quota_leaf_block_header(dh); dh->dqdh_next_free = 0xFFFFFFFF; dh->dqdh_prev_free = 0xFFFFFFFF; dh->dqdh_entries = 0xFFFF; ocfs2_swap_quota_leaf_block_header(dh); ret = o2fswreck_write_blk(fs, qtype, buf + fs->fs_blocksize * tree_depth, g_actref); if (ret) FSWRK_COM_FATAL(progname, ret); fprintf(stdout, "DQBLK_INVALID: " "Corrupt global %s quota data block.\n", type2name(qtype)); break; case DUP_DQBLK_INVALID: qtype = GRPQUOTA; ret = o2fswreck_get_data_blk(fs, qtype, QT_TREEOFF, 0, buf); ddquot = (struct ocfs2_global_disk_dqblk *)(buf + fs->fs_blocksize * tree_depth + sizeof(struct qt_disk_dqdbheader)); ddquot[1].dqb_id = ddquot[0].dqb_id; ddquot[1].dqb_isoftlimit += 1; ddquot[1].dqb_ihardlimit += 2; ddquot[1].dqb_bsoftlimit += 3; ddquot[1].dqb_bhardlimit += 4; dh = (struct qt_disk_dqdbheader *)(buf + fs->fs_blocksize * tree_depth); ocfs2_swap_quota_leaf_block_header(dh); dh->dqdh_next_free = 0xFFFFFFFF; dh->dqdh_prev_free = 0xFFFFFFFF; dh->dqdh_entries = 0xFFFF; ocfs2_swap_quota_leaf_block_header(dh); ret = o2fswreck_write_blk(fs, qtype, buf + fs->fs_blocksize * tree_depth, g_actref); if (ret) FSWRK_COM_FATAL(progname, ret); fprintf(stdout, "DUP_DQBLK_INVALID: " "Duplicate %s quota data block with a invalid entry.\n", type2name(qtype)); break; case DUP_DQBLK_VALID: qtype = GRPQUOTA; ret = o2fswreck_get_data_blk(fs, qtype, QT_TREEOFF, 0, buf); ddquot = (struct ocfs2_global_disk_dqblk *)(buf + fs->fs_blocksize * tree_depth + sizeof(struct qt_disk_dqdbheader)); ddquot[1].dqb_id = ddquot[0].dqb_id; ddquot[1].dqb_isoftlimit = ddquot[0].dqb_isoftlimit; ddquot[1].dqb_ihardlimit = ddquot[0].dqb_isoftlimit; ddquot[1].dqb_bsoftlimit = ddquot[0].dqb_isoftlimit; ddquot[1].dqb_bhardlimit = ddquot[0].dqb_isoftlimit; ret = o2fswreck_write_blk(fs, qtype, buf + fs->fs_blocksize * tree_depth, g_actref); if (ret) FSWRK_COM_FATAL(progname, ret); fprintf(stdout, "DUP_DQBLK_VALID: " "Duplicate %s quota data block with a valid entry.\n", type2name(qtype)); break; default: FSWRK_FATAL("Invalid type[%d]\n", type); } if (buf) ocfs2_free(&buf); } ocfs2-tools-ocfs2-tools-1.8.6/fswreck/refcount.c000066400000000000000000000273151347147137200215260ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * refcount.c * * Copyright (C) 2009 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License version 2 as published by the Free Software Foundation. * * 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. */ #include #include "main.h" extern char *progname; /* * Create a refcount tree. * * If tree = 0, the root is just a refcount block with refcount recs. * Otherwise, we will create a refcount extent tree. * */ static void create_refcount_tree(ocfs2_filesys *fs, uint64_t blkno, uint64_t *rf_blkno, int tree_depth) { errcode_t ret; uint64_t file1, file2, file3, root_blkno, new_clusters, tmpblk; int i, recs_num = ocfs2_refcount_recs_per_rb(fs->fs_blocksize); int bpc = ocfs2_clusters_to_blocks(fs, 1), offset = 0; uint32_t n_clusters; uint64_t file_size; /* * Create 3 files. * file1 and file2 are used to sharing a refcount tree. * file2 is used to waste some clusters so that the refcount * tree won't be increased easily. */ create_file(fs, blkno, &file1); create_file(fs, blkno, &file2); create_file(fs, blkno, &file3); ret = ocfs2_new_refcount_block(fs, &root_blkno, 0, 0); if (ret) FSWRK_COM_FATAL(progname, ret); /* attach the create refcount tree in these 2 files. */ ret = ocfs2_attach_refcount_tree(fs, file1, root_blkno); if (ret) FSWRK_COM_FATAL(progname, ret); ret = ocfs2_attach_refcount_tree(fs, file2, root_blkno); if (ret) FSWRK_COM_FATAL(progname, ret); /* * Calculate how much clusters we need in order to create * the required extent tree. */ new_clusters = 1; while (tree_depth-- > 0) new_clusters *= recs_num; new_clusters += recs_num / 2; /* * We double the new_clusters so that half of them will be inserted * into tree, while another half is inserted into file3. */ new_clusters *= 2; while (new_clusters) { ret = ocfs2_new_clusters(fs, 1, new_clusters, &blkno, &n_clusters); if (ret) FSWRK_COM_FATAL(progname, ret); if (!n_clusters) FSWRK_FATAL("ENOSPC"); /* * In order to ensure the extent records are not coalesced, * we insert each cluster in reverse. */ for (i = n_clusters; i > 1; i -= 2, offset++) { tmpblk = blkno + ocfs2_clusters_to_blocks(fs, i - 2); ret = ocfs2_inode_insert_extent(fs, file1, offset, tmpblk, 1, OCFS2_EXT_REFCOUNTED); if (ret) FSWRK_COM_FATAL(progname, ret); ret = ocfs2_inode_insert_extent(fs, file2, offset, tmpblk, 1, OCFS2_EXT_REFCOUNTED); if (ret) FSWRK_COM_FATAL(progname, ret); ret = ocfs2_change_refcount(fs, root_blkno, ocfs2_blocks_to_clusters(fs, tmpblk), 1, 2); if (ret) FSWRK_COM_FATAL(progname, ret); ret = ocfs2_inode_insert_extent(fs, file3, offset, tmpblk + bpc, 1, 0); if (ret) FSWRK_COM_FATAL(progname, ret); } new_clusters -= n_clusters; } file_size = (offset + 1) * fs->fs_clustersize; ret = ocfs2_extend_file(fs, file1, file_size); if (ret) FSWRK_COM_FATAL(progname, ret); ret = ocfs2_extend_file(fs, file2, file_size); if (ret) FSWRK_COM_FATAL(progname, ret); ret = ocfs2_extend_file(fs, file3, file_size); if (ret) FSWRK_COM_FATAL(progname, ret); *rf_blkno = root_blkno; return; } static void damage_refcount_block(ocfs2_filesys *fs, enum fsck_type type, struct ocfs2_refcount_block *rb) { uint32_t oldno; uint64_t oldblkno; switch (type) { case RB_BLKNO: oldblkno = rb->rf_blkno; rb->rf_blkno += 1; fprintf(stdout, "RB_BLKNO: " "change refcount block's number from %"PRIu64" to " "%"PRIu64"\n", oldblkno, (uint64_t)rb->rf_blkno); break; case RB_GEN: case RB_GEN_FIX: case DUP_CLUSTERS_ADD_REFCOUNT: oldno = rb->rf_fs_generation; rb->rf_fs_generation = 0x1234; if (type == RB_GEN) fprintf(stdout, "RB_GEN: "); else if (type == RB_GEN_FIX) fprintf(stdout, "RB_GEN_FIX: "); fprintf(stdout, "change refcount block %"PRIu64 " generation number from 0x%x to 0x%x\n", (uint64_t)rb->rf_blkno, oldno, rb->rf_fs_generation); break; case RB_PARENT: oldblkno = rb->rf_parent; rb->rf_parent += 1; fprintf(stdout, "RB_PARENT: " "change refcount block's parent from %"PRIu64" to " "%"PRIu64"\n", oldblkno, (uint64_t)rb->rf_parent); break; case REFCOUNT_BLOCK_INVALID: case REFCOUNT_ROOT_BLOCK_INVALID: memset(rb->rf_signature, 'a', sizeof(rb->rf_signature)); fprintf(stdout, "Corrupt the signature of refcount block " "%"PRIu64"\n", (uint64_t)rb->rf_blkno); break; default: FSWRK_FATAL("Invalid type=%d", type); } } static void damage_refcount_list(ocfs2_filesys *fs, enum fsck_type type, struct ocfs2_refcount_block *rb) { uint32_t oldno; uint64_t oldblkno; switch (type) { case REFCOUNT_LIST_COUNT: oldno = rb->rf_records.rl_count; rb->rf_records.rl_count *= 2; fprintf(stdout, "REFCOUNT_LIST_COUNT: Corrupt refcount block #" "%"PRIu64", change rl_count from %u to %u\n", (uint64_t)rb->rf_blkno, oldno, rb->rf_records.rl_count); break; case REFCOUNT_LIST_USED: oldno = rb->rf_records.rl_used; rb->rf_records.rl_used = 2 * rb->rf_records.rl_count; fprintf(stdout, "REFCOUNT_LIST_USED: Corrupt refcount block #" "%"PRIu64", change rl_used from %u to %u\n", (uint64_t)rb->rf_blkno, oldno, rb->rf_records.rl_used); break; case REFCOUNT_CLUSTER_RANGE: oldblkno = rb->rf_records.rl_recs[0].r_cpos; rb->rf_records.rl_recs[0].r_cpos = fs->fs_clusters + 1; fprintf(stdout, "REFCOUNT_CLUSTER_RANGE, Corrupt refcount " "block #%"PRIu64", change recs[0] from %"PRIu64 " to %"PRIu64"\n", (uint64_t)rb->rf_blkno, oldblkno, (uint64_t)rb->rf_records.rl_recs[0].r_cpos); break; case REFCOUNT_CLUSTER_COLLISION: oldblkno = rb->rf_records.rl_recs[0].r_cpos; rb->rf_records.rl_recs[0].r_cpos = fs->fs_clusters - 1; fprintf(stdout, "REFCOUNT_CLUSTER_COLLISION, Corrupt refcount " "block #%"PRIu64", change recs[0] from %"PRIu64 " to %"PRIu64"\n", (uint64_t)rb->rf_blkno, oldblkno, (uint64_t)rb->rf_records.rl_recs[0].r_cpos); break; case REFCOUNT_LIST_EMPTY: oldno = rb->rf_records.rl_used; rb->rf_records.rl_used = 0; fprintf(stdout, "REFCOUNT_LIST_EMPTY: Corrupt refcount block #" "%"PRIu64", change rl_used from %u to 0\n", (uint64_t)rb->rf_blkno, oldno); break; default: FSWRK_FATAL("Invalid type=%d", type); } } static void damage_refcount_record(ocfs2_filesys *fs, enum fsck_type type, struct ocfs2_refcount_block *rb) { uint32_t oldno; uint64_t oldblkno; switch (type) { case REFCOUNT_REC_REDUNDANT: oldblkno = rb->rf_records.rl_recs[0].r_cpos; rb->rf_records.rl_recs[0].r_cpos = 1; rb->rf_records.rl_recs[1].r_clusters += 1; rb->rf_records.rl_recs[3].r_cpos -= 1; rb->rf_records.rl_recs[3].r_clusters += 10; fprintf(stdout, "REFCOUNT_REC_REDUNDANT: Corrupt refcount " "record in block %"PRIu64", change recs[0].r_cpos " "from %"PRIu64" to 1, add recs[1].r_clusters by 1," "decrease recs[3].r_cpos by 1 and " "increase r_clusters by 100\n", (uint64_t)rb->rf_blkno, oldblkno); break; case REFCOUNT_COUNT_INVALID: oldno = rb->rf_records.rl_recs[0].r_refcount; rb->rf_records.rl_recs[0].r_refcount = 100; fprintf(stdout, "REFCOUNT_COUNT_INVALID: Corrupt refcount " "record in block %"PRIu64", change recs[0].r_count " "from %u to 100\n", (uint64_t)rb->rf_blkno, oldno); break; default: FSWRK_FATAL("Invalid type=%d", type); } } void mess_up_refcount_tree_block(ocfs2_filesys *fs, enum fsck_type type, uint64_t blkno) { errcode_t ret; char *buf1 = NULL, *buf2 = NULL, *buf2_leaf = NULL; uint64_t rf_blkno1, rf_blkno2, rf_leaf_blkno; struct ocfs2_refcount_block *rb1, *rb2, *rb2_leaf; if (!ocfs2_refcount_tree(OCFS2_RAW_SB(fs->fs_super))) FSWRK_FATAL("Should specify a refcount supported " "volume to do this corruption\n"); ret = ocfs2_malloc_block(fs->fs_io, &buf1); if (ret) FSWRK_COM_FATAL(progname, ret); ret = ocfs2_malloc_block(fs->fs_io, &buf2); if (ret) FSWRK_COM_FATAL(progname, ret); ret = ocfs2_malloc_block(fs->fs_io, &buf2_leaf); if (ret) FSWRK_COM_FATAL(progname, ret); /* * We create 2 refcount trees. One only has a root refcount block, * and the other has a tree with depth = 1. So we can corrupt both * of them and verify whether fsck works for different block types. */ create_refcount_tree(fs, blkno, &rf_blkno1, 0); create_refcount_tree(fs, blkno, &rf_blkno2, 1); ret = ocfs2_read_refcount_block(fs, rf_blkno1, buf1); if (ret) FSWRK_COM_FATAL(progname, ret); rb1 = (struct ocfs2_refcount_block *)buf1; /* tree 2 is an extent tree, so find the 1st leaf refcount block. */ ret = ocfs2_read_refcount_block(fs, rf_blkno2, buf2); if (ret) FSWRK_COM_FATAL(progname, ret); rb2 = (struct ocfs2_refcount_block *)buf2; assert(rb2->rf_flags & OCFS2_REFCOUNT_TREE_FL); rf_leaf_blkno = rb2->rf_list.l_recs[0].e_blkno; ret = ocfs2_read_refcount_block(fs, rf_leaf_blkno, buf2_leaf); if (ret) FSWRK_COM_FATAL(progname, ret); rb2_leaf = (struct ocfs2_refcount_block *)buf2_leaf; switch (type) { case RB_BLKNO: case RB_GEN: case RB_GEN_FIX: case DUP_CLUSTERS_ADD_REFCOUNT: damage_refcount_block(fs, type, rb1); damage_refcount_block(fs, type, rb2_leaf); break; case RB_PARENT: damage_refcount_block(fs, type, rb2_leaf); break; case REFCOUNT_BLOCK_INVALID: damage_refcount_block(fs, type, rb2_leaf); break; case REFCOUNT_ROOT_BLOCK_INVALID: damage_refcount_block(fs, type, rb1); damage_refcount_block(fs, type, rb2); break; case REFCOUNT_LIST_COUNT: case REFCOUNT_LIST_USED: case REFCOUNT_CLUSTER_RANGE: case REFCOUNT_CLUSTER_COLLISION: case REFCOUNT_LIST_EMPTY: damage_refcount_list(fs, type, rb1); damage_refcount_list(fs, type, rb2_leaf); break; case REFCOUNT_REC_REDUNDANT: case REFCOUNT_COUNT_INVALID: damage_refcount_record(fs, type, rb1); damage_refcount_record(fs, type, rb2_leaf); break; default: FSWRK_FATAL("Invalid type[%d]\n", type); } ret = ocfs2_write_refcount_block(fs, rf_blkno1, buf1); if (ret) FSWRK_COM_FATAL(progname, ret); ret = ocfs2_write_refcount_block(fs, rf_blkno2, buf2); if (ret) FSWRK_COM_FATAL(progname, ret); ret = ocfs2_write_refcount_block(fs, rf_leaf_blkno, buf2_leaf); if (ret) FSWRK_COM_FATAL(progname, ret); ocfs2_free(&buf1); ocfs2_free(&buf2); ocfs2_free(&buf2_leaf); return; } void mess_up_refcount_tree(ocfs2_filesys *fs, enum fsck_type type, uint64_t blkno) { errcode_t ret; char *buf = NULL; uint64_t rf_blkno; uint32_t oldno; struct ocfs2_refcount_block *rb; if (!ocfs2_refcount_tree(OCFS2_RAW_SB(fs->fs_super))) FSWRK_FATAL("Should specify a refcount supported " "volume to do this corruption\n"); ret = ocfs2_malloc_block(fs->fs_io, &buf); if (ret) FSWRK_COM_FATAL(progname, ret); create_refcount_tree(fs, blkno, &rf_blkno, 2); ret = ocfs2_read_refcount_block(fs, rf_blkno, buf); if (ret) FSWRK_COM_FATAL(progname, ret); rb = (struct ocfs2_refcount_block *)buf; switch (type) { case REFCOUNT_CLUSTERS: oldno = rb->rf_clusters; rb->rf_clusters = 1; fprintf(stdout, "REFCOUNT_CLUSTERS: Corrupt refcount block #" "%"PRIu64", change rf_clusters from %u to %u\n", (uint64_t)rb->rf_blkno, oldno, rb->rf_clusters); break; case REFCOUNT_COUNT: oldno = rb->rf_count; rb->rf_count = 0; fprintf(stdout, "REFCOUNT_COUNT: Corrupt refcount block #" "%"PRIu64", change rf_count from %u to %u\n", (uint64_t)rb->rf_blkno, oldno, rb->rf_count); break; default: FSWRK_FATAL("Invalid type[%d]\n", type); } ret = ocfs2_write_refcount_block(fs, rf_blkno, buf); if (ret) FSWRK_COM_FATAL(progname, ret); ocfs2_free(&buf); return; } ocfs2-tools-ocfs2-tools-1.8.6/fswreck/special.c000066400000000000000000000034151347147137200213140ustar00rootroot00000000000000/* * special.c * * root, lost+found corruptions * * Copyright (C) 2006 Oracle. All rights reserved. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * */ #include "main.h" extern char *progname; /* This file will corrupt inode root. * And as a sequence, lost+found will also disappear. * * Special files error: ROOT_NOTDIR, ROOT_DIR_MISSING, LOSTFOUND_MISSING * */ void mess_up_root(ocfs2_filesys *fs, enum fsck_type type, uint64_t blkno) { errcode_t ret; char *inobuf = NULL; struct ocfs2_dinode *di; struct ocfs2_super_block *sb = OCFS2_RAW_SB(fs->fs_super); blkno = sb->s_root_blkno; ret = ocfs2_malloc_block(fs->fs_io, &inobuf); if (ret) FSWRK_COM_FATAL(progname, ret); ret = ocfs2_read_inode(fs, blkno, inobuf); if (ret) FSWRK_COM_FATAL(progname, ret); di = (struct ocfs2_dinode *)inobuf; if (!(di->i_flags & OCFS2_VALID_FL)) FSWRK_FATAL("not a file"); di->i_mode = 0; ret = ocfs2_write_inode(fs, blkno, inobuf); if (ret) FSWRK_COM_FATAL(progname, ret); if (inobuf) ocfs2_free(&inobuf); fprintf(stdout, "ROOT_NOTDIR: " "Corrupt root inode#%"PRIu64"\n", blkno); return; } ocfs2-tools-ocfs2-tools-1.8.6/fswreck/symlink.c000066400000000000000000000134551347147137200213670ustar00rootroot00000000000000/* * symlink.c * * symlink file corruptions * * Copyright (C) 2006 Oracle. All rights reserved. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * */ /* This file will create the errors for the link file. * * Link file error: LINK_FAST_DATA, LINK_NULLTERM, LINK_SIZE, LINK_BLOCKS * */ #include "main.h" static char *dummy = "/dummy00/dummy00"; extern char *progname; /*this function fill up the block with dummy texts. */ static int fillup_block(ocfs2_filesys *fs, uint64_t blkno, uint64_t bcount, uint16_t ext_flags, void *priv_data) { errcode_t ret = 0; int i; char *buf = NULL, *out = NULL; int len = strlen(dummy); ret = ocfs2_malloc_block(fs->fs_io, &buf); if (ret) FSWRK_COM_FATAL(progname, ret); out = buf; for (i = 0; i < fs->fs_blocksize/len; i++, out+=len) memcpy(out, dummy, len); if(fs->fs_blocksize%len) memcpy(out, dummy, fs->fs_blocksize%len); ret = io_write_block(fs->fs_io, blkno, 1, buf); if (ret) FSWRK_COM_FATAL(progname, ret); fprintf(stdout, "Fill block#%"PRIu64" with dummy texts.\n", blkno); if (buf) ocfs2_free(&buf); return 0; } /* Add dummy texts as symname to the inodes. * Attention: we will not use fast symlink, and the cluster * has already been allocated to the file. * Here just get it, copy the symname and write back * to the disk. */ static void add_symlink(ocfs2_filesys *fs, uint64_t blkno) { errcode_t ret = 0; ocfs2_cached_inode *cinode = NULL; uint64_t new_blk; uint64_t contig; char *buf = NULL; ret = ocfs2_read_cached_inode(fs, blkno, &cinode); if (ret) FSWRK_COM_FATAL(progname, ret); /* get first block of the file */ ret = ocfs2_extent_map_get_blocks(cinode, 0, 1, &new_blk, &contig, NULL); if (ret) FSWRK_COM_FATAL(progname, ret); ret = ocfs2_malloc_block(fs->fs_io, &buf); if (ret) FSWRK_COM_FATAL(progname, ret); strcpy(buf, dummy); ret = io_write_block(fs->fs_io, new_blk, 1, buf); if (ret) FSWRK_COM_FATAL(progname, ret); cinode->ci_inode->i_size = strlen(dummy); ret = ocfs2_write_cached_inode(fs, cinode); if (ret) FSWRK_COM_FATAL(progname, ret); if (buf) ocfs2_free(&buf); if (cinode) ocfs2_free_cached_inode(fs, cinode); return; } static void create_symlink(ocfs2_filesys *fs, uint64_t blkno, uint64_t *retblkno) { errcode_t ret; uint64_t tmp_blkno; uint32_t clusters = 1; char random_name[OCFS2_MAX_FILENAME_LEN]; memset(random_name, 0, sizeof(random_name)); sprintf(random_name, "testXXXXXX"); /* Don't use mkstemp since it will create a file * in the working directory which is no use. * Use mktemp instead Although there is a compiling warning. * mktemp fails to work in some implementations follow BSD 4.3, * but currently ocfs2 will only support linux, * so it will not affect us. */ if (!mktemp(random_name)) FSWRK_COM_FATAL(progname, errno); ret = ocfs2_check_directory(fs, blkno); if (ret) FSWRK_COM_FATAL(progname, ret); ret = ocfs2_new_inode(fs, &tmp_blkno, S_IFLNK | 0755); if (ret) FSWRK_COM_FATAL(progname, ret); ret = ocfs2_link(fs, blkno, random_name, tmp_blkno, OCFS2_FT_SYMLINK); if (ret) FSWRK_COM_FATAL(progname, ret); ret = ocfs2_extend_allocation(fs, tmp_blkno, clusters); if (ret) FSWRK_COM_FATAL(progname, ret); add_symlink(fs, tmp_blkno); *retblkno = tmp_blkno; return; } static void corrupt_symlink_file(ocfs2_filesys *fs, uint64_t blkno, enum fsck_type type) { errcode_t ret; char *buf = NULL; struct ocfs2_dinode *di; struct ocfs2_extent_list *el; struct ocfs2_extent_rec *er; ret = ocfs2_malloc_block(fs->fs_io, &buf); if (ret) FSWRK_COM_FATAL(progname, ret); ret = ocfs2_read_inode(fs, blkno, buf); if (ret) FSWRK_COM_FATAL(progname, ret); di = (struct ocfs2_dinode *)buf; if (!(di->i_flags & OCFS2_VALID_FL)) FSWRK_FATAL("not a file"); if (!S_ISLNK(di->i_mode)) FSWRK_FATAL("not a link file"); el = &(di->id2.i_list); switch(type) { case LINK_FAST_DATA: di->i_clusters = 0; fprintf(stdout, "LINK_FAST_DATA: " "Corrupt inode#%"PRIu64"," "change clusters from %u to 0", blkno, di->i_clusters); break; case LINK_NULLTERM: ocfs2_block_iterate_inode(fs, di, OCFS2_BLOCK_FLAG_APPEND, fillup_block, NULL); fprintf(stdout, "LINK_NULLTERM: " "Corrupt inode#%"PRIu64"," "fill all blocks with dummy texts\n", blkno); di->i_clusters = 1; di->i_size = di->i_clusters * fs->fs_clustersize; break; case LINK_SIZE: fprintf(stdout, "LINK_SIZE: " "Corrupt inode#%"PRIu64"," "change size from %"PRIu64" to %"PRIu64"\n", blkno, (uint64_t)di->i_size, ((uint64_t)di->i_size + 10)); di->i_size += 10; break; case LINK_BLOCKS: er = el->l_recs; fprintf(stdout, "LINK_BLOCKS: " "Corrupt inode#%"PRIu64"," "change e_leaf_clusters from %u to %u\n", blkno, er->e_leaf_clusters, (er->e_leaf_clusters + 1)); er->e_leaf_clusters += 1; break; default: FSWRK_FATAL("Invalid type[%d]\n", type); } ret = ocfs2_write_inode(fs, blkno, buf); if (ret) FSWRK_COM_FATAL(progname, ret); if (buf) ocfs2_free(&buf); return; } void mess_up_symlink(ocfs2_filesys *fs, enum fsck_type type, uint64_t blkno) { uint64_t tmp_blkno; create_symlink(fs, blkno, &tmp_blkno); corrupt_symlink_file(fs, tmp_blkno, type); return ; } ocfs2-tools-ocfs2-tools-1.8.6/fswreck/truncate_log.c000066400000000000000000000150051347147137200223600ustar00rootroot00000000000000/* * truncate_log.c * * truncate log corruptions * * Copyright (C) 2006 Oracle. All rights reserved. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * */ /* This file will create the errors for the inode. * * Truncate log list error: DEALLOC_COUNT, DEALLOC_USED * * Truncate log rec error: TRUNCATE_REC_START_RANGE, TRUNCATE_REC_WRAP, * TRUNCATE_REC_RANGE * */ #include "main.h" extern char *progname; static void create_truncate_log(ocfs2_filesys *fs, uint64_t blkno, uint16_t used, uint32_t clusters) { errcode_t ret; char *buf = NULL; struct ocfs2_dinode *di; struct ocfs2_truncate_log *tl; uint16_t i, max; uint32_t found; uint64_t begin; max = ocfs2_truncate_recs_per_inode(fs->fs_blocksize); if (used > max) FSWRK_FATAL("recnum exceeds the limit of truncate log"); ret = ocfs2_malloc_block(fs->fs_io, &buf); if (ret) FSWRK_COM_FATAL(progname, ret); ret = ocfs2_read_inode(fs, blkno, buf); if (ret) FSWRK_COM_FATAL(progname, ret); di = (struct ocfs2_dinode *)buf; if (!(di->i_flags & OCFS2_VALID_FL)) FSWRK_FATAL("not a valid file"); if (!(di->i_flags & OCFS2_DEALLOC_FL)) FSWRK_FATAL("not a valid truncate log"); tl = &di->id2.i_dealloc; if (tl->tl_used > 0) { FSWRK_WARN("truncate log#%"PRIu64" file not empty." "Can't create a new one.\n", blkno); goto bail; } used = min(used, tl->tl_count); tl->tl_used = used; for (i = 0; i < tl->tl_used; i++) { ret = ocfs2_new_clusters(fs, 1, clusters, &begin, &found); if (ret) FSWRK_COM_FATAL(progname, ret); tl->tl_recs[i].t_start = cpu_to_le32(ocfs2_blocks_to_clusters(fs, begin)); tl->tl_recs[i].t_clusters = cpu_to_le32(found); } ret = ocfs2_write_inode(fs, blkno, buf); if (ret) FSWRK_COM_FATAL(progname, ret); bail: if(buf) ocfs2_free(&buf); return; } static void damage_truncate_log(ocfs2_filesys *fs, uint64_t blkno, enum fsck_type type, int recnum) { errcode_t ret; char *buf = NULL; struct ocfs2_dinode *di; struct ocfs2_truncate_log *tl; struct ocfs2_truncate_rec *tr; ret = ocfs2_malloc_block(fs->fs_io, &buf); if (ret) FSWRK_COM_FATAL(progname, ret); ret = ocfs2_read_inode(fs, blkno, buf); if (ret) FSWRK_COM_FATAL(progname, ret); di = (struct ocfs2_dinode *)buf; if (!(di->i_flags & OCFS2_VALID_FL)) FSWRK_FATAL("not a valid file"); if (!(di->i_flags & OCFS2_DEALLOC_FL)) FSWRK_FATAL("not a valid truncate log"); tl = &di->id2.i_dealloc; /* For TRUNCATE_REC_START_RANGE, TRUNCATE_REC_WRAP, TRUNCATE_REC_RANGE, * tl_used must be greater than 0 and recnum must be less than tl_used. * So check it first. */ if (type == TRUNCATE_REC_START_RANGE || type == TRUNCATE_REC_WRAP || type == TRUNCATE_REC_RANGE) { if (tl->tl_used == 0) { FSWRK_WARN("truncate log#%"PRIu64" is empty, so can't" "corrupt it for type[%d]\n", blkno, type); goto bail; } if(tl->tl_used <= recnum) { FSWRK_WARN("truncate log#%"PRIu64" can't corrupt " "item[%d] corrupt it for type[%d]\n", blkno, recnum, type); goto bail; } } switch (type) { case DEALLOC_COUNT: fprintf(stdout, "DEALLOC_COUNT: " "Corrupt truncate log inode#%"PRIu64", change tl_count" " from %u to %u\n", blkno, tl->tl_count, (tl->tl_count + 10)); tl->tl_count += 10; break; case DEALLOC_USED: fprintf(stdout, "DEALLOC_USED: " "Corrupt truncate log inode#%"PRIu64", change tl_used" " from %u to %u\n", blkno, tl->tl_used, (tl->tl_count + 10)); tl->tl_used = tl->tl_count + 10; break; case TRUNCATE_REC_START_RANGE: tr = &tl->tl_recs[recnum]; fprintf(stdout, "TRUNCATE_REC_START_RANGE: " "Corrupt truncate log inode#%"PRIu64",rec#%d " "change t_start from %u to %u\n", blkno, recnum, tr->t_start, (fs->fs_clusters + 10)); tr->t_start = fs->fs_clusters + 10; break; case TRUNCATE_REC_WRAP: tr = &tl->tl_recs[recnum]; fprintf(stdout, "TRUNCATE_REC_WRAP: " "Corrupt truncate log inode#%"PRIu64",rec#%d " "change t_start from %u to 10000\n," "change t_clusters from %u to %u\n", blkno, recnum, tr->t_start,tr->t_clusters, (UINT32_MAX - 10)); tr->t_start = 10000; tr->t_clusters = UINT32_MAX - 10; break; case TRUNCATE_REC_RANGE: tr = &tl->tl_recs[recnum]; fprintf(stdout, "TRUNCATE_REC_RANGE: " "Corrupt truncate log inode#%"PRIu64",rec#%d " "change t_clusters from %u to %u\n", blkno, recnum, tr->t_clusters, (fs->fs_clusters + 10)); tr->t_clusters = fs->fs_clusters + 10; break; default: FSWRK_FATAL("Unknown type = %d", type); } ret = ocfs2_write_inode(fs, blkno, buf); if (ret) FSWRK_COM_FATAL(progname, ret); bail: if (buf) ocfs2_free(&buf); return; } static void get_truncate_log(ocfs2_filesys *fs, uint16_t slotnum, uint64_t *blkno) { errcode_t ret; char truncate_log[OCFS2_MAX_FILENAME_LEN]; struct ocfs2_super_block *sb = OCFS2_RAW_SB(fs->fs_super); if (slotnum == UINT16_MAX) slotnum = 0; snprintf(truncate_log, sizeof(truncate_log), ocfs2_system_inodes[TRUNCATE_LOG_SYSTEM_INODE].si_name, slotnum); ret = ocfs2_lookup(fs, sb->s_system_dir_blkno, truncate_log, strlen(truncate_log), NULL, blkno); if (ret) FSWRK_COM_FATAL(progname, ret); return; } void mess_up_truncate_log_list(ocfs2_filesys *fs, enum fsck_type type, uint16_t slotnum) { uint64_t blkno; int i = 0; get_truncate_log(fs, slotnum, &blkno); switch (type) { case DEALLOC_COUNT: i = 0; break; case DEALLOC_USED: i = 1; break; default: break; } damage_truncate_log(fs, blkno, type, i); return; } void mess_up_truncate_log_rec(ocfs2_filesys *fs, enum fsck_type type, uint16_t slotnum) { uint64_t blkno; int i = 0; switch (type) { case TRUNCATE_REC_START_RANGE: i = 0; break; case TRUNCATE_REC_WRAP: i = 1; break; case TRUNCATE_REC_RANGE: i = 2; break; default: break; } get_truncate_log(fs, slotnum, &blkno); create_truncate_log(fs, blkno, 10, 10); damage_truncate_log(fs, blkno, type, i); return; } ocfs2-tools-ocfs2-tools-1.8.6/glib-2.0.m4000066400000000000000000000201411347147137200175330ustar00rootroot00000000000000# Configure paths for GLIB # Owen Taylor 1997-2001 dnl AM_PATH_GLIB_2_0([MINIMUM-VERSION, [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND [, MODULES]]]]) dnl Test for GLIB, and define GLIB_CFLAGS and GLIB_LIBS, if gmodule, gobject or dnl gthread is specified in MODULES, pass to pkg-config dnl AC_DEFUN([AM_PATH_GLIB_2_0], [dnl dnl Get the cflags and libraries from pkg-config dnl AC_ARG_ENABLE(glibtest, [ --disable-glibtest do not try to compile and run a test GLIB program], , enable_glibtest=yes) pkg_config_args=glib-2.0 for module in . $4 do case "$module" in gmodule) pkg_config_args="$pkg_config_args gmodule-2.0" ;; gmodule-no-export) pkg_config_args="$pkg_config_args gmodule-no-export-2.0" ;; gobject) pkg_config_args="$pkg_config_args gobject-2.0" ;; gthread) pkg_config_args="$pkg_config_args gthread-2.0" ;; esac done AC_PATH_PROG(PKG_CONFIG, pkg-config, no) no_glib="" if test x$PKG_CONFIG != xno ; then if $PKG_CONFIG --atleast-pkgconfig-version 0.7 ; then : else echo *** pkg-config too old; version 0.7 or better required. no_glib=yes PKG_CONFIG=no fi else no_glib=yes fi min_glib_version=ifelse([$1], ,2.0.0,$1) AC_MSG_CHECKING(for GLIB - version >= $min_glib_version) if test x$PKG_CONFIG != xno ; then ## don't try to run the test against uninstalled libtool libs if $PKG_CONFIG --uninstalled $pkg_config_args; then echo "Will use uninstalled version of GLib found in PKG_CONFIG_PATH" enable_glibtest=no fi if $PKG_CONFIG --atleast-version $min_glib_version $pkg_config_args; then : else no_glib=yes fi fi if test x"$no_glib" = x ; then GLIB_GENMARSHAL=`$PKG_CONFIG --variable=glib_genmarshal glib-2.0` GOBJECT_QUERY=`$PKG_CONFIG --variable=gobject_query glib-2.0` GLIB_MKENUMS=`$PKG_CONFIG --variable=glib_mkenums glib-2.0` GLIB_CFLAGS=`$PKG_CONFIG --cflags $pkg_config_args` GLIB_LIBS=`$PKG_CONFIG --libs $pkg_config_args` glib_config_major_version=`$PKG_CONFIG --modversion glib-2.0 | \ sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\1/'` glib_config_minor_version=`$PKG_CONFIG --modversion glib-2.0 | \ sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\2/'` glib_config_micro_version=`$PKG_CONFIG --modversion glib-2.0 | \ sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\3/'` if test "x$enable_glibtest" = "xyes" ; then ac_save_CFLAGS="$CFLAGS" ac_save_LIBS="$LIBS" CFLAGS="$CFLAGS $GLIB_CFLAGS" LIBS="$GLIB_LIBS $LIBS" dnl dnl Now check if the installed GLIB is sufficiently new. (Also sanity dnl checks the results of pkg-config to some extent) dnl rm -f conf.glibtest AC_TRY_RUN([ #include #include #include int main () { int major, minor, micro; char *tmp_version; system ("touch conf.glibtest"); /* HP/UX 9 (%@#!) writes to sscanf strings */ tmp_version = g_strdup("$min_glib_version"); if (sscanf(tmp_version, "%d.%d.%d", &major, &minor, µ) != 3) { printf("%s, bad version string\n", "$min_glib_version"); exit(1); } if ((glib_major_version != $glib_config_major_version) || (glib_minor_version != $glib_config_minor_version) || (glib_micro_version != $glib_config_micro_version)) { printf("\n*** 'pkg-config --modversion glib-2.0' returned %d.%d.%d, but GLIB (%d.%d.%d)\n", $glib_config_major_version, $glib_config_minor_version, $glib_config_micro_version, glib_major_version, glib_minor_version, glib_micro_version); printf ("*** was found! If pkg-config was correct, then it is best\n"); printf ("*** to remove the old version of GLib. You may also be able to fix the error\n"); printf("*** by modifying your LD_LIBRARY_PATH enviroment variable, or by editing\n"); printf("*** /etc/ld.so.conf. Make sure you have run ldconfig if that is\n"); printf("*** required on your system.\n"); printf("*** If pkg-config was wrong, set the environment variable PKG_CONFIG_PATH\n"); printf("*** to point to the correct configuration files\n"); } else if ((glib_major_version != GLIB_MAJOR_VERSION) || (glib_minor_version != GLIB_MINOR_VERSION) || (glib_micro_version != GLIB_MICRO_VERSION)) { printf("*** GLIB header files (version %d.%d.%d) do not match\n", GLIB_MAJOR_VERSION, GLIB_MINOR_VERSION, GLIB_MICRO_VERSION); printf("*** library (version %d.%d.%d)\n", glib_major_version, glib_minor_version, glib_micro_version); } else { if ((glib_major_version > major) || ((glib_major_version == major) && (glib_minor_version > minor)) || ((glib_major_version == major) && (glib_minor_version == minor) && (glib_micro_version >= micro))) { return 0; } else { printf("\n*** An old version of GLIB (%d.%d.%d) was found.\n", glib_major_version, glib_minor_version, glib_micro_version); printf("*** You need a version of GLIB newer than %d.%d.%d. The latest version of\n", major, minor, micro); printf("*** GLIB is always available from ftp://ftp.gtk.org.\n"); printf("***\n"); printf("*** If you have already installed a sufficiently new version, this error\n"); printf("*** probably means that the wrong copy of the pkg-config shell script is\n"); printf("*** being found. The easiest way to fix this is to remove the old version\n"); printf("*** of GLIB, but you can also set the PKG_CONFIG environment to point to the\n"); printf("*** correct copy of pkg-config. (In this case, you will have to\n"); printf("*** modify your LD_LIBRARY_PATH enviroment variable, or edit /etc/ld.so.conf\n"); printf("*** so that the correct libraries are found at run-time))\n"); } } return 1; } ],, no_glib=yes,[echo $ac_n "cross compiling; assumed OK... $ac_c"]) CFLAGS="$ac_save_CFLAGS" LIBS="$ac_save_LIBS" fi fi if test "x$no_glib" = x ; then AC_MSG_RESULT(yes (version $glib_config_major_version.$glib_config_minor_version.$glib_config_micro_version)) ifelse([$2], , :, [$2]) else AC_MSG_RESULT(no) if test "$PKG_CONFIG" = "no" ; then echo "*** A new enough version of pkg-config was not found." echo "*** See http://www.freedesktop.org/software/pkgconfig/" else if test -f conf.glibtest ; then : else echo "*** Could not run GLIB test program, checking why..." ac_save_CFLAGS="$CFLAGS" ac_save_LIBS="$LIBS" CFLAGS="$CFLAGS $GLIB_CFLAGS" LIBS="$LIBS $GLIB_LIBS" AC_TRY_LINK([ #include #include ], [ return ((glib_major_version) || (glib_minor_version) || (glib_micro_version)); ], [ echo "*** The test program compiled, but did not run. This usually means" echo "*** that the run-time linker is not finding GLIB or finding the wrong" echo "*** version of GLIB. If it is not finding GLIB, you'll need to set your" echo "*** LD_LIBRARY_PATH environment variable, or edit /etc/ld.so.conf to point" echo "*** to the installed location Also, make sure you have run ldconfig if that" echo "*** is required on your system" echo "***" echo "*** If you have an old version installed, it is best to remove it, although" echo "*** you may also be able to get things to work by modifying LD_LIBRARY_PATH" ], [ echo "*** The test program failed to compile or link. See the file config.log for the" echo "*** exact error that occured. This usually means GLIB is incorrectly installed."]) CFLAGS="$ac_save_CFLAGS" LIBS="$ac_save_LIBS" fi fi GLIB_CFLAGS="" GLIB_LIBS="" GLIB_GENMARSHAL="" GOBJECT_QUERY="" GLIB_MKENUMS="" ifelse([$3], , :, [$3]) fi AC_SUBST(GLIB_CFLAGS) AC_SUBST(GLIB_LIBS) AC_SUBST(GLIB_GENMARSHAL) AC_SUBST(GOBJECT_QUERY) AC_SUBST(GLIB_MKENUMS) rm -f conf.glibtest ]) ocfs2-tools-ocfs2-tools-1.8.6/include/000077500000000000000000000000001347147137200175045ustar00rootroot00000000000000ocfs2-tools-ocfs2-tools-1.8.6/include/Makefile000066400000000000000000000002071347147137200211430ustar00rootroot00000000000000TOPDIR = .. include $(TOPDIR)/Preamble.make SUBDIRS = tools-internal ocfs2-kernel o2dlm o2cb ocfs2 include $(TOPDIR)/Postamble.make ocfs2-tools-ocfs2-tools-1.8.6/include/o2cb/000077500000000000000000000000001347147137200203315ustar00rootroot00000000000000ocfs2-tools-ocfs2-tools-1.8.6/include/o2cb/.gitignore000066400000000000000000000000131347147137200223130ustar00rootroot00000000000000o2cb_err.h ocfs2-tools-ocfs2-tools-1.8.6/include/o2cb/Makefile000066400000000000000000000007131347147137200217720ustar00rootroot00000000000000TOPDIR = ../.. include $(TOPDIR)/Preamble.make HFILES_GEN = o2cb_err.h all: $(HFILES_GEN) HFILES = o2cb.h ocfs2_nodemanager.h ocfs2_heartbeat.h o2cb_client_proto.h HEADERS_SUBDIR = o2cb HEADERS = $(HFILES) $(HFILES_GEN) o2cb_err.h: $(TOPDIR)/libo2cb/o2cb_err.h cp $< $@ $(TOPDIR)/libo2cb/o2cb_err.h: make -C $(TOPDIR)/libo2cb o2cb_err.h DIST_FILES = $(HFILES) CLEAN_RULES = clean-err clean-err: rm -f o2cb_err.h include $(TOPDIR)/Postamble.make ocfs2-tools-ocfs2-tools-1.8.6/include/o2cb/o2cb.h000066400000000000000000000140261347147137200213320ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * o2cb.h * * Routines for accessing the o2cb configuration. * * Copyright (C) 2004 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License, version 2, as published by the Free Software Foundation. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. */ #ifndef _O2CB_H #define _O2CB_H #ifndef _XOPEN_SOURCE # define _XOPEN_SOURCE 600 #endif #ifndef _LARGEFILE64_SOURCE # define _LARGEFILE64_SOURCE #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #define OCFS2_FS_NAME "ocfs2" /* Classic (historically speaking) cluster stack */ #define OCFS2_CLASSIC_CLUSTER_STACK "o2cb" #define OCFS2_PCMK_CLUSTER_STACK "pcmk" #define OCFS2_CMAN_CLUSTER_STACK "cman" static inline int o2cb_valid_stack_name(char *name) { return !strcmp(name, OCFS2_CLASSIC_CLUSTER_STACK) || !strcmp(name, OCFS2_PCMK_CLUSTER_STACK) || !strcmp(name, OCFS2_CMAN_CLUSTER_STACK); } static inline int o2cb_valid_cluster_name(char *name) { unsigned len; if (!name) return 0; len = strlen(name); if (len == 0 || len > OCFS2_CLUSTER_NAME_LEN) return 0; return 1; } static inline int o2cb_valid_o2cb_cluster_name(char *name) { int len; if (!name) return 0; len = strlen(name); if (!len) return 0; while(isalnum(*name++) && len--); if (len) return 0; return 1; } #define O2CB_GLOBAL_HEARTBEAT_TAG "global" #define O2CB_LOCAL_HEARTBEAT_TAG "local" static inline int o2cb_valid_heartbeat_mode(char *mode) { return !strcmp(mode, O2CB_GLOBAL_HEARTBEAT_TAG) || !strcmp(mode, O2CB_LOCAL_HEARTBEAT_TAG); } errcode_t o2cb_init(void); errcode_t o2cb_get_stack_name(const char **name); errcode_t o2cb_create_cluster(const char *cluster_name); errcode_t o2cb_remove_cluster(const char *cluster_name); errcode_t o2cb_add_node(const char *cluster_name, const char *node_name, const char *node_num, const char *ip_address, const char *ip_port, const char *local); errcode_t o2cb_del_node(const char *cluster_name, const char *node_name); errcode_t o2cb_list_clusters(char ***clusters); void o2cb_free_cluster_list(char **clusters); errcode_t o2cb_list_nodes(char *cluster_name, char ***nodes); void o2cb_free_nodes_list(char **nodes); errcode_t o2cb_list_hb_regions(char *cluster_name, char ***regions); void o2cb_free_hb_regions_list(char **regions); errcode_t o2cb_global_heartbeat_mode(char *cluster_name, int *global); errcode_t o2cb_set_heartbeat_mode(char *cluster_name, char *mode); errcode_t o2cb_control_daemon_debug(char **debug); struct o2cb_cluster_desc { char *c_stack; /* The cluster stack, NULL for classic */ char *c_cluster; /* The name of the cluster, NULL for the default cluster, which is only valid in the classic stack. */ uint8_t c_flags; }; struct o2cb_region_desc { char *r_name; /* The uuid of the region */ char *r_device_name; /* The device the region is on */ char *r_service; /* A program or mountpoint */ int r_block_bytes; uint64_t r_start_block; uint64_t r_blocks; int r_persist; /* Persist past process exit */ }; /* Expected use case for the region descriptor is to allocate it on * the stack and completely fill it before calling * begin_group_join(). Regular programs (not mount.ocfs2) should provide * a mountpoint that does not begin with a '/'. Eg, fsck could use "fsck" */ errcode_t o2cb_start_heartbeat(struct o2cb_cluster_desc *cluster, struct o2cb_region_desc *region); errcode_t o2cb_stop_heartbeat(struct o2cb_cluster_desc *cluster, struct o2cb_region_desc *region); errcode_t o2cb_begin_group_join(struct o2cb_cluster_desc *cluster, struct o2cb_region_desc *region); errcode_t o2cb_complete_group_join(struct o2cb_cluster_desc *cluster, struct o2cb_region_desc *region, int result); errcode_t o2cb_group_leave(struct o2cb_cluster_desc *cluster, struct o2cb_region_desc *region); errcode_t o2cb_get_hb_thread_pid (const char *cluster_name, const char *region_name, pid_t *pid); errcode_t o2cb_get_region_ref(const char *region_name, int undo); errcode_t o2cb_put_region_ref(const char *region_name, int undo); errcode_t o2cb_num_region_refs(const char *region_name, int *num_refs); errcode_t o2cb_get_node_num(const char *cluster_name, const char *node_name, uint16_t *node_num); errcode_t o2cb_get_node_port(const char *cluster_name, const char *node_name, uint32_t *ip_port); errcode_t o2cb_get_node_ip_string(const char *cluster_name, const char *node_name, char *ip_address, int count); errcode_t o2cb_get_node_local(const char *cluster_name, const char *node_name, uint32_t *local); void o2cb_free_cluster_desc(struct o2cb_cluster_desc *cluster); errcode_t o2cb_running_cluster_desc(struct o2cb_cluster_desc *cluster); struct ocfs2_protocol_version { uint8_t pv_major; uint8_t pv_minor; }; errcode_t o2cb_get_max_locking_protocol(struct ocfs2_protocol_version *proto); errcode_t o2cb_control_open(unsigned int this_node, struct ocfs2_protocol_version *proto); void o2cb_control_close(void); errcode_t o2cb_control_node_down(const char *uuid, unsigned int nodeid); errcode_t o2cb_get_hb_ctl_path(char *buf, int count); errcode_t o2cb_setup_stack(char *stack_name); #endif /* _O2CB_H */ ocfs2-tools-ocfs2-tools-1.8.6/include/o2cb/o2cb_client_proto.h000066400000000000000000000041311347147137200241070ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: */ /****************************************************************************** ******************************************************************************* ** ** Copyright (C) 2005 Red Hat, Inc. All rights reserved. ** ** This copyrighted material is made available to anyone wishing to use, ** modify, copy, or redistribute it subject to the terms and conditions ** of the GNU General Public License v.2. ** ******************************************************************************* ******************************************************************************/ /* * Copyright (C) 2007 Oracle. All rights reserved. * * This copyrighted material is made available to anyone wishing to use, * modify, copy, or redistribute it subject to the terms and conditions * of the GNU General Public License v.2. */ #ifndef __O2CB_CLIENT_PROTO_H #define __O2CB_CLIENT_PROTO_H /* Basic communication properties */ #define OCFS2_CONTROLD_MAXLINE 256 #define OCFS2_CONTROLD_MAXARGS 16 #define OCFS2_CONTROLD_SOCK_PATH "ocfs2_controld_sock" #define O2CB_CONTROLD_SOCK_PATH "o2cb_controld_sock" /* Client messages */ typedef enum { CM_MOUNT, CM_MRESULT, CM_UNMOUNT, CM_STATUS, CM_LISTFS, CM_LISTMOUNTS, CM_LISTCLUSTERS, CM_ITEMCOUNT, CM_ITEM, CM_DUMP, } client_message; int client_listen(const char *path); int client_connect(const char *path); static inline int ocfs2_client_listen(void) { return client_listen(OCFS2_CONTROLD_SOCK_PATH); } static inline int ocfs2_client_connect(void) { return client_connect(OCFS2_CONTROLD_SOCK_PATH); } const char *message_to_string(client_message message); int send_message(int fd, client_message message, ...); int receive_message(int fd, char *buf, client_message *message, char **argv); int receive_message_full(int fd, char *buf, client_message *message, char **argv, char **rest); void free_received_list(char **list); int receive_list(int fd, char *buf, char ***ret_list); int parse_status(char **args, int *error, char **error_msg); #endif /* __O2CB_CLIENT_PROTO_H */ ocfs2-tools-ocfs2-tools-1.8.6/include/o2cb/ocfs2_heartbeat.h000066400000000000000000000022211347147137200235320ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * ocfs2_heartbeat.h * * On-disk structures for ocfs2_heartbeat * * Copyright (C) 2002, 2004 Oracle. All rights reserved. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. */ #ifndef _OCFS2_HEARTBEAT_H #define _OCFS2_HEARTBEAT_H struct o2hb_disk_heartbeat_block { __le64 hb_seq; __u8 hb_node; __u8 hb_pad1[3]; __le32 hb_cksum; __le64 hb_generation; __le32 hb_dead_ms; }; #endif /* _OCFS2_HEARTBEAT_H */ ocfs2-tools-ocfs2-tools-1.8.6/include/o2cb/ocfs2_nodemanager.h000066400000000000000000000026551347147137200240660ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * ocfs2_nodemanager.h * * Header describing the interface between userspace and the kernel * for the ocfs2_nodemanager module. * * Copyright (C) 2002, 2004 Oracle. All rights reserved. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * */ #ifndef _OCFS2_NODEMANAGER_H #define _OCFS2_NODEMANAGER_H #define O2NM_API_VERSION 5 #define O2NM_MAX_NODES 255 #define O2NM_INVALID_NODE_NUM 255 /* host name, group name, cluster name all 64 bytes */ #define O2NM_MAX_NAME_LEN 64 // __NEW_UTS_LEN /* * Maximum number of global heartbeat regions allowed. * **CAUTION** Changing this number will break dlm compatibility. */ #define O2NM_MAX_REGIONS 32 #endif /* _OCFS2_NODEMANAGER_H */ ocfs2-tools-ocfs2-tools-1.8.6/include/o2dlm/000077500000000000000000000000001347147137200205215ustar00rootroot00000000000000ocfs2-tools-ocfs2-tools-1.8.6/include/o2dlm/.gitignore000066400000000000000000000000141347147137200225040ustar00rootroot00000000000000o2dlm_err.h ocfs2-tools-ocfs2-tools-1.8.6/include/o2dlm/Makefile000066400000000000000000000006341347147137200221640ustar00rootroot00000000000000TOPDIR = ../.. include $(TOPDIR)/Preamble.make HFILES_GEN = o2dlm_err.h all: $(HFILES_GEN) HFILES = o2dlm.h HEADERS_SUBDIR = o2dlm HEADERS = $(HFILES) $(HFILES_GEN) o2dlm_err.h: $(TOPDIR)/libo2dlm/o2dlm_err.h cp $< $@ $(TOPDIR)/libo2dlm/o2dlm_err.h: make -C $(TOPDIR)/libo2dlm o2dlm_err.h DIST_FILES = $(HFILES) CLEAN_RULES = clean-err clean-err: rm -f o2dlm_err.h include $(TOPDIR)/Postamble.make ocfs2-tools-ocfs2-tools-1.8.6/include/o2dlm/o2dlm.h000066400000000000000000000074141347147137200217150ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * o2dlm.h * * Defines the userspace locking api * * Copyright (C) 2004 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License, version 2, as published by the Free Software Foundation. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * * Authors: Mark Fasheh */ #ifndef _O2DLM_H_ #define _O2DLM_H_ #include #include #include #include #include #include #include #define O2DLM_LOCK_ID_MAX_LEN 32 #define O2DLM_DOMAIN_MAX_LEN 255 /* + null pointer */ #define O2DLM_MAX_FULL_DOMAIN_PATH (PATH_MAX + 1) /* valid lock flags */ #define O2DLM_TRYLOCK 0x01 #define O2DLM_VALID_FLAGS (O2DLM_TRYLOCK) /* Forward declarations */ struct o2dlm_ctxt; /* valid lock levels */ enum o2dlm_lock_level { O2DLM_LEVEL_PRMODE, O2DLM_LEVEL_EXMODE }; /* Expects to be given a path to the root of a valid ocfs2_dlmfs file * system and a domain identifier of length <= 255 characters including * the '\0' */ errcode_t o2dlm_initialize(const char *dlmfs_path, const char *domain_name, struct o2dlm_ctxt **ctxt); /* * lock_name, is a valid lock name -- 32 bytes long including the null * character * * Returns: 0 if we got the lock we wanted */ errcode_t o2dlm_lock(struct o2dlm_ctxt *ctxt, const char *lockid, int lockflags, enum o2dlm_lock_level level); /* * Like o2dlm_lock, but also registers a BAST function for this lock. This * returns a file descriptor in poll_fd that can be fed to select(2) or * poll(2). When there is POLLIN on the descriptor, call o2dlm_process_bast(). */ errcode_t o2dlm_lock_with_bast(struct o2dlm_ctxt *ctxt, const char *lockid, int lockflags, enum o2dlm_lock_level level, void (*bast_func)(void *bast_arg), void *bast_arg, int *poll_fd); /* returns 0 on success */ errcode_t o2dlm_unlock(struct o2dlm_ctxt *ctxt, const char *lockid); /* Remove an unlocked lock from the domain */ errcode_t o2dlm_drop_lock(struct o2dlm_ctxt *ctxt, const char *lockid); /* Read the LVB out of a lock. * 'len' is the amount to read into 'lvb' * * We can only read LVB_MAX bytes out of the lock, even if you * specificy a len larger than that. For classic o2dlm, LVB_MAX is * 64 bytes. For fsdlm, it is 32 bytes. * * If you want to know how much was read, then pass 'bytes_read' */ errcode_t o2dlm_read_lvb(struct o2dlm_ctxt *ctxt, char *lockid, char *lvb, unsigned int len, unsigned int *bytes_read); errcode_t o2dlm_write_lvb(struct o2dlm_ctxt *ctxt, char *lockid, const char *lvb, unsigned int len, unsigned int *bytes_written); /* * Call this when select(2) or poll(2) says there is data on poll_fd. It * will fire off the BAST associated with poll_fd. */ void o2dlm_process_bast(struct o2dlm_ctxt *ctxt, int poll_fd); /* * Unlocks all pending locks and frees the lock context. */ errcode_t o2dlm_destroy(struct o2dlm_ctxt *ctxt); /* * Optional features that libo2dlm and dlmfs can support. */ errcode_t o2dlm_supports_bast(int *supports); errcode_t o2dlm_supports_stackglue(int *supports); #endif /* _O2DLM_H_ */ ocfs2-tools-ocfs2-tools-1.8.6/include/ocfs2-kernel/000077500000000000000000000000001347147137200217765ustar00rootroot00000000000000ocfs2-tools-ocfs2-tools-1.8.6/include/ocfs2-kernel/Makefile000066400000000000000000000004501347147137200234350ustar00rootroot00000000000000TOPDIR = ../.. include $(TOPDIR)/Preamble.make HEADERS_SUBDIR = ocfs2-kernel HEADERS = \ fiemap.h \ kernel-list.h \ ocfs1_fs_compat.h \ ocfs2_fs.h \ ocfs2_ioctl.h \ ocfs2_lockid.h \ quota_tree.h \ sparse_endian_types.h DIST_FILES = $(HEADERS) include $(TOPDIR)/Postamble.make ocfs2-tools-ocfs2-tools-1.8.6/include/ocfs2-kernel/fiemap.h000066400000000000000000000052411347147137200234120ustar00rootroot00000000000000/* * FS_IOC_FIEMAP ioctl infrastructure. * * Some portions copyright (C) 2007 Cluster File Systems, Inc * * Authors: Mark Fasheh * Kalpak Shah * Andreas Dilger */ #ifndef _LINUX_FIEMAP_H #define _LINUX_FIEMAP_H #include struct fiemap_extent { __u64 fe_logical; /* logical offset in bytes for the start of * the extent from the beginning of the file */ __u64 fe_physical; /* physical offset in bytes for the start * of the extent from the beginning of the disk */ __u64 fe_length; /* length in bytes for this extent */ __u64 fe_reserved64[2]; __u32 fe_flags; /* FIEMAP_EXTENT_* flags for this extent */ __u32 fe_reserved[3]; }; struct fiemap { __u64 fm_start; /* logical offset (inclusive) at * which to start mapping (in) */ __u64 fm_length; /* logical length of mapping which * userspace wants (in) */ __u32 fm_flags; /* FIEMAP_FLAG_* flags for request (in/out) */ __u32 fm_mapped_extents;/* number of extents that were mapped (out) */ __u32 fm_extent_count; /* size of fm_extents array (in) */ __u32 fm_reserved; struct fiemap_extent fm_extents[0]; /* array of mapped extents (out) */ }; #ifndef FS_IOC_FIEMAP #define FS_IOC_FIEMAP _IOWR('f', 11, struct fiemap) #endif #define FIEMAP_MAX_OFFSET (~0ULL) #define FIEMAP_FLAG_SYNC 0x00000001 /* sync file data before map */ #define FIEMAP_FLAG_XATTR 0x00000002 /* map extended attribute tree */ #define FIEMAP_FLAGS_COMPAT (FIEMAP_FLAG_SYNC | FIEMAP_FLAG_XATTR) #define FIEMAP_EXTENT_LAST 0x00000001 /* Last extent in file. */ #define FIEMAP_EXTENT_UNKNOWN 0x00000002 /* Data location unknown. */ #define FIEMAP_EXTENT_DELALLOC 0x00000004 /* Location still pending. * Sets EXTENT_UNKNOWN. */ #define FIEMAP_EXTENT_ENCODED 0x00000008 /* Data can not be read * while fs is unmounted */ #define FIEMAP_EXTENT_DATA_ENCRYPTED 0x00000080 /* Data is encrypted by fs. * Sets EXTENT_NO_BYPASS. */ #define FIEMAP_EXTENT_NOT_ALIGNED 0x00000100 /* Extent offsets may not be * block aligned. */ #define FIEMAP_EXTENT_DATA_INLINE 0x00000200 /* Data mixed with metadata. * Sets EXTENT_NOT_ALIGNED.*/ #define FIEMAP_EXTENT_DATA_TAIL 0x00000400 /* Multiple files in block. * Sets EXTENT_NOT_ALIGNED.*/ #define FIEMAP_EXTENT_UNWRITTEN 0x00000800 /* Space allocated, but * no data (i.e. zero). */ #define FIEMAP_EXTENT_MERGED 0x00001000 /* File does not natively * support extents. Result * merged for efficiency. */ #define FIEMAP_EXTENT_SHARED 0x00002000 /* Space shared with other * files. */ #endif /* _LINUX_FIEMAP_H */ ocfs2-tools-ocfs2-tools-1.8.6/include/ocfs2-kernel/kernel-list.h000066400000000000000000000051471347147137200244070ustar00rootroot00000000000000#ifndef _LINUX_LIST_H #define _LINUX_LIST_H /* * Simple doubly linked list implementation. * * Some of the internal functions ("__xxx") are useful when * manipulating whole lists rather than single entries, as * sometimes we already know the next/prev entries and we can * generate better code by using them directly rather than * using the generic single-entry routines. */ struct list_head { struct list_head *next, *prev; }; #define LIST_HEAD_INIT(name) { &(name), &(name) } #define LIST_HEAD(name) \ struct list_head name = { &name, &name } #define INIT_LIST_HEAD(ptr) do { \ (ptr)->next = (ptr); (ptr)->prev = (ptr); \ } while (0) #if (!defined(__GNUC__) && !defined(__WATCOMC__)) #define __inline__ #endif /* * Insert a new entry between two known consecutive entries. * * This is only for internal list manipulation where we know * the prev/next entries already! */ static __inline__ void __list_add(struct list_head * new, struct list_head * prev, struct list_head * next) { next->prev = new; new->next = next; new->prev = prev; prev->next = new; } /* * Insert a new entry after the specified head.. */ static __inline__ void list_add(struct list_head *new, struct list_head *head) { __list_add(new, head, head->next); } /* * Insert a new entry at the tail */ static __inline__ void list_add_tail(struct list_head *new, struct list_head *head) { __list_add(new, head->prev, head); } /* * Delete a list entry by making the prev/next entries * point to each other. * * This is only for internal list manipulation where we know * the prev/next entries already! */ static __inline__ void __list_del(struct list_head * prev, struct list_head * next) { next->prev = prev; prev->next = next; } static __inline__ void list_del(struct list_head *entry) { __list_del(entry->prev, entry->next); } static __inline__ int list_empty(struct list_head *head) { return head->next == head; } /* * Splice in "list" into "head" */ static __inline__ void list_splice(struct list_head *list, struct list_head *head) { struct list_head *first = list->next; if (first != list) { struct list_head *last = list->prev; struct list_head *at = head->next; first->prev = head; head->next = first; last->next = at; at->prev = last; } } #define list_entry(ptr, type, member) \ ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member))) #define list_for_each(pos, head) \ for (pos = (head)->next; pos != (head); pos = pos->next) #define list_for_each_safe(pos, n, head) \ for (pos = (head)->next, n = pos->next; pos != (head); \ pos = n, n = pos->next) #endif ocfs2-tools-ocfs2-tools-1.8.6/include/ocfs2-kernel/ocfs1_fs_compat.h000066400000000000000000000056511347147137200252240ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * ocfs1_fs_compat.h * * OCFS1 volume header definitions. OCFS2 creates valid but unmountable * OCFS1 volume headers on the first two sectors of an OCFS2 volume. * This allows an OCFS1 volume to see the partition and cleanly fail to * mount it. * * Copyright (C) 2002, 2004 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License, version 2, as published by the Free Software Foundation. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. */ #ifndef _OCFS1_FS_COMPAT_H #define _OCFS1_FS_COMPAT_H #define OCFS1_MAX_VOL_SIGNATURE_LEN 128 #define OCFS1_MAX_MOUNT_POINT_LEN 128 #define OCFS1_MAX_VOL_ID_LENGTH 16 #define OCFS1_MAX_VOL_LABEL_LEN 64 #define OCFS1_MAX_CLUSTER_NAME_LEN 64 #define OCFS1_MAJOR_VERSION (2) #define OCFS1_MINOR_VERSION (0) #define OCFS1_VOLUME_SIGNATURE "OracleCFS" /* * OCFS1 superblock. Lives at sector 0. */ struct ocfs1_vol_disk_hdr { /*00*/ __u32 minor_version; __u32 major_version; /*08*/ __u8 signature[OCFS1_MAX_VOL_SIGNATURE_LEN]; /*88*/ __u8 mount_point[OCFS1_MAX_MOUNT_POINT_LEN]; /*108*/ __u64 serial_num; /*110*/ __u64 device_size; __u64 start_off; /*120*/ __u64 bitmap_off; __u64 publ_off; /*130*/ __u64 vote_off; __u64 root_bitmap_off; /*140*/ __u64 data_start_off; __u64 root_bitmap_size; /*150*/ __u64 root_off; __u64 root_size; /*160*/ __u64 cluster_size; __u64 num_nodes; /*170*/ __u64 num_clusters; __u64 dir_node_size; /*180*/ __u64 file_node_size; __u64 internal_off; /*190*/ __u64 node_cfg_off; __u64 node_cfg_size; /*1A0*/ __u64 new_cfg_off; __u32 prot_bits; __s32 excl_mount; /*1B0*/ }; struct ocfs1_disk_lock { /*00*/ __u32 curr_master; __u8 file_lock; __u8 compat_pad[3]; /* Not in original definition. Used to make the already existing alignment explicit */ __u64 last_write_time; /*10*/ __u64 last_read_time; __u32 writer_node_num; __u32 reader_node_num; /*20*/ __u64 oin_node_map; __u64 dlock_seq_num; /*30*/ }; /* * OCFS1 volume label. Lives at sector 1. */ struct ocfs1_vol_label { /*00*/ struct ocfs1_disk_lock disk_lock; /*30*/ __u8 label[OCFS1_MAX_VOL_LABEL_LEN]; /*70*/ __u16 label_len; /*72*/ __u8 vol_id[OCFS1_MAX_VOL_ID_LENGTH]; /*82*/ __u16 vol_id_len; /*84*/ __u8 cluster_name[OCFS1_MAX_CLUSTER_NAME_LEN]; /*A4*/ __u16 cluster_name_len; /*A6*/ }; #endif /* _OCFS1_FS_COMPAT_H */ ocfs2-tools-ocfs2-tools-1.8.6/include/ocfs2-kernel/ocfs2_fs.h000066400000000000000000001505771347147137200236720ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * ocfs2_fs.h * * On-disk structures for OCFS2. * * Copyright (C) 2002, 2004 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License, version 2, as published by the Free Software Foundation. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. */ #ifndef _OCFS2_FS_H #define _OCFS2_FS_H /* Version */ #define OCFS2_MAJOR_REV_LEVEL 0 #define OCFS2_MINOR_REV_LEVEL 90 /* * An OCFS2 volume starts this way: * Sector 0: Valid ocfs1_vol_disk_hdr that cleanly fails to mount OCFS. * Sector 1: Valid ocfs1_vol_label that cleanly fails to mount OCFS. * Block OCFS2_SUPER_BLOCK_BLKNO: OCFS2 superblock. * * All other structures are found from the superblock information. * * OCFS2_SUPER_BLOCK_BLKNO is in blocks, not sectors. eg, for a * blocksize of 2K, it is 4096 bytes into disk. */ #define OCFS2_SUPER_BLOCK_BLKNO 2 /* * Cluster size limits. The maximum is kept arbitrarily at 1 MB, and could * grow if needed. */ #define OCFS2_MIN_CLUSTERSIZE 4096 #define OCFS2_MAX_CLUSTERSIZE 1048576 /* * Blocks cannot be bigger than clusters, so the maximum blocksize is the * minimum cluster size. */ #define OCFS2_MIN_BLOCKSIZE 512 #define OCFS2_MAX_BLOCKSIZE OCFS2_MIN_CLUSTERSIZE /* Filesystem magic number */ #define OCFS2_SUPER_MAGIC 0x7461636f /* Object signatures */ #define OCFS2_SUPER_BLOCK_SIGNATURE "OCFSV2" #define OCFS2_INODE_SIGNATURE "INODE01" #define OCFS2_EXTENT_BLOCK_SIGNATURE "EXBLK01" #define OCFS2_GROUP_DESC_SIGNATURE "GROUP01" #define OCFS2_XATTR_BLOCK_SIGNATURE "XATTR01" #define OCFS2_DIR_TRAILER_SIGNATURE "DIRTRL1" #define OCFS2_DX_ROOT_SIGNATURE "DXDIR01" #define OCFS2_DX_LEAF_SIGNATURE "DXLEAF1" #define OCFS2_REFCOUNT_BLOCK_SIGNATURE "REFCNT1" /* Compatibility flags */ #define OCFS2_HAS_COMPAT_FEATURE(sb,mask) \ ( OCFS2_SB(sb)->s_feature_compat & (mask) ) #define OCFS2_HAS_RO_COMPAT_FEATURE(sb,mask) \ ( OCFS2_SB(sb)->s_feature_ro_compat & (mask) ) #define OCFS2_HAS_INCOMPAT_FEATURE(sb,mask) \ ( OCFS2_SB(sb)->s_feature_incompat & (mask) ) #define OCFS2_SET_COMPAT_FEATURE(sb,mask) \ OCFS2_SB(sb)->s_feature_compat |= (mask) #define OCFS2_SET_RO_COMPAT_FEATURE(sb,mask) \ OCFS2_SB(sb)->s_feature_ro_compat |= (mask) #define OCFS2_SET_INCOMPAT_FEATURE(sb,mask) \ OCFS2_SB(sb)->s_feature_incompat |= (mask) #define OCFS2_CLEAR_COMPAT_FEATURE(sb,mask) \ OCFS2_SB(sb)->s_feature_compat &= ~(mask) #define OCFS2_CLEAR_RO_COMPAT_FEATURE(sb,mask) \ OCFS2_SB(sb)->s_feature_ro_compat &= ~(mask) #define OCFS2_CLEAR_INCOMPAT_FEATURE(sb,mask) \ OCFS2_SB(sb)->s_feature_incompat &= ~(mask) #define OCFS2_FEATURE_COMPAT_SUPP (OCFS2_FEATURE_COMPAT_BACKUP_SB \ | OCFS2_FEATURE_COMPAT_JBD2_SB) #define OCFS2_FEATURE_INCOMPAT_SUPP (OCFS2_FEATURE_INCOMPAT_LOCAL_MOUNT \ | OCFS2_FEATURE_INCOMPAT_SPARSE_ALLOC \ | OCFS2_FEATURE_INCOMPAT_INLINE_DATA \ | OCFS2_FEATURE_INCOMPAT_EXTENDED_SLOT_MAP \ | OCFS2_FEATURE_INCOMPAT_USERSPACE_STACK \ | OCFS2_FEATURE_INCOMPAT_XATTR \ | OCFS2_FEATURE_INCOMPAT_META_ECC \ | OCFS2_FEATURE_INCOMPAT_INDEXED_DIRS \ | OCFS2_FEATURE_INCOMPAT_REFCOUNT_TREE \ | OCFS2_FEATURE_INCOMPAT_DISCONTIG_BG \ | OCFS2_FEATURE_INCOMPAT_CLUSTERINFO \ | OCFS2_FEATURE_INCOMPAT_APPEND_DIO) #define OCFS2_FEATURE_RO_COMPAT_SUPP (OCFS2_FEATURE_RO_COMPAT_UNWRITTEN \ | OCFS2_FEATURE_RO_COMPAT_USRQUOTA \ | OCFS2_FEATURE_RO_COMPAT_GRPQUOTA) /* * Heartbeat-only devices are missing journals and other files. The * filesystem driver can't load them, but the library can. Never put * this in OCFS2_FEATURE_INCOMPAT_SUPP, *ever*. */ #define OCFS2_FEATURE_INCOMPAT_HEARTBEAT_DEV 0x0002 /* * tunefs sets this incompat flag before starting the resize and clears it * at the end. This flag protects users from inadvertently mounting the fs * after an aborted run without fsck-ing. */ #define OCFS2_FEATURE_INCOMPAT_RESIZE_INPROG 0x0004 /* Used to denote a non-clustered volume */ #define OCFS2_FEATURE_INCOMPAT_LOCAL_MOUNT 0x0008 /* Support for sparse allocation in b-trees */ #define OCFS2_FEATURE_INCOMPAT_SPARSE_ALLOC 0x0010 /* * Tunefs sets this incompat flag before starting an operation which * would require cleanup on abort. This is done to protect users from * inadvertently mounting the fs after an aborted run without * fsck-ing. * * s_tunefs_flags on the super block describes precisely which * operations were in progress. */ #define OCFS2_FEATURE_INCOMPAT_TUNEFS_INPROG 0x0020 /* Support for data packed into inode blocks */ #define OCFS2_FEATURE_INCOMPAT_INLINE_DATA 0x0040 /* * Support for alternate, userspace cluster stacks. If set, the superblock * field s_cluster_info contains a tag for the alternate stack in use as * well as the name of the cluster being joined. * mount.ocfs2 must pass in a matching stack name. * * If not set, the classic stack will be used. This is compatbile with * all older versions. */ #define OCFS2_FEATURE_INCOMPAT_USERSPACE_STACK 0x0080 /* Support for the extended slot map */ #define OCFS2_FEATURE_INCOMPAT_EXTENDED_SLOT_MAP 0x100 /* Support for extended attributes */ #define OCFS2_FEATURE_INCOMPAT_XATTR 0x0200 /* Support for indexed directores */ #define OCFS2_FEATURE_INCOMPAT_INDEXED_DIRS 0x0400 /* Metadata checksum and error correction */ #define OCFS2_FEATURE_INCOMPAT_META_ECC 0x0800 /* Refcount tree support */ #define OCFS2_FEATURE_INCOMPAT_REFCOUNT_TREE 0x1000 /* Discontigous block groups */ #define OCFS2_FEATURE_INCOMPAT_DISCONTIG_BG 0x2000 /* * Incompat bit to indicate useable clusterinfo with stackflags for all * cluster stacks (userspace adnd o2cb). If this bit is set, * INCOMPAT_USERSPACE_STACK becomes superfluous and thus should not be set. */ #define OCFS2_FEATURE_INCOMPAT_CLUSTERINFO 0x4000 /* * Append Direct IO support */ #define OCFS2_FEATURE_INCOMPAT_APPEND_DIO 0x8000 /* * backup superblock flag is used to indicate that this volume * has backup superblocks. */ #define OCFS2_FEATURE_COMPAT_BACKUP_SB 0x0001 /* * The filesystem will correctly handle journal feature bits. */ #define OCFS2_FEATURE_COMPAT_JBD2_SB 0x0002 /* * Unwritten extents support. */ #define OCFS2_FEATURE_RO_COMPAT_UNWRITTEN 0x0001 /* * Maintain quota information for this filesystem */ #define OCFS2_FEATURE_RO_COMPAT_USRQUOTA 0x0002 #define OCFS2_FEATURE_RO_COMPAT_GRPQUOTA 0x0004 /* The byte offset of the first backup block will be 1G. * The following will be 4G, 16G, 64G, 256G and 1T. */ #define OCFS2_BACKUP_SB_START 1 << 30 /* the max backup superblock nums */ #define OCFS2_MAX_BACKUP_SUPERBLOCKS 6 /* * Flags on ocfs2_super_block.s_tunefs_flags */ #define OCFS2_TUNEFS_INPROG_REMOVE_SLOT 0x0001 /* Removing slots */ /* Adding directory block trailers */ #define OCFS2_TUNEFS_INPROG_DIR_TRAILER 0x0002 /* * Flags on ocfs2_dinode.i_flags */ #define OCFS2_VALID_FL (0x00000001) /* Inode is valid */ #define OCFS2_UNUSED2_FL (0x00000002) #define OCFS2_ORPHANED_FL (0x00000004) /* On the orphan list */ #define OCFS2_UNUSED3_FL (0x00000008) /* System inode flags */ #define OCFS2_SYSTEM_FL (0x00000010) /* System inode */ #define OCFS2_SUPER_BLOCK_FL (0x00000020) /* Super block */ #define OCFS2_LOCAL_ALLOC_FL (0x00000040) /* Slot local alloc bitmap */ #define OCFS2_BITMAP_FL (0x00000080) /* Allocation bitmap */ #define OCFS2_JOURNAL_FL (0x00000100) /* Slot local journal */ #define OCFS2_HEARTBEAT_FL (0x00000200) /* Heartbeat area */ #define OCFS2_CHAIN_FL (0x00000400) /* Chain allocator */ #define OCFS2_DEALLOC_FL (0x00000800) /* Truncate log */ #define OCFS2_QUOTA_FL (0x00001000) /* Quota file */ #define OCFS2_DIO_ORPHANED_FL (0X00002000) /* On the orphan list especially * for dio */ /* * Flags on ocfs2_dinode.i_dyn_features * * These can change much more often than i_flags. When adding flags, * keep in mind that i_dyn_features is only 16 bits wide. */ #define OCFS2_INLINE_DATA_FL (0x0001) /* Data stored in inode block */ #define OCFS2_HAS_XATTR_FL (0x0002) #define OCFS2_INLINE_XATTR_FL (0x0004) #define OCFS2_INDEXED_DIR_FL (0x0008) #define OCFS2_HAS_REFCOUNT_FL (0x0010) /* Inode attributes, keep in sync with EXT2 */ #define OCFS2_SECRM_FL FS_SECRM_FL /* Secure deletion */ #define OCFS2_UNRM_FL FS_UNRM_FL /* Undelete */ #define OCFS2_COMPR_FL FS_COMPR_FL /* Compress file */ #define OCFS2_SYNC_FL FS_SYNC_FL /* Synchronous updates */ #define OCFS2_IMMUTABLE_FL FS_IMMUTABLE_FL /* Immutable file */ #define OCFS2_APPEND_FL FS_APPEND_FL /* writes to file may only append */ #define OCFS2_NODUMP_FL FS_NODUMP_FL /* do not dump file */ #define OCFS2_NOATIME_FL FS_NOATIME_FL /* do not update atime */ /* Reserved for compression usage... */ #define OCFS2_DIRTY_FL FS_DIRTY_FL #define OCFS2_COMPRBLK_FL FS_COMPRBLK_FL /* One or more compressed clusters */ #define OCFS2_NOCOMP_FL FS_NOCOMP_FL /* Don't compress */ #define OCFS2_ECOMPR_FL FS_ECOMPR_FL /* Compression error */ /* End compression flags --- maybe not all used */ #define OCFS2_BTREE_FL FS_BTREE_FL /* btree format dir */ #define OCFS2_INDEX_FL FS_INDEX_FL /* hash-indexed directory */ #define OCFS2_IMAGIC_FL FS_IMAGIC_FL /* AFS directory */ #define OCFS2_JOURNAL_DATA_FL FS_JOURNAL_DATA_FL /* Reserved for ext3 */ #define OCFS2_NOTAIL_FL FS_NOTAIL_FL /* file tail should not be merged */ #define OCFS2_DIRSYNC_FL FS_DIRSYNC_FL /* dirsync behaviour (directories only) */ #define OCFS2_TOPDIR_FL FS_TOPDIR_FL /* Top of directory hierarchies*/ #define OCFS2_RESERVED_FL FS_RESERVED_FL /* reserved for ext2 lib */ #define OCFS2_FL_VISIBLE FS_FL_USER_VISIBLE /* User visible flags */ #define OCFS2_FL_MODIFIABLE FS_FL_USER_MODIFIABLE /* User modifiable flags */ /* * Extent record flags (e_node.leaf.flags) */ #define OCFS2_EXT_UNWRITTEN (0x01) /* Extent is allocated but * unwritten */ #define OCFS2_EXT_REFCOUNTED (0x02) /* Extent is reference * counted in an associated * refcount tree */ /* * Journal Flags (ocfs2_dinode.id1.journal1.i_flags) */ #define OCFS2_JOURNAL_DIRTY_FL (0x00000001) /* Journal needs recovery */ /* * superblock s_state flags */ #define OCFS2_ERROR_FS (0x00000001) /* FS saw errors */ /* Limit of space in ocfs2_dir_entry */ #define OCFS2_MAX_FILENAME_LEN 255 /* Maximum slots on an ocfs2 file system */ #define OCFS2_MAX_SLOTS 255 /* Slot map indicator for an empty slot */ #define OCFS2_INVALID_SLOT -1 #define OCFS2_VOL_UUID_LEN 16 #define OCFS2_MAX_VOL_LABEL_LEN 64 /* The cluster stack fields */ #define OCFS2_STACK_LABEL_LEN 4 #define OCFS2_CLUSTER_NAME_LEN 16 /* Journal limits (in bytes) */ #define OCFS2_MIN_JOURNAL_SIZE (4 * 1024 * 1024) /* Minimum Journal size shift with respect to cluster size */ #define OCFS2_MIN_CLUSTER_TO_JOURNAL_SIZE_SHIFT 3 /* * Default local alloc size (in megabytes) * * The value chosen should be such that most allocations, including new * block groups, use local alloc. */ #define OCFS2_DEFAULT_LOCAL_ALLOC_SIZE 8 /* * Inline extended attribute size (in bytes) * The value chosen should be aligned to 16 byte boundaries. */ #define OCFS2_MIN_XATTR_INLINE_SIZE 256 /* * Cluster info flags (ocfs2_cluster_info.ci_stackflags) */ #define OCFS2_CLUSTER_O2CB_GLOBAL_HEARTBEAT (0x01) struct ocfs2_system_inode_info { char *si_name; int si_iflags; int si_mode; }; /* System file index */ enum { BAD_BLOCK_SYSTEM_INODE = 0, GLOBAL_INODE_ALLOC_SYSTEM_INODE, SLOT_MAP_SYSTEM_INODE, #define OCFS2_FIRST_ONLINE_SYSTEM_INODE SLOT_MAP_SYSTEM_INODE HEARTBEAT_SYSTEM_INODE, GLOBAL_BITMAP_SYSTEM_INODE, USER_QUOTA_SYSTEM_INODE, GROUP_QUOTA_SYSTEM_INODE, #define OCFS2_LAST_GLOBAL_SYSTEM_INODE GROUP_QUOTA_SYSTEM_INODE #define OCFS2_FIRST_LOCAL_SYSTEM_INODE ORPHAN_DIR_SYSTEM_INODE ORPHAN_DIR_SYSTEM_INODE, EXTENT_ALLOC_SYSTEM_INODE, INODE_ALLOC_SYSTEM_INODE, JOURNAL_SYSTEM_INODE, LOCAL_ALLOC_SYSTEM_INODE, TRUNCATE_LOG_SYSTEM_INODE, LOCAL_USER_QUOTA_SYSTEM_INODE, LOCAL_GROUP_QUOTA_SYSTEM_INODE, #define OCFS2_LAST_LOCAL_SYSTEM_INODE LOCAL_GROUP_QUOTA_SYSTEM_INODE NUM_SYSTEM_INODES }; #define NUM_GLOBAL_SYSTEM_INODES OCFS2_FIRST_LOCAL_SYSTEM_INODE #define NUM_LOCAL_SYSTEM_INODES \ (NUM_SYSTEM_INODES - OCFS2_FIRST_LOCAL_SYSTEM_INODE) static struct ocfs2_system_inode_info ocfs2_system_inodes[NUM_SYSTEM_INODES] = { /* Global system inodes (single copy) */ /* The first two are only used from userspace mfks/tunefs */ [BAD_BLOCK_SYSTEM_INODE] = { "bad_blocks", 0, S_IFREG | 0644 }, [GLOBAL_INODE_ALLOC_SYSTEM_INODE] = { "global_inode_alloc", OCFS2_BITMAP_FL | OCFS2_CHAIN_FL, S_IFREG | 0644 }, /* These are used by the running filesystem */ [SLOT_MAP_SYSTEM_INODE] = { "slot_map", 0, S_IFREG | 0644 }, [HEARTBEAT_SYSTEM_INODE] = { "heartbeat", OCFS2_HEARTBEAT_FL, S_IFREG | 0644 }, [GLOBAL_BITMAP_SYSTEM_INODE] = { "global_bitmap", 0, S_IFREG | 0644 }, [USER_QUOTA_SYSTEM_INODE] = { "aquota.user", OCFS2_QUOTA_FL, S_IFREG | 0644 }, [GROUP_QUOTA_SYSTEM_INODE] = { "aquota.group", OCFS2_QUOTA_FL, S_IFREG | 0644 }, /* Slot-specific system inodes (one copy per slot) */ [ORPHAN_DIR_SYSTEM_INODE] = { "orphan_dir:%04d", 0, S_IFDIR | 0755 }, [EXTENT_ALLOC_SYSTEM_INODE] = { "extent_alloc:%04d", OCFS2_BITMAP_FL | OCFS2_CHAIN_FL, S_IFREG | 0644 }, [INODE_ALLOC_SYSTEM_INODE] = { "inode_alloc:%04d", OCFS2_BITMAP_FL | OCFS2_CHAIN_FL, S_IFREG | 0644 }, [JOURNAL_SYSTEM_INODE] = { "journal:%04d", OCFS2_JOURNAL_FL, S_IFREG | 0644 }, [LOCAL_ALLOC_SYSTEM_INODE] = { "local_alloc:%04d", OCFS2_BITMAP_FL | OCFS2_LOCAL_ALLOC_FL, S_IFREG | 0644 }, [TRUNCATE_LOG_SYSTEM_INODE] = { "truncate_log:%04d", OCFS2_DEALLOC_FL, S_IFREG | 0644 }, [LOCAL_USER_QUOTA_SYSTEM_INODE] = { "aquota.user:%04d", OCFS2_QUOTA_FL, S_IFREG | 0644 }, [LOCAL_GROUP_QUOTA_SYSTEM_INODE] = { "aquota.group:%04d", OCFS2_QUOTA_FL, S_IFREG | 0644 }, }; /* Parameter passed from mount.ocfs2 to module */ #define OCFS2_HB_NONE "heartbeat=none" #define OCFS2_HB_LOCAL "heartbeat=local" #define OCFS2_HB_GLOBAL "heartbeat=global" /* * OCFS2 directory file types. Only the low 3 bits are used. The * other bits are reserved for now. */ #define OCFS2_FT_UNKNOWN 0 #define OCFS2_FT_REG_FILE 1 #define OCFS2_FT_DIR 2 #define OCFS2_FT_CHRDEV 3 #define OCFS2_FT_BLKDEV 4 #define OCFS2_FT_FIFO 5 #define OCFS2_FT_SOCK 6 #define OCFS2_FT_SYMLINK 7 #define OCFS2_FT_MAX 8 /* * OCFS2_DIR_PAD defines the directory entries boundaries * * NOTE: It must be a multiple of 4 */ #define OCFS2_DIR_PAD 4 #define OCFS2_DIR_ROUND (OCFS2_DIR_PAD - 1) #define OCFS2_DIR_MEMBER_LEN offsetof(struct ocfs2_dir_entry, name) #define OCFS2_DIR_REC_LEN(name_len) (((name_len) + OCFS2_DIR_MEMBER_LEN + \ OCFS2_DIR_ROUND) & \ ~OCFS2_DIR_ROUND) #define OCFS2_DIR_MIN_REC_LEN OCFS2_DIR_REC_LEN(1) #define OCFS2_LINK_MAX 32000 #define OCFS2_DX_LINK_MAX ((1U << 31) - 1U) #define OCFS2_LINKS_HI_SHIFT 16 #define OCFS2_DX_ENTRIES_MAX (0xffffffffU) #define S_SHIFT 12 static unsigned char ocfs2_type_by_mode[S_IFMT >> S_SHIFT] = { [S_IFREG >> S_SHIFT] = OCFS2_FT_REG_FILE, [S_IFDIR >> S_SHIFT] = OCFS2_FT_DIR, [S_IFCHR >> S_SHIFT] = OCFS2_FT_CHRDEV, [S_IFBLK >> S_SHIFT] = OCFS2_FT_BLKDEV, [S_IFIFO >> S_SHIFT] = OCFS2_FT_FIFO, [S_IFSOCK >> S_SHIFT] = OCFS2_FT_SOCK, [S_IFLNK >> S_SHIFT] = OCFS2_FT_SYMLINK, }; /* * Convenience casts */ #define OCFS2_RAW_SB(dinode) (&((dinode)->id2.i_super)) /* * Block checking structure. This is used in metadata to validate the * contents. If OCFS2_FEATURE_INCOMPAT_META_ECC is not set, it is all * zeros. */ struct ocfs2_block_check { /*00*/ __le32 bc_crc32e; /* 802.3 Ethernet II CRC32 */ __le16 bc_ecc; /* Single-error-correction parity vector. This is a simple Hamming code dependent on the blocksize. OCFS2's maximum blocksize, 4K, requires 16 parity bits, so we fit in __le16. */ __le16 bc_reserved1; /*08*/ }; /* * On disk extent record for OCFS2 * It describes a range of clusters on disk. * * Length fields are divided into interior and leaf node versions. * This leaves room for a flags field (OCFS2_EXT_*) in the leaf nodes. */ struct ocfs2_extent_rec { /*00*/ __le32 e_cpos; /* Offset into the file, in clusters */ union { __le32 e_int_clusters; /* Clusters covered by all children */ struct { __le16 e_leaf_clusters; /* Clusters covered by this extent */ __u8 e_reserved1; __u8 e_flags; /* Extent flags */ }; }; __le64 e_blkno; /* Physical disk offset, in blocks */ /*10*/ }; struct ocfs2_chain_rec { __le32 c_free; /* Number of free bits in this chain. */ __le32 c_total; /* Number of total bits in this chain */ __le64 c_blkno; /* Physical disk offset (blocks) of 1st group */ }; struct ocfs2_truncate_rec { __le32 t_start; /* 1st cluster in this log */ __le32 t_clusters; /* Number of total clusters covered */ }; /* * On disk extent list for OCFS2 (node in the tree). Note that this * is contained inside ocfs2_dinode or ocfs2_extent_block, so the * offsets are relative to ocfs2_dinode.id2.i_list or * ocfs2_extent_block.h_list, respectively. */ struct ocfs2_extent_list { /*00*/ __le16 l_tree_depth; /* Extent tree depth from this point. 0 means data extents hang directly off this header (a leaf) NOTE: The high 8 bits cannot be used - tree_depth is never that big. */ __le16 l_count; /* Number of extent records */ __le16 l_next_free_rec; /* Next unused extent slot */ __le16 l_reserved1; __le64 l_reserved2; /* Pad to sizeof(ocfs2_extent_rec) */ /*10*/ struct ocfs2_extent_rec l_recs[0]; /* Extent records */ }; /* * On disk allocation chain list for OCFS2. Note that this is * contained inside ocfs2_dinode, so the offsets are relative to * ocfs2_dinode.id2.i_chain. */ struct ocfs2_chain_list { /*00*/ __le16 cl_cpg; /* Clusters per Block Group */ __le16 cl_bpc; /* Bits per cluster */ __le16 cl_count; /* Total chains in this list */ __le16 cl_next_free_rec; /* Next unused chain slot */ __le64 cl_reserved1; /*10*/ struct ocfs2_chain_rec cl_recs[0]; /* Chain records */ }; /* * On disk deallocation log for OCFS2. Note that this is * contained inside ocfs2_dinode, so the offsets are relative to * ocfs2_dinode.id2.i_dealloc. */ struct ocfs2_truncate_log { /*00*/ __le16 tl_count; /* Total records in this log */ __le16 tl_used; /* Number of records in use */ __le32 tl_reserved1; /*08*/ struct ocfs2_truncate_rec tl_recs[0]; /* Truncate records */ }; /* * On disk extent block (indirect block) for OCFS2 */ struct ocfs2_extent_block { /*00*/ __u8 h_signature[8]; /* Signature for verification */ struct ocfs2_block_check h_check; /* Error checking */ /*10*/ __le16 h_suballoc_slot; /* Slot suballocator this extent_header belongs to */ __le16 h_suballoc_bit; /* Bit offset in suballocator block group */ __le32 h_fs_generation; /* Must match super block */ __le64 h_blkno; /* Offset on disk, in blocks */ /*20*/ __le64 h_suballoc_loc; /* Suballocator block group this eb belongs to. Only valid if allocated from a discontiguous block group */ __le64 h_next_leaf_blk; /* Offset on disk, in blocks, of next leaf header pointing to data */ /*30*/ struct ocfs2_extent_list h_list; /* Extent record list */ /* Actual on-disk size is one block */ }; /* * On disk slot map for OCFS2. This defines the contents of the "slot_map" * system file. A slot is valid if it contains a node number >= 0. The * value -1 (0xFFFF) is OCFS2_INVALID_SLOT. This marks a slot empty. */ struct ocfs2_slot_map { /*00*/ __le16 sm_slots[0]; /* * Actual on-disk size is one block. OCFS2_MAX_SLOTS is 255, * 255 * sizeof(__le16) == 512B, within the 512B block minimum blocksize. */ }; struct ocfs2_extended_slot { /*00*/ __u8 es_valid; __u8 es_reserved1[3]; __le32 es_node_num; /*10*/ }; /* * The extended slot map, used when OCFS2_FEATURE_INCOMPAT_EXTENDED_SLOT_MAP * is set. It separates out the valid marker from the node number, and * has room to grow. Unlike the old slot map, this format is defined by * i_size. */ struct ocfs2_slot_map_extended { /*00*/ struct ocfs2_extended_slot se_slots[0]; /* * Actual size is i_size of the slot_map system file. It should * match s_max_slots * sizeof(struct ocfs2_extended_slot) */ }; /* * ci_stackflags is only valid if the incompat bit * OCFS2_FEATURE_INCOMPAT_CLUSTERINFO is set. */ struct ocfs2_cluster_info { /*00*/ __u8 ci_stack[OCFS2_STACK_LABEL_LEN]; union { __le32 ci_reserved; struct { __u8 ci_stackflags; __u8 ci_reserved1; __u8 ci_reserved2; __u8 ci_reserved3; }; }; /*08*/ __u8 ci_cluster[OCFS2_CLUSTER_NAME_LEN]; /*18*/ }; /* * On disk superblock for OCFS2 * Note that it is contained inside an ocfs2_dinode, so all offsets * are relative to the start of ocfs2_dinode.id2. */ struct ocfs2_super_block { /*00*/ __le16 s_major_rev_level; __le16 s_minor_rev_level; __le16 s_mnt_count; __le16 s_max_mnt_count; __le16 s_state; /* File system state */ __le16 s_errors; /* Behaviour when detecting errors */ __le32 s_checkinterval; /* Max time between checks */ /*10*/ __le64 s_lastcheck; /* Time of last check */ __le32 s_creator_os; /* OS */ __le32 s_feature_compat; /* Compatible feature set */ /*20*/ __le32 s_feature_incompat; /* Incompatible feature set */ __le32 s_feature_ro_compat; /* Readonly-compatible feature set */ __le64 s_root_blkno; /* Offset, in blocks, of root directory dinode */ /*30*/ __le64 s_system_dir_blkno; /* Offset, in blocks, of system directory dinode */ __le32 s_blocksize_bits; /* Blocksize for this fs */ __le32 s_clustersize_bits; /* Clustersize for this fs */ /*40*/ __le16 s_max_slots; /* Max number of simultaneous mounts before tunefs required */ __le16 s_tunefs_flag; __le32 s_uuid_hash; /* hash value of uuid */ __le64 s_first_cluster_group; /* Block offset of 1st cluster * group header */ /*50*/ __u8 s_label[OCFS2_MAX_VOL_LABEL_LEN]; /* Label for mounting, etc. */ /*90*/ __u8 s_uuid[OCFS2_VOL_UUID_LEN]; /* 128-bit uuid */ /*A0*/ struct ocfs2_cluster_info s_cluster_info; /* Only valid if either userspace or clusterinfo INCOMPAT flag set. */ /*B8*/ __le16 s_xattr_inline_size; /* extended attribute inline size for this fs*/ __le16 s_reserved0; __le32 s_dx_seed[3]; /* seed[0-2] for dx dir hash. * s_uuid_hash serves as seed[3]. */ /*C0*/ __le64 s_reserved2[15]; /* Fill out superblock */ /*140*/ /* * NOTE: As stated above, all offsets are relative to * ocfs2_dinode.id2, which is at 0xC0 in the inode. * 0xC0 + 0x140 = 0x200 or 512 bytes. A superblock must fit within * our smallest blocksize, which is 512 bytes. To ensure this, * we reserve the space in s_reserved2. Anything past s_reserved2 * will not be available on the smallest blocksize. */ }; /* * Local allocation bitmap for OCFS2 slots * Note that it exists inside an ocfs2_dinode, so all offsets are * relative to the start of ocfs2_dinode.id2. */ struct ocfs2_local_alloc { /*00*/ __le32 la_bm_off; /* Starting bit offset in main bitmap */ __le16 la_size; /* Size of included bitmap, in bytes */ __le16 la_reserved1; __le64 la_reserved2; /*10*/ __u8 la_bitmap[0]; }; /* * Data-in-inode header. This is only used if i_dyn_features has * OCFS2_INLINE_DATA_FL set. */ struct ocfs2_inline_data { /*00*/ __le16 id_count; /* Number of bytes that can be used * for data, starting at id_data */ __le16 id_reserved0; __le32 id_reserved1; __u8 id_data[0]; /* Start of user data */ }; /* * On disk inode for OCFS2 */ struct ocfs2_dinode { /*00*/ __u8 i_signature[8]; /* Signature for validation */ __le32 i_generation; /* Generation number */ __le16 i_suballoc_slot; /* Slot suballocator this inode belongs to */ __le16 i_suballoc_bit; /* Bit offset in suballocator block group */ /*10*/ __le16 i_links_count_hi; /* High 16 bits of links count */ __le16 i_xattr_inline_size; __le32 i_clusters; /* Cluster count */ __le32 i_uid; /* Owner UID */ __le32 i_gid; /* Owning GID */ /*20*/ __le64 i_size; /* Size in bytes */ __le16 i_mode; /* File mode */ __le16 i_links_count; /* Links count */ __le32 i_flags; /* File flags */ /*30*/ __le64 i_atime; /* Access time */ __le64 i_ctime; /* Creation time */ /*40*/ __le64 i_mtime; /* Modification time */ __le64 i_dtime; /* Deletion time */ /*50*/ __le64 i_blkno; /* Offset on disk, in blocks */ __le64 i_last_eb_blk; /* Pointer to last extent block */ /*60*/ __le32 i_fs_generation; /* Generation per fs-instance */ __le32 i_atime_nsec; __le32 i_ctime_nsec; __le32 i_mtime_nsec; /*70*/ __le32 i_attr; __le16 i_orphaned_slot; /* Only valid when OCFS2_ORPHANED_FL was set in i_flags */ __le16 i_dyn_features; __le64 i_xattr_loc; /*80*/ struct ocfs2_block_check i_check; /* Error checking */ /*88*/ __le64 i_dx_root; /* Pointer to dir index root block */ /*90*/ __le64 i_refcount_loc; __le64 i_suballoc_loc; /* Suballocator block group this inode belongs to. Only valid if allocated from a discontiguous block group */ /*A0*/ __le16 i_dio_orphaned_slot; /* only used for append dio write */ __le16 i_reserved1[3]; __le64 i_reserved2[2]; /*B8*/ union { __le64 i_pad1; /* Generic way to refer to this 64bit union */ struct { __le64 i_rdev; /* Device number */ } dev1; struct { /* Info for bitmap system inodes */ __le32 i_used; /* Bits (ie, clusters) used */ __le32 i_total; /* Total bits (clusters) available */ } bitmap1; struct { /* Info for journal system inodes */ __le32 ij_flags; /* Mounted, version, etc. */ __le32 ij_recovery_generation; /* Incremented when the journal is recovered after an unclean shutdown */ } journal1; } id1; /* Inode type dependent 1 */ /*C0*/ union { struct ocfs2_super_block i_super; struct ocfs2_local_alloc i_lab; struct ocfs2_chain_list i_chain; struct ocfs2_extent_list i_list; struct ocfs2_truncate_log i_dealloc; struct ocfs2_inline_data i_data; __u8 i_symlink[0]; } id2; /* Actual on-disk size is one block */ }; /* * On-disk directory entry structure for OCFS2 * * Packed as this structure could be accessed unaligned on 64-bit platforms */ struct ocfs2_dir_entry { /*00*/ __le64 inode; /* Inode number */ __le16 rec_len; /* Directory entry length */ __u8 name_len; /* Name length */ __u8 file_type; /*0C*/ char name[OCFS2_MAX_FILENAME_LEN]; /* File name */ /* Actual on-disk length specified by rec_len */ } __attribute__ ((packed)); /* * Per-block record for the unindexed directory btree. This is carefully * crafted so that the rec_len and name_len records of an ocfs2_dir_entry are * mirrored. That way, the directory manipulation code needs a minimal amount * of update. * * NOTE: Keep this structure aligned to a multiple of 4 bytes. */ struct ocfs2_dir_block_trailer { /*00*/ __le64 db_compat_inode; /* Always zero. Was inode */ __le16 db_compat_rec_len; /* Backwards compatible with * ocfs2_dir_entry. */ __u8 db_compat_name_len; /* Always zero. Was name_len */ __u8 db_reserved0; __le16 db_reserved1; __le16 db_free_rec_len; /* Size of largest empty hole * in this block. (unused) */ /*10*/ __u8 db_signature[8]; /* Signature for verification */ __le64 db_reserved2; __le64 db_free_next; /* Next block in list (unused) */ /*20*/ __le64 db_blkno; /* Offset on disk, in blocks */ __le64 db_parent_dinode; /* dinode which owns me, in blocks */ /*30*/ struct ocfs2_block_check db_check; /* Error checking */ /*40*/ }; /* * A directory entry in the indexed tree. We don't store the full name here, * but instead provide a pointer to the full dirent in the unindexed tree. * * We also store name_len here so as to reduce the number of leaf blocks we * need to search in case of collisions. */ struct ocfs2_dx_entry { __le32 dx_major_hash; /* Used to find logical * cluster in index */ __le32 dx_minor_hash; /* Lower bits used to find * block in cluster */ __le64 dx_dirent_blk; /* Physical block in unindexed * tree holding this dirent. */ }; struct ocfs2_dx_entry_list { __le32 de_reserved; __le16 de_count; /* Maximum number of entries * possible in de_entries */ __le16 de_num_used; /* Current number of * de_entries entries */ struct ocfs2_dx_entry de_entries[0]; /* Indexed dir entries * in a packed array of * length de_num_used */ }; #define OCFS2_DX_FLAG_INLINE 0x01 /* * A directory indexing block. Each indexed directory has one of these, * pointed to by ocfs2_dinode. * * This block stores an indexed btree root, and a set of free space * start-of-list pointers. */ struct ocfs2_dx_root_block { __u8 dr_signature[8]; /* Signature for verification */ struct ocfs2_block_check dr_check; /* Error checking */ __le16 dr_suballoc_slot; /* Slot suballocator this * block belongs to. */ __le16 dr_suballoc_bit; /* Bit offset in suballocator * block group */ __le32 dr_fs_generation; /* Must match super block */ __le64 dr_blkno; /* Offset on disk, in blocks */ __le64 dr_last_eb_blk; /* Pointer to last * extent block */ __le32 dr_clusters; /* Clusters allocated * to the indexed tree. */ __u8 dr_flags; /* OCFS2_DX_FLAG_* flags */ __u8 dr_reserved0; __le16 dr_reserved1; __le64 dr_dir_blkno; /* Pointer to parent inode */ __le32 dr_num_entries; /* Total number of * names stored in * this directory.*/ __le32 dr_reserved2; __le64 dr_free_blk; /* Pointer to head of free * unindexed block list. */ __le64 dr_suballoc_loc; /* Suballocator block group this root belongs to. Only valid if allocated from a discontiguous block group */ __le64 dr_reserved3[14]; union { struct ocfs2_extent_list dr_list; /* Keep this aligned to 128 * bits for maximum space * efficiency. */ struct ocfs2_dx_entry_list dr_entries; /* In-root-block list of * entries. We grow out * to extents if this * gets too big. */ }; }; /* * The header of a leaf block in the indexed tree. */ struct ocfs2_dx_leaf { __u8 dl_signature[8];/* Signature for verification */ struct ocfs2_block_check dl_check; /* Error checking */ __le64 dl_blkno; /* Offset on disk, in blocks */ __le32 dl_fs_generation;/* Must match super block */ __le32 dl_reserved0; __le64 dl_reserved1; struct ocfs2_dx_entry_list dl_list; }; /* * Largest bitmap for a block (suballocator) group in bytes. This limit * does not affect cluster groups (global allocator). Cluster group * bitmaps run to the end of the block. */ #define OCFS2_MAX_BG_BITMAP_SIZE 256 /* * On disk allocator group structure for OCFS2 */ struct ocfs2_group_desc { /*00*/ __u8 bg_signature[8]; /* Signature for validation */ __le16 bg_size; /* Size of included bitmap in bytes. */ __le16 bg_bits; /* Bits represented by this group. */ __le16 bg_free_bits_count; /* Free bits count */ __le16 bg_chain; /* What chain I am in. */ /*10*/ __le32 bg_generation; __le32 bg_reserved1; __le64 bg_next_group; /* Next group in my list, in blocks */ /*20*/ __le64 bg_parent_dinode; /* dinode which owns me, in blocks */ __le64 bg_blkno; /* Offset on disk, in blocks */ /*30*/ struct ocfs2_block_check bg_check; /* Error checking */ __le64 bg_reserved2; /*40*/ union { __u8 bg_bitmap[0]; struct { /* * Block groups may be discontiguous when * OCFS2_FEATURE_INCOMPAT_DISCONTIG_BG is set. * The extents of a discontigous block group are * stored in bg_list. It is a flat list. * l_tree_depth must always be zero. A * discontiguous group is signified by a non-zero * bg_list->l_next_free_rec. Only block groups * can be discontiguous; Cluster groups cannot. * We've never made a block group with more than * 2048 blocks (256 bytes of bg_bitmap). This * codifies that limit so that we can fit bg_list. * bg_size of a discontiguous block group will * be 256 to match bg_bitmap_filler. */ __u8 bg_bitmap_filler[OCFS2_MAX_BG_BITMAP_SIZE]; /*140*/ struct ocfs2_extent_list bg_list; }; }; /* Actual on-disk size is one block */ }; struct ocfs2_refcount_rec { /*00*/ __le64 r_cpos; /* Physical offset, in clusters */ __le32 r_clusters; /* Clusters covered by this extent */ __le32 r_refcount; /* Reference count of this extent */ /*10*/ }; #define OCFS2_32BIT_POS_MASK (0xffffffffULL) #define OCFS2_REFCOUNT_LEAF_FL (0x00000001) #define OCFS2_REFCOUNT_TREE_FL (0x00000002) struct ocfs2_refcount_list { /*00*/ __le16 rl_count; /* Maximum number of entries possible in rl_records */ __le16 rl_used; /* Current number of used records */ __le32 rl_reserved2; __le64 rl_reserved1; /* Pad to sizeof(ocfs2_refcount_record) */ /*10*/ struct ocfs2_refcount_rec rl_recs[0]; /* Refcount records */ }; struct ocfs2_refcount_block { /*00*/ __u8 rf_signature[8]; /* Signature for verification */ __le16 rf_suballoc_slot; /* Slot suballocator this block belongs to */ __le16 rf_suballoc_bit; /* Bit offset in suballocator block group */ __le32 rf_fs_generation; /* Must match superblock */ /*10*/ __le64 rf_blkno; /* Offset on disk, in blocks */ __le64 rf_parent; /* Parent block, only valid if OCFS2_REFCOUNT_LEAF_FL is set in rf_flags */ /*20*/ struct ocfs2_block_check rf_check; /* Error checking */ __le64 rf_last_eb_blk; /* Pointer to last extent block */ /*30*/ __le32 rf_count; /* Number of inodes sharing this refcount tree */ __le32 rf_flags; /* See the flags above */ __le32 rf_clusters; /* clusters covered by refcount tree. */ __le32 rf_cpos; /* cluster offset in refcount tree.*/ /*40*/ __le32 rf_generation; /* generation number. all be the same * for the same refcount tree. */ __le32 rf_reserved0; __le64 rf_suballoc_loc; /* Suballocator block group this refcount block belongs to. Only valid if allocated from a discontiguous block group */ /*50*/ __le64 rf_reserved1[6]; /*80*/ union { struct ocfs2_refcount_list rf_records; /* List of refcount records */ struct ocfs2_extent_list rf_list; /* Extent record list, only valid if OCFS2_REFCOUNT_TREE_FL is set in rf_flags */ }; /* Actual on-disk size is one block */ }; /* * On disk extended attribute structure for OCFS2. */ /* * ocfs2_xattr_entry indicates one extend attribute. * * Note that it can be stored in inode, one block or one xattr bucket. */ struct ocfs2_xattr_entry { __le32 xe_name_hash; /* hash value of xattr prefix+suffix. */ __le16 xe_name_offset; /* byte offset from the 1st entry in the local xattr storage(inode, xattr block or xattr bucket). */ __u8 xe_name_len; /* xattr name len, doesn't include prefix. */ __u8 xe_type; /* the low 7 bits indicate the name prefix * type and the highest bit indicates whether * the EA is stored in the local storage. */ __le64 xe_value_size; /* real xattr value length. */ }; /* * On disk structure for xattr header. * * One ocfs2_xattr_header describes how many ocfs2_xattr_entry records in * the local xattr storage. */ struct ocfs2_xattr_header { __le16 xh_count; /* contains the count of how many records are in the local xattr storage. */ __le16 xh_free_start; /* current offset for storing xattr. */ __le16 xh_name_value_len; /* total length of name/value length in this bucket. */ __le16 xh_num_buckets; /* Number of xattr buckets in this extent record, only valid in the first bucket. */ struct ocfs2_block_check xh_check; /* Error checking (Note, this is only used for xattr buckets. A block uses xb_check and sets this field to zero.) */ struct ocfs2_xattr_entry xh_entries[0]; /* xattr entry list. */ }; /* * On disk structure for xattr value root. * * When an xattr's value is large enough, it is stored in an external * b-tree like file data. The xattr value root points to this structure. */ struct ocfs2_xattr_value_root { /*00*/ __le32 xr_clusters; /* clusters covered by xattr value. */ __le32 xr_reserved0; __le64 xr_last_eb_blk; /* Pointer to last extent block */ /*10*/ struct ocfs2_extent_list xr_list; /* Extent record list */ }; /* * On disk structure for xattr tree root. * * It is used when there are too many extended attributes for one file. These * attributes will be organized and stored in an indexed-btree. */ struct ocfs2_xattr_tree_root { /*00*/ __le32 xt_clusters; /* clusters covered by xattr. */ __le32 xt_reserved0; __le64 xt_last_eb_blk; /* Pointer to last extent block */ /*10*/ struct ocfs2_extent_list xt_list; /* Extent record list */ }; #define OCFS2_XATTR_INLINE_SIZE 80 #define OCFS2_XATTR_INDEXED 0x1 #define OCFS2_HASH_SHIFT 5 #define OCFS2_XATTR_ROUND 3 #define OCFS2_XATTR_SIZE(size) (((size) + OCFS2_XATTR_ROUND) & \ ~(OCFS2_XATTR_ROUND)) #define OCFS2_XATTR_BUCKET_SIZE 4096 #define OCFS2_XATTR_MAX_BLOCKS_PER_BUCKET (OCFS2_XATTR_BUCKET_SIZE \ / OCFS2_MIN_BLOCKSIZE) /* * On disk structure for xattr block. */ struct ocfs2_xattr_block { /*00*/ __u8 xb_signature[8]; /* Signature for verification */ __le16 xb_suballoc_slot; /* Slot suballocator this block belongs to. */ __le16 xb_suballoc_bit; /* Bit offset in suballocator block group */ __le32 xb_fs_generation; /* Must match super block */ /*10*/ __le64 xb_blkno; /* Offset on disk, in blocks */ struct ocfs2_block_check xb_check; /* Error checking */ /*20*/ __le16 xb_flags; /* Indicates whether this block contains real xattr or a xattr tree. */ __le16 xb_reserved0; __le32 xb_reserved1; __le64 xb_suballoc_loc; /* Suballocator block group this xattr block belongs to. Only valid if allocated from a discontiguous block group */ /*30*/ union { struct ocfs2_xattr_header xb_header; /* xattr header if this block contains xattr */ struct ocfs2_xattr_tree_root xb_root;/* xattr tree root if this block cotains xattr tree. */ } xb_attrs; }; #define OCFS2_XATTR_ENTRY_LOCAL 0x80 #define OCFS2_XATTR_TYPE_MASK 0x7F static inline void ocfs2_xattr_set_local(struct ocfs2_xattr_entry *xe, int local) { if (local) xe->xe_type |= OCFS2_XATTR_ENTRY_LOCAL; else xe->xe_type &= ~OCFS2_XATTR_ENTRY_LOCAL; } static inline int ocfs2_xattr_is_local(struct ocfs2_xattr_entry *xe) { return xe->xe_type & OCFS2_XATTR_ENTRY_LOCAL; } static inline void ocfs2_xattr_set_type(struct ocfs2_xattr_entry *xe, int type) { xe->xe_type |= type & OCFS2_XATTR_TYPE_MASK; } static inline int ocfs2_xattr_get_type(struct ocfs2_xattr_entry *xe) { return xe->xe_type & OCFS2_XATTR_TYPE_MASK; } /* * On disk structures for global quota file */ /* Magic numbers and known versions for global quota files */ #define OCFS2_GLOBAL_QMAGICS {\ 0x0cf52470, /* USRQUOTA */ \ 0x0cf52471 /* GRPQUOTA */ \ } #define OCFS2_GLOBAL_QVERSIONS {\ 0, \ 0, \ } /* Each block of each quota file has a certain fixed number of bytes reserved * for OCFS2 internal use at its end. OCFS2 can use it for things like * checksums, etc. */ #define OCFS2_QBLK_RESERVED_SPACE 8 /* Generic header of all quota files */ struct ocfs2_disk_dqheader { __le32 dqh_magic; /* Magic number identifying file */ __le32 dqh_version; /* Quota format version */ }; #define OCFS2_GLOBAL_INFO_OFF (sizeof(struct ocfs2_disk_dqheader)) /* Information header of global quota file (immediately follows the generic * header) */ struct ocfs2_global_disk_dqinfo { /*00*/ __le32 dqi_bgrace; /* Grace time for space softlimit excess */ __le32 dqi_igrace; /* Grace time for inode softlimit excess */ __le32 dqi_syncms; /* Time after which we sync local changes to * global quota file */ __le32 dqi_blocks; /* Number of blocks in quota file */ /*10*/ __le32 dqi_free_blk; /* First free block in quota file */ __le32 dqi_free_entry; /* First block with free dquot entry in quota * file */ }; /* Structure with global user / group information. We reserve some space * for future use. */ struct ocfs2_global_disk_dqblk { /*00*/ __le32 dqb_id; /* ID the structure belongs to */ __le32 dqb_use_count; /* Number of nodes having reference to this structure */ __le64 dqb_ihardlimit; /* absolute limit on allocated inodes */ /*10*/ __le64 dqb_isoftlimit; /* preferred inode limit */ __le64 dqb_curinodes; /* current # allocated inodes */ /*20*/ __le64 dqb_bhardlimit; /* absolute limit on disk space */ __le64 dqb_bsoftlimit; /* preferred limit on disk space */ /*30*/ __le64 dqb_curspace; /* current space occupied */ __le64 dqb_btime; /* time limit for excessive disk use */ /*40*/ __le64 dqb_itime; /* time limit for excessive inode use */ __le64 dqb_pad1; /*50*/ __le64 dqb_pad2; }; /* * On-disk structures for local quota file */ /* Magic numbers and known versions for local quota files */ #define OCFS2_LOCAL_QMAGICS {\ 0x0cf524c0, /* USRQUOTA */ \ 0x0cf524c1 /* GRPQUOTA */ \ } #define OCFS2_LOCAL_QVERSIONS {\ 0, \ 0, \ } /* Quota flags in dqinfo header */ #define OLQF_CLEAN 0x0001 /* Quota file is empty (this should be after\ * quota has been cleanly turned off) */ #define OCFS2_LOCAL_INFO_OFF (sizeof(struct ocfs2_disk_dqheader)) /* Information header of local quota file (immediately follows the generic * header) */ struct ocfs2_local_disk_dqinfo { __le32 dqi_flags; /* Flags for quota file */ __le32 dqi_chunks; /* Number of chunks of quota structures * with a bitmap */ __le32 dqi_blocks; /* Number of blocks allocated for quota file */ }; /* Header of one chunk of a quota file */ struct ocfs2_local_disk_chunk { __le32 dqc_free; /* Number of free entries in the bitmap */ __u8 dqc_bitmap[0]; /* Bitmap of entries in the corresponding * chunk of quota file */ }; /* One entry in local quota file */ struct ocfs2_local_disk_dqblk { /*00*/ __le64 dqb_id; /* id this quota applies to */ __le64 dqb_spacemod; /* Change in the amount of used space */ /*10*/ __le64 dqb_inodemod; /* Change in the amount of used inodes */ }; /* * The quota trailer lives at the end of each quota block. */ struct ocfs2_disk_dqtrailer { /*00*/ struct ocfs2_block_check dq_check; /* Error checking */ /*08*/ /* Cannot be larger than OCFS2_QBLK_RESERVED_SPACE */ }; static inline struct ocfs2_disk_dqtrailer *ocfs2_block_dqtrailer(int blocksize, void *buf) { char *ptr = buf; ptr += blocksize - OCFS2_QBLK_RESERVED_SPACE; return (struct ocfs2_disk_dqtrailer *)ptr; } #ifdef __KERNEL__ static inline int ocfs2_fast_symlink_chars(struct super_block *sb) { return sb->s_blocksize - offsetof(struct ocfs2_dinode, id2.i_symlink); } static inline int ocfs2_max_inline_data_with_xattr(struct super_block *sb, struct ocfs2_dinode *di) { unsigned int xattrsize = le16_to_cpu(di->i_xattr_inline_size); if (le16_to_cpu(di->i_dyn_features) & OCFS2_INLINE_XATTR_FL) return sb->s_blocksize - offsetof(struct ocfs2_dinode, id2.i_data.id_data) - xattrsize; else return sb->s_blocksize - offsetof(struct ocfs2_dinode, id2.i_data.id_data); } static inline int ocfs2_extent_recs_per_inode(struct super_block *sb) { int size; size = sb->s_blocksize - offsetof(struct ocfs2_dinode, id2.i_list.l_recs); return size / sizeof(struct ocfs2_extent_rec); } static inline int ocfs2_extent_recs_per_inode_with_xattr( struct super_block *sb, struct ocfs2_dinode *di) { int size; unsigned int xattrsize = le16_to_cpu(di->i_xattr_inline_size); if (le16_to_cpu(di->i_dyn_features) & OCFS2_INLINE_XATTR_FL) size = sb->s_blocksize - offsetof(struct ocfs2_dinode, id2.i_list.l_recs) - xattrsize; else size = sb->s_blocksize - offsetof(struct ocfs2_dinode, id2.i_list.l_recs); return size / sizeof(struct ocfs2_extent_rec); } static inline int ocfs2_extent_recs_per_dx_root(struct super_block *sb) { int size; size = sb->s_blocksize - offsetof(struct ocfs2_dx_root_block, dr_list.l_recs); return size / sizeof(struct ocfs2_extent_rec); } static inline int ocfs2_chain_recs_per_inode(struct super_block *sb) { int size; size = sb->s_blocksize - offsetof(struct ocfs2_dinode, id2.i_chain.cl_recs); return size / sizeof(struct ocfs2_chain_rec); } static inline u16 ocfs2_extent_recs_per_eb(struct super_block *sb) { int size; size = sb->s_blocksize - offsetof(struct ocfs2_extent_block, h_list.l_recs); return size / sizeof(struct ocfs2_extent_rec); } static inline u16 ocfs2_extent_recs_per_gd(struct super_block *sb) { int size; size = sb->s_blocksize - offsetof(struct ocfs2_group_desc, bg_list.l_recs); return size / sizeof(struct ocfs2_extent_rec); } static inline int ocfs2_dx_entries_per_leaf(struct super_block *sb) { int size; size = sb->s_blocksize - offsetof(struct ocfs2_dx_leaf, dl_list.de_entries); return size / sizeof(struct ocfs2_dx_entry); } static inline int ocfs2_dx_entries_per_root(struct super_block *sb) { int size; size = sb->s_blocksize - offsetof(struct ocfs2_dx_root_block, dr_entries.de_entries); return size / sizeof(struct ocfs2_dx_entry); } static inline u16 ocfs2_local_alloc_size(struct super_block *sb) { u16 size; size = sb->s_blocksize - offsetof(struct ocfs2_dinode, id2.i_lab.la_bitmap); return size; } static inline int ocfs2_group_bitmap_size(struct super_block *sb, int suballocator, u32 feature_incompat) { int size = sb->s_blocksize - offsetof(struct ocfs2_group_desc, bg_bitmap); /* * The cluster allocator uses the entire block. Suballocators have * never used more than OCFS2_MAX_BG_BITMAP_SIZE. Unfortunately, older * code expects bg_size set to the maximum. Thus we must keep * bg_size as-is unless discontig_bg is enabled. */ if (suballocator && (feature_incompat & OCFS2_FEATURE_INCOMPAT_DISCONTIG_BG)) size = OCFS2_MAX_BG_BITMAP_SIZE; return size; } static inline int ocfs2_truncate_recs_per_inode(struct super_block *sb) { int size; size = sb->s_blocksize - offsetof(struct ocfs2_dinode, id2.i_dealloc.tl_recs); return size / sizeof(struct ocfs2_truncate_rec); } static inline u64 ocfs2_backup_super_blkno(struct super_block *sb, int index) { u64 offset = OCFS2_BACKUP_SB_START; if (index >= 0 && index < OCFS2_MAX_BACKUP_SUPERBLOCKS) { offset <<= (2 * index); offset >>= sb->s_blocksize_bits; return offset; } return 0; } static inline u16 ocfs2_xattr_recs_per_xb(struct super_block *sb) { int size; size = sb->s_blocksize - offsetof(struct ocfs2_xattr_block, xb_attrs.xb_root.xt_list.l_recs); return size / sizeof(struct ocfs2_extent_rec); } static inline u16 ocfs2_extent_recs_per_rb(struct super_block *sb) { int size; size = sb->s_blocksize - offsetof(struct ocfs2_refcount_block, rf_list.l_recs); return size / sizeof(struct ocfs2_extent_rec); } static inline u16 ocfs2_refcount_recs_per_rb(struct super_block *sb) { int size; size = sb->s_blocksize - offsetof(struct ocfs2_refcount_block, rf_records.rl_recs); return size / sizeof(struct ocfs2_refcount_rec); } static inline u32 ocfs2_get_ref_rec_low_cpos(const struct ocfs2_refcount_rec *rec) { return le64_to_cpu(rec->r_cpos) & OCFS2_32BIT_POS_MASK; } #else static inline int ocfs2_fast_symlink_chars(int blocksize) { return blocksize - offsetof(struct ocfs2_dinode, id2.i_symlink); } static inline int ocfs2_max_inline_data_with_xattr(int blocksize, struct ocfs2_dinode *di) { if (di && (di->i_dyn_features & OCFS2_INLINE_XATTR_FL)) return blocksize - offsetof(struct ocfs2_dinode, id2.i_data.id_data) - di->i_xattr_inline_size; else return blocksize - offsetof(struct ocfs2_dinode, id2.i_data.id_data); } static inline int ocfs2_extent_recs_per_inode(int blocksize) { int size; size = blocksize - offsetof(struct ocfs2_dinode, id2.i_list.l_recs); return size / sizeof(struct ocfs2_extent_rec); } static inline int ocfs2_chain_recs_per_inode(int blocksize) { int size; size = blocksize - offsetof(struct ocfs2_dinode, id2.i_chain.cl_recs); return size / sizeof(struct ocfs2_chain_rec); } static inline int ocfs2_extent_recs_per_eb(int blocksize) { int size; size = blocksize - offsetof(struct ocfs2_extent_block, h_list.l_recs); return size / sizeof(struct ocfs2_extent_rec); } static inline int ocfs2_extent_recs_per_gd(int blocksize) { int size; size = blocksize - offsetof(struct ocfs2_group_desc, bg_list.l_recs); return size / sizeof(struct ocfs2_extent_rec); } static inline int ocfs2_dx_entries_per_leaf(int blocksize) { int size; size = blocksize - offsetof(struct ocfs2_dx_leaf, dl_list.de_entries); return size / sizeof(struct ocfs2_dx_entry); } static inline int ocfs2_dx_entries_per_root(int blocksize) { int size; size = blocksize - offsetof(struct ocfs2_dx_root_block, dr_entries.de_entries); return size / sizeof(struct ocfs2_dx_entry); } static inline int ocfs2_extent_recs_per_dx_root(int blocksize) { int size; size = blocksize - offsetof(struct ocfs2_dx_root_block, dr_list.l_recs); return size / sizeof(struct ocfs2_extent_rec); } static inline int ocfs2_local_alloc_size(int blocksize) { int size; size = blocksize - offsetof(struct ocfs2_dinode, id2.i_lab.la_bitmap); return size; } static inline int ocfs2_group_bitmap_size(int blocksize, int suballocator, uint32_t feature_incompat) { int size = blocksize - offsetof(struct ocfs2_group_desc, bg_bitmap); /* * The cluster allocator uses the entire block. Suballocators have * never used more than OCFS2_MAX_BG_BITMAP_SIZE. Unfortunately, older * code expects bg_size set to the maximum. Thus we must keep * bg_size as-is unless discontig_bg is enabled. */ if (suballocator && (feature_incompat & OCFS2_FEATURE_INCOMPAT_DISCONTIG_BG)) size = OCFS2_MAX_BG_BITMAP_SIZE; return size; } static inline int ocfs2_truncate_recs_per_inode(int blocksize) { int size; size = blocksize - offsetof(struct ocfs2_dinode, id2.i_dealloc.tl_recs); return size / sizeof(struct ocfs2_truncate_rec); } static inline uint64_t ocfs2_backup_super_blkno(int blocksize, int index) { uint64_t offset = OCFS2_BACKUP_SB_START; if (index >= 0 && index < OCFS2_MAX_BACKUP_SUPERBLOCKS) { offset <<= (2 * index); offset /= blocksize; return offset; } return 0; } static inline int ocfs2_xattr_recs_per_xb(int blocksize) { int size; size = blocksize - offsetof(struct ocfs2_xattr_block, xb_attrs.xb_root.xt_list.l_recs); return size / sizeof(struct ocfs2_extent_rec); } static inline int ocfs2_extent_recs_per_rb(int blocksize) { int size; size = blocksize - offsetof(struct ocfs2_refcount_block, rf_list.l_recs); return size / sizeof(struct ocfs2_extent_rec); } static inline int ocfs2_refcount_recs_per_rb(int blocksize) { int size; size = blocksize - offsetof(struct ocfs2_refcount_block, rf_records.rl_recs); return size / sizeof(struct ocfs2_refcount_rec); } static inline uint32_t ocfs2_get_ref_rec_low_cpos(const struct ocfs2_refcount_rec *rec) { return rec->r_cpos & OCFS2_32BIT_POS_MASK; } #endif /* __KERNEL__ */ static inline int ocfs2_system_inode_is_global(int type) { return ((type >= 0) && (type <= OCFS2_LAST_GLOBAL_SYSTEM_INODE)); } static inline int ocfs2_sprintf_system_inode_name(char *buf, int len, int type, int slot) { int chars; /* * Global system inodes can only have one copy. Everything * after OCFS2_LAST_GLOBAL_SYSTEM_INODE in the system inode * list has a copy per slot. */ if (type <= OCFS2_LAST_GLOBAL_SYSTEM_INODE) chars = snprintf(buf, len, "%s", ocfs2_system_inodes[type].si_name); else chars = snprintf(buf, len, ocfs2_system_inodes[type].si_name, slot); return chars; } static inline void ocfs2_set_de_type(struct ocfs2_dir_entry *de, unsigned short mode) { de->file_type = ocfs2_type_by_mode[(mode & S_IFMT)>>S_SHIFT]; } static inline int ocfs2_gd_is_discontig(struct ocfs2_group_desc *gd) { if ((offsetof(struct ocfs2_group_desc, bg_bitmap) + gd->bg_size) != offsetof(struct ocfs2_group_desc, bg_list)) return 0; /* * Only valid to check l_next_free_rec if * bg_bitmap + bg_size == bg_list */ if (!gd->bg_list.l_next_free_rec) return 0; return 1; } #endif /* _OCFS2_FS_H */ ocfs2-tools-ocfs2-tools-1.8.6/include/ocfs2-kernel/ocfs2_ioctl.h000066400000000000000000000156611347147137200243660ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * ocfs2_ioctl.h * * Defines OCFS2 ioctls. * * Copyright (C) 2010 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License, version 2, as published by the Free Software Foundation. * * 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. */ #ifndef OCFS2_IOCTL_H #define OCFS2_IOCTL_H /* * ioctl commands */ #define OCFS2_IOC_GETFLAGS _IOR('f', 1, long) #define OCFS2_IOC_SETFLAGS _IOW('f', 2, long) #define OCFS2_IOC32_GETFLAGS _IOR('f', 1, int) #define OCFS2_IOC32_SETFLAGS _IOW('f', 2, int) /* * Space reservation / allocation / free ioctls and argument structure * are designed to be compatible with XFS. * * ALLOCSP* and FREESP* are not and will never be supported, but are * included here for completeness. */ struct ocfs2_space_resv { __s16 l_type; __s16 l_whence; __s64 l_start; __s64 l_len; /* len == 0 means until end of file */ __s32 l_sysid; __u32 l_pid; __s32 l_pad[4]; /* reserve area */ }; #define OCFS2_IOC_ALLOCSP _IOW ('X', 10, struct ocfs2_space_resv) #define OCFS2_IOC_FREESP _IOW ('X', 11, struct ocfs2_space_resv) #define OCFS2_IOC_RESVSP _IOW ('X', 40, struct ocfs2_space_resv) #define OCFS2_IOC_UNRESVSP _IOW ('X', 41, struct ocfs2_space_resv) #define OCFS2_IOC_ALLOCSP64 _IOW ('X', 36, struct ocfs2_space_resv) #define OCFS2_IOC_FREESP64 _IOW ('X', 37, struct ocfs2_space_resv) #define OCFS2_IOC_RESVSP64 _IOW ('X', 42, struct ocfs2_space_resv) #define OCFS2_IOC_UNRESVSP64 _IOW ('X', 43, struct ocfs2_space_resv) /* Used to pass group descriptor data when online resize is done */ struct ocfs2_new_group_input { __u64 group; /* Group descriptor's blkno. */ __u32 clusters; /* Total number of clusters in this group */ __u32 frees; /* Total free clusters in this group */ __u16 chain; /* Chain for this group */ __u16 reserved1; __u32 reserved2; }; #define OCFS2_IOC_GROUP_EXTEND _IOW('o', 1, int) #define OCFS2_IOC_GROUP_ADD _IOW('o', 2,struct ocfs2_new_group_input) #define OCFS2_IOC_GROUP_ADD64 _IOW('o', 3,struct ocfs2_new_group_input) /* Used to pass 2 file names to reflink. */ struct reflink_arguments { __u64 old_path; __u64 new_path; __u64 preserve; }; #define OCFS2_IOC_REFLINK _IOW('o', 4, struct reflink_arguments) /* Following definitions dedicated for ocfs2_info_request ioctls. */ #define OCFS2_INFO_MAX_REQUEST (50) #define OCFS2_TEXT_UUID_LEN (OCFS2_VOL_UUID_LEN * 2) /* Magic number of all requests */ #define OCFS2_INFO_MAGIC (0x4F32494E) /* * Always try to separate info request into small pieces to * guarantee the backward&forward compatibility. */ struct ocfs2_info { __u64 oi_requests; /* Array of __u64 pointers to requests */ __u32 oi_count; /* Number of requests in info_requests */ __u32 oi_pad; }; struct ocfs2_info_request { /*00*/ __u32 ir_magic; /* Magic number */ __u32 ir_code; /* Info request code */ __u32 ir_size; /* Size of request */ __u32 ir_flags; /* Request flags */ /*10*/ /* Request specific fields */ }; struct ocfs2_info_clustersize { struct ocfs2_info_request ic_req; __u32 ic_clustersize; __u32 ic_pad; }; struct ocfs2_info_blocksize { struct ocfs2_info_request ib_req; __u32 ib_blocksize; __u32 ib_pad; }; struct ocfs2_info_maxslots { struct ocfs2_info_request im_req; __u32 im_max_slots; __u32 im_pad; }; struct ocfs2_info_label { struct ocfs2_info_request il_req; __u8 il_label[OCFS2_MAX_VOL_LABEL_LEN]; } __attribute__ ((packed)); struct ocfs2_info_uuid { struct ocfs2_info_request iu_req; __u8 iu_uuid_str[OCFS2_TEXT_UUID_LEN + 1]; } __attribute__ ((packed)); struct ocfs2_info_fs_features { struct ocfs2_info_request if_req; __u32 if_compat_features; __u32 if_incompat_features; __u32 if_ro_compat_features; __u32 if_pad; }; struct ocfs2_info_journal_size { struct ocfs2_info_request ij_req; __u64 ij_journal_size; }; struct ocfs2_info_freeinode { struct ocfs2_info_request ifi_req; struct ocfs2_info_local_freeinode { __u64 lfi_total; __u64 lfi_free; } ifi_stat[OCFS2_MAX_SLOTS]; __u32 ifi_slotnum; /* out */ __u32 ifi_pad; }; #define OCFS2_INFO_MAX_HIST (32) struct ocfs2_info_freefrag { struct ocfs2_info_request iff_req; struct ocfs2_info_freefrag_stats { /* (out) */ struct ocfs2_info_free_chunk_list { __u32 fc_chunks[OCFS2_INFO_MAX_HIST]; __u32 fc_clusters[OCFS2_INFO_MAX_HIST]; } ffs_fc_hist; __u32 ffs_clusters; __u32 ffs_free_clusters; __u32 ffs_free_chunks; __u32 ffs_free_chunks_real; __u32 ffs_min; /* Minimum free chunksize in clusters */ __u32 ffs_max; __u32 ffs_avg; __u32 ffs_pad; } iff_ffs; __u32 iff_chunksize; /* chunksize in clusters(in) */ __u32 iff_pad; }; /* Codes for ocfs2_info_request */ enum ocfs2_info_type { OCFS2_INFO_CLUSTERSIZE = 1, OCFS2_INFO_BLOCKSIZE, OCFS2_INFO_MAXSLOTS, OCFS2_INFO_LABEL, OCFS2_INFO_UUID, OCFS2_INFO_FS_FEATURES, OCFS2_INFO_JOURNAL_SIZE, OCFS2_INFO_FREEINODE, OCFS2_INFO_FREEFRAG, OCFS2_INFO_NUM_TYPES }; /* Flags for struct ocfs2_info_request */ /* Filled by the caller */ #define OCFS2_INFO_FL_NON_COHERENT (0x00000001) /* Cluster coherency not required. This is a hint. It is up to ocfs2 whether the request can be fulfilled without locking. */ /* Filled by ocfs2 */ #define OCFS2_INFO_FL_FILLED (0x40000000) /* Filesystem understood this request and filled in the answer */ #define OCFS2_INFO_FL_ERROR (0x80000000) /* Error happened during request handling. */ #define OCFS2_IOC_INFO _IOR('o', 5, struct ocfs2_info) struct ocfs2_move_extents { /* All values are in bytes */ /* in */ __u64 me_start; /* Virtual start in the file to move */ __u64 me_len; /* Length of the extents to be moved */ __u64 me_goal; /* Physical offset of the goal, it's in block unit */ __u64 me_threshold; /* Maximum distance from goal or threshold for auto defragmentation */ __u64 me_flags; /* Flags for the operation: * - auto defragmentation. * - refcount,xattr cases. */ /* out */ __u64 me_moved_len; /* Moved/defraged length */ __u64 me_new_offset; /* Resulting physical location */ __u32 me_reserved[2]; /* Reserved for futhure */ }; #define OCFS2_MOVE_EXT_FL_AUTO_DEFRAG (0x00000001) /* Kernel manages to claim new clusters as the goal place for extents moving */ #define OCFS2_MOVE_EXT_FL_PART_DEFRAG (0x00000002) /* Allow partial extent moving, is to make movement less likely to fail, may make fs even more fragmented */ #define OCFS2_MOVE_EXT_FL_COMPLETE (0x00000004) /* Move or defragmenation completely gets done. */ #define OCFS2_IOC_MOVE_EXT _IOW('o', 6, struct ocfs2_move_extents) #endif /* OCFS2_IOCTL_H */ ocfs2-tools-ocfs2-tools-1.8.6/include/ocfs2-kernel/ocfs2_lockid.h000066400000000000000000000063241347147137200245150ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * ocfs2_lockid.h * * Defines OCFS2 lockid bits. * * Copyright (C) 2002, 2005 Oracle. All rights reserved. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. */ #ifndef OCFS2_LOCKID_H #define OCFS2_LOCKID_H /* lock ids are made up in the following manner: * name[0] --> type * name[1-6] --> 6 pad characters, reserved for now * name[7-22] --> block number, expressed in hex as 16 chars * name[23-30] --> i_generation, expressed in hex 8 chars * name[31] --> '\0' */ #define OCFS2_LOCK_ID_MAX_LEN 32 #define OCFS2_LOCK_ID_PAD "000000" #define OCFS2_DENTRY_LOCK_INO_START 18 enum ocfs2_lock_type { OCFS2_LOCK_TYPE_META = 0, OCFS2_LOCK_TYPE_DATA, OCFS2_LOCK_TYPE_SUPER, OCFS2_LOCK_TYPE_RENAME, OCFS2_LOCK_TYPE_RW, OCFS2_LOCK_TYPE_DENTRY, OCFS2_LOCK_TYPE_OPEN, OCFS2_LOCK_TYPE_FLOCK, OCFS2_LOCK_TYPE_QINFO, OCFS2_LOCK_TYPE_NFS_SYNC, OCFS2_LOCK_TYPE_ORPHAN_SCAN, OCFS2_LOCK_TYPE_REFCOUNT, OCFS2_NUM_LOCK_TYPES }; static inline char ocfs2_lock_type_char(enum ocfs2_lock_type type) { char c; switch (type) { case OCFS2_LOCK_TYPE_META: c = 'M'; break; case OCFS2_LOCK_TYPE_DATA: c = 'D'; break; case OCFS2_LOCK_TYPE_SUPER: c = 'S'; break; case OCFS2_LOCK_TYPE_RENAME: c = 'R'; break; case OCFS2_LOCK_TYPE_RW: c = 'W'; break; case OCFS2_LOCK_TYPE_DENTRY: c = 'N'; break; case OCFS2_LOCK_TYPE_OPEN: c = 'O'; break; case OCFS2_LOCK_TYPE_FLOCK: c = 'F'; break; case OCFS2_LOCK_TYPE_QINFO: c = 'Q'; break; case OCFS2_LOCK_TYPE_NFS_SYNC: c = 'Y'; break; case OCFS2_LOCK_TYPE_ORPHAN_SCAN: c = 'P'; break; case OCFS2_LOCK_TYPE_REFCOUNT: c = 'T'; break; default: c = '\0'; } return c; } static char *ocfs2_lock_type_strings[] = { [OCFS2_LOCK_TYPE_META] = "Meta", [OCFS2_LOCK_TYPE_DATA] = "Data", [OCFS2_LOCK_TYPE_SUPER] = "Super", [OCFS2_LOCK_TYPE_RENAME] = "Rename", /* Need to differntiate from [R]ename.. serializing writes is the * important job it does, anyway. */ [OCFS2_LOCK_TYPE_RW] = "Write/Read", [OCFS2_LOCK_TYPE_DENTRY] = "Dentry", [OCFS2_LOCK_TYPE_OPEN] = "Open", [OCFS2_LOCK_TYPE_FLOCK] = "Flock", [OCFS2_LOCK_TYPE_QINFO] = "Quota", [OCFS2_LOCK_TYPE_NFS_SYNC] = "NFSSync", [OCFS2_LOCK_TYPE_ORPHAN_SCAN] = "OrphanScan", [OCFS2_LOCK_TYPE_REFCOUNT] = "Refcount", }; static inline const char *ocfs2_lock_type_string(enum ocfs2_lock_type type) { #ifdef __KERNEL__ BUG_ON(type >= OCFS2_NUM_LOCK_TYPES); #endif return ocfs2_lock_type_strings[type]; } #endif /* OCFS2_LOCKID_H */ ocfs2-tools-ocfs2-tools-1.8.6/include/ocfs2-kernel/quota_tree.h000066400000000000000000000005671347147137200243270ustar00rootroot00000000000000#ifndef _QUOTA_TREE_H #define _QUOTA_TREE_H #define QT_TREEOFF 1 /* Header of leaf tree block */ struct qt_disk_dqdbheader { __le32 dqdh_next_free; /* Number of next block with free entry */ __le32 dqdh_prev_free; /* Number of previous block with free entry */ __le16 dqdh_entries; /* Number of valid entries in block */ __le16 dqdh_pad1; __le32 dqdh_pad2; }; #endif ocfs2-tools-ocfs2-tools-1.8.6/include/ocfs2-kernel/sparse_endian_types.h000066400000000000000000000004161347147137200262070ustar00rootroot00000000000000#ifndef O2CB_SPARSE_ENDIAN_TYPES_H #define O2CB_SPARSE_ENDIAN_TYPES_H #include typedef __u16 __le16; typedef __u16 __be16; typedef __u32 __le32; typedef __u32 __be32; typedef __u64 __le64; typedef __u64 __be64; #endif /* O2CB_SPARSE_ENDIAN_TYPES_H */ ocfs2-tools-ocfs2-tools-1.8.6/include/ocfs2/000077500000000000000000000000001347147137200205205ustar00rootroot00000000000000ocfs2-tools-ocfs2-tools-1.8.6/include/ocfs2/.gitignore000066400000000000000000000000521347147137200225050ustar00rootroot00000000000000cscope* stamp-md5 *.sw? *.cmd ocfs2_err.h ocfs2-tools-ocfs2-tools-1.8.6/include/ocfs2/Makefile000066400000000000000000000007201347147137200221570ustar00rootroot00000000000000TOPDIR = ../.. include $(TOPDIR)/Preamble.make HFILES_GEN = ocfs2_err.h all: $(HFILES_GEN) HFILES = ocfs2.h jbd2.h bitops.h byteorder.h kernel-rbtree.h image.h HEADERS_SUBDIR = ocfs2 HEADERS = $(HFILES) $(HFILES_GEN) ocfs2_err.h: $(TOPDIR)/libocfs2/ocfs2_err.h cp $< $@ $(TOPDIR)/libocfs2/ocfs2_err.h: make -C $(TOPDIR)/libocfs2 ocfs2_err.h DIST_FILES = $(HFILES) CLEAN_RULES = clean-err clean-err: rm -f ocfs2_err.h include $(TOPDIR)/Postamble.make ocfs2-tools-ocfs2-tools-1.8.6/include/ocfs2/bitops.h000066400000000000000000000030041347147137200221660ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * bitops.h * * Bitmap frobbing routines for the OCFS2 userspace library. * * Copyright (C) 2004 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License, version 2, as published by the Free Software Foundation. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * * Authors: Joel Becker * * This code is a port of e2fsprogs/lib/ext2fs/bitops.h * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o. */ #ifndef _BITOPS_H #define _BITOPS_H extern int ocfs2_set_bit(int nr,void * addr); extern int ocfs2_clear_bit(int nr, void * addr); extern int ocfs2_test_bit(int nr, const void * addr); extern int ocfs2_find_first_bit_set(void *addr, int size); extern int ocfs2_find_first_bit_clear(void *addr, int size); extern int ocfs2_find_next_bit_set(void *addr, int size, int offset); extern int ocfs2_find_next_bit_clear(void *addr, int size, int offset); extern int ocfs2_get_bits_set(void *addr, int size, int offset); #endif ocfs2-tools-ocfs2-tools-1.8.6/include/ocfs2/byteorder.h000066400000000000000000000061211347147137200226700ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * byteorder.h * * Byteswapping! * * Copyright (C) 2004 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License, version 2, as published by the Free Software Foundation. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * * Authors: Joel Becker */ #ifndef _BYTEORDER_H #define _BYTEORDER_H #include #include #include /* * All OCFS2 on-disk values are in little endian, except for jbd's journal * fields which it takes care of itself. */ #if __BYTE_ORDER == __LITTLE_ENDIAN #define cpu_is_little_endian 1 # ifndef cpu_to_le16 # define cpu_to_le16(x) ((uint16_t)(x)) # endif # ifndef le16_to_cpu # define le16_to_cpu(x) ((uint16_t)(x)) # endif # ifndef cpu_to_le32 # define cpu_to_le32(x) ((uint32_t)(x)) # endif # ifndef le32_to_cpu # define le32_to_cpu(x) ((uint32_t)(x)) # endif # ifndef cpu_to_le64 # define cpu_to_le64(x) ((uint64_t)(x)) # endif # ifndef le64_to_cpu # define le64_to_cpu(x) ((uint64_t)(x)) # endif # ifndef cpu_to_be16 # define cpu_to_be16(x) ((uint16_t)bswap_16(x)) # endif # ifndef be16_to_cpu # define be16_to_cpu(x) ((uint16_t)bswap_16(x)) # endif # ifndef cpu_to_be32 # define cpu_to_be32(x) ((uint32_t)bswap_32(x)) # endif # ifndef be32_to_cpu # define be32_to_cpu(x) ((uint32_t)bswap_32(x)) # endif # ifndef cpu_to_be64 # define cpu_to_be64(x) ((uint64_t)bswap_64(x)) # endif # ifndef be64_to_cpu # define be64_to_cpu(x) ((uint64_t)bswap_64(x)) # endif #elif __BYTE_ORDER == __BIG_ENDIAN #define cpu_is_little_endian 0 # ifndef cpu_to_le16 # define cpu_to_le16(x) ((uint16_t)bswap_16(x)) # endif # ifndef le16_to_cpu # define le16_to_cpu(x) ((uint16_t)bswap_16(x)) # endif # ifndef cpu_to_le32 # define cpu_to_le32(x) ((uint32_t)bswap_32(x)) # endif # ifndef le32_to_cpu # define le32_to_cpu(x) ((uint32_t)bswap_32(x)) # endif # ifndef cpu_to_le64 # define cpu_to_le64(x) ((uint64_t)bswap_64(x)) # endif # ifndef le64_to_cpu # define le64_to_cpu(x) ((uint64_t)bswap_64(x)) # endif # ifndef cpu_to_be16 # define cpu_to_be16(x) ((uint16_t)(x)) # endif # ifndef be16_to_cpu # define be16_to_cpu(x) ((uint16_t)(x)) # endif # ifndef cpu_to_be32 # define cpu_to_be32(x) ((uint32_t)(x)) # endif # ifndef be32_to_cpu # define be32_to_cpu(x) ((uint32_t)(x)) # endif # ifndef cpu_to_be64 # define cpu_to_be64(x) ((uint64_t)(x)) # endif # ifndef be64_to_cpu # define be64_to_cpu(x) ((uint64_t)(x)) # endif #else # error Invalid byte order __BYTE_ORDER #endif /* __BYTE_ORDER */ #define cpu_is_big_endian (!cpu_is_little_endian) #endif /* _BYTEORDER_H */ ocfs2-tools-ocfs2-tools-1.8.6/include/ocfs2/image.h000066400000000000000000000103611347147137200217540ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * image.h * * Header file describing image disk/memory structures for ocfs2 image * * Copyright (C) 2008 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License, version 2, as published by the Free Software Foundation. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. */ /* * o2image is an ocfs2 tool to save critical ocfs2 filesystem metadata to a * specified image-file. Image-file may be examined using debugfs.ocfs2 or * may be used to restore using o2image. Image-file can be of two formats: * * 1. Packed - This format(default) contains a ocfs2 image header, packed * metadata blocks and a bitmap. * 2. raw - A raw image is a sparse file containing the metadata blocks. * * Usage: o2image [-rI] * * Packed format contains bitmap towards the end of the image-file. Each bit in * the bitmap represents a block in the filesystem. * * When the packed image is opened using o2image or debugfs.ocfs2, bitmap is * loaded into memory and used to map disk blocks to image blocks. * * Raw image is a sparse file containing metadata blocks at the same offset as * the filesystem. * * debugfs.ocfs2 is modified to detect image-file when the image-file is * specified with -i option. */ #define OCFS2_IMAGE_MAGIC 0x72a3d45f #define OCFS2_IMAGE_DESC "OCFS2 IMAGE" #define OCFS2_IMAGE_VERSION 1 #define OCFS2_IMAGE_READ_CHAIN_NO 0 #define OCFS2_IMAGE_READ_INODE_NO 1 #define OCFS2_IMAGE_READ_INODE_YES 2 #define OCFS2_IMAGE_BITMAP_BLOCKSIZE 4096 #define OCFS2_IMAGE_BITS_IN_BLOCK (OCFS2_IMAGE_BITMAP_BLOCKSIZE * 8) /* on disk ocfs2 image header format */ struct ocfs2_image_hdr { __le32 hdr_magic; __le32 hdr_timestamp; /* Time of image creation */ __u8 hdr_magic_desc[16]; /* "OCFS2 IMAGE" */ __le64 hdr_version; /* ocfs2 image version */ __le64 hdr_fsblkcnt; /* blocks in filesystem */ __le64 hdr_fsblksz; /* Filesystem block size */ __le64 hdr_imgblkcnt; /* Filesystem blocks in image */ __le64 hdr_bmpblksz; /* bitmap block size */ __le64 hdr_superblkcnt; /* number of super blocks */ __le64 hdr_superblocks[OCFS2_MAX_BACKUP_SUPERBLOCKS]; }; /* * array to hold pointers to bitmap blocks. arr_set_bit_cnt holds cumulative * count of bits used previous to the current block. arr_self will be pointing * to the memory chunks allocated. arr_map will be pointing to bitmap blocks * of size OCFS2_IMAGE_BITMAP_BLOCKSIZE. Each block maps to * OCFS2_IMAGE_BITS_IN_BLOCK number of filesystem blocks. */ struct _ocfs2_image_bitmap_arr { uint64_t arr_set_bit_cnt; char *arr_self; char *arr_map; }; typedef struct _ocfs2_image_bitmap_arr ocfs2_image_bitmap_arr; /* ocfs2 image state holds runtime values */ struct ocfs2_image_state { uint64_t ost_fsblksz; uint64_t ost_fsblkcnt; uint64_t ost_imgblkcnt; /* filesystem blocks in image */ uint64_t ost_glbl_bitmap_inode; uint64_t ost_glbl_inode_alloc; uint64_t *ost_inode_allocs; /* holds inode_alloc inodes */ uint64_t ost_bmpblks; /* blocks that store bitmaps */ uint64_t ost_bmpblksz; /* size of each bitmap blk */ uint64_t ost_superblocks[OCFS2_MAX_BACKUP_SUPERBLOCKS]; int ost_glbl_inode_traversed; int ost_bpc; /* blocks per cluster */ int ost_superblkcnt; /* number of super blocks */ ocfs2_image_bitmap_arr *ost_bmparr; /* points to bitmap blocks */ }; errcode_t ocfs2_image_load_bitmap(ocfs2_filesys *ofs); void ocfs2_image_free_bitmap(ocfs2_filesys *ofs); errcode_t ocfs2_image_alloc_bitmap(ocfs2_filesys *ofs); void ocfs2_image_mark_bitmap(ocfs2_filesys *ofs, uint64_t blkno); int ocfs2_image_test_bit(ocfs2_filesys *ofs, uint64_t blkno); uint64_t ocfs2_image_get_blockno(ocfs2_filesys *ofs, uint64_t blkno); void ocfs2_image_swap_header(struct ocfs2_image_hdr *hdr); ocfs2-tools-ocfs2-tools-1.8.6/include/ocfs2/jbd2.h000066400000000000000000000120531347147137200215130ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * jbd2.h * * header file extracted from linux/include/linux/jbd2.h * * Originally written by Stephen C. Tweedie * * Copyright 1998-2000 Red Hat, Inc --- All Rights Reserved * * This file is part of the Linux kernel and is made available under * the terms of the GNU General Public License version 2 as published by * the Free Software Foundation. * * Definitions for transaction data structures for the buffer cache * filesystem journaling support. */ #ifndef _JBD2_H_ #define _JBD2_H_ /* * Internal structures used by the logging mechanism: */ #define JBD2_MAGIC_NUMBER 0xc03b3998U /* The first 4 bytes of /dev/random! */ /* * On-disk structures */ /* * Descriptor block types: */ #define JBD2_DESCRIPTOR_BLOCK 1 #define JBD2_COMMIT_BLOCK 2 #define JBD2_SUPERBLOCK_V1 3 #define JBD2_SUPERBLOCK_V2 4 #define JBD2_REVOKE_BLOCK 5 /* * Standard header for all descriptor blocks: */ typedef struct journal_header_s { __be32 h_magic; __be32 h_blocktype; __be32 h_sequence; } journal_header_t; /* * Checksum types. */ #define JBD2_CRC32_CHKSUM 1 #define JBD2_MD5_CHKSUM 2 #define JBD2_SHA2_CHKSUM 3 #define JBD2_CRC32_CHKSUM_SIZE 4 #define JBD2_CHECKSUM_BYTES (32 / sizeof(uint32_t)) /* * Commit block header for storing transactional checksums: */ struct commit_header { __be32 h_magic; __be32 h_blocktype; __be32 h_sequence; unsigned char h_chksum_type; unsigned char h_chksum_size; unsigned char h_padding[2]; __be32 h_chksum[JBD2_CHECKSUM_BYTES]; __be64 h_commit_sec; __be32 h_commit_nsec; }; /* * The block tag: used to describe a single buffer in the journal. * t_blocknr_high is only used if INCOMPAT_64BIT is set, so this * raw struct shouldn't be used for pointer math or sizeof() - use * journal_tag_bytes(journal) instead to compute this. */ typedef struct journal_block_tag_s { __be32 t_blocknr; /* The on-disk block number */ __be32 t_flags; /* See below */ __be32 t_blocknr_high; /* most-significant high 32bits. */ } journal_block_tag_t; #define JBD2_TAG_SIZE32 (offsetof(journal_block_tag_t, t_blocknr_high)) #define JBD2_TAG_SIZE64 (sizeof(journal_block_tag_t)) /* * The revoke descriptor: used on disk to describe a series of blocks to * be revoked from the log */ typedef struct journal_revoke_header_s { journal_header_t r_header; int r_count; /* Count of bytes used in the block */ } journal_revoke_header_t; /* Definitions for the journal tag flags word: */ #define JBD2_FLAG_ESCAPE 1 /* on-disk block is escaped */ #define JBD2_FLAG_SAME_UUID 2 /* block has same uuid as previous */ #define JBD2_FLAG_DELETED 4 /* block deleted by this transaction */ #define JBD2_FLAG_LAST_TAG 8 /* last tag in this descriptor block */ /* * The journal superblock. All fields are in big-endian byte order. */ typedef struct journal_superblock_s { /* 0x0000 */ journal_header_t s_header; /* 0x000C */ /* Static information describing the journal */ __be32 s_blocksize; /* journal device blocksize */ __be32 s_maxlen; /* total blocks in journal file */ __be32 s_first; /* first block of log information */ /* 0x0018 */ /* Dynamic information describing the current state of the log */ __be32 s_sequence; /* first commit ID expected in log */ __be32 s_start; /* blocknr of start of log */ /* 0x0020 */ /* Error value, as set by jbd2_journal_abort(). */ __be32 s_errno; /* 0x0024 */ /* Remaining fields are only valid in a version-2 superblock */ __be32 s_feature_compat; /* compatible feature set */ __be32 s_feature_incompat; /* incompatible feature set */ __be32 s_feature_ro_compat; /* readonly-compatible feature set */ /* 0x0030 */ __u8 s_uuid[16]; /* 128-bit uuid for journal */ /* 0x0040 */ __be32 s_nr_users; /* Nr of filesystems sharing log */ __be32 s_dynsuper; /* Blocknr of dynamic superblock copy*/ /* 0x0048 */ __be32 s_max_transaction; /* Limit of journal blocks per trans.*/ __be32 s_max_trans_data; /* Limit of data blocks per trans. */ /* 0x0050 */ __be32 s_padding[44]; /* 0x0100 */ __u8 s_users[16*48]; /* ids of all fs'es sharing the log */ /* 0x0400 */ } journal_superblock_t; #define JBD2_HAS_COMPAT_FEATURE(jsb,mask) \ (((jsb)->s_header.h_blocktype == JBD2_SUPERBLOCK_V2) && \ ((jsb)->s_feature_compat & mask)) #define JBD2_HAS_RO_COMPAT_FEATURE(jsb,mask) \ (((jsb)->s_header.h_blocktype == JBD2_SUPERBLOCK_V2) && \ ((jsb)->s_feature_ro_compat & mask)) #define JBD2_HAS_INCOMPAT_FEATURE(jsb,mask) \ (((jsb)->s_header.h_blocktype == JBD2_SUPERBLOCK_V2) && \ ((jsb)->s_feature_incompat & mask)) #define JBD2_FEATURE_COMPAT_CHECKSUM 0x00000001 #define JBD2_FEATURE_INCOMPAT_REVOKE 0x00000001 #define JBD2_FEATURE_INCOMPAT_64BIT 0x00000002 #define JBD2_FEATURE_INCOMPAT_ASYNC_COMMIT 0x00000004 /* Features known to this kernel version: */ #define JBD2_KNOWN_COMPAT_FEATURES 0 #define JBD2_KNOWN_ROCOMPAT_FEATURES 0 #define JBD2_KNOWN_INCOMPAT_FEATURES (JBD2_FEATURE_INCOMPAT_REVOKE \ | JBD2_FEATURE_INCOMPAT_64BIT) #endif /* _JBD2_H_ */ ocfs2-tools-ocfs2-tools-1.8.6/include/ocfs2/kernel-rbtree.h000066400000000000000000000077011347147137200234370ustar00rootroot00000000000000/* Red Black Trees (C) 1999 Andrea Arcangeli 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA rbtree.h To use rbtrees you'll have to implement your own insert and search cores. This will avoid us to use callbacks and to drop drammatically performances. I know it's not the cleaner way, but in C (not in C++) to get performances and genericity... Some example of insert and search follows here. The search is a plain normal search over an ordered tree. The insert instead must be implemented int two steps: as first thing the code must insert the element in order as a red leaf in the tree, then the support library function rb_insert_color() must be called. Such function will do the not trivial work to rebalance the rbtree if necessary. ----------------------------------------------------------------------- static inline struct page * rb_search_page_cache(struct inode * inode, unsigned long offset) { struct rb_node * n = inode->i_rb_page_cache.rb_node; struct page * page; while (n) { page = rb_entry(n, struct page, rb_page_cache); if (offset < page->offset) n = n->rb_left; else if (offset > page->offset) n = n->rb_right; else return page; } return NULL; } static inline struct page * __rb_insert_page_cache(struct inode * inode, unsigned long offset, struct rb_node * node) { struct rb_node ** p = &inode->i_rb_page_cache.rb_node; struct rb_node * parent = NULL; struct page * page; while (*p) { parent = *p; page = rb_entry(parent, struct page, rb_page_cache); if (offset < page->offset) p = &(*p)->rb_left; else if (offset > page->offset) p = &(*p)->rb_right; else return page; } rb_link_node(node, parent, p); return NULL; } static inline struct page * rb_insert_page_cache(struct inode * inode, unsigned long offset, struct rb_node * node) { struct page * ret; if ((ret = __rb_insert_page_cache(inode, offset, node))) goto out; rb_insert_color(node, &inode->i_rb_page_cache); out: return ret; } ----------------------------------------------------------------------- */ #ifndef _LINUX_RBTREE_H #define _LINUX_RBTREE_H #include struct rb_node { struct rb_node *rb_parent; int rb_color; #define RB_RED 0 #define RB_BLACK 1 struct rb_node *rb_right; struct rb_node *rb_left; }; struct rb_root { struct rb_node *rb_node; }; #define RB_ROOT (struct rb_root) { NULL, } #define rb_entry(ptr, type, member) \ ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member))) extern void rb_insert_color(struct rb_node *, struct rb_root *); extern void rb_erase(struct rb_node *, struct rb_root *); /* Find logical next and previous nodes in a tree */ extern struct rb_node *rb_next(struct rb_node *); extern struct rb_node *rb_prev(struct rb_node *); extern struct rb_node *rb_first(struct rb_root *); extern struct rb_node *rb_last(struct rb_root *); /* Fast replacement of a single node without remove/rebalance/add/rebalance */ extern void rb_replace_node(struct rb_node *victim, struct rb_node *new, struct rb_root *root); static inline void rb_link_node(struct rb_node * node, struct rb_node * parent, struct rb_node ** rb_link) { node->rb_parent = parent; node->rb_color = RB_RED; node->rb_left = node->rb_right = NULL; *rb_link = node; } #endif /* _LINUX_RBTREE_H */ ocfs2-tools-ocfs2-tools-1.8.6/include/ocfs2/ocfs2.h000066400000000000000000001614751347147137200217230ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * ocfs2.h * * Filesystem object routines for the OCFS2 userspace library. * * Copyright (C) 2004 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License, version 2, as published by the Free Software Foundation. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * * Authors: Joel Becker */ #ifndef _FILESYS_H #define _FILESYS_H #ifndef _XOPEN_SOURCE # define _XOPEN_SOURCE 600 #endif #ifndef _LARGEFILE64_SOURCE # define _LARGEFILE64_SOURCE #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define OCFS2_LIB_FEATURE_INCOMPAT_SUPP (OCFS2_FEATURE_INCOMPAT_SUPP | \ OCFS2_FEATURE_INCOMPAT_HEARTBEAT_DEV | \ OCFS2_FEATURE_INCOMPAT_RESIZE_INPROG | \ OCFS2_FEATURE_INCOMPAT_LOCAL_MOUNT | \ OCFS2_FEATURE_INCOMPAT_INLINE_DATA | \ OCFS2_FEATURE_INCOMPAT_TUNEFS_INPROG) #define OCFS2_LIB_FEATURE_RO_COMPAT_SUPP OCFS2_FEATURE_RO_COMPAT_SUPP #define OCFS2_LIB_FEATURE_COMPAT_SUPP OCFS2_FEATURE_COMPAT_SUPP #define OCFS2_LIB_ABORTED_TUNEFS_SUPP OCFS2_TUNEFS_INPROG_REMOVE_SLOT /* define OCFS2_SB for ocfs2-tools */ #define OCFS2_SB(sb) (sb) /* Flags for the ocfs2_filesys structure */ #define OCFS2_FLAG_RO 0x00 #define OCFS2_FLAG_RW 0x01 #define OCFS2_FLAG_CHANGED 0x02 #define OCFS2_FLAG_DIRTY 0x04 #define OCFS2_FLAG_SWAP_BYTES 0x08 #define OCFS2_FLAG_BUFFERED 0x10 #define OCFS2_FLAG_NO_REV_CHECK 0x20 /* Do not check the OCFS vol_header structure for revision info */ #define OCFS2_FLAG_HEARTBEAT_DEV_OK 0x40 #define OCFS2_FLAG_STRICT_COMPAT_CHECK 0x80 #define OCFS2_FLAG_IMAGE_FILE 0x0100 #define OCFS2_FLAG_NO_ECC_CHECKS 0x0200 /* Do not validate metaecc * information on block * reads. */ #define OCFS2_FLAG_HARD_RO 0x0400 /* Return flags for the directory iterator functions */ #define OCFS2_DIRENT_CHANGED 0x01 #define OCFS2_DIRENT_ABORT 0x02 #define OCFS2_DIRENT_ERROR 0x04 /* Directory iterator flags */ #define OCFS2_DIRENT_FLAG_INCLUDE_EMPTY 0x01 #define OCFS2_DIRENT_FLAG_INCLUDE_REMOVED 0x02 #define OCFS2_DIRENT_FLAG_EXCLUDE_DOTS 0x04 #define OCFS2_DIRENT_FLAG_INCLUDE_TRAILER 0x08 /* Return flags for the chain iterator functions */ #define OCFS2_CHAIN_CHANGED 0x01 #define OCFS2_CHAIN_ABORT 0x02 #define OCFS2_CHAIN_ERROR 0x04 /* Directory constants */ #define OCFS2_DIRENT_DOT_FILE 1 #define OCFS2_DIRENT_DOT_DOT_FILE 2 #define OCFS2_DIRENT_OTHER_FILE 3 #define OCFS2_DIRENT_DELETED_FILE 4 /* Directory scan flags */ #define OCFS2_DIR_SCAN_FLAG_EXCLUDE_DOTS 0x01 /* Check if mounted flags */ #define OCFS2_MF_MOUNTED 1 #define OCFS2_MF_ISROOT 2 #define OCFS2_MF_READONLY 4 #define OCFS2_MF_SWAP 8 #define OCFS2_MF_BUSY 16 #define OCFS2_MF_MOUNTED_CLUSTER 32 /* check_heartbeats progress states */ #define OCFS2_CHB_START 1 #define OCFS2_CHB_WAITING 2 #define OCFS2_CHB_COMPLETE 3 /* Flags for global quotafile info */ #define OCFS2_QF_INFO_DIRTY 1 #define OCFS2_QF_INFO_LOADED 2 typedef void (*ocfs2_chb_notify)(int state, char *progress, void *data); typedef struct _ocfs2_filesys ocfs2_filesys; typedef struct _ocfs2_cached_inode ocfs2_cached_inode; typedef struct _ocfs2_cached_dquot ocfs2_cached_dquot; typedef struct _io_channel io_channel; typedef struct _ocfs2_inode_scan ocfs2_inode_scan; typedef struct _ocfs2_dir_scan ocfs2_dir_scan; typedef struct _ocfs2_bitmap ocfs2_bitmap; typedef struct _ocfs2_devices ocfs2_devices; enum ocfs2_block_type { OCFS2_BLOCK_UNKNOWN, OCFS2_BLOCK_INODE, OCFS2_BLOCK_SUPERBLOCK, OCFS2_BLOCK_EXTENT_BLOCK, OCFS2_BLOCK_GROUP_DESCRIPTOR, OCFS2_BLOCK_DIR_BLOCK, OCFS2_BLOCK_XATTR, OCFS2_BLOCK_REFCOUNT, OCFS2_BLOCK_DXROOT, OCFS2_BLOCK_DXLEAF, }; #define MAXQUOTAS 2 #define USRQUOTA 0 #define GRPQUOTA 1 #define OCFS2_DEF_BLOCK_GRACE 604800 /* 1 week */ #define OCFS2_DEF_INODE_GRACE 604800 /* 1 week */ #define OCFS2_DEF_QUOTA_SYNC 10000 /* 10 seconds */ struct _ocfs2_quota_info { ocfs2_cached_inode *qi_inode; int flags; struct ocfs2_global_disk_dqinfo qi_info; }; typedef struct _ocfs2_quota_info ocfs2_quota_info; struct _ocfs2_filesys { char *fs_devname; uint32_t fs_flags; io_channel *fs_io; struct ocfs2_dinode *fs_super; struct ocfs2_dinode *fs_orig_super; unsigned int fs_blocksize; unsigned int fs_clustersize; uint32_t fs_clusters; uint64_t fs_blocks; uint32_t fs_umask; uint64_t fs_root_blkno; uint64_t fs_sysdir_blkno; uint64_t fs_first_cg_blkno; char uuid_str[OCFS2_VOL_UUID_LEN * 2 + 1]; /* Allocators */ ocfs2_cached_inode *fs_cluster_alloc; ocfs2_cached_inode **fs_inode_allocs; ocfs2_cached_inode *fs_system_inode_alloc; ocfs2_cached_inode **fs_eb_allocs; ocfs2_cached_inode *fs_system_eb_alloc; struct o2dlm_ctxt *fs_dlm_ctxt; struct ocfs2_image_state *ost; ocfs2_quota_info qinfo[MAXQUOTAS]; /* Reserved for the use of the calling application. */ void *fs_private; }; struct _ocfs2_cached_inode { struct _ocfs2_filesys *ci_fs; uint64_t ci_blkno; struct ocfs2_dinode *ci_inode; ocfs2_bitmap *ci_chains; }; typedef unsigned int qid_t; struct _ocfs2_cached_dquot { loff_t d_off; /* Offset of structure in the file */ struct _ocfs2_cached_dquot *d_next; /* Next entry in hashchain */ struct _ocfs2_cached_dquot **d_pprev; /* Previous pointer in hashchain */ struct ocfs2_global_disk_dqblk d_ddquot; /* Quota entry */ }; struct ocfs2_slot_data { int sd_valid; unsigned int sd_node_num; }; struct ocfs2_slot_map_data { int md_num_slots; struct ocfs2_slot_data *md_slots; }; struct _ocfs2_devices { struct list_head list; char dev_name[PATH_MAX]; uint8_t label[64]; uint8_t uuid[16]; int mount_flags; int fs_type; /* 0=unknown, 1=ocfs, 2=ocfs2 */ int hb_dev; char stack[OCFS2_STACK_LABEL_LEN + 1]; /* Local, O2CB, CMAN, PCMK */ char cluster[OCFS2_CLUSTER_NAME_LEN + 1]; unsigned char stackflags; uint32_t maj_num; /* major number of the device */ uint32_t min_num; /* minor number of the device */ errcode_t errcode; /* error encountered reading device */ void *private; struct ocfs2_slot_map_data *map; /* Mounted nodes, must be freed */ }; typedef struct _ocfs2_fs_options ocfs2_fs_options; struct _ocfs2_fs_options { uint32_t opt_compat; uint32_t opt_incompat; uint32_t opt_ro_compat; }; enum ocfs2_mkfs_types { OCFS2_MKFSTYPE_DEFAULT, OCFS2_MKFSTYPE_DATAFILES, OCFS2_MKFSTYPE_MAIL, OCFS2_MKFSTYPE_VMSTORE, }; struct _ocfs2_quota_hash { int alloc_entries; int used_entries; ocfs2_cached_dquot **hash; }; struct ocfs2_dx_hinfo { uint32_t major_hash; uint32_t minor_hash; }; struct ocfs2_dir_lookup_result { struct ocfs2_dx_hinfo dl_hinfo; /* name hash results */ char * dl_leaf; /* unindexed block buffer */ uint64_t dl_leaf_blkno; /* blk number of dl_leaf */ struct ocfs2_dir_entry * dl_entry; /* dirent pointed into dl_leaf */ struct ocfs2_dx_leaf * dl_dx_leaf; /* indexed block buffer */ uint64_t dl_dx_leaf_blkno; /* blk number of dl_dx_leaf */ struct ocfs2_dx_entry * dl_dx_entry; /* indexed entry pointed to dl_dx_leaf */ int dl_dx_entry_idx; /* index of dl_dx_entry in entries list */ }; typedef struct _ocfs2_quota_hash ocfs2_quota_hash; errcode_t ocfs2_malloc(unsigned long size, void *ptr); errcode_t ocfs2_malloc0(unsigned long size, void *ptr); errcode_t ocfs2_free(void *ptr); errcode_t ocfs2_realloc(unsigned long size, void *ptr); errcode_t ocfs2_realloc0(unsigned long size, void *ptr, unsigned long old_size); errcode_t ocfs2_malloc_blocks(io_channel *channel, int num_blocks, void *ptr); errcode_t ocfs2_malloc_block(io_channel *channel, void *ptr); int io_is_device_readonly(io_channel *channel); errcode_t io_open(const char *name, int flags, io_channel **channel); errcode_t io_close(io_channel *channel); int io_get_error(io_channel *channel); errcode_t io_set_blksize(io_channel *channel, int blksize); int io_get_blksize(io_channel *channel); int io_get_fd(io_channel *channel); struct ocfs2_io_stats { uint64_t is_bytes_read; uint64_t is_bytes_written; uint32_t is_cache_hits; uint32_t is_cache_misses; uint32_t is_cache_inserts; uint32_t is_cache_removes; }; void io_get_stats(io_channel *channel, struct ocfs2_io_stats *stats); /* * Raw I/O functions. They will use the I/O cache if available. The * _nocache version will not add a block to the cache, but if the block is * already in the cache it will be moved to the end of the LRU and kept * in a good state. * * Use ocfs2_read_blocks() if your application might handle o2image file. * * If a channel is set to 'nocache' via io_set_nocache(), it will use * the _nocache() functions even if called via the regular functions. * This allows control of naive code that we don't want to have to carry * nocache parameters around. Smarter code can ignore this function and * use the _nocache() functions directly. */ errcode_t io_read_block(io_channel *channel, int64_t blkno, int count, char *data); errcode_t io_read_block_nocache(io_channel *channel, int64_t blkno, int count, char *data); errcode_t io_write_block(io_channel *channel, int64_t blkno, int count, const char *data); errcode_t io_write_block_nocache(io_channel *channel, int64_t blkno, int count, const char *data); errcode_t io_init_cache(io_channel *channel, size_t nr_blocks); void io_set_nocache(io_channel *channel, bool nocache); errcode_t io_init_cache_size(io_channel *channel, size_t bytes); size_t io_get_cache_size(io_channel *channel); errcode_t io_share_cache(io_channel *from, io_channel *to); errcode_t io_mlock_cache(io_channel *channel); void io_destroy_cache(io_channel *channel); struct io_vec_unit { uint64_t ivu_blkno; char *ivu_buf; uint32_t ivu_buflen; }; errcode_t io_vec_read_blocks(io_channel *channel, struct io_vec_unit *ivus, int count); errcode_t ocfs2_read_super(ocfs2_filesys *fs, uint64_t superblock, char *sb); /* Writes the main superblock at OCFS2_SUPER_BLOCK_BLKNO */ errcode_t ocfs2_write_primary_super(ocfs2_filesys *fs); /* Writes the primary and backups if enabled */ errcode_t ocfs2_write_super(ocfs2_filesys *fs); /* * ocfs2_read_blocks() is a wraper around io_read_block. If device is an * image file it translates disk offset to image offset. * ocfs2_read_blocks_nocache() calls io_read_block_nocache(). */ errcode_t ocfs2_read_blocks(ocfs2_filesys *fs, int64_t blkno, int count, char *data); errcode_t ocfs2_read_blocks_nocache(ocfs2_filesys *fs, int64_t blkno, int count, char *data); int ocfs2_is_hard_readonly(ocfs2_filesys *fs); int ocfs2_mount_local(ocfs2_filesys *fs); errcode_t ocfs2_open(const char *name, int flags, unsigned int superblock, unsigned int blksize, ocfs2_filesys **ret_fs); errcode_t ocfs2_flush(ocfs2_filesys *fs); errcode_t ocfs2_close(ocfs2_filesys *fs); void ocfs2_freefs(ocfs2_filesys *fs); void ocfs2_swap_inode_from_cpu(ocfs2_filesys *fs, struct ocfs2_dinode *di); void ocfs2_swap_inode_to_cpu(ocfs2_filesys *fs, struct ocfs2_dinode *di); errcode_t ocfs2_read_inode(ocfs2_filesys *fs, uint64_t blkno, char *inode_buf); errcode_t ocfs2_write_inode(ocfs2_filesys *fs, uint64_t blkno, char *inode_buf); errcode_t ocfs2_write_inode_without_meta_ecc(ocfs2_filesys *fs, uint64_t blkno, char *inode_buf); errcode_t ocfs2_check_directory(ocfs2_filesys *fs, uint64_t dir); int ocfs2_check_dir_entry(ocfs2_filesys *fs, struct ocfs2_dir_entry *de, char *dir_buf, unsigned int offset); errcode_t ocfs2_read_cached_inode(ocfs2_filesys *fs, uint64_t blkno, ocfs2_cached_inode **ret_ci); errcode_t ocfs2_write_cached_inode(ocfs2_filesys *fs, ocfs2_cached_inode *cinode); errcode_t ocfs2_free_cached_inode(ocfs2_filesys *fs, ocfs2_cached_inode *cinode); errcode_t ocfs2_refresh_cached_inode(ocfs2_filesys *fs, ocfs2_cached_inode *cinode); /* * obj is the object containing the extent list. eg, if you are swapping * an inode's extent list, you're passing 'di' for the obj and * '&di->id2.i_list' for the el. obj is needed to make sure the * byte swapping code doesn't walk off the end of the buffer in the * presence of corruption. */ void ocfs2_swap_extent_list_from_cpu(ocfs2_filesys *fs, void *obj, struct ocfs2_extent_list *el); void ocfs2_swap_extent_list_to_cpu(ocfs2_filesys *fs, void *obj, struct ocfs2_extent_list *el); errcode_t ocfs2_extent_map_get_blocks(ocfs2_cached_inode *cinode, uint64_t v_blkno, int count, uint64_t *p_blkno, uint64_t *ret_count, uint16_t *extent_flags); errcode_t ocfs2_get_clusters(ocfs2_cached_inode *cinode, uint32_t v_cluster, uint32_t *p_cluster, uint32_t *num_clusters, uint16_t *extent_flags); errcode_t ocfs2_xattr_get_clusters(ocfs2_filesys *fs, struct ocfs2_extent_list *el, uint64_t el_blkno, char *el_blk, uint32_t v_cluster, uint32_t *p_cluster, uint32_t *num_clusters, uint16_t *extent_flags); int ocfs2_find_leaf(ocfs2_filesys *fs, struct ocfs2_dinode *di, uint32_t cpos, char **leaf_buf); int ocfs2_search_extent_list(struct ocfs2_extent_list *el, uint32_t v_cluster); void ocfs2_swap_journal_superblock(journal_superblock_t *jsb); errcode_t ocfs2_init_journal_superblock(ocfs2_filesys *fs, char *buf, int buflen, uint32_t jrnl_size); errcode_t ocfs2_read_journal_superblock(ocfs2_filesys *fs, uint64_t blkno, char *jsb_buf); errcode_t ocfs2_write_journal_superblock(ocfs2_filesys *fs, uint64_t blkno, char *jsb_buf); errcode_t ocfs2_make_journal(ocfs2_filesys *fs, uint64_t blkno, uint32_t clusters, ocfs2_fs_options *features); errcode_t ocfs2_journal_clear_features(journal_superblock_t *jsb, ocfs2_fs_options *features); errcode_t ocfs2_journal_set_features(journal_superblock_t *jsb, ocfs2_fs_options *features); extern size_t ocfs2_journal_tag_bytes(journal_superblock_t *jsb); extern uint64_t ocfs2_journal_tag_block(journal_block_tag_t *tag, size_t tag_bytes); void ocfs2_swap_extent_block_to_cpu(ocfs2_filesys *fs, struct ocfs2_extent_block *eb); void ocfs2_swap_extent_block_from_cpu(ocfs2_filesys *fs, struct ocfs2_extent_block *eb); errcode_t ocfs2_read_extent_block(ocfs2_filesys *fs, uint64_t blkno, char *eb_buf); errcode_t ocfs2_read_extent_block_nocheck(ocfs2_filesys *fs, uint64_t blkno, char *eb_buf); errcode_t ocfs2_write_extent_block(ocfs2_filesys *fs, uint64_t blkno, char *eb_buf); void ocfs2_swap_refcount_list_to_cpu(ocfs2_filesys *fs, void *obj, struct ocfs2_refcount_list *rl); void ocfs2_swap_refcount_list_from_cpu(ocfs2_filesys *fs, void *obj, struct ocfs2_refcount_list *rl); void ocfs2_swap_refcount_block_to_cpu(ocfs2_filesys *fs, struct ocfs2_refcount_block *rb); void ocfs2_swap_refcount_block_from_cpu(ocfs2_filesys *fs, struct ocfs2_refcount_block *rb); errcode_t ocfs2_read_refcount_block(ocfs2_filesys *fs, uint64_t blkno, char *eb_buf); errcode_t ocfs2_read_refcount_block_nocheck(ocfs2_filesys *fs, uint64_t blkno, char *eb_buf); errcode_t ocfs2_write_refcount_block(ocfs2_filesys *fs, uint64_t blkno, char *rb_buf); errcode_t ocfs2_delete_refcount_block(ocfs2_filesys *fs, uint64_t blkno); errcode_t ocfs2_new_refcount_block(ocfs2_filesys *fs, uint64_t *blkno, uint64_t root_blkno, uint32_t rf_generation); errcode_t ocfs2_increase_refcount(ocfs2_filesys *fs, uint64_t ino, uint64_t cpos, uint32_t len); errcode_t ocfs2_decrease_refcount(ocfs2_filesys *fs, uint64_t ino, uint32_t cpos, uint32_t len, int delete); errcode_t ocfs2_refcount_cow(ocfs2_cached_inode *cinode, uint32_t cpos, uint32_t write_len, uint32_t max_cpos); errcode_t ocfs2_refcount_cow_xattr(ocfs2_cached_inode *ci, char *xe_buf, uint64_t xe_blkno, char *value_buf, uint64_t value_blkno, struct ocfs2_xattr_value_root *xv, uint32_t cpos, uint32_t write_len); errcode_t ocfs2_change_refcount_flag(ocfs2_filesys *fs, uint64_t i_blkno, uint32_t v_cpos, uint32_t clusters, uint64_t p_cpos, int new_flags, int clear_flags); errcode_t ocfs2_refcount_tree_get_rec(ocfs2_filesys *fs, struct ocfs2_refcount_block *rb, uint32_t phys_cpos, uint64_t *p_blkno, uint32_t *e_cpos, uint32_t *num_clusters); errcode_t ocfs2_refcount_punch_hole(ocfs2_filesys *fs, uint64_t rf_blkno, uint64_t p_start, uint32_t len); errcode_t ocfs2_change_refcount(ocfs2_filesys *fs, uint64_t rf_blkno, uint64_t p_start, uint32_t len, uint32_t refcount); int ocfs2_get_refcount_rec(ocfs2_filesys *fs, char *ref_root_buf, uint64_t cpos, unsigned int len, struct ocfs2_refcount_rec *ret_rec, int *index, char *ret_buf); errcode_t ocfs2_create_refcount_tree(ocfs2_filesys *fs, uint64_t *refcount_loc); errcode_t ocfs2_attach_refcount_tree(ocfs2_filesys *fs, uint64_t ino, uint64_t refcount_loc); errcode_t ocfs2_detach_refcount_tree(ocfs2_filesys *fs, uint64_t ino, uint64_t refcount_loc); errcode_t ocfs2_swap_dir_entries_from_cpu(void *buf, uint64_t bytes); errcode_t ocfs2_swap_dir_entries_to_cpu(void *buf, uint64_t bytes); void ocfs2_swap_dir_trailer(struct ocfs2_dir_block_trailer *trailer); errcode_t ocfs2_read_dir_block(ocfs2_filesys *fs, struct ocfs2_dinode *di, uint64_t block, void *buf); errcode_t ocfs2_write_dir_block(ocfs2_filesys *fs, struct ocfs2_dinode *di, uint64_t block, void *buf); unsigned int ocfs2_dir_trailer_blk_off(ocfs2_filesys *fs); struct ocfs2_dir_block_trailer *ocfs2_dir_trailer_from_block(ocfs2_filesys *fs, void *data); int ocfs2_supports_dir_trailer(ocfs2_filesys *fs); int ocfs2_dir_has_trailer(ocfs2_filesys *fs, struct ocfs2_dinode *di); int ocfs2_skip_dir_trailer(ocfs2_filesys *fs, struct ocfs2_dinode *di, struct ocfs2_dir_entry *de, unsigned long offset); void ocfs2_init_dir_trailer(ocfs2_filesys *fs, struct ocfs2_dinode *di, uint64_t blkno, void *buf); void ocfs2_swap_dx_root_to_cpu(ocfs2_filesys *fs, struct ocfs2_dx_root_block *dx_root); void ocfs2_swap_dx_leaf_to_cpu(struct ocfs2_dx_leaf *dx_leaf); void ocfs2_swap_dx_root_from_cpu(ocfs2_filesys *fs, struct ocfs2_dx_root_block *dx_root); errcode_t ocfs2_read_dx_root(ocfs2_filesys *fs, uint64_t block, void *buf); void ocfs2_swap_dx_leaf_from_cpu(struct ocfs2_dx_leaf *dx_leaf); errcode_t ocfs2_read_dx_leaf(ocfs2_filesys *fs, uint64_t block, void *buf); int ocfs2_dir_indexed(struct ocfs2_dinode *di); errcode_t ocfs2_dx_dir_truncate(ocfs2_filesys *fs, uint64_t dir); errcode_t ocfs2_dir_iterate2(ocfs2_filesys *fs, uint64_t dir, int flags, char *block_buf, int (*func)(uint64_t dir, int entry, struct ocfs2_dir_entry *dirent, uint64_t blocknr, int offset, int blocksize, char *buf, void *priv_data), void *priv_data); extern errcode_t ocfs2_dir_iterate(ocfs2_filesys *fs, uint64_t dir, int flags, char *block_buf, int (*func)(struct ocfs2_dir_entry *dirent, uint64_t blocknr, int offset, int blocksize, char *buf, void *priv_data), void *priv_data); extern errcode_t ocfs2_dx_entries_iterate(ocfs2_filesys *fs, struct ocfs2_dinode *dir, int flags, int (*func)(ocfs2_filesys *fs, struct ocfs2_dx_entry_list *entry_list, struct ocfs2_dx_root_block *dx_root, struct ocfs2_dx_leaf *dx_leaf, void *priv_data), void *priv_data); extern errcode_t ocfs2_dx_frees_iterate(ocfs2_filesys *fs, struct ocfs2_dinode *dir, struct ocfs2_dx_root_block *dx_root, int flags, int (*func)(ocfs2_filesys *fs, uint64_t blkno, struct ocfs2_dir_block_trailer *trailer, char *dirblock, void *priv_data), void *priv_data); errcode_t ocfs2_lookup(ocfs2_filesys *fs, uint64_t dir, const char *name, int namelen, char *buf, uint64_t *inode); errcode_t ocfs2_lookup_system_inode(ocfs2_filesys *fs, int type, int slot_num, uint64_t *blkno); errcode_t ocfs2_link(ocfs2_filesys *fs, uint64_t dir, const char *name, uint64_t ino, int flags); errcode_t ocfs2_unlink(ocfs2_filesys *fs, uint64_t dir, const char *name, uint64_t ino, int flags); errcode_t ocfs2_open_inode_scan(ocfs2_filesys *fs, ocfs2_inode_scan **ret_scan); void ocfs2_close_inode_scan(ocfs2_inode_scan *scan); errcode_t ocfs2_get_next_inode(ocfs2_inode_scan *scan, uint64_t *blkno, char *inode); uint64_t ocfs2_get_max_inode_count(ocfs2_inode_scan *scan); errcode_t ocfs2_open_dir_scan(ocfs2_filesys *fs, uint64_t dir, int flags, ocfs2_dir_scan **ret_scan); void ocfs2_close_dir_scan(ocfs2_dir_scan *scan); errcode_t ocfs2_get_next_dir_entry(ocfs2_dir_scan *scan, struct ocfs2_dir_entry *dirent); errcode_t ocfs2_cluster_bitmap_new(ocfs2_filesys *fs, const char *description, ocfs2_bitmap **ret_bitmap); errcode_t ocfs2_block_bitmap_new(ocfs2_filesys *fs, const char *description, ocfs2_bitmap **ret_bitmap); void ocfs2_bitmap_free(ocfs2_bitmap **bitmap); errcode_t ocfs2_bitmap_set(ocfs2_bitmap *bitmap, uint64_t bitno, int *oldval); errcode_t ocfs2_bitmap_clear(ocfs2_bitmap *bitmap, uint64_t bitno, int *oldval); errcode_t ocfs2_bitmap_test(ocfs2_bitmap *bitmap, uint64_t bitno, int *val); errcode_t ocfs2_bitmap_find_next_set(ocfs2_bitmap *bitmap, uint64_t start, uint64_t *found); errcode_t ocfs2_bitmap_find_next_clear(ocfs2_bitmap *bitmap, uint64_t start, uint64_t *found); errcode_t ocfs2_bitmap_read(ocfs2_bitmap *bitmap); errcode_t ocfs2_bitmap_write(ocfs2_bitmap *bitmap); uint64_t ocfs2_bitmap_get_set_bits(ocfs2_bitmap *bitmap); errcode_t ocfs2_bitmap_alloc_range(ocfs2_bitmap *bitmap, uint64_t min, uint64_t len, uint64_t *first_bit, uint64_t *bits_found); errcode_t ocfs2_bitmap_clear_range(ocfs2_bitmap *bitmap, uint64_t len, uint64_t first_bit); errcode_t ocfs2_get_device_size(const char *file, int blocksize, uint64_t *retblocks); errcode_t ocfs2_get_device_sectsize(const char *file, int *sectsize); errcode_t ocfs2_check_if_mounted(const char *file, int *mount_flags); errcode_t ocfs2_check_mount_point(const char *device, int *mount_flags, char *mtpt, int mtlen); errcode_t ocfs2_read_whole_file(ocfs2_filesys *fs, uint64_t blkno, char **buf, int *len); void ocfs2_swap_disk_heartbeat_block(struct o2hb_disk_heartbeat_block *hb); errcode_t ocfs2_check_heartbeat(char *device, int *mount_flags, struct list_head *nodes_list); errcode_t ocfs2_check_heartbeats(struct list_head *dev_list, int ignore_local); errcode_t ocfs2_get_ocfs1_label(char *device, uint8_t *label, uint16_t label_len, uint8_t *uuid, uint16_t uuid_len); void ocfs2_swap_group_desc_from_cpu(ocfs2_filesys *fs, struct ocfs2_group_desc *gd); void ocfs2_swap_group_desc_to_cpu(ocfs2_filesys *fs, struct ocfs2_group_desc *gd); errcode_t ocfs2_read_group_desc(ocfs2_filesys *fs, uint64_t blkno, char *gd_buf); errcode_t ocfs2_write_group_desc(ocfs2_filesys *fs, uint64_t blkno, char *gd_buf); uint64_t ocfs2_get_block_from_group(ocfs2_filesys *fs, struct ocfs2_group_desc *grp, int bpc, int bit_offset); errcode_t ocfs2_cache_chain_allocator_blocks(ocfs2_filesys *fs, struct ocfs2_dinode *di); errcode_t ocfs2_chain_iterate(ocfs2_filesys *fs, uint64_t blkno, int (*func)(ocfs2_filesys *fs, uint64_t gd_blkno, int chain_num, void *priv_data), void *priv_data); errcode_t ocfs2_load_chain_allocator(ocfs2_filesys *fs, ocfs2_cached_inode *cinode); errcode_t ocfs2_write_chain_allocator(ocfs2_filesys *fs, ocfs2_cached_inode *cinode); errcode_t ocfs2_chain_alloc(ocfs2_filesys *fs, ocfs2_cached_inode *cinode, uint64_t *gd_blkno, uint16_t *suballoc_bit, uint64_t *bitno); errcode_t ocfs2_chain_free(ocfs2_filesys *fs, ocfs2_cached_inode *cinode, uint64_t bitno); errcode_t ocfs2_chain_alloc_range(ocfs2_filesys *fs, ocfs2_cached_inode *cinode, uint64_t min, uint64_t requested, uint64_t *start_bit, uint64_t *bits_found); errcode_t ocfs2_chain_free_range(ocfs2_filesys *fs, ocfs2_cached_inode *cinode, uint64_t len, uint64_t start_bit); errcode_t ocfs2_chain_test(ocfs2_filesys *fs, ocfs2_cached_inode *cinode, uint64_t bitno, int *oldval); errcode_t ocfs2_chain_force_val(ocfs2_filesys *fs, ocfs2_cached_inode *cinode, uint64_t blkno, int newval, int *oldval); errcode_t ocfs2_chain_add_group(ocfs2_filesys *fs, ocfs2_cached_inode *cinode); errcode_t ocfs2_init_dir(ocfs2_filesys *fs, uint64_t dir, uint64_t parent_dir); errcode_t ocfs2_expand_dir(ocfs2_filesys *fs, uint64_t dir); errcode_t ocfs2_test_inode_allocated(ocfs2_filesys *fs, uint64_t blkno, int *is_allocated); void ocfs2_init_group_desc(ocfs2_filesys *fs, struct ocfs2_group_desc *gd, uint64_t blkno, uint32_t generation, uint64_t parent_inode, uint16_t bits, uint16_t chain, int suballoc); errcode_t ocfs2_new_dir_block(ocfs2_filesys *fs, uint64_t dir_ino, uint64_t parent_ino, char **block); errcode_t ocfs2_inode_insert_extent(ocfs2_filesys *fs, uint64_t ino, uint32_t cpos, uint64_t c_blkno, uint32_t clusters, uint16_t flag); errcode_t ocfs2_cached_inode_insert_extent(ocfs2_cached_inode *ci, uint32_t cpos, uint64_t c_blkno, uint32_t clusters, uint16_t flag); void ocfs2_dinode_new_extent_list(ocfs2_filesys *fs, struct ocfs2_dinode *di); void ocfs2_set_inode_data_inline(ocfs2_filesys *fs, struct ocfs2_dinode *di); errcode_t ocfs2_convert_inline_data_to_extents(ocfs2_cached_inode *ci); errcode_t ocfs2_new_inode(ocfs2_filesys *fs, uint64_t *ino, int mode); errcode_t ocfs2_new_system_inode(ocfs2_filesys *fs, uint64_t *ino, int mode, int flags); errcode_t ocfs2_delete_inode(ocfs2_filesys *fs, uint64_t ino); errcode_t ocfs2_new_extent_block(ocfs2_filesys *fs, uint64_t *blkno); errcode_t ocfs2_new_dx_root(ocfs2_filesys *fs, struct ocfs2_dinode *di, uint64_t *dr_blkno); errcode_t ocfs2_delete_extent_block(ocfs2_filesys *fs, uint64_t blkno); errcode_t ocfs2_delete_dx_root(ocfs2_filesys *fs, uint64_t dr_blkno); /* * Allocate the blocks and insert them to the file. * only i_clusters of dinode will be updated accordingly, i_size not changed. */ errcode_t ocfs2_extend_allocation(ocfs2_filesys *fs, uint64_t ino, uint32_t new_clusters); /* Ditto for cached inode */ errcode_t ocfs2_cached_inode_extend_allocation(ocfs2_cached_inode *ci, uint32_t new_clusters); /* Extend the file to the new size. No clusters will be allocated. */ errcode_t ocfs2_extend_file(ocfs2_filesys *fs, uint64_t ino, uint64_t new_size); int ocfs2_mark_extent_written(ocfs2_filesys *fs, struct ocfs2_dinode *di, uint32_t cpos, uint32_t len, uint64_t p_blkno); /* Reserve spaces at "offset" with a "len" in the files. */ errcode_t ocfs2_allocate_unwritten_extents(ocfs2_filesys *fs, uint64_t ino, uint64_t offset, uint64_t len); errcode_t ocfs2_truncate(ocfs2_filesys *fs, uint64_t ino, uint64_t new_i_size); errcode_t ocfs2_truncate_inline(ocfs2_filesys *fs, uint64_t ino, uint64_t new_i_size); errcode_t ocfs2_truncate_full(ocfs2_filesys *fs, uint64_t ino, uint64_t new_i_size, errcode_t (*free_clusters)(ocfs2_filesys *fs, uint32_t len, uint64_t start, void *free_data), void *free_data); errcode_t ocfs2_zero_tail_and_truncate(ocfs2_filesys *fs, ocfs2_cached_inode *ci, uint64_t new_size, uint32_t *new_clusters); errcode_t ocfs2_grow_chain_allocator(ocfs2_filesys *fs, int type, int slot_num, uint32_t num_clusters); errcode_t ocfs2_new_clusters(ocfs2_filesys *fs, uint32_t min, uint32_t requested, uint64_t *start_blkno, uint32_t *clusters_found); errcode_t ocfs2_test_cluster_allocated(ocfs2_filesys *fs, uint32_t cpos, int *is_allocated); errcode_t ocfs2_new_specific_cluster(ocfs2_filesys *fs, uint32_t cpos); errcode_t ocfs2_free_clusters(ocfs2_filesys *fs, uint32_t len, uint64_t start_blkno); errcode_t ocfs2_test_clusters(ocfs2_filesys *fs, uint32_t len, uint64_t start_blkno, int test, int *matches); errcode_t ocfs2_lookup(ocfs2_filesys *fs, uint64_t dir, const char *name, int namelen, char *buf, uint64_t *inode); errcode_t ocfs2_namei(ocfs2_filesys *fs, uint64_t root, uint64_t cwd, const char *name, uint64_t *inode); errcode_t ocfs2_namei_follow(ocfs2_filesys *fs, uint64_t root, uint64_t cwd, const char *name, uint64_t *inode); errcode_t ocfs2_follow_link(ocfs2_filesys *fs, uint64_t root, uint64_t cwd, uint64_t inode, uint64_t *res_inode); errcode_t ocfs2_file_read(ocfs2_cached_inode *ci, void *buf, uint32_t count, uint64_t offset, uint32_t *got); errcode_t ocfs2_file_write(ocfs2_cached_inode *ci, void *buf, uint32_t count, uint64_t offset, uint32_t *wrote); errcode_t ocfs2_fill_cluster_desc(ocfs2_filesys *fs, struct o2cb_cluster_desc *desc); errcode_t ocfs2_set_cluster_desc(ocfs2_filesys *fs, struct o2cb_cluster_desc *desc); errcode_t ocfs2_fill_heartbeat_desc(ocfs2_filesys *fs, struct o2cb_region_desc *desc); errcode_t ocfs2_lock_down_cluster(ocfs2_filesys *fs); errcode_t ocfs2_release_cluster(ocfs2_filesys *fs); errcode_t ocfs2_initialize_dlm(ocfs2_filesys *fs, const char *service); errcode_t ocfs2_shutdown_dlm(ocfs2_filesys *fs, const char *service); errcode_t ocfs2_super_lock(ocfs2_filesys *fs); errcode_t ocfs2_super_unlock(ocfs2_filesys *fs); errcode_t ocfs2_meta_lock(ocfs2_filesys *fs, ocfs2_cached_inode *inode, enum o2dlm_lock_level level, int flags); errcode_t ocfs2_meta_unlock(ocfs2_filesys *fs, ocfs2_cached_inode *ci); /* Quota operations */ static inline int ocfs2_global_dqstr_in_blk(int blocksize) { return (blocksize - OCFS2_QBLK_RESERVED_SPACE - sizeof(struct qt_disk_dqdbheader)) / sizeof(struct ocfs2_global_disk_dqblk); } void ocfs2_swap_quota_header(struct ocfs2_disk_dqheader *header); void ocfs2_swap_quota_local_info(struct ocfs2_local_disk_dqinfo *info); void ocfs2_swap_quota_chunk_header(struct ocfs2_local_disk_chunk *chunk); void ocfs2_swap_quota_global_info(struct ocfs2_global_disk_dqinfo *info); void ocfs2_swap_quota_global_dqblk(struct ocfs2_global_disk_dqblk *dqblk); void ocfs2_swap_quota_leaf_block_header(struct qt_disk_dqdbheader *bheader); errcode_t ocfs2_init_local_quota_file(ocfs2_filesys *fs, int type, uint64_t blkno); errcode_t ocfs2_init_local_quota_files(ocfs2_filesys *fs, int type); int ocfs2_qtree_depth(int blocksize); int ocfs2_qtree_entry_unused(struct ocfs2_global_disk_dqblk *ddquot); errcode_t ocfs2_init_global_quota_file(ocfs2_filesys *fs, int type); errcode_t ocfs2_init_fs_quota_info(ocfs2_filesys *fs, int type); errcode_t ocfs2_read_global_quota_info(ocfs2_filesys *fs, int type); errcode_t ocfs2_load_fs_quota_info(ocfs2_filesys *fs); errcode_t ocfs2_write_global_quota_info(ocfs2_filesys *fs, int type); errcode_t ocfs2_write_dquot(ocfs2_filesys *fs, int type, ocfs2_cached_dquot *dquot); errcode_t ocfs2_delete_dquot(ocfs2_filesys *fs, int type, ocfs2_cached_dquot *dquot); errcode_t ocfs2_read_dquot(ocfs2_filesys *fs, int type, qid_t id, ocfs2_cached_dquot **ret_dquot); errcode_t ocfs2_new_quota_hash(ocfs2_quota_hash **hashp); errcode_t ocfs2_free_quota_hash(ocfs2_quota_hash *hash); errcode_t ocfs2_insert_quota_hash(ocfs2_quota_hash *hash, ocfs2_cached_dquot *dquot); errcode_t ocfs2_remove_quota_hash(ocfs2_quota_hash *hash, ocfs2_cached_dquot *dquot); errcode_t ocfs2_find_quota_hash(ocfs2_quota_hash *hash, qid_t id, ocfs2_cached_dquot **dquotp); errcode_t ocfs2_find_create_quota_hash(ocfs2_quota_hash *hash, qid_t id, ocfs2_cached_dquot **dquotp); errcode_t ocfs2_find_read_quota_hash(ocfs2_filesys *fs, ocfs2_quota_hash *hash, int type, qid_t id, ocfs2_cached_dquot **dquotp); errcode_t ocfs2_compute_quota_usage(ocfs2_filesys *fs, ocfs2_quota_hash *usr_hash, ocfs2_quota_hash *grp_hash); errcode_t ocfs2_init_quota_change(ocfs2_filesys *fs, ocfs2_quota_hash **usrhash, ocfs2_quota_hash **grphash); errcode_t ocfs2_finish_quota_change(ocfs2_filesys *fs, ocfs2_quota_hash *usrhash, ocfs2_quota_hash *grphash); errcode_t ocfs2_apply_quota_change(ocfs2_filesys *fs, ocfs2_quota_hash *usrhash, ocfs2_quota_hash *grphash, uid_t uid, gid_t gid, int64_t space_change, int64_t inode_change); errcode_t ocfs2_iterate_quota_hash(ocfs2_quota_hash *hash, errcode_t (*f)(ocfs2_cached_dquot *, void *), void *data); errcode_t ocfs2_write_release_dquots(ocfs2_filesys *fs, int type, ocfs2_quota_hash *hash); /* Low level */ void ocfs2_swap_slot_map(struct ocfs2_slot_map *sm, int num_slots); void ocfs2_swap_slot_map_extended(struct ocfs2_slot_map_extended *se, int num_slots); errcode_t ocfs2_read_slot_map(ocfs2_filesys *fs, int num_slots, struct ocfs2_slot_map **map_ret); errcode_t ocfs2_read_slot_map_extended(ocfs2_filesys *fs, int num_slots, struct ocfs2_slot_map_extended **map_ret); errcode_t ocfs2_write_slot_map(ocfs2_filesys *fs, int num_slots, struct ocfs2_slot_map *sm); errcode_t ocfs2_write_slot_map_extended(ocfs2_filesys *fs, int num_slots, struct ocfs2_slot_map_extended *se); /* High level functions for metadata ecc */ void ocfs2_compute_meta_ecc(ocfs2_filesys *fs, void *data, struct ocfs2_block_check *bc); errcode_t ocfs2_validate_meta_ecc(ocfs2_filesys *fs, void *data, struct ocfs2_block_check *bc); /* Low level checksum compute functions. Use the high-level ones. */ extern void ocfs2_block_check_compute(void *data, size_t blocksize, struct ocfs2_block_check *bc); extern errcode_t ocfs2_block_check_validate(void *data, size_t blocksize, struct ocfs2_block_check *bc); /* High level */ errcode_t ocfs2_format_slot_map(ocfs2_filesys *fs); errcode_t ocfs2_load_slot_map(ocfs2_filesys *fs, struct ocfs2_slot_map_data **data_ret); errcode_t ocfs2_store_slot_map(ocfs2_filesys *fs, struct ocfs2_slot_map_data *md); enum ocfs2_lock_type ocfs2_get_lock_type(char c); char *ocfs2_get_lock_type_string(enum ocfs2_lock_type type); errcode_t ocfs2_encode_lockres(enum ocfs2_lock_type type, uint64_t blkno, uint32_t generation, uint64_t parent, char *lockres); errcode_t ocfs2_decode_lockres(char *lockres, enum ocfs2_lock_type *type, uint64_t *blkno, uint32_t *generation, uint64_t *parent); errcode_t ocfs2_printable_lockres(char *lockres, char *name, int len); /* write the superblock at the specific block. */ errcode_t ocfs2_write_backup_super(ocfs2_filesys *fs, uint64_t blkno); /* Get the blkno according to the file system info. * The unused ones, depending on the volume size, are zeroed. * Return the length of the block array. */ int ocfs2_get_backup_super_offsets(ocfs2_filesys *fs, uint64_t *blocks, size_t len); /* This function will get the superblock pointed to by fs and copy it to * the blocks. But first it will ensure all the appropriate clusters are free. * If not, it will error out with ENOSPC. If free, it will set bits for all * the clusters, zero the clusters and write the backup sb. * In case of updating, it will override the backup blocks with the newest * superblock information. */ errcode_t ocfs2_set_backup_super_list(ocfs2_filesys *fs, uint64_t *blocks, size_t len); /* Conversely, this clears all the allocator bits associated with the * specified backup superblocks */ errcode_t ocfs2_clear_backup_super_list(ocfs2_filesys *fs, uint64_t *blocks, size_t len); /* Refresh the backup superblock information */ errcode_t ocfs2_refresh_backup_supers(ocfs2_filesys *fs); /* Refresh a specific list of backup superblocks */ errcode_t ocfs2_refresh_backup_super_list(ocfs2_filesys *fs, uint64_t *blocks, size_t len); errcode_t ocfs2_read_backup_super(ocfs2_filesys *fs, int backup, char *sbbuf); /* get the virtual offset of the last allocated cluster. */ errcode_t ocfs2_get_last_cluster_offset(ocfs2_filesys *fs, struct ocfs2_dinode *di, uint32_t *v_cluster); /* Filesystem features */ enum ocfs2_feature_levels { OCFS2_FEATURE_LEVEL_DEFAULT = 0, OCFS2_FEATURE_LEVEL_MAX_COMPAT, OCFS2_FEATURE_LEVEL_MAX_FEATURES, }; errcode_t ocfs2_snprint_feature_flags(char *str, size_t size, ocfs2_fs_options *flags); errcode_t ocfs2_snprint_tunefs_flags(char *str, size_t size, uint16_t flags); errcode_t ocfs2_snprint_extent_flags(char *str, size_t size, uint8_t flags); errcode_t ocfs2_snprint_refcount_flags(char *str, size_t size, uint8_t flags); errcode_t ocfs2_snprint_cluster_o2cb_flags(char *str, size_t size, uint8_t flags); errcode_t ocfs2_parse_feature(const char *opts, ocfs2_fs_options *feature_flags, ocfs2_fs_options *reverse_flags); errcode_t ocfs2_parse_feature_level(const char *typestr, enum ocfs2_feature_levels *level); errcode_t ocfs2_merge_feature_flags_with_level(ocfs2_fs_options *dest, enum ocfs2_mkfs_types fstype, int level, ocfs2_fs_options *feature_set, ocfs2_fs_options *reverse_set); /* * Get a callback with each feature in feature_set in order. This will * calculate the dependencies of each feature in feature_set, then call func * once per feature, with only that feature passed to func. */ void ocfs2_feature_foreach(ocfs2_fs_options *feature_set, int (*func)(ocfs2_fs_options *feature, void *user_data), void *user_data); /* The reverse function. It will call the features in reverse order. */ void ocfs2_feature_reverse_foreach(ocfs2_fs_options *reverse_set, int (*func)(ocfs2_fs_options *feature, void *user_data), void *user_data); /* These are deprecated names - don't use them */ int ocfs2_get_backup_super_offset(ocfs2_filesys *fs, uint64_t *blocks, size_t len); errcode_t ocfs2_refresh_backup_super(ocfs2_filesys *fs, uint64_t *blocks, size_t len); errcode_t ocfs2_set_backup_super(ocfs2_filesys *fs, uint64_t *blocks, size_t len); /* * ${foo}_to_${bar} is a floor function. blocks_to_clusters will * returns the cluster that contains a block, not the number of clusters * that hold a given number of blocks. * * ${foo}_in_${bar} is a ceiling function. clusters_in_blocks will give * the number of clusters needed to hold a given number of blocks. * * These functions return UINTxx_MAX when they overflow, but UINTxx_MAX * cannot be used to check overflow. UINTxx_MAX is a valid value in much * of ocfs2. The caller is responsible for preventing overflow before * using these functions. */ static inline uint64_t ocfs2_clusters_to_blocks(ocfs2_filesys *fs, uint32_t clusters) { int c_to_b_bits = OCFS2_RAW_SB(fs->fs_super)->s_clustersize_bits - OCFS2_RAW_SB(fs->fs_super)->s_blocksize_bits; return (uint64_t)clusters << c_to_b_bits; } static inline uint32_t ocfs2_blocks_to_clusters(ocfs2_filesys *fs, uint64_t blocks) { int b_to_c_bits = OCFS2_RAW_SB(fs->fs_super)->s_clustersize_bits - OCFS2_RAW_SB(fs->fs_super)->s_blocksize_bits; uint64_t ret = blocks >> b_to_c_bits; if (ret > UINT32_MAX) ret = UINT32_MAX; return (uint32_t)ret; } static inline uint64_t ocfs2_clusters_to_bytes(ocfs2_filesys *fs, uint32_t clusters) { uint64_t ret = clusters; ret = ret << OCFS2_RAW_SB(fs->fs_super)->s_clustersize_bits; if (ret < clusters) ret = UINT64_MAX; return ret; } static inline uint32_t ocfs2_bytes_to_clusters(ocfs2_filesys *fs, uint64_t bytes) { uint64_t ret = bytes >> OCFS2_RAW_SB(fs->fs_super)->s_clustersize_bits; if (ret > UINT32_MAX) ret = UINT32_MAX; return (uint32_t)ret; } static inline uint64_t ocfs2_block_to_cluster_start(ocfs2_filesys *fs, uint64_t blocks) { int c_to_b_bits = OCFS2_RAW_SB(fs->fs_super)->s_clustersize_bits - OCFS2_RAW_SB(fs->fs_super)->s_blocksize_bits; uint32_t clusters; clusters = ocfs2_blocks_to_clusters(fs, blocks); return (uint64_t)clusters << c_to_b_bits; } static inline uint64_t ocfs2_blocks_to_bytes(ocfs2_filesys *fs, uint64_t blocks) { uint64_t ret = blocks << OCFS2_RAW_SB(fs->fs_super)->s_blocksize_bits; if (ret < blocks) ret = UINT64_MAX; return ret; } static inline uint64_t ocfs2_bytes_to_blocks(ocfs2_filesys *fs, uint64_t bytes) { return bytes >> OCFS2_RAW_SB(fs->fs_super)->s_blocksize_bits; } static inline uint32_t ocfs2_clusters_in_blocks(ocfs2_filesys *fs, uint64_t blocks) { int c_to_b_bits = OCFS2_RAW_SB(fs->fs_super)->s_clustersize_bits - OCFS2_RAW_SB(fs->fs_super)->s_blocksize_bits; uint64_t ret = blocks + ((1 << c_to_b_bits) - 1); if (ret < blocks) /* deal with wrapping */ ret = UINT64_MAX; ret = ret >> c_to_b_bits; if (ret > UINT32_MAX) ret = UINT32_MAX; return (uint32_t)ret; } static inline uint32_t ocfs2_clusters_in_bytes(ocfs2_filesys *fs, uint64_t bytes) { uint64_t ret = bytes + fs->fs_clustersize - 1; if (ret < bytes) /* deal with wrapping */ ret = UINT64_MAX; ret = ret >> OCFS2_RAW_SB(fs->fs_super)->s_clustersize_bits; if (ret > UINT32_MAX) ret = UINT32_MAX; return (uint32_t)ret; } static inline uint64_t ocfs2_blocks_in_bytes(ocfs2_filesys *fs, uint64_t bytes) { uint64_t ret = bytes + fs->fs_blocksize - 1; if (ret < bytes) /* deal with wrapping */ return UINT64_MAX; return ret >> OCFS2_RAW_SB(fs->fs_super)->s_blocksize_bits; } static inline uint64_t ocfs2_align_bytes_to_clusters(ocfs2_filesys *fs, uint64_t bytes) { uint32_t clusters; clusters = ocfs2_clusters_in_bytes(fs, bytes); return (uint64_t)clusters << OCFS2_RAW_SB(fs->fs_super)->s_clustersize_bits; } static inline uint64_t ocfs2_align_bytes_to_blocks(ocfs2_filesys *fs, uint64_t bytes) { uint64_t blocks; blocks = ocfs2_blocks_in_bytes(fs, bytes); return blocks << OCFS2_RAW_SB(fs->fs_super)->s_blocksize_bits; } /* given a cluster offset, calculate which block group it belongs to * and return that block offset. */ static inline uint64_t ocfs2_which_cluster_group(ocfs2_filesys *fs, uint16_t cpg, uint32_t cluster) { struct ocfs2_super_block *sb = OCFS2_RAW_SB(fs->fs_super); uint32_t group_no; group_no = cluster / cpg; if (!group_no) return sb->s_first_cluster_group; return ocfs2_clusters_to_blocks(fs, group_no * cpg); } static inline int ocfs2_block_out_of_range(ocfs2_filesys *fs, uint64_t block) { return (block < OCFS2_SUPER_BLOCK_BLKNO) || (block > fs->fs_blocks); } struct ocfs2_cluster_group_sizes { uint16_t cgs_cpg; uint16_t cgs_tail_group_bits; uint32_t cgs_cluster_groups; }; static inline void ocfs2_calc_cluster_groups(uint64_t clusters, uint64_t blocksize, struct ocfs2_cluster_group_sizes *cgs) { uint16_t max_bits = 8 * ocfs2_group_bitmap_size(blocksize, 0, 0); cgs->cgs_cpg = max_bits; if (max_bits > clusters) cgs->cgs_cpg = clusters; cgs->cgs_cluster_groups = (clusters + cgs->cgs_cpg - 1) / cgs->cgs_cpg; cgs->cgs_tail_group_bits = clusters % cgs->cgs_cpg; if (cgs->cgs_tail_group_bits == 0) cgs->cgs_tail_group_bits = cgs->cgs_cpg; } /* * This is only valid for leaf nodes, which are the only ones that can * have empty extents anyway. */ static inline int ocfs2_is_empty_extent(struct ocfs2_extent_rec *rec) { return !rec->e_leaf_clusters; } /* * Helper function to look at the # of clusters in an extent record. */ static inline uint32_t ocfs2_rec_clusters(uint16_t tree_depth, struct ocfs2_extent_rec *rec) { /* * Cluster count in extent records is slightly different * between interior nodes and leaf nodes. This is to support * unwritten extents which need a flags field in leaf node * records, thus shrinking the available space for a clusters * field. */ if (tree_depth) return rec->e_int_clusters; else return rec->e_leaf_clusters; } static inline void ocfs2_set_rec_clusters(uint16_t tree_depth, struct ocfs2_extent_rec *rec, uint32_t clusters) { if (tree_depth) rec->e_int_clusters = clusters; else rec->e_leaf_clusters = clusters; } static inline int ocfs2_sparse_alloc(struct ocfs2_super_block *osb) { if (osb->s_feature_incompat & OCFS2_FEATURE_INCOMPAT_SPARSE_ALLOC) return 1; return 0; } static inline int ocfs2_clusterinfo_valid(struct ocfs2_super_block *osb) { if (osb->s_feature_incompat & (OCFS2_FEATURE_INCOMPAT_USERSPACE_STACK | OCFS2_FEATURE_INCOMPAT_CLUSTERINFO)) return 1; return 0; } static inline int ocfs2_userspace_stack(struct ocfs2_super_block *osb) { if (ocfs2_clusterinfo_valid(osb) && memcmp(osb->s_cluster_info.ci_stack, OCFS2_CLASSIC_CLUSTER_STACK, OCFS2_STACK_LABEL_LEN)) return 1; return 0; } static inline int ocfs2_o2cb_stack(struct ocfs2_super_block *osb) { if (ocfs2_clusterinfo_valid(osb) && !memcmp(osb->s_cluster_info.ci_stack, OCFS2_CLASSIC_CLUSTER_STACK, OCFS2_STACK_LABEL_LEN)) return 1; return 0; } static inline int ocfs2_cluster_o2cb_global_heartbeat(struct ocfs2_super_block *osb) { if (!ocfs2_o2cb_stack(osb)) return 0; return osb->s_cluster_info.ci_stackflags & OCFS2_CLUSTER_O2CB_GLOBAL_HEARTBEAT; } static inline int ocfs2_writes_unwritten_extents(struct ocfs2_super_block *osb) { /* * Support for sparse files is a pre-requisite */ if (!ocfs2_sparse_alloc(osb)) return 0; if (osb->s_feature_ro_compat & OCFS2_FEATURE_RO_COMPAT_UNWRITTEN) return 1; return 0; } static inline int ocfs2_supports_append_dio(struct ocfs2_super_block *osb) { if (osb->s_feature_incompat & OCFS2_FEATURE_INCOMPAT_APPEND_DIO) return 1; return 0; } static inline int ocfs2_uses_extended_slot_map(struct ocfs2_super_block *osb) { if (osb->s_feature_incompat & OCFS2_FEATURE_INCOMPAT_EXTENDED_SLOT_MAP) return 1; return 0; } static inline int ocfs2_support_inline_data(struct ocfs2_super_block *osb) { if (osb->s_feature_incompat & OCFS2_FEATURE_INCOMPAT_INLINE_DATA) return 1; return 0; } static inline int ocfs2_meta_ecc(struct ocfs2_super_block *osb) { if (OCFS2_HAS_INCOMPAT_FEATURE(osb, OCFS2_FEATURE_INCOMPAT_META_ECC)) return 1; return 0; } static inline int ocfs2_support_xattr(struct ocfs2_super_block *osb) { if (osb->s_feature_incompat & OCFS2_FEATURE_INCOMPAT_XATTR) return 1; return 0; } static inline int ocfs2_supports_indexed_dirs(struct ocfs2_super_block *osb) { if (osb->s_feature_incompat & OCFS2_FEATURE_INCOMPAT_INDEXED_DIRS) return 1; return 0; } /* * When we're swapping some of our disk structures, a garbage count * can send us past the edge of a block buffer. This function guards * against that. It returns true if the element would walk off then end * of the block buffer. */ static inline int ocfs2_swap_barrier(ocfs2_filesys *fs, void *block_buffer, void *element, size_t element_size) { char *limit, *end; limit = block_buffer; limit += fs->fs_blocksize; end = element; end += element_size; return end > limit; } static inline int ocfs2_refcount_tree(struct ocfs2_super_block *osb) { if (OCFS2_HAS_INCOMPAT_FEATURE(osb, OCFS2_FEATURE_INCOMPAT_REFCOUNT_TREE)) return 1; return 0; } static inline int ocfs2_supports_discontig_bg(struct ocfs2_super_block *osb) { if (OCFS2_HAS_INCOMPAT_FEATURE(osb, OCFS2_FEATURE_INCOMPAT_DISCONTIG_BG)) return 1; return 0; } /* * shamelessly lifted from the kernel * * min()/max() macros that also do * strict type-checking.. See the * "unnecessary" pointer comparison. */ #define ocfs2_min(x,y) ({ \ const typeof(x) _x = (x); \ const typeof(y) _y = (y); \ (void) (&_x == &_y); \ _x < _y ? _x : _y; }) #define ocfs2_max(x,y) ({ \ const typeof(x) _x = (x); \ const typeof(y) _y = (y); \ (void) (&_x == &_y); \ _x > _y ? _x : _y; }) /* lifted from the kernel. include/linux/kernel.h */ #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) /* * DEPRECATED: Extent/block iterate functions. * * Do not use these for reading/writing regular files - they don't properly * handle holes or inline data. */ /* Return flags for the extent iterator functions */ #define OCFS2_EXTENT_CHANGED 0x01 #define OCFS2_EXTENT_ABORT 0x02 #define OCFS2_EXTENT_ERROR 0x04 /* * Extent iterate flags * * OCFS2_EXTENT_FLAG_APPEND indicates that the iterator function should * be called on extents past the leaf next_free_rec. This is used by * ocfs2_expand_dir() to add a new extent to a directory (via * OCFS2_BLOCK_FLAG_APPEND and the block iteration functions). * * OCFS2_EXTENT_FLAG_DEPTH_TRAVERSE indicates that the iterator * function for tree_depth > 0 records (ocfs2_extent_blocks, iow) * should be called after all of the extents contained in the * extent_block are processed. This is useful if you are going to be * deallocating extents. * * OCFS2_EXTENT_FLAG_DATA_ONLY indicates that the iterator function * should be called for data extents (depth == 0) only. */ #define OCFS2_EXTENT_FLAG_APPEND 0x01 #define OCFS2_EXTENT_FLAG_DEPTH_TRAVERSE 0x02 #define OCFS2_EXTENT_FLAG_DATA_ONLY 0x04 /* Return flags for the block iterator functions */ #define OCFS2_BLOCK_CHANGED 0x01 #define OCFS2_BLOCK_ABORT 0x02 #define OCFS2_BLOCK_ERROR 0x04 #define OCFS2_IS_VALID_DX_ROOT(ptr) \ (!strcmp((char *)(ptr)->dr_signature, OCFS2_DX_ROOT_SIGNATURE)) /* * Block iterate flags * * In OCFS2, block iteration runs through the blocks contained in an * inode's data extents. As such, "DATA_ONLY" and "DEPTH_TRAVERSE" * can't really apply. * * OCFS2_BLOCK_FLAG_APPEND is as OCFS2_EXTENT_FLAG_APPEND, except on a * blocksize basis. This may mean that the underlying extent already * contains the space for a new block, and i_size is updated * accordingly. */ #define OCFS2_BLOCK_FLAG_APPEND 0x01 errcode_t ocfs2_extent_iterate(ocfs2_filesys *fs, uint64_t blkno, int flags, char *block_buf, int (*func)(ocfs2_filesys *fs, struct ocfs2_extent_rec *rec, int tree_depth, uint32_t ccount, uint64_t ref_blkno, int ref_recno, void *priv_data), void *priv_data); errcode_t ocfs2_extent_iterate_inode(ocfs2_filesys *fs, struct ocfs2_dinode *inode, int flags, char *block_buf, int (*func)(ocfs2_filesys *fs, struct ocfs2_extent_rec *rec, int tree_depth, uint32_t ccount, uint64_t ref_blkno, int ref_recno, void *priv_data), void *priv_data); errcode_t ocfs2_extent_iterate_dx_root(ocfs2_filesys *fs, struct ocfs2_dx_root_block *dx_root, int flags, char *block_buf, int (*func)(ocfs2_filesys *fs, struct ocfs2_extent_rec *rec, int tree_depth, uint32_t ccount, uint64_t ref_blkno, int ref_recno, void *priv_data), void *priv_data); errcode_t ocfs2_block_iterate(ocfs2_filesys *fs, uint64_t blkno, int flags, int (*func)(ocfs2_filesys *fs, uint64_t blkno, uint64_t bcount, uint16_t ext_flags, void *priv_data), void *priv_data); errcode_t ocfs2_block_iterate_inode(ocfs2_filesys *fs, struct ocfs2_dinode *inode, int flags, int (*func)(ocfs2_filesys *fs, uint64_t blkno, uint64_t bcount, uint16_t ext_flags, void *priv_data), void *priv_data); #define OCFS2_XATTR_ABORT 0x01 #define OCFS2_XATTR_ERROR 0x02 errcode_t ocfs2_xattr_iterate(ocfs2_cached_inode *ci, int (*func)(ocfs2_cached_inode *ci, char *xe_buf, uint64_t xe_blkno, struct ocfs2_xattr_entry *xe, char *value_buf, uint64_t value_blkno, void *value, int in_bucket, void *priv_data), void *priv_data); uint32_t ocfs2_xattr_uuid_hash(unsigned char *uuid); uint32_t ocfs2_xattr_name_hash(uint32_t uuid_hash, const char *name, int name_len); int ocfs2_tree_find_leaf(ocfs2_filesys *fs, struct ocfs2_extent_list *el, uint64_t el_blkno, char *el_blk, uint32_t cpos, char **leaf_buf); uint16_t ocfs2_xattr_buckets_per_cluster(ocfs2_filesys *fs); uint16_t ocfs2_blocks_per_xattr_bucket(ocfs2_filesys *fs); /* See ocfs2_swap_extent_list() for a discussion of obj */ void ocfs2_swap_xattrs_to_cpu(ocfs2_filesys *fs, void *obj, struct ocfs2_xattr_header *xh); void ocfs2_swap_xattrs_from_cpu(ocfs2_filesys *fs, void *obj, struct ocfs2_xattr_header *xh); void ocfs2_swap_xattr_block_to_cpu(ocfs2_filesys *fs, struct ocfs2_xattr_block *xb); void ocfs2_swap_xattr_block_from_cpu(ocfs2_filesys *fs, struct ocfs2_xattr_block *xb); errcode_t ocfs2_read_xattr_block(ocfs2_filesys *fs, uint64_t blkno, char *xb_buf); errcode_t ocfs2_write_xattr_block(ocfs2_filesys *fs, uint64_t blkno, char *xb_buf); errcode_t ocfs2_xattr_get_rec(ocfs2_filesys *fs, struct ocfs2_xattr_block *xb, uint32_t name_hash, uint64_t *p_blkno, uint32_t *e_cpos, uint32_t *num_clusters); uint16_t ocfs2_xattr_value_real_size(uint16_t name_len, uint16_t value_len); uint16_t ocfs2_xattr_min_offset(struct ocfs2_xattr_header *xh, uint16_t size); uint16_t ocfs2_xattr_name_value_len(struct ocfs2_xattr_header *xh); errcode_t ocfs2_read_xattr_bucket(ocfs2_filesys *fs, uint64_t blkno, char *bucket_buf); errcode_t ocfs2_write_xattr_bucket(ocfs2_filesys *fs, uint64_t blkno, char *bucket_buf); errcode_t ocfs2_xattr_value_truncate(ocfs2_filesys *fs, uint64_t ino, struct ocfs2_xattr_value_root *xv); errcode_t ocfs2_xattr_tree_truncate(ocfs2_filesys *fs, struct ocfs2_xattr_tree_root *xt); errcode_t ocfs2_extent_iterate_xattr(ocfs2_filesys *fs, struct ocfs2_extent_list *el, uint64_t last_eb_blk, int flags, int (*func)(ocfs2_filesys *fs, struct ocfs2_extent_rec *rec, int tree_depth, uint32_t ccount, uint64_t ref_blkno, int ref_recno, void *priv_data), void *priv_data, int *changed); errcode_t ocfs2_delete_xattr_block(ocfs2_filesys *fs, uint64_t blkno); errcode_t ocfs2_dir_indexed_tree_truncate(ocfs2_filesys *fs, struct ocfs2_dx_root_block *dx_root); errcode_t ocfs2_write_dx_root(ocfs2_filesys *fs, uint64_t block, char *buf); errcode_t ocfs2_write_dx_leaf(ocfs2_filesys *fs, uint64_t block, void *buf); errcode_t ocfs2_dx_dir_build(ocfs2_filesys *fs, uint64_t dir); errcode_t ocfs2_dx_dir_insert_entry(ocfs2_filesys *fs, uint64_t dir, const char *name, uint64_t ino, uint64_t blkno); int ocfs2_search_dirblock(ocfs2_filesys *fs, char *dir_buf, const char *name, int namelen, unsigned int bytes, struct ocfs2_dir_entry **res_dir); void ocfs2_dx_dir_name_hash(ocfs2_filesys *fs, const char *name, int len, struct ocfs2_dx_hinfo *hinfo); errcode_t ocfs2_dx_dir_lookup(ocfs2_filesys *fs, struct ocfs2_dx_root_block *dx_root, struct ocfs2_extent_list *el, struct ocfs2_dx_hinfo *hinfo, uint32_t *ret_cpos, uint64_t *ret_phys_blkno); errcode_t ocfs2_dx_dir_search(ocfs2_filesys *fs, const char *name, int namelen, struct ocfs2_dx_root_block *dx_root, struct ocfs2_dir_lookup_result *res); void release_lookup_res(struct ocfs2_dir_lookup_result *res); int ocfs2_find_max_rec_len(ocfs2_filesys *fs, char *buf); void ocfs2_dx_list_remove_entry(struct ocfs2_dx_entry_list *entry_list, int index); int ocfs2_is_dir_trailer(ocfs2_filesys *fs, struct ocfs2_dinode *di, unsigned long de_off); /* routines for block check */ uint32_t ocfs2_hamming_encode(uint32_t parity, void *data, unsigned int d, unsigned int nr); uint32_t ocfs2_hamming_encode_block(void *data, unsigned int d); void ocfs2_hamming_fix(void *data, unsigned int d, unsigned int nr, unsigned int fix); void ocfs2_hamming_fix_block(void *data, unsigned int d, unsigned int fix); uint32_t crc32_le(uint32_t crc, unsigned char const *p, size_t len); enum ocfs2_block_type ocfs2_detect_block(char *buf); void ocfs2_swap_block_from_cpu(ocfs2_filesys *fs, void *block); void ocfs2_swap_block_to_cpu(ocfs2_filesys *fs, void *block); #endif /* _FILESYS_H */ ocfs2-tools-ocfs2-tools-1.8.6/include/tools-internal/000077500000000000000000000000001347147137200224565ustar00rootroot00000000000000ocfs2-tools-ocfs2-tools-1.8.6/include/tools-internal/Makefile000066400000000000000000000002341347147137200241150ustar00rootroot00000000000000TOPDIR = ../.. include $(TOPDIR)/Preamble.make HFILES = verbose.h progress.h utils.h scandisk.h DIST_FILES = $(HFILES) include $(TOPDIR)/Postamble.make ocfs2-tools-ocfs2-tools-1.8.6/include/tools-internal/progress.h000066400000000000000000000062251347147137200245000ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * progress.h * * Internal progress output functions. * * Copyright (C) 2008 Oracle. All rights reserved. * * 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. * * 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. */ #ifndef _INTERNAL_PROGRESS_H #define _INTERNAL_PROGRESS_H struct tools_progress; /* * Enables progress display. This is generally called when a program * is passed the '--progress' option. */ void tools_progress_enable(void); /* * Disable progress display. This is a true disable. Users do not need * to disable by hand when using verbosef() and friends for output. They * interact correctly with the progress display. */ void tools_progress_disable(void); /* * Returns 1 if progress display is enabled, 0 if not. */ int tools_progress_enabled(void); /* * Callers should use the progress API unconditionally. If the progress * display is not enabled, the functions are no-ops. * * The progress bar can contain multiple progress displays. A toplevel * action may run a sub-action that takes time. Thus, the bar displays * both the progress state of the toplevel action and the sub-action. * * The sub-action can start its own progress display without knowledge * of the toplevel. The progress code understands how to handle it. */ /* * Start a new progress item. * * The long name should be no longer than 25 characters or so. The * short name really wants to be no more than 8. When displaying the * progress bar, the progress code sees if the long names of all * registered progress items can fit. If not, it will use the short * names, starting at the outermost progress item. If the short names * don't fit, it will truncate the short names. * * count is how many steps are required for completion of this progress * item. If count is non-zero, the progress bar will display a * completion percentage. If count is zero, the item is considered * unbounded, and the progress bar will display a simple spinner. * * A new progress item is returned. If NULL is returned, its because * there is no memory available. */ struct tools_progress *tools_progress_start(const char *long_name, const char *short_name, uint64_t count); /* * Increment the progress item. * * step is the number of steps to add to the completed count. This will * update the progress bar. As an optimization, the bar is changed at * most once every 1/8s. In addition, it will not be updated if the * completion percentage has not changed. */ void tools_progress_step(struct tools_progress *prog, unsigned int step); /* * Stop this progress item. This will free the item and remove it from the * progress bar. */ void tools_progress_stop(struct tools_progress *prog); #endif /* _INTERNAL_PROGRESS_H */ ocfs2-tools-ocfs2-tools-1.8.6/include/tools-internal/scandisk.h000066400000000000000000000067021347147137200244330ustar00rootroot00000000000000/****************************************************************************** ******************************************************************************* ** ** Copyright (C) 2008 Red Hat, Inc. All rights reserved. ** ** Author: Fabio M. Di Nitto ** ** Original design by: ** Joel Becker ** Fabio M. Di Nitto ** ** This copyrighted material is made available to anyone wishing to use, ** modify, copy, or redistribute it subject to the terms and conditions ** of the GNU General Public License v.2. ** ******************************************************************************* ******************************************************************************/ #ifndef __SCANDISK_H__ #define __SCANDISK_H__ #ifndef DEVPATH #define DEVPATH "/dev" #endif #ifndef SYSFSPATH #define SYSFSPATH "/sys" #endif #ifndef SYSBLOCKPATH #define SYSBLOCKPATH SYSFSPATH "/block" #endif #ifndef DEVCACHETIMEOUT #define DEVCACHETIMEOUT 30 /* expressed in seconds */ #endif /* each entry can be (generally): * > 0 on success or good hit * 0 on success with no hit * < 0 on error */ struct sysfsattrs { /* usual 0 | 1 game */ int sysfs; /* did we find an entry in sysfs at all? */ int slaves; /* device has slaves */ int holders; /* device has holders */ int removable; /* device is removable */ int disk; /* device is a disk */ }; /* this structure is required because we don't know upfront how many * entries for a certain maj/min will be found in /dev, and so we need * to alloc them dynamically. */ struct devpath { struct devpath *next; char path[MAXPATHLEN]; }; /* this structure holds all the data for each maj/min found in the system * that is a block device */ struct devnode { struct devnode *next; struct devpath *devpath; /* point to the first path entry */ int maj; /* device major */ int min; /* device minor */ struct sysfsattrs sysfsattrs; /* like the others.. scanning /sys */ int procpart; /* 0 if the device is not in proc/part or 1 on success. <0 on error */ char procname[MAXPATHLEN]; /* non-NULL if we find a maj/min match */ int md; /* 0 nothing to do with raid, 1 is raid, * 2 is raid slave - data from /proc/mdstat */ int mapper; /* 0 nothing, 1 we believe it's a devmap dev */ int power; /* 0 nothing, 1 we believe it is powerpath device */ void *filter; /* your filter output.. whatever it is */ }; /* this is what you get after a scan... if you are lucky */ /* each entry can be 0 if we can't scan or < 0 if there are errors */ struct devlisthead { struct devnode *devnode; /* points to the first entry */ struct devnode *tail; /* last entry (for fast append) */ time_t cache_timestamp; /* this cache timestamp */ int cache_timeout; /* for how long this cache is valid */ int sysfs; /* set to 1 if we were able to scan * /sys */ int procpart; /* set to 1 if we were able to scan * /proc/partitions */ int lsdev; /* set to 1 if we were able to ls /dev */ int mdstat; /* set to 1 if we were able to scan * /proc/mdstat */ int mapper; /* set to 1 if we were able to run * something against mapper */ int power; /* set to 1 if we detected a powerpath device */ }; typedef void (*devfilter) (struct devnode * cur, void *arg); struct devlisthead *scan_for_dev(struct devlisthead *devlisthead, time_t timeout, devfilter filter, void *filter_args); void free_dev_list(struct devlisthead *devlisthead); #endif /* __SCANDISK_H__ */ ocfs2-tools-ocfs2-tools-1.8.6/include/tools-internal/utils.h000066400000000000000000000023231347147137200237670ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * utils.h * * Utility functions * * Copyright (C) 2010 Oracle. All rights reserved. * * 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. * * 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. */ #ifndef _INTERNAL_UTILS_H #define _INTERNAL_UTILS_H /* * Removes trailing whitespace from a string. It does not allocate or reallocate * any memory. It modifies the string in place. */ char *tools_strchomp(char *str); /* * Removes leading whitespace from a string, by moving the rest of the * characters forward. It does not allocate or reallocate any memory. * It modifies the string in place. */ char *tools_strchug(char *str); /* * Removes both the leading and trailing whitespaces */ #define tools_strstrip(str) tools_strchug(tools_strchomp(str)) #endif /* _INTERNAL_UTILS_H */ ocfs2-tools-ocfs2-tools-1.8.6/include/tools-internal/verbose.h000066400000000000000000000045621347147137200243030ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * verbose.h * * Internal verbose output functions. * * Copyright (C) 2008 Oracle. All rights reserved. * * 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. * * 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. */ #ifndef _INTERNAL_VERBOSE_H #define _INTERNAL_VERBOSE_H /* Verbosity levels for verbosef/errorf/tcom_err */ #define VL_FLAG_STDOUT 0x100 /* or'd with a level, output to stdout */ enum tools_verbosity_level { VL_CRIT = 0, /* Don't use this! I still haven't thought of anything so critical that -q should be ignored */ VL_ERR = 1, /* Error messages */ /* Regular output is the same level as errors */ #define VL_OUT (VL_ERR | VL_FLAG_STDOUT) VL_APP = 2, /* Verbose application status */ VL_LIB = 3, /* Status from shared code */ VL_DEBUG = 4, /* Debugging output */ }; /* Call this to set the program name */ void tools_setup_argv0(const char *argv0); /* Returns the program name from argv0 */ const char *tools_progname(void); /* Prints the tools version */ void tools_version(void); /* Increase and decrease the verbosity level */ void tools_verbose(void); void tools_quiet(void); /* Sets the process interactive */ void tools_interactive(void); /* Sets automatic answers for interactive questions */ void tools_interactive_yes(void); void tools_interactive_no(void); /* * Output that honors the verbosity level. tcom_err() is for errcode_t * errors. errorf() is for all other errors. verbosef() is for verbose * output. */ void verbosef(enum tools_verbosity_level level, const char *fmt, ...) __attribute__ ((format (printf, 2, 3))); void errorf(const char *fmt, ...) __attribute__ ((format (printf, 1, 2))); void tcom_err(errcode_t code, const char *fmt, ...) __attribute__ ((format (printf, 2, 3))); int tools_interact(const char *fmt, ...) __attribute__ ((format (printf, 1, 2))); int tools_interact_critical(const char *fmt, ...) __attribute__ ((format (printf, 1, 2))); #endif /* _INTERNAL_VERBOSE_H */ ocfs2-tools-ocfs2-tools-1.8.6/install-sh000077500000000000000000000214321347147137200200670ustar00rootroot00000000000000#!/bin/sh # install - install a program, script, or datafile scriptversion=2004-01-12.10 # This originates from X11R5 (mit/util/scripts/install.sh), which was # later released in X11R6 (xc/config/util/install.sh) with the # following copyright and license. # # Copyright (C) 1994 X Consortium # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to # deal in the Software without restriction, including without limitation the # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or # sell copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN # AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC- # TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # # Except as contained in this notice, the name of the X Consortium shall not # be used in advertising or otherwise to promote the sale, use or other deal- # ings in this Software without prior written authorization from the X Consor- # tium. # # # FSF changes to this file are in the public domain. # # 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= usage="Usage: $0 [OPTION]... SRCFILE DSTFILE or: $0 [OPTION]... SRCFILES... DIRECTORY or: $0 -d DIRECTORIES... In the first form, install SRCFILE to DSTFILE, removing SRCFILE by default. In the second, create the directory path DIR. Options: -b=TRANSFORMBASENAME -c copy source (using $cpprog) instead of moving (using $mvprog). -d create directories instead of installing files. -g GROUP $chgrp installed files to GROUP. -m MODE $chmod installed files to MODE. -o USER $chown installed files to USER. -s strip installed files (using $stripprog). -t=TRANSFORM --help display this help and exit. --version display version info and exit. Environment variables override the default commands: CHGRPPROG CHMODPROG CHOWNPROG CPPROG MKDIRPROG MVPROG RMPROG STRIPPROG " while test -n "$1"; do case $1 in -b=*) transformbasename=`echo $1 | sed 's/-b=//'` shift continue;; -c) instcmd=$cpprog shift continue;; -d) dir_arg=true shift continue;; -g) chgrpcmd="$chgrpprog $2" shift shift continue;; --help) echo "$usage"; exit 0;; -m) chmodcmd="$chmodprog $2" shift shift continue;; -o) chowncmd="$chownprog $2" shift shift continue;; -s) stripcmd=$stripprog shift continue;; -t=*) transformarg=`echo $1 | sed 's/-t=//'` shift continue;; --version) echo "$0 $scriptversion"; exit 0;; *) # When -d is used, all remaining arguments are directories to create. test -n "$dir_arg" && break # Otherwise, the last argument is the destination. Remove it from $@. for arg do if test -n "$dstarg"; then # $@ is not empty: it contains at least $arg. set fnord "$@" "$dstarg" shift # fnord fi shift # arg dstarg=$arg done break;; esac done if test -z "$1"; then if test -z "$dir_arg"; then echo "$0: no input file specified." >&2 exit 1 fi # It's OK to call `install-sh -d' without argument. # This can happen when creating conditional directories. exit 0 fi for src do # Protect names starting with `-'. case $src in -*) src=./$src ;; esac if test -n "$dir_arg"; then dst=$src src= if test -d "$dst"; then instcmd=: chmodcmd= else instcmd=$mkdirprog 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 test ! -f "$src" && test ! -d "$src"; then echo "$0: $src does not exist." >&2 exit 1 fi if test -z "$dstarg"; then echo "$0: no destination specified." >&2 exit 1 fi dst=$dstarg # Protect names starting with `-'. case $dst in -*) dst=./$dst ;; esac # If destination is a directory, append the input filename; won't work # if double slashes aren't ignored. if test -d "$dst"; then dst=$dst/`basename "$src"` fi fi # This sed command emulates the dirname command. dstdir=`echo "$dst" | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'` # Make sure that the destination directory exists. # Skip lots of stat calls in the usual case. if test ! -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 test $# -ne 0 ; do pathcomp=$pathcomp$1 shift test -d "$pathcomp" || $mkdirprog "$pathcomp" pathcomp=$pathcomp/ done fi if test -n "$dir_arg"; then $doit $instcmd "$dst" \ && { test -z "$chowncmd" || $doit $chowncmd "$dst"; } \ && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } \ && { test -z "$stripcmd" || $doit $stripcmd "$dst"; } \ && { test -z "$chmodcmd" || $doit $chmodcmd "$dst"; } else # If we're going to rename the final executable, determine the name now. if test -z "$transformarg"; then dstfile=`basename "$dst"` else dstfile=`basename "$dst" $transformbasename \ | sed $transformarg`$transformbasename fi # don't allow the sed command to completely eliminate the filename. test -z "$dstfile" && dstfile=`basename "$dst"` # Make a couple of temp file names in the proper directory. dsttmp=$dstdir/_inst.$$_ rmtmp=$dstdir/_rm.$$_ # Trap to clean up those temp files at exit. trap 'status=$?; rm -f "$dsttmp" "$rmtmp" && exit $status' 0 trap '(exit $?); exit' 1 2 13 15 # Move or copy the file name to the temp name $doit $instcmd "$src" "$dsttmp" && # 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. # { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } \ && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } \ && { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } \ && { test -z "$chmodcmd" || $doit $chmodcmd "$dsttmp"; } && # Now remove or move aside any old file at destination location. We # try this two ways since rm can't unlink itself on some systems and # the destination file might be busy for other reasons. In this case, # the final cleanup might fail but the new file should still install # successfully. { if test -f "$dstdir/$dstfile"; then $doit $rmcmd -f "$dstdir/$dstfile" 2>/dev/null \ || $doit $mvcmd -f "$dstdir/$dstfile" "$rmtmp" 2>/dev/null \ || { echo "$0: cannot unlink or rename $dstdir/$dstfile" >&2 (exit 1); exit } else : fi } && # Now rename the file to the real destination. $doit $mvcmd "$dsttmp" "$dstdir/$dstfile" fi || { (exit 1); exit; } done # The final little trick to "correctly" pass the exit status to the exit trap. { (exit 0); exit } # Local variables: # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-end: "$" # End: ocfs2-tools-ocfs2-tools-1.8.6/libo2cb/000077500000000000000000000000001347147137200173755ustar00rootroot00000000000000ocfs2-tools-ocfs2-tools-1.8.6/libo2cb/.gitignore000066400000000000000000000001011347147137200213550ustar00rootroot00000000000000*.sw? *.d debug_* o2cb_err.c o2cb_err.h cscope* libo2cb.a o2cb.7 ocfs2-tools-ocfs2-tools-1.8.6/libo2cb/Cscope.make000066400000000000000000000007341347147137200214540ustar00rootroot00000000000000.PHONY: cscope cscope: rm -f cscope.* echo "-k" >> cscope.files echo "-I inc" >> cscope.files find . -maxdepth 2 -name '*.c' -print >>cscope.files find . -maxdepth 2 -name '*.h' -print >>cscope.files find ../libocfs2/ -maxdepth 2 -name '*.c' -print >>cscope.files find ../libocfs2/ -maxdepth 2 -name '*.h' -print >>cscope.files find ../libo2dlm/ -maxdepth 2 -name '*.c' -print >>cscope.files find ../libo2dlm/ -maxdepth 2 -name '*.h' -print >>cscope.files cscope -b ocfs2-tools-ocfs2-tools-1.8.6/libo2cb/Makefile000066400000000000000000000025041347147137200210360ustar00rootroot00000000000000TOPDIR = .. include $(TOPDIR)/Preamble.make WARNINGS = -Wall -Wstrict-prototypes -Wmissing-prototypes \ -Wmissing-declarations INCLUDES = -I$(TOPDIR)/include -I. LIBRARIES = libo2cb.a CFLAGS += -fPIC ifneq ($(BUILD_CMAP_SUPPORT),) DEFINES += -DHAVE_CMAP endif ifneq ($(BUILD_FSDLM_SUPPORT),) DEFINES += -DHAVE_FSDLM endif ifneq ($(OCFS2_DEBUG_EXE),) DEBUG_EXE_FILES = $(shell awk '/DEBUG_EXE/{if (k[FILENAME] == 0) {print FILENAME; k[FILENAME] = 1;}}' $(CFILES)) DEBUG_EXE_PROGRAMS = $(addprefix debug_,$(subst .c,,$(DEBUG_EXE_FILES))) .SECONDARY: UNINST_PROGRAMS += $(DEBUG_EXE_PROGRAMS) debug_%.o : %.c $(CC) $(CFLAGS) $(LOCAL_CFLAGS) $(CPPFLAGS) $(LOCAL_CPPFLAGS) \ $(INCLUDES) $(DEFINES) \ -DDEBUG_EXE -o $@ -c $< debug_%: debug_%.o libo2cb.a $(LINK) $(COM_ERR_LIBS) endif CFILES = \ o2cb_abi.c \ o2cb_crc32.c \ client_proto.c HFILES = \ o2cb_abi.h \ o2cb_crc32.h HFILES_GEN = o2cb_err.h OBJS = $(subst .c,.o,$(CFILES)) \ o2cb_err.o ifneq ($(BUILD_CMAN_SUPPORT),) DEFINES += -DHAVE_CMAN endif $(OBJS): $(HFILES_GEN) o2cb_err.c o2cb_err.h: o2cb_err.et compile_et o2cb_err.et libo2cb.a: $(OBJS) rm -f $@ $(AR) r $@ $^ $(RANLIB) $@ MANS = o2cb.7 DIST_FILES = $(CFILES) $(HFILES) o2cb_err.et o2cb.7.in CLEAN_RULES = clean-err clean-err: rm -f o2cb_err.c o2cb_err.h include $(TOPDIR)/Postamble.make ocfs2-tools-ocfs2-tools-1.8.6/libo2cb/TODO000066400000000000000000000005351347147137200200700ustar00rootroot00000000000000* change the name and/or path of /etc/cluster.conf to something friendlier maybe /etc/sysconfig/cluster/xxxx or similar * change the cluster.conf format to jlbec's stanza style * harden the tools for setuid-ness * fix mount.ocfs2 to handle other normal mount options appropriately (at least handle rw, which is passed magically by /sbin/mount) ocfs2-tools-ocfs2-tools-1.8.6/libo2cb/client_proto.c000066400000000000000000000216431347147137200222500ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * Copyright (C) 2007 Oracle. All rights reserved. * * This copyrighted material is made available to anyone wishing to use, * modify, copy, or redistribute it subject to the terms and conditions * of the GNU General Public License v.2. */ #include #include #include #include #include #include #include #include #include #include #include "o2cb/o2cb_client_proto.h" struct client_message { char *cm_command; int cm_argcount; char *cm_format; }; #define BEGIN_MESSAGES(_list) struct client_message _list[] = { #define END_MESSAGES(_list) }; \ int _list##_len = sizeof(_list) / sizeof(_list[0]); #define DEFINE_MESSAGE(_name, _argcount, _format) [CM_##_name] = { \ .cm_command = #_name, \ .cm_argcount = _argcount, \ .cm_format = #_name " " _format, \ }, BEGIN_MESSAGES(message_list) DEFINE_MESSAGE(MOUNT, 5, "%s %s %s %s %s") DEFINE_MESSAGE(MRESULT, 4, "%s %s %d %s") DEFINE_MESSAGE(UNMOUNT, 3, "%s %s %s") DEFINE_MESSAGE(STATUS, 2, "%d %s") DEFINE_MESSAGE(LISTFS, 2, "%s %s") DEFINE_MESSAGE(LISTMOUNTS, 2, "%s %s") DEFINE_MESSAGE(LISTCLUSTERS, 0, "") DEFINE_MESSAGE(ITEMCOUNT, 1, "%u") DEFINE_MESSAGE(ITEM, 1, "%s") DEFINE_MESSAGE(DUMP, 0, "") END_MESSAGES(message_list) const char *message_to_string(client_message message) { return message_list[message].cm_command; } /* No short reads allowed */ static int full_read(int fd, void *buf, size_t count) { size_t off = 0; ssize_t rc = 0; while (off < count) { rc = read(fd, buf + off, count - off); if (rc == 0) return -EPIPE; if (rc == -1) { rc = -errno; if (rc == -EINTR) continue; break; } off += rc; rc = 0; } return rc; } /* No short writes allowed */ static int full_write(int fd, void *buf, size_t count) { size_t off = 0; ssize_t rc = 0; while (off < count) { rc = write(fd, buf + off, count - off); if (rc == 0) return -EPIPE; if (rc == -1) { rc = -errno; if (rc == -EINTR) continue; break; } off += rc; rc = 0; } return rc; } int send_message(int fd, client_message message, ...) { int rc; size_t len; va_list args; char mbuf[OCFS2_CONTROLD_MAXLINE]; memset(mbuf, 0, OCFS2_CONTROLD_MAXLINE); va_start(args, message); rc = vsnprintf(mbuf, OCFS2_CONTROLD_MAXLINE, message_list[message].cm_format, args); va_end(args); /* Remove the trailing space from zero-argument messages */ if (!message_list[message].cm_argcount) { len = strlen(mbuf); if (mbuf[len - 1] == ' ') mbuf[len - 1] = '\0'; } if (rc >= OCFS2_CONTROLD_MAXLINE) rc = -E2BIG; else rc = full_write(fd, mbuf, OCFS2_CONTROLD_MAXLINE); return rc; } static char *get_args(char *buf, int *argc, char **argv, char sep, int want) { char *p = buf, *rp = NULL; int i = 0; /* Skip the first word, which is the command */ p = strchr(buf, sep); if (!p) goto out; p += 1; argv[0] = p; for (i = 1; i < OCFS2_CONTROLD_MAXARGS; i++) { p = strchr(p, sep); if (!p) { rp = p + 1; break; } if (want == i) break; *p = '\0'; p += 1; argv[i] = p; } out: if (argc) *argc = i; /* Terminate the list, the caller expects us to */ argv[i] = NULL; /* we ended by hitting \0, return the point following that */ if (!rp) rp = strchr(buf, '\0') + 1; return rp; } int receive_message_full(int fd, char *buf, client_message *message, char **argv, char **rest) { int i, rc, len, count; client_message msg; char *r; rc = full_read(fd, buf, OCFS2_CONTROLD_MAXLINE); if (rc) goto out; /* Safety first */ buf[OCFS2_CONTROLD_MAXLINE - 1] = '\0'; /* fprintf(stderr, "Got messsage \"%s\"\n", buf); */ for (i = 0; i < message_list_len; i++) { len = strlen(message_list[i].cm_command); if (!strncmp(buf, message_list[i].cm_command, len) && ((buf[len] == ' ') || (buf[len] == '\0'))) break; } if (i >= message_list_len) { rc = -EBADMSG; goto out; } msg = i; r = get_args(buf, &count, argv, ' ', message_list[msg].cm_argcount); if (count != message_list[msg].cm_argcount) { rc = -EBADMSG; } else { /* for (i = 0; i < count; i++) fprintf(stderr, "Arg %d: \"%s\"\n", i, argv[i]); */ if (message) *message = msg; if (rest) *rest = r; } out: return rc; } int receive_message(int fd, char *buf, client_message *message, char **argv) { return receive_message_full(fd, buf, message, argv, NULL); } static int parse_itemcount(char **args, unsigned int *count) { int rc = 0; unsigned long n; char *ptr = NULL; n = strtoul(args[0], &ptr, 10); if (ptr && *ptr != '\0') { fprintf(stderr, "Invalid error code string: %s", args[0]); rc = -EINVAL; } else if ((n == LONG_MAX) || (n > UINT_MAX)) { fprintf(stderr, "Item count %lu out of range", n); rc = -ERANGE; } else { *count = n; } return rc; } int parse_status(char **args, int *error, char **error_msg) { int rc = 0; long err; char *ptr = NULL; err = strtol(args[0], &ptr, 10); if (ptr && *ptr != '\0') { fprintf(stderr, "Invalid error code string: %s", args[0]); rc = -EINVAL; } else if ((err == LONG_MIN) || (err == LONG_MAX) || (err < INT_MIN) || (err > INT_MAX)) { fprintf(stderr, "Error code %ld out of range", err); rc = -ERANGE; } else { *error_msg = args[1]; *error = err; } return rc; } /* * A list is sent as * * ITEMCOUNT * ITEM * x * STATUS 0 OK * * If there are errors in the middle, we'll get a STATUS error. */ int receive_list(int fd, char *buf, char ***ret_list) { int rc, done = 0; int error; unsigned int count = 0, seen = 0; char *error_msg; client_message message; char **list = NULL; char *argv[OCFS2_CONTROLD_MAXARGS + 1]; /* * States are simple. If list==NULL, we haven't gotten ITEMCOUNT * yet. If list!=NULL and seen= count) { rc = -E2BIG; fprintf(stderr, "Too many items!\n"); } else { list[seen] = strdup(argv[0]); if (!list[seen]) rc = -ENOMEM; else seen++; } break; default: rc = -EINVAL; fprintf(stderr, "Unexpected message %s from daemon\n", message_to_string(message)); break; } if (rc) done = 1; } if (!rc) { if (ret_list) *ret_list = list; } else if (list) { for (seen = 0; list[seen]; seen++) free(list[seen]); free(list); } return rc; } void free_received_list(char **list) { int i; for (i = 0; list[i]; i++) free(list[i]); free(list); } int client_listen(const char *path) { struct sockaddr_un addr; socklen_t addrlen; int rv, s; /* we listen for new client connections on socket s */ s = socket(AF_LOCAL, SOCK_STREAM, 0); if (s < 0) { /* log_error("socket error %d %d", s, errno); */ return s; } memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_LOCAL; strcpy(&addr.sun_path[1], path); addrlen = sizeof(sa_family_t) + strlen(addr.sun_path+1) + 1; rv = bind(s, (struct sockaddr *) &addr, addrlen); if (rv < 0) { /* log_error("bind error %d %d", rv, errno); */ close(s); return rv; } rv = listen(s, 5); if (rv < 0) { /* log_error("listen error %d %d", rv, errno); */ close(s); return rv; } /* log_debug("listen %d", s); */ return s; } int client_connect(const char *path) { struct sockaddr_un sun; socklen_t addrlen; int rv, fd; fd = socket(PF_UNIX, SOCK_STREAM, 0); if (fd < 0) { fd = -errno; goto out; } memset(&sun, 0, sizeof(sun)); sun.sun_family = AF_UNIX; strcpy(&sun.sun_path[1], path); addrlen = sizeof(sa_family_t) + strlen(sun.sun_path+1) + 1; rv = connect(fd, (struct sockaddr *) &sun, addrlen); if (rv < 0) { close(fd); fd = -errno; } out: return fd; } ocfs2-tools-ocfs2-tools-1.8.6/libo2cb/o2cb.7.in000066400000000000000000000250201347147137200207160ustar00rootroot00000000000000.TH "o2cb" "7" "August 2011" "Version @VERSION@" "OCFS2 Manual Pages" .SH "NAME" o2cb \- Default cluster stack of the \fIOCFS2\fR file system. .SH "SYNOPSIS" .PP \fBo2cb\fR is the default cluster stack of the \fIOCFS2\fR file system. It is an in-kernel cluster stack that includes a node manager (o2nm) to keep track of the nodes in the cluster, a disk heartbeat agent (o2hb) to detect node live-ness, a network agent (o2net) for intra-cluster node communication and a distributed lock manager (o2dlm) to keep track of lock resources. It also includes a synthetic file system, dlmfs, to allow applications to access the in-kernel dlm. .SH "CONFIGURATION" .PP The stack is configured using the \fBo2cb(8)\fR cluster configuration utility and operated (online/offline/status) using the \fIo2cb\fR init service. .TP \fBCLUSTER CONFIGURATION\fR It has two configuration files. One for the cluster layout (/etc/ocfs2/cluster.conf) and the other for the cluster timeouts, etc. (/etc/sysconfig/o2cb). More information about these two files can be found in \fBocfs2.cluster.conf(5)\fR and \fBo2cb.sysconfig(5)\fR. The \fBo2cb\fR cluster stack supports two heartbeat modes, namely, \fBlocal\fR and \fBglobal\fR. Only one heartbeat mode can be active at any one time. \fBLocal heartbeat\fR refers to disk heartbeating on all shared devices. In this mode, the \fIheartbeat is started during mount and stopped during umount\fR. This mode is easy to setup as it does not require configuring heartbeat devices. The one drawback in this mode is the overhead on servers having a large number of \fIOCFS2\fR mounts. For example, a server with 50 mounts will have 50 heartbeat threads. This is the default heartbeat mode. \fBGlobal heartbeat\fR, on the other hand, refers to heartbeating on specific shared devices. These devices are normal \fIOCFS2\fR formatted volumes that could also be mounted and used as clustered file systems. In this mode, the \fIheartbeat is started during cluster online and stopped during cluster offline\fR. While this mode can be used for all clusters, it is \fIstrongly\fR recommended for clusters having a large number of mounts. More information on disk heartbeat is provided below. .TP \fBKERNEL CONFIGURATION\fR Two sysctl values need to be set for \fBo2cb\fR to function properly. The first, panic_on_oops, must be enabled to turn a kernel oops into a panic. If a kernel thread required for \fBo2cb\fR to function crashes, the system must be reset to prevent a cluster hang. If it is not set, another node may not be able to distinguish whether a node is unable to respond or slow to respond. The other related sysctl parameter is panic, which specifies the number of seconds after a panic that the system will be auto-reset. Setting this parameter to zero disables autoreset; the cluster will require manual intervention. This is not preferred in a cluster environment. To manually enable panic on oops and set a 30 sec timeout for reboot on panic, do: .nf .ft 6 # echo 1 > /proc/sys/kernel/panic_on_oops # echo 30 > /proc/sys/kernel/panic .ft .fi To enable the above on every boot, add the following to /etc/sysctl.conf: .nf .ft 6 kernel.panic_on_oops = 1 kernel.panic = 30 .ft .fi .TP \fBOS CONFIGURATION\fR The \fBo2cb\fR cluster stack also requires iptables (firewalling) to be either disabled or modified to allow network traffic on the private network interface. The port used by \fBo2cb\fR is specified in /etc/ocfs2/cluster.conf. .SH "DISK HEARTBEAT" .PP O2CB uses disk heartbeat to detect node liveness. The disk heartbeat thread, \fBo2hb\fR, periodically reads and writes to a heartbeat file in a OCFS2 file system. Its write payload contains a sequence number that it increments in each write. This allows other nodes reading the same heartbeat file to detect the change and associate that with a live node. Conversely, a node whose sequence number has stopped changing is marked as a possible dead node. Possible. Not confirmed. That is because it just could be slow I/Os. To differentiate between a dead node and one that has slow I/Os, O2CB has a disk heartbeat threshold (timeout). Only nodes whose sequence number has not incremented for that duration are marked dead. However that node may not be dead but just experiencing slow I/O. To prevent that, the heartbeat thread keeps track of the time elapsed since the last completed write. If that time exceeds the timeout, it forces a self-fence. It does so to prevent other nodes from marking it as dead while it is still alive. This self-fencing scheme has proven to be very reliable as it relies on kernel timers and pci bus reset. External fencing, while attractive, is rarely as reliable as it relies on external hardware and software that is prone to failure due to misconfiguration, etc. Having said that, O2CB disk heartbeat has had its share of problems with self fencing. Nodes experiencing slow I/O on only one of multiple devices have to initiate self-fence. This is because in the default \fBlocal heartbeat\fR scheme, nodes in a cluster may not be heartbeating on the same set of devices. The \fBglobal heartbeat\fR mode addresses this shortcoming by introducing a scheme that forces all nodes to heartbeat on the same set of devices. In this scheme, a node experiencing a slowdown in I/O on a device may not need to initiate self-fence. It will only have to do so if it encounters slowdown on 50% or more of the heartbeat devices. In a cluster with 3 heartbeat regions, a slowdown in 1 region will be tolerated. In a cluster with 5 regions, a slowdown in 2 will be tolerated. It is for this reason, this mode is recommended for users that have 3 or more OCFS2 mounts. O2CB allows up to \fB32\fR heartbeat regions to be configured in the global heartbeat mode. .SH "ONLINE CLUSTER MODIFICATION" .PP The O2CB cluster stack allows \fIadding and removing nodes in an online cluster\fR when run in the \fBglobal\fR heartbeat mode. Use the \fBo2cb(8)\fR utility to make the changes in the configuration and (re)online the cluster using the \fIo2cb\fR init script. The user \fBmust\fR do the same on \fIall\fR nodes in the cluster. The cluster will not allow any new cluster mounts if the node configuration on all nodes is not the same. The removal of nodes will only succeed if that node is no longer in use. If the user removes an active node from the configuration, the re-online will fail. The cluster stack also allows \fIadding and removing heartbeat regions in an online cluster\fR. Use the \fBo2cb(8)\fR utility to make the changes in the configuration file and (re)online the cluster using the \fIo2cb\fR init script. The user \fBmust\fR do the same on \fIall\fR nodes in the cluster. The cluster will not allow any new cluster mounts if the heartbeat region configuration on all nodes is not the same. The removal of heartbeat regions will only succeed if the active heartbeat region count is greater than \fB3\fR. This is to protect against edge conditions that can destabilize the cluster. .SH "GETTING STARTED" .PP The first step in configuring \fBo2cb\fR is deciding whether to setup \fBlocal\fR or \fBglobal\fR heartbeat. If \fBglobal\fR heartbeat, then one has to format atleast one heartbeat device. To format a OCFS2 volume with global heartbeat enabled, do: .nf .ps 8 .ft 6 # mkfs.ocfs2 --cluster-stack=o2cb --cluster-name=webcluster --global-heartbeat -L "hbvol1" /dev/sdb1 .ft .ps .fi Once formatted, setup /etc/ocfs2/cluster.conf following the example provided in \fBocfs2.cluster.conf(5)\fR. If \fBlocal\fR heartbeat, then one can setup cluster.conf without any heartbeat devices. The next step is starting the cluster. To online the cluster stack, do: .nf .ft 6 # service o2cb online Loading stack plugin "o2cb": OK Loading filesystem "ocfs2_dlmfs": OK Mounting ocfs2_dlmfs filesystem at /dlm: OK Setting cluster stack "o2cb": OK Registering O2CB cluster "webcluster": OK Setting O2CB cluster timeouts : OK Starting global heartbeat for cluster "webcluster": OK .ft .fi Once the cluster stack is online, new \fBOCFS2\fR volumes can be formatted normally without specifying the cluster stack information. \fImkfs.ocfs2(8)\fR will pick up that information automatically. .nf .ft 6 # mkfs.ocfs2 -L "datavol" /dev/sdc1 .ft .fi Meanwhile existing volumes can be converted to the new cluster stack using \fBtunefs.ocfs2(8)\fR utility. .nf .ps 9 .ft 6 # tunefs.ocfs2 --update-cluster-stack /dev/sdd1 Updating on-disk cluster information to match the running cluster. DANGER: YOU MUST BE ABSOLUTELY SURE THAT NO OTHER NODE IS USING THIS FILESYSTEM BEFORE MODIFYING ITS CLUSTER CONFIGURATION. Update the on-disk cluster information? y .ft .ps .fi Another utility \fBmounted.ocfs2(8)\fR is useful is listing all the \fIOCFS2\fR volumes alonghwith the cluster stack information. To get a list of OCFS2 volumes, do: .nf .ps 9 .ft 6 # mounted.ocfs2 -d Device Stack Cluster F UUID Label /dev/sdb1 o2cb webcluster G DCDA2845177F4D59A0F2DCD8DE507CC3 hbvol1 /dev/sdc1 None 23878C320CF3478095D1318CB5C99EED localmount /dev/sdd1 o2cb webcluster G 8AB016CD59FC4327A2CDAB69F08518E3 webvol /dev/sdg1 o2cb webcluster G 77D95EF51C0149D2823674FCC162CF8B logsvol /dev/sdh1 o2cb webcluster G BBA1DBD0F73F449384CE75197D9B7098 scratch .ft .ps .fi The \fIo2cb\fR init script can also be used to check the status of the cluster, offline the cluster, etc. To check the status of the cluster stack, do: .nf .ft 6 # service o2cb status Driver for "configfs": Loaded Filesystem "configfs": Mounted Stack glue driver: Loaded Stack plugin "o2cb": Loaded Driver for "ocfs2_dlmfs": Loaded Filesystem "ocfs2_dlmfs": Mounted Checking O2CB cluster "webcluster": Online Heartbeat dead threshold: 62 Network idle timeout: 60000 Network keepalive delay: 2000 Network reconnect delay: 2000 Heartbeat mode: Global Checking O2CB heartbeat: Active 77D95EF51C0149D2823674FCC162CF8B /dev/sdg1 DCDA2845177F4D59A0F2DCD8DE507CC3 /dev/sdk1 BBA1DBD0F73F449384CE75197D9B7098 /dev/sdh1 Nodes in O2CB cluster: 6 7 10 Active userdlm domains: ovm .ft .fi To offline and unload the cluster stack, do: .nf .ft 6 # service o2cb offline Clean userdlm domains: OK Stopping global heartbeat on cluster "webcluster": OK Stopping O2CB cluster webcluster: OK Unregistering O2CB cluster "webcluster": OK # service o2cb unload Clean userdlm domains: OK Unmounting ocfs2_dlmfs filesystem: OK Unloading module "ocfs2_dlmfs": OK Unloading module "ocfs2_stack_o2cb": OK .ft .fi .SH "SEE ALSO" .BR o2cb(8) .BR o2cb.sysconfig(5) .BR ocfs2.cluster.conf(5) .BR o2hbmonitor(8) .SH "AUTHORS" Oracle Corporation .SH "COPYRIGHT" Copyright \(co 2004, 2011 Oracle. All rights reserved. ocfs2-tools-ocfs2-tools-1.8.6/libo2cb/o2cb_abi.c000066400000000000000000001542331347147137200212110ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * o2cb_abi.c * * Kernel<->User ABI for modifying cluster configuration. * * Copyright (C) 2004,2007 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License, version 2, as published by the Free Software Foundation. * * 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. */ #define _XOPEN_SOURCE 600 /* Triggers XOPEN2K in features.h */ #define _LARGEFILE64_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_CMAP #include #endif #ifdef HAVE_FSDLM #include #endif #include "o2cb/o2cb.h" #include "o2cb/o2cb_client_proto.h" #include "o2cb_abi.h" #include "o2cb_crc32.h" #include "ocfs2/ocfs2.h" #define CLUSTER_STACK_FILE "/sys/fs/ocfs2/cluster_stack" #define LOCKING_PROTOCOL_FILE "/sys/fs/ocfs2/max_locking_protocol" #define OCFS2_STACK_LABEL_LEN 4 #define CONTROL_DEVICE "/dev/misc/ocfs2_control" #define DLM_RECOVER_CALLBACK "/sys/fs/ocfs2/dlm_recover_callback_support" static errcode_t o2cb_validate_cluster_name(struct o2cb_cluster_desc *desc); static errcode_t o2cb_validate_cluster_flags(struct o2cb_cluster_desc *desc, int *globalhb); struct o2cb_stack_ops { errcode_t (*list_clusters)(char ***clusters); errcode_t (*begin_group_join)(struct o2cb_cluster_desc *cluster, struct o2cb_region_desc *region); errcode_t (*complete_group_join)(struct o2cb_cluster_desc *cluster, struct o2cb_region_desc *region, int result); errcode_t (*group_leave)(struct o2cb_cluster_desc *cluster, struct o2cb_region_desc *region); }; struct o2cb_stack { char s_name[OCFS2_STACK_LABEL_LEN + 1]; struct o2cb_stack_ops *s_ops; }; static errcode_t classic_list_clusters(char ***clusters); static errcode_t classic_begin_group_join(struct o2cb_cluster_desc *cluster, struct o2cb_region_desc *region); static errcode_t classic_complete_group_join(struct o2cb_cluster_desc *cluster, struct o2cb_region_desc *region, int result); static errcode_t classic_group_leave(struct o2cb_cluster_desc *cluster, struct o2cb_region_desc *region); static struct o2cb_stack_ops classic_ops = { .list_clusters = classic_list_clusters, .begin_group_join = classic_begin_group_join, .complete_group_join = classic_complete_group_join, .group_leave = classic_group_leave, }; static struct o2cb_stack classic_stack = { .s_name = "o2cb", .s_ops = &classic_ops, }; static errcode_t user_list_clusters(char ***clusters); static errcode_t user_begin_group_join(struct o2cb_cluster_desc *cluster, struct o2cb_region_desc *region); static errcode_t user_complete_group_join(struct o2cb_cluster_desc *cluster, struct o2cb_region_desc *region, int result); static errcode_t user_group_leave(struct o2cb_cluster_desc *cluster, struct o2cb_region_desc *region); static struct o2cb_stack_ops user_ops = { .list_clusters = user_list_clusters, .begin_group_join = user_begin_group_join, .complete_group_join = user_complete_group_join, .group_leave = user_group_leave, }; static struct o2cb_stack user_stack = { .s_ops = &user_ops, }; static struct o2cb_stack *current_stack; static int control_daemon_fd = -1; static int control_device_fd = -1; static char *configfs_path; static char *do_strchomp(char *str) { int len = strlen(str); char *p; if (!len) return str; p = str + len - 1; while (isspace(*p) && len--) *p-- = '\0'; return str; } static ssize_t read_single_line_file(const char *file, char *line, size_t count) { ssize_t ret = 0; FILE *f; f = fopen(file, "r"); if (f) { if (fgets(line, count, f)) ret = strlen(line); fclose(f); } else ret = -errno; return ret; } static int write_single_line_file(char *filename, char *line, size_t count) { ssize_t ret = 0; FILE *f; f = fopen(filename, "w"); if (f) { if (fputs(line, f)) ret = strlen(line); fclose(f); } else ret = -errno; return ret; } static ssize_t read_stack_file(char *line, size_t count) { return read_single_line_file(CLUSTER_STACK_FILE, line, count); } static errcode_t determine_stack(void) { ssize_t len; char line[100]; errcode_t err = O2CB_ET_SERVICE_UNAVAILABLE; int setup_performed = 0; redo: len = read_stack_file(line, sizeof(line)); if (len > 0) { if (line[len - 1] == '\n') { line[len - 1] = '\0'; len--; } if (len != OCFS2_STACK_LABEL_LEN) err = O2CB_ET_INTERNAL_FAILURE; else if (!strcmp(line, classic_stack.s_name)) { current_stack = &classic_stack; err = 0; } else { strncpy(user_stack.s_name, line, OCFS2_STACK_LABEL_LEN); current_stack = &user_stack; err = 0; } } else if (len == -ENOENT) { if (!setup_performed) { o2cb_setup_stack(OCFS2_CLASSIC_CLUSTER_STACK); setup_performed = 1; goto redo; } else { current_stack = &classic_stack; err = 0; } } return err; } errcode_t o2cb_get_stack_name(const char **name) { if (!current_stack) return O2CB_ET_SERVICE_UNAVAILABLE; *name = current_stack->s_name; return 0; } static ssize_t read_locking_proto_file(char *line, size_t count) { return read_single_line_file(LOCKING_PROTOCOL_FILE, line, count); } errcode_t o2cb_get_max_locking_protocol(struct ocfs2_protocol_version *proto) { ssize_t len; char line[100]; errcode_t err; unsigned int major, minor; len = read_locking_proto_file(line, sizeof(line)); if (len <= 0) { switch (-len) { case EACCES: case EPERM: err = O2CB_ET_PERMISSION_DENIED; break; case ENOMEM: err = O2CB_ET_NO_MEMORY; break; case 0: case ENOENT: case ENOTDIR: err = O2CB_ET_SERVICE_UNAVAILABLE; break; default: err = O2CB_ET_INTERNAL_FAILURE; break; } goto out; } if (line[len - 1] == '\n') { line[len - 1] = '\0'; len--; } err = O2CB_ET_SERVICE_UNAVAILABLE; if (sscanf(line, "%u.%u", &major, &minor) != 2) goto out; /* Major and minor can't be more than a u8 */ if ((major > (uint8_t)-1) || (minor > (uint8_t)-1)) goto out; proto->pv_major = major; proto->pv_minor = minor; err = 0; out: return err; } errcode_t o2cb_create_cluster(const char *cluster_name) { char path[PATH_MAX]; int ret; errcode_t err = 0; ret = snprintf(path, PATH_MAX - 1, O2CB_FORMAT_CLUSTER, configfs_path, cluster_name); if ((ret <= 0) || (ret == (PATH_MAX - 1))) return O2CB_ET_INTERNAL_FAILURE; ret = mkdir(path, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); if (ret) { switch (errno) { case EEXIST: err = O2CB_ET_CLUSTER_EXISTS; break; case EACCES: case EPERM: case EROFS: err = O2CB_ET_PERMISSION_DENIED; break; case ENOMEM: err = O2CB_ET_NO_MEMORY; break; case ENOTDIR: case ENOENT: err = O2CB_ET_SERVICE_UNAVAILABLE; break; default: err = O2CB_ET_INTERNAL_FAILURE; break; } } return err; } errcode_t o2cb_remove_cluster(const char *cluster_name) { char path[PATH_MAX]; int ret; errcode_t err = 0; ret = snprintf(path, PATH_MAX - 1, O2CB_FORMAT_CLUSTER, configfs_path, cluster_name); if ((ret <= 0) || (ret == (PATH_MAX - 1))) return O2CB_ET_INTERNAL_FAILURE; ret = rmdir(path); if (ret) { switch (errno) { case EACCES: case EPERM: case EROFS: err = O2CB_ET_PERMISSION_DENIED; break; case ENOMEM: err = O2CB_ET_NO_MEMORY; break; case ENOTDIR: err = O2CB_ET_SERVICE_UNAVAILABLE; break; case ENOENT: err = 0; break; default: err = O2CB_ET_INTERNAL_FAILURE; break; } } return err; } static int do_read(int fd, void *bytes, size_t count) { int total = 0; int ret; while (total < count) { ret = read(fd, bytes + total, count - total); if (ret < 0) { ret = -errno; if ((ret == -EAGAIN) || (ret == -EINTR)) continue; total = ret; break; } if (ret == 0) break; total += ret; } return total; } static errcode_t do_write(int fd, const void *bytes, size_t count) { int total = 0; int ret; int err = 0; while (total < count) { ret = write(fd, bytes + total, count - total); if (ret < 0) { ret = -errno; if ((ret == -EAGAIN) || (ret == -EINTR)) continue; if (ret == -EIO) err = O2CB_ET_IO; else err = O2CB_ET_INTERNAL_FAILURE; break; } total += ret; } return err; } static errcode_t o2cb_set_attribute(const char *attr_path, const char *attr_value) { errcode_t err = 0; int fd; fd = open(attr_path, O_WRONLY); if (fd < 0) { switch (errno) { default: err = O2CB_ET_INTERNAL_FAILURE; break; case ENOTDIR: case ENOENT: case EISDIR: err = O2CB_ET_SERVICE_UNAVAILABLE; break; case EACCES: case EPERM: case EROFS: err = O2CB_ET_PERMISSION_DENIED; break; } } else { err = do_write(fd, attr_value, strlen(attr_value)); close(fd); } return err; } static errcode_t o2cb_get_attribute(const char *attr_path, char *attr_value, size_t count) { int ret; errcode_t err = 0; int fd; fd = open(attr_path, O_RDONLY); if (fd < 0) { switch (errno) { default: err = O2CB_ET_INTERNAL_FAILURE; break; case ENOTDIR: case ENOENT: case EISDIR: err = O2CB_ET_SERVICE_UNAVAILABLE; break; case EACCES: case EPERM: case EROFS: err = O2CB_ET_PERMISSION_DENIED; break; } } else { ret = do_read(fd, attr_value, count); close(fd); if (ret == -EIO) err = O2CB_ET_IO; else if (ret < 0) err = O2CB_ET_INTERNAL_FAILURE; else if (ret < count) attr_value[ret] = '\0'; } return err; } static errcode_t o2cb_set_node_attribute(const char *cluster_name, const char *node_name, const char *attr_name, const char *attr_value) { int ret; char attr_path[PATH_MAX]; ret = snprintf(attr_path, PATH_MAX - 1, O2CB_FORMAT_NODE_ATTR, configfs_path, cluster_name, node_name, attr_name); if ((ret <= 0) || (ret == (PATH_MAX - 1))) return O2CB_ET_INTERNAL_FAILURE; return o2cb_set_attribute(attr_path, attr_value); } static errcode_t o2cb_get_node_attribute(const char *cluster_name, const char *node_name, const char *attr_name, char *attr_value, size_t count) { int ret; char attr_path[PATH_MAX]; ret = snprintf(attr_path, PATH_MAX - 1, O2CB_FORMAT_NODE_ATTR, configfs_path, cluster_name, node_name, attr_name); if ((ret <= 0) || (ret == (PATH_MAX - 1))) return O2CB_ET_INTERNAL_FAILURE; return o2cb_get_attribute(attr_path, attr_value, count); } /* XXX there is no commit yet, so this just creates the node in place * and then sets the attributes in order. if the ipaddr is set * successfully then the node is live */ errcode_t o2cb_add_node(const char *cluster_name, const char *node_name, const char *node_num, const char *ip_address, const char *ip_port, const char *local) { char node_path[PATH_MAX]; int ret; errcode_t err; ret = snprintf(node_path, PATH_MAX - 1, O2CB_FORMAT_NODE, configfs_path, cluster_name, node_name); if (ret <= 0 || ret == PATH_MAX - 1) { err = O2CB_ET_INTERNAL_FAILURE; goto out; } ret = mkdir(node_path, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); if (ret) { switch (errno) { case EEXIST: err = O2CB_ET_NODE_EXISTS; break; case EACCES: case EPERM: case EROFS: err = O2CB_ET_PERMISSION_DENIED; break; case ENOMEM: err = O2CB_ET_NO_MEMORY; break; case ENOTDIR: case ENOENT: err = O2CB_ET_SERVICE_UNAVAILABLE; break; default: err = O2CB_ET_INTERNAL_FAILURE; break; } goto out; } err = o2cb_set_node_attribute(cluster_name, node_name, "ipv4_port", ip_port); if (err) goto out_rmdir; err = o2cb_set_node_attribute(cluster_name, node_name, "ipv4_address", ip_address); if (err) goto out_rmdir; err = o2cb_set_node_attribute(cluster_name, node_name, "num", node_num); if (err) goto out_rmdir; err = o2cb_set_node_attribute(cluster_name, node_name, "local", local); out_rmdir: if (err) rmdir(node_path); out: return err; } errcode_t o2cb_del_node(const char *cluster_name, const char *node_name) { char node_path[PATH_MAX]; int ret; errcode_t err = 0; ret = snprintf(node_path, PATH_MAX - 1, O2CB_FORMAT_NODE, configfs_path, cluster_name, node_name); if (ret <= 0 || ret == PATH_MAX - 1) { err = O2CB_ET_INTERNAL_FAILURE; goto out; } ret = rmdir(node_path); if (ret) { switch (errno) { case EACCES: case EPERM: case EROFS: err = O2CB_ET_PERMISSION_DENIED; break; case ENOMEM: err = O2CB_ET_NO_MEMORY; break; case ENOTDIR: err = O2CB_ET_SERVICE_UNAVAILABLE; break; case ENOENT: err = 0; break; default: err = O2CB_ET_INTERNAL_FAILURE; break; } } out: return err; } static errcode_t try_file(const char *name, int *fd) { int open_fd; errcode_t err = 0; open_fd = open(name, O_RDONLY); if (open_fd < 0) { switch (errno) { default: err = O2CB_ET_INTERNAL_FAILURE; break; case ENOTDIR: case ENOENT: case EISDIR: err = O2CB_ET_SERVICE_UNAVAILABLE; break; case EACCES: case EPERM: case EROFS: err = O2CB_ET_PERMISSION_DENIED; break; } } if (!err) *fd = open_fd; return err; } #define O2CB_NEW_CONFIGFS_PATH "/sys/kernel" #define O2CB_OLD_CONFIGFS_PATH "" #define CONFIGFS_MAGIC 0x62656570 static errcode_t try_configfs_path(const char *path) { errcode_t ret; char attr_path[PATH_MAX]; struct stat64 stat_buf; struct statfs64 statfs_buf; ret = snprintf(attr_path, PATH_MAX - 1, CONFIGFS_FORMAT_PATH, path); if ((ret <= 0) || (ret == (PATH_MAX - 1))) return O2CB_ET_INTERNAL_FAILURE; ret = stat64(attr_path, &stat_buf); if (ret || !S_ISDIR(stat_buf.st_mode)) return O2CB_ET_SERVICE_UNAVAILABLE; ret = statfs64(attr_path, &statfs_buf); if (ret || (statfs_buf.f_type != CONFIGFS_MAGIC)) return O2CB_ET_SERVICE_UNAVAILABLE; return 0; } static errcode_t init_configfs(void) { configfs_path = O2CB_NEW_CONFIGFS_PATH; if (!try_configfs_path(configfs_path)) return 0; configfs_path = O2CB_OLD_CONFIGFS_PATH; if (!try_configfs_path(configfs_path)) return 0; configfs_path = NULL; return O2CB_ET_SERVICE_UNAVAILABLE; } #define O2CB_INTERFACE_REVISION_PATH_OLD_PROC "/proc/fs/ocfs2_nodemanager/interface_revision" #define O2CB_INTERFACE_REVISION_PATH_OLD_SYS "/sys/o2cb/interface_revision" #define O2CB_INTERFACE_REVISION_PATH "/sys/fs/o2cb/interface_revision" errcode_t o2cb_init(void) { int ret, fd; unsigned int module_version; errcode_t err; char revision_string[16]; err = determine_stack(); if (err) return err; err = try_file(O2CB_INTERFACE_REVISION_PATH, &fd); if (err == O2CB_ET_SERVICE_UNAVAILABLE) err = try_file(O2CB_INTERFACE_REVISION_PATH_OLD_SYS, &fd); if (err == O2CB_ET_SERVICE_UNAVAILABLE) err = try_file(O2CB_INTERFACE_REVISION_PATH_OLD_PROC, &fd); if (err) return err; ret = do_read(fd, revision_string, sizeof(revision_string) - 1); close(fd); if (ret < 0) { err = O2CB_ET_INTERNAL_FAILURE; if (ret == -EIO) err = O2CB_ET_IO; return err; } revision_string[ret] = '\0'; ret = sscanf(revision_string, "%u\n", &module_version); if (ret < 0) return O2CB_ET_INTERNAL_FAILURE; if (O2NM_API_VERSION < module_version) return O2CB_ET_BAD_VERSION; return init_configfs(); } /* o2cb_get_region_attribute() would just be s/set/get/ of this function */ static errcode_t o2cb_set_region_attribute(const char *cluster_name, const char *region_name, const char *attr_name, const char *attr_value) { int ret; char attr_path[PATH_MAX]; ret = snprintf(attr_path, PATH_MAX - 1, O2CB_FORMAT_HEARTBEAT_REGION_ATTR, configfs_path, cluster_name, region_name, attr_name); if ((ret <= 0) || (ret == (PATH_MAX - 1))) return O2CB_ET_INTERNAL_FAILURE; return o2cb_set_attribute(attr_path, attr_value); } static errcode_t _fake_default_cluster(char *cluster) { errcode_t ret; char **clusters; ret = o2cb_list_clusters(&clusters); if (ret) return ret; snprintf(cluster, NAME_MAX - 1, "%s", clusters[0]); o2cb_free_cluster_list(clusters); return 0; } static errcode_t o2cb_create_heartbeat_region(const char *cluster_name, const char *region_name, const char *device_name, int block_bytes, uint64_t start_block, uint64_t blocks) { char _fake_cluster_name[NAME_MAX]; char region_path[PATH_MAX]; char num_buf[NAME_MAX]; int ret, fd; errcode_t err; if (!cluster_name) { err = _fake_default_cluster(_fake_cluster_name); if (err) return err; cluster_name = _fake_cluster_name; } #define O2CB_MAXIMUM_HEARTBEAT_BLOCKSIZE 4096 if (block_bytes > O2CB_MAXIMUM_HEARTBEAT_BLOCKSIZE) { err = O2CB_ET_INVALID_BLOCK_SIZE; goto out; } #define O2CB_MAX_NODE_COUNT 255 if (!blocks || (blocks > O2CB_MAX_NODE_COUNT)) { err = O2CB_ET_INVALID_BLOCK_COUNT; goto out; } ret = snprintf(region_path, PATH_MAX - 1, O2CB_FORMAT_HEARTBEAT_REGION, configfs_path, cluster_name, region_name); if (ret <= 0 || ret == PATH_MAX - 1) { err = O2CB_ET_INTERNAL_FAILURE; goto out; } ret = mkdir(region_path, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); if (ret) { switch (errno) { case EEXIST: err = O2CB_ET_REGION_EXISTS; break; case EACCES: case EPERM: case EROFS: err = O2CB_ET_PERMISSION_DENIED; break; case ENOMEM: err = O2CB_ET_NO_MEMORY; break; case ENOTDIR: case ENOENT: err = O2CB_ET_SERVICE_UNAVAILABLE; break; default: err = O2CB_ET_INTERNAL_FAILURE; break; } goto out; } ret = snprintf(num_buf, NAME_MAX - 1, "%d", block_bytes); if (ret <= 0 || ret == PATH_MAX - 1) { err = O2CB_ET_INTERNAL_FAILURE; goto out_rmdir; } err = o2cb_set_region_attribute(cluster_name, region_name, "block_bytes", num_buf); if (err) goto out_rmdir; ret = snprintf(num_buf, NAME_MAX - 1, "%"PRIu64, start_block); if (ret <= 0 || ret == PATH_MAX - 1) { err = O2CB_ET_INTERNAL_FAILURE; goto out_rmdir; } err = o2cb_set_region_attribute(cluster_name, region_name, "start_block", num_buf); if (err) goto out_rmdir; ret = snprintf(num_buf, NAME_MAX - 1, "%"PRIu64, blocks); if (ret <= 0 || ret == PATH_MAX - 1) { err = O2CB_ET_INTERNAL_FAILURE; goto out_rmdir; } err = o2cb_set_region_attribute(cluster_name, region_name, "blocks", num_buf); if (err) goto out_rmdir; fd = open64(device_name, O_RDWR); if (fd < 0) { switch (errno) { default: err = O2CB_ET_INTERNAL_FAILURE; break; case ENOTDIR: case ENOENT: case EISDIR: err = O2CB_ET_SERVICE_UNAVAILABLE; break; case EACCES: case EPERM: case EROFS: err = O2CB_ET_PERMISSION_DENIED; break; } goto out_rmdir; } ret = snprintf(num_buf, NAME_MAX - 1, "%d", fd); if (ret <= 0 || ret == PATH_MAX - 1) { err = O2CB_ET_INTERNAL_FAILURE; goto out_close; } err = o2cb_set_region_attribute(cluster_name, region_name, "dev", num_buf); out_close: close(fd); out_rmdir: if (err) rmdir(region_path); out: return err; } static errcode_t o2cb_destroy_sem_set(int semid) { int error; errcode_t ret = 0; error = semctl(semid, 0, IPC_RMID); if (error) { switch(errno) { case EPERM: case EACCES: ret = O2CB_ET_PERMISSION_DENIED; break; case EIDRM: /* Someone raced us to it... can this * happen? */ ret = 0; break; default: ret = O2CB_ET_INTERNAL_FAILURE; } } return ret; } static errcode_t o2cb_get_semid(const char *region, int *semid) { int ret; key_t key; key = (key_t) o2cb_crc32(region); ret = semget(key, 2, IPC_CREAT); if (ret < 0) return O2CB_ET_BAD_SEM; *semid = ret; return 0; } static inline errcode_t o2cb_semop_err(int err) { errcode_t ret; switch (err) { case EACCES: ret = O2CB_ET_PERMISSION_DENIED; break; case EIDRM: /* Other paths depend on us returning this for EIDRM */ ret = O2CB_ET_NO_SEM; break; case EINVAL: ret = O2CB_ET_SERVICE_UNAVAILABLE; break; case ENOMEM: ret = O2CB_ET_NO_MEMORY; break; default: ret = O2CB_ET_INTERNAL_FAILURE; } return ret; } static errcode_t o2cb_mutex_down(int semid) { int err; struct sembuf sops[2] = { { .sem_num = 0, .sem_op = 0, .sem_flg = SEM_UNDO }, { .sem_num = 0, .sem_op = 1, .sem_flg = SEM_UNDO } }; err = semop(semid, sops, 2); if (err) return o2cb_semop_err(errno); return 0; } /* We have coded our semaphore destruction such that you will legally * only get EIDRM when waiting on the mutex. Use this function to look * it up and return it locked - it knows how to loop around on * EIDRM. */ static errcode_t o2cb_mutex_down_lookup(const char *region, int *semid) { int tmpid; errcode_t ret; ret = O2CB_ET_NO_SEM; while (ret == O2CB_ET_NO_SEM) { ret = o2cb_get_semid(region, &tmpid); if (ret) return ret; ret = o2cb_mutex_down(tmpid); if (!ret) { /* At this point, we're the only ones who can destroy * this sem set. */ *semid = tmpid; } } return ret; } static errcode_t o2cb_mutex_up(int semid) { int err; struct sembuf sop = { .sem_num = 0, .sem_op = -1, .sem_flg = SEM_UNDO }; err = semop(semid, &sop, 1); if (err) return o2cb_semop_err(errno); return 0; } static errcode_t __o2cb_get_ref(int semid, int undo) { int err; struct sembuf sop = { .sem_num = 1, .sem_op = 1, .sem_flg = undo ? SEM_UNDO : 0 }; err = semop(semid, &sop, 1); if (err) return o2cb_semop_err(errno); return 0; } errcode_t o2cb_get_region_ref(const char *region_name, int undo) { errcode_t ret, up_ret; int semid; ret = o2cb_mutex_down_lookup(region_name, &semid); if (ret) return ret; ret = __o2cb_get_ref(semid, undo); /* XXX: Maybe try to drop ref if we get an error here? */ up_ret = o2cb_mutex_up(semid); if (up_ret && !ret) ret = up_ret; return ret; } static errcode_t __o2cb_drop_ref(int semid, int undo) { int err; struct sembuf sop = { .sem_num = 1, .sem_op = -1, .sem_flg = undo ? SEM_UNDO : 0 }; err = semop(semid, &sop, 1); if (err) return o2cb_semop_err(errno); return 0; } errcode_t o2cb_put_region_ref(const char *region_name, int undo) { errcode_t ret, up_ret; int semid; ret = o2cb_mutex_down_lookup(region_name, &semid); if (ret) return ret; ret = __o2cb_drop_ref(semid, undo); up_ret = o2cb_mutex_up(semid); if (up_ret && !ret) ret = up_ret; return ret; } static errcode_t __o2cb_get_num_refs(int semid, int *num_refs) { int ret; ret = semctl(semid, 1, GETVAL, NULL); if (ret == -1) return o2cb_semop_err(errno); *num_refs = ret; return 0; } errcode_t o2cb_num_region_refs(const char *region_name, int *num_refs) { errcode_t ret; int semid; ret = o2cb_get_semid(region_name, &semid); if (ret) return ret; ret = __o2cb_get_num_refs(semid, num_refs); /* The semaphore set was destroyed underneath us. We treat * that as zero reference and return success. */ if (ret == O2CB_ET_NO_SEM) { *num_refs = 0; ret = 0; } return ret; } static errcode_t o2cb_remove_heartbeat_region(const char *cluster_name, const char *region_name) { char _fake_cluster_name[NAME_MAX]; char region_path[PATH_MAX]; int ret; errcode_t err = 0; if (!cluster_name) { err = _fake_default_cluster(_fake_cluster_name); if (err) return err; cluster_name = _fake_cluster_name; } ret = snprintf(region_path, PATH_MAX - 1, O2CB_FORMAT_HEARTBEAT_REGION, configfs_path, cluster_name, region_name); if (ret <= 0 || ret == PATH_MAX - 1) { err = O2CB_ET_INTERNAL_FAILURE; goto out; } ret = rmdir(region_path); if (ret) { switch (errno) { case EACCES: case EPERM: case EROFS: err = O2CB_ET_PERMISSION_DENIED; break; case ENOMEM: err = O2CB_ET_NO_MEMORY; break; case ENOTDIR: case ENOENT: err = O2CB_ET_SERVICE_UNAVAILABLE; break; case ENOTEMPTY: case EBUSY: err = O2CB_ET_REGION_IN_USE; break; default: err = O2CB_ET_INTERNAL_FAILURE; break; } } out: return err; } errcode_t o2cb_start_heartbeat(struct o2cb_cluster_desc *cluster, struct o2cb_region_desc *region) { errcode_t ret, up_ret; int semid, global = 0; ret = o2cb_mutex_down_lookup(region->r_name, &semid); if (ret) return ret; ret = o2cb_global_heartbeat_mode(cluster->c_cluster, &global); if (ret) goto up; ret = o2cb_create_heartbeat_region(cluster->c_cluster, region->r_name, region->r_device_name, region->r_block_bytes, region->r_start_block, region->r_blocks); if (ret && ret != O2CB_ET_REGION_EXISTS) goto up; if (global && ret == O2CB_ET_REGION_EXISTS) { ret = 0; goto up; } ret = __o2cb_get_ref(semid, !region->r_persist); if (ret) o2cb_remove_heartbeat_region(cluster->c_cluster, region->r_name); up: up_ret = o2cb_mutex_up(semid); if (up_ret && !ret) ret = up_ret; return ret; } errcode_t o2cb_stop_heartbeat(struct o2cb_cluster_desc *cluster, struct o2cb_region_desc *region) { errcode_t ret, up_ret; int hb_refs = 0; int semid; ret = o2cb_mutex_down_lookup(region->r_name, &semid); if (ret) return ret; ret = __o2cb_get_num_refs(semid, &hb_refs); if (ret) goto up; /* A previous process may have died and left us with no * references on the region. We avoid a negative error count * here and clean up the region as normal. */ if (hb_refs) { ret = __o2cb_drop_ref(semid, !region->r_persist); if (ret) goto up; /* No need to call get_num_refs again -- this was * atomic so we know what the new value must be. */ hb_refs--; } if (!hb_refs) { /* XXX: If this fails, shouldn't we still destroy the * semaphore set? */ ret = o2cb_remove_heartbeat_region(cluster->c_cluster, region->r_name); if (ret) goto up; ret = o2cb_destroy_sem_set(semid); if (ret) goto up; goto done; } up: up_ret = o2cb_mutex_up(semid); if (up_ret && !ret) /* XXX: Maybe stop heartbeat here then? */ ret = up_ret; done: return ret; } /* For ref counting purposes, we need to know whether this process * called o2cb_create_heartbeat_region_disk. If it did, then we want * to drop the reference taken during startup, otherwise that * reference was dropped automatically at process shutdown so there's * no need to drop one here. */ static errcode_t classic_group_leave(struct o2cb_cluster_desc *cluster, struct o2cb_region_desc *region) { errcode_t ret; int globalhb; ret = o2cb_validate_cluster_name(cluster); if (ret) goto bail; ret = o2cb_validate_cluster_flags(cluster, &globalhb); if (ret) goto bail; if (!globalhb) ret = o2cb_stop_heartbeat(cluster, region); bail: return ret; } /* * Cluster stack is validated in o2cb_begin_group_join(). Here we validate * the cluster name and the cluster flags (aka heartbeat mode). */ static errcode_t classic_begin_group_join(struct o2cb_cluster_desc *cluster, struct o2cb_region_desc *region) { errcode_t ret; int globalhb; ret = o2cb_validate_cluster_name(cluster); if (ret) goto bail; ret = o2cb_validate_cluster_flags(cluster, &globalhb); if (ret) goto bail; if (!globalhb) ret = o2cb_start_heartbeat(cluster, region); bail: return ret; } static errcode_t classic_complete_group_join(struct o2cb_cluster_desc *cluster, struct o2cb_region_desc *region, int result) { errcode_t ret = 0; if (result) ret = classic_group_leave(cluster, region); return ret; } static errcode_t user_parse_status(char **args, int *error, char **error_msg) { errcode_t err = O2CB_ET_IO; long result; char *ptr = NULL; result = strtol(args[0], &ptr, 10); if (ptr && *ptr != '\0') { /* fprintf(stderr, "Invalid error code string: %s", args[0]); */ } else if ((result == LONG_MIN) || (result == LONG_MAX) || (result < INT_MIN) || (result > INT_MAX)) { /* fprintf(stderr, "Error code %ld out of range", err); */ } else { *error_msg = args[1]; *error = result; err = 0; } return err; } static errcode_t user_begin_group_join(struct o2cb_cluster_desc *cluster, struct o2cb_region_desc *region) { errcode_t err; int rc; int error; char *error_msg; client_message message; char *argv[OCFS2_CONTROLD_MAXARGS + 1]; char buf[OCFS2_CONTROLD_MAXLINE]; #ifdef HAVE_FSDLM uint32_t maj, min, pat; if (strncmp(cluster->c_stack, OCFS2_PCMK_CLUSTER_STACK, OCFS2_STACK_LABEL_LEN)) goto no_pcmk; rc = dlm_kernel_version(&maj, &min, &pat); if (rc < 0) return O2CB_ET_SERVICE_UNAVAILABLE; if (read_single_line_file(DLM_RECOVER_CALLBACK, buf, 3) > 0) { /* Controld is not required */ if (maj < 6) return O2CB_ET_INTERNAL_FAILURE; return 0; } no_pcmk: #endif if (control_daemon_fd != -1) { /* fprintf(stderr, "Join already in progress!\n"); */ err = O2CB_ET_INTERNAL_FAILURE; goto out; } rc = ocfs2_client_connect(); if (rc < 0) { /* fprintf(stderr, "Unable to connect to ocfs2_controld: %s\n", strerror(-rc)); */ switch (rc) { case -EACCES: case -EPERM: err = O2CB_ET_PERMISSION_DENIED; break; default: err = O2CB_ET_SERVICE_UNAVAILABLE; break; } goto out; } control_daemon_fd = rc; rc = send_message(control_daemon_fd, CM_MOUNT, OCFS2_FS_NAME, region->r_name, cluster->c_cluster, region->r_device_name, region->r_service); if (rc) { /* fprintf(stderr, "Unable to send MOUNT message: %s\n", strerror(-rc)); */ err = O2CB_ET_IO; goto out; } rc = receive_message(control_daemon_fd, buf, &message, argv); if (rc < 0) { /* fprintf(stderr, "Error reading from daemon: %s\n", strerror(-rc)); */ err = O2CB_ET_IO; goto out; } switch (message) { case CM_STATUS: err = user_parse_status(argv, &error, &error_msg); if (err) { /* fprintf(stderr, "Bad status message: %s\n", strerror(-rc)); */ goto out; } if (error && (error != EALREADY)) { /* fprintf(stderr, "Error %d from daemon: %s\n", error, error_msg); */ err = O2CB_ET_CONFIGURATION_ERROR; goto out; } break; default: /* fprintf(stderr, "Unexpected message %s from daemon\n", message_to_string(message)); */ err = O2CB_ET_INTERNAL_FAILURE; goto out; break; } err = 0; out: if (err && (control_daemon_fd != -1)) { close(control_daemon_fd); control_daemon_fd = -1; } return err; } static errcode_t user_complete_group_join(struct o2cb_cluster_desc *cluster, struct o2cb_region_desc *region, int result) { errcode_t err = O2CB_ET_SERVICE_UNAVAILABLE; int rc; int error; char *error_msg; client_message message; char *argv[OCFS2_CONTROLD_MAXARGS + 1]; char buf[OCFS2_CONTROLD_MAXLINE]; #ifdef HAVE_FSDLM if (read_single_line_file(DLM_RECOVER_CALLBACK, buf, 3) > 0) { /* Controld is not required */ return 0; } #endif if (control_daemon_fd == -1) { /* fprintf(stderr, "Join not started!\n"); */ err = O2CB_ET_SERVICE_UNAVAILABLE; goto out; } rc = send_message(control_daemon_fd, CM_MRESULT, OCFS2_FS_NAME, region->r_name, result, region->r_service); if (rc) { /* fprintf(stderr, "Unable to send MRESULT message: %s\n", strerror(-rc)); */ err = O2CB_ET_IO; goto out; } rc = receive_message(control_daemon_fd, buf, &message, argv); if (rc < 0) { /* fprintf(stderr, "Error reading from daemon: %s\n", strerror(-rc)); */ err = O2CB_ET_IO; goto out; } switch (message) { case CM_STATUS: err = user_parse_status(argv, &error, &error_msg); if (err) { /* fprintf(stderr, "Bad status message: %s\n", strerror(-rc)); */ goto out; } if (error) { /* fprintf(stderr, "Error %d from daemon: %s\n", error, error_msg); */ err = O2CB_ET_CONFIGURATION_ERROR; } break; default: /* fprintf(stderr, "Unexpected message %s from daemon\n", message_to_string(message)); */ err = O2CB_ET_INTERNAL_FAILURE; goto out; break; } err = 0; out: if (control_daemon_fd != -1) { close(control_daemon_fd); control_daemon_fd = -1; } return err; } static errcode_t user_group_leave(struct o2cb_cluster_desc *cluster, struct o2cb_region_desc *region) { errcode_t err = O2CB_ET_SERVICE_UNAVAILABLE; int rc; int error; char *error_msg; client_message message; char *argv[OCFS2_CONTROLD_MAXARGS + 1]; char buf[OCFS2_CONTROLD_MAXLINE]; #ifdef HAVE_FSDLM if (read_single_line_file(DLM_RECOVER_CALLBACK, buf, 3) > 0) { /* Controld is not required */ return 0; } #endif if (control_daemon_fd != -1) { /* fprintf(stderr, "Join in progress!\n"); */ err = O2CB_ET_INTERNAL_FAILURE; goto out; } rc = ocfs2_client_connect(); if (rc < 0) { /* fprintf(stderr, "Unable to connect to ocfs2_controld: %s\n", strerror(-rc)); */ switch (rc) { case -EACCES: case -EPERM: err = O2CB_ET_PERMISSION_DENIED; break; default: err = O2CB_ET_SERVICE_UNAVAILABLE; break; } goto out; } control_daemon_fd = rc; rc = send_message(control_daemon_fd, CM_UNMOUNT, OCFS2_FS_NAME, region->r_name, region->r_service); if (rc) { /* fprintf(stderr, "Unable to send UNMOUNT message: %s\n", strerror(-rc)); */ err = O2CB_ET_IO; goto out; } rc = receive_message(control_daemon_fd, buf, &message, argv); if (rc < 0) { /* fprintf(stderr, "Error reading from daemon: %s\n", strerror(-rc)); */ err = O2CB_ET_IO; goto out; } switch (message) { case CM_STATUS: err = user_parse_status(argv, &error, &error_msg); if (err) { /* fprintf(stderr, "Bad status message: %s\n", strerror(-rc)); */ goto out; } if (error) { /* fprintf(stderr, "Error %d from daemon: %s\n", error, error_msg); */ err = O2CB_ET_CONFIGURATION_ERROR; goto out; } break; default: /* fprintf(stderr, "Unexpected message %s from daemon\n", message_to_string(message)); */ err = O2CB_ET_INTERNAL_FAILURE; goto out; break; } err = 0; out: if (control_daemon_fd != -1) { close(control_daemon_fd); control_daemon_fd = -1; } return err; } static errcode_t o2cb_validate_cluster_flags(struct o2cb_cluster_desc *desc, int *globalhb) { errcode_t ret; int disk_mode = 0; ret = o2cb_global_heartbeat_mode(desc->c_cluster, globalhb); if (ret) goto bail; disk_mode = !!(desc->c_flags & OCFS2_CLUSTER_O2CB_GLOBAL_HEARTBEAT); if (disk_mode != *globalhb) ret = O2CB_ET_INVALID_HEARTBEAT_MODE; bail: return ret; } static errcode_t o2cb_validate_cluster_name(struct o2cb_cluster_desc *desc) { errcode_t ret; char **clusters = NULL; ret = o2cb_list_clusters(&clusters); if (ret) goto bail; /* The first cluster is the default cluster */ if (!clusters[0] || strcmp(clusters[0], desc->c_cluster)) ret = O2CB_ET_INVALID_CLUSTER_NAME; o2cb_free_cluster_list(clusters); bail: return ret; } static errcode_t o2cb_validate_cluster_stack(struct o2cb_cluster_desc *desc) { errcode_t err; const char *name; if (!desc) return O2CB_ET_INVALID_STACK_NAME; if (desc->c_stack && !desc->c_cluster) return O2CB_ET_INVALID_STACK_NAME; err = o2cb_get_stack_name(&name); if (err) return err; if (desc->c_stack) { if (strcmp(desc->c_stack, name)) return O2CB_ET_INVALID_STACK_NAME; } else if (strcmp(name, classic_stack.s_name)) return O2CB_ET_INVALID_STACK_NAME; return 0; } errcode_t o2cb_begin_group_join(struct o2cb_cluster_desc *cluster, struct o2cb_region_desc *region) { errcode_t err; struct o2cb_cluster_desc desc; char _fake_cluster_name[NAME_MAX]; if (!current_stack) return O2CB_ET_SERVICE_UNAVAILABLE; err = o2cb_validate_cluster_stack(cluster); if (err) return err; desc = *cluster; if (!desc.c_cluster) { err = _fake_default_cluster(_fake_cluster_name); if (err) return err; desc.c_cluster = _fake_cluster_name; } return current_stack->s_ops->begin_group_join(&desc, region); } errcode_t o2cb_complete_group_join(struct o2cb_cluster_desc *cluster, struct o2cb_region_desc *region, int result) { errcode_t err; struct o2cb_cluster_desc desc; char _fake_cluster_name[NAME_MAX]; if (!current_stack) return O2CB_ET_SERVICE_UNAVAILABLE; err = o2cb_validate_cluster_stack(cluster); if (err) return err; desc = *cluster; if (!desc.c_cluster) { err = _fake_default_cluster(_fake_cluster_name); if (err) return err; desc.c_cluster = _fake_cluster_name; } return current_stack->s_ops->complete_group_join(&desc, region, result); } errcode_t o2cb_group_leave(struct o2cb_cluster_desc *cluster, struct o2cb_region_desc *region) { errcode_t err; struct o2cb_cluster_desc desc; char _fake_cluster_name[NAME_MAX]; if (!current_stack) return O2CB_ET_SERVICE_UNAVAILABLE; err = o2cb_validate_cluster_stack(cluster); if (err) return err; desc = *cluster; if (!desc.c_cluster) { err = _fake_default_cluster(_fake_cluster_name); if (err) return err; desc.c_cluster = _fake_cluster_name; } return current_stack->s_ops->group_leave(&desc, region); } void o2cb_free_cluster_desc(struct o2cb_cluster_desc *cluster) { if (cluster->c_stack) ocfs2_free(&cluster->c_stack); if (cluster->c_cluster) ocfs2_free(&cluster->c_cluster); } errcode_t o2cb_running_cluster_desc(struct o2cb_cluster_desc *cluster) { errcode_t err; const char *stack; char **clusters = NULL; int globalhb; cluster->c_stack = NULL; cluster->c_cluster = NULL; cluster->c_flags = 0; /* c_stack */ err = o2cb_get_stack_name(&stack); if (err) goto out; err = O2CB_ET_NO_MEMORY; cluster->c_stack = strdup(stack); if (!cluster->c_stack) goto out; /* c_cluster */ err = o2cb_list_clusters(&clusters); if (err) goto out; /* The first cluster is the default cluster */ if (!clusters[0]) err = O2CB_ET_SERVICE_UNAVAILABLE; else { cluster->c_cluster = strdup(clusters[0]); if (!cluster->c_cluster) err = O2CB_ET_NO_MEMORY; } if (err) goto out; /* c_flags */ err = o2cb_global_heartbeat_mode(cluster->c_cluster, &globalhb); if (err) goto out; if (globalhb) cluster->c_flags |= OCFS2_CLUSTER_O2CB_GLOBAL_HEARTBEAT; out: if (clusters) o2cb_free_cluster_list(clusters); if (err) o2cb_free_cluster_desc(cluster); return err; } static inline int is_dots(const char *name) { size_t len = strlen(name); if (len == 0) return 0; if (name[0] == '.') { if (len == 1) return 1; if (len == 2 && name[1] == '.') return 1; } return 0; } static errcode_t o2cb_list_dir(char *path, char ***objs) { errcode_t ret; int count; char statpath[PATH_MAX]; struct stat stat_buf; DIR *dir; struct dirent *dirent; struct dlist { struct dlist *next; char *name; } *tmp, *list; dir = opendir(path); if (!dir) { switch (errno) { default: ret = O2CB_ET_INTERNAL_FAILURE; break; case ENOTDIR: case ENOENT: ret = O2CB_ET_SERVICE_UNAVAILABLE; break; case ENOMEM: ret = O2CB_ET_NO_MEMORY; break; case EACCES: ret = O2CB_ET_PERMISSION_DENIED; break; } goto out; } ret = O2CB_ET_NO_MEMORY; count = 0; list = NULL; while ((dirent = readdir(dir)) != NULL) { if (is_dots(dirent->d_name)) continue; snprintf(statpath, sizeof(statpath), "%s/%s", path, dirent->d_name); /* Silently ignore, we can't access it anyway */ if (lstat(statpath, &stat_buf)) continue; /* Non-directories are attributes */ if (!S_ISDIR(stat_buf.st_mode)) continue; tmp = malloc(sizeof(struct dlist)); if (!tmp) goto out_free_list; tmp->name = strdup(dirent->d_name); if (!tmp->name) { free(tmp); goto out_free_list; } tmp->next = list; list = tmp; count++; } *objs = malloc(sizeof(char *) * (count + 1)); if (!*objs) goto out_free_list; for (tmp = list, count = 0; tmp; tmp = tmp->next, count++) { (*objs)[count] = tmp->name; tmp->name = NULL; } (*objs)[count] = NULL; ret = 0; out_free_list: while (list) { tmp = list; list = list->next; if (tmp->name) free(tmp->name); free(tmp); } closedir(dir); out: return ret; } static void o2cb_free_dir_list(char **objs) { int i; for (i = 0; objs[i]; i++) free(objs[i]); free(objs); } static errcode_t classic_list_clusters(char ***clusters) { char path[PATH_MAX]; errcode_t ret; if (configfs_path == NULL) return O2CB_ET_SERVICE_UNAVAILABLE; ret = snprintf(path, PATH_MAX - 1, O2CB_FORMAT_CLUSTER_DIR, configfs_path); if ((ret <= 0) || (ret == (PATH_MAX - 1))) return O2CB_ET_INTERNAL_FAILURE; return o2cb_list_dir(path, clusters); } #ifdef HAVE_CMAP static errcode_t user_list_clusters(char ***clusters) { cmap_handle_t handle; char **list; int rv; rv = cmap_initialize(&handle); if (rv != CS_OK) return O2CB_ET_SERVICE_UNAVAILABLE; /* We supply only one cluster_name */ list = (char **)malloc(sizeof(char *) * 2); rv = cmap_get_string(handle, "totem.cluster_name", &list[0]); if (rv != CS_OK) { free(list); return O2CB_ET_INTERNAL_FAILURE; } list[1] = NULL; *clusters = list; return 0; } #else static errcode_t user_list_clusters(char ***clusters) { errcode_t err = O2CB_ET_SERVICE_UNAVAILABLE; int rc, fd = -1; char buf[OCFS2_CONTROLD_MAXLINE]; rc = ocfs2_client_connect(); if (rc < 0) { /* fprintf(stderr, "Unable to connect to ocfs2_controld: %s\n", strerror(-rc)); */ switch (rc) { case -EACCES: case -EPERM: err = O2CB_ET_PERMISSION_DENIED; break; default: err = O2CB_ET_SERVICE_UNAVAILABLE; break; } goto out; } fd = rc; rc = send_message(fd, CM_LISTCLUSTERS); if (rc) { /* fprintf(stderr, "Unable to send LISTCLUSTERS message: %s\n", strerror(-rc)); */ err = O2CB_ET_IO; goto out; } rc = receive_list(fd, buf, clusters); if (rc) { /* fprintf(stderr, "Error reading from daemon: %s\n", strerror(-rc)); */ err = O2CB_ET_IO; goto out; } err = 0; out: if (fd != -1) close(fd); return err; } #endif errcode_t o2cb_list_clusters(char ***clusters) { if (!current_stack) return O2CB_ET_SERVICE_UNAVAILABLE; return current_stack->s_ops->list_clusters(clusters); } void o2cb_free_cluster_list(char **clusters) { o2cb_free_dir_list(clusters); } errcode_t o2cb_list_nodes(char *cluster_name, char ***nodes) { char path[PATH_MAX]; errcode_t ret; if (configfs_path == NULL) return O2CB_ET_SERVICE_UNAVAILABLE; ret = snprintf(path, PATH_MAX - 1, O2CB_FORMAT_NODE_DIR, configfs_path, cluster_name); if ((ret <= 0) || (ret == (PATH_MAX - 1))) return O2CB_ET_INTERNAL_FAILURE; return o2cb_list_dir(path, nodes); } void o2cb_free_nodes_list(char **nodes) { o2cb_free_dir_list(nodes); } errcode_t o2cb_list_hb_regions(char *cluster_name, char ***regions) { char path[PATH_MAX]; errcode_t ret; if (configfs_path == NULL) return O2CB_ET_SERVICE_UNAVAILABLE; ret = snprintf(path, PATH_MAX - 1, O2CB_FORMAT_HEARTBEAT_DIR, configfs_path, cluster_name); if ((ret <= 0) || (ret == (PATH_MAX - 1))) return O2CB_ET_INTERNAL_FAILURE; return o2cb_list_dir(path, regions); } void o2cb_free_hb_regions_list(char **regions) { o2cb_free_dir_list(regions); } errcode_t o2cb_global_heartbeat_mode(char *cluster_name, int *global) { char attr_path[PATH_MAX]; char _fake_cluster_name[NAME_MAX]; char attr_value[16]; errcode_t ret; if (!cluster_name) { ret = _fake_default_cluster(_fake_cluster_name); if (ret) return ret; cluster_name = _fake_cluster_name; } ret = snprintf(attr_path, PATH_MAX - 1, O2CB_FORMAT_HEARTBEAT_MODE, configfs_path, cluster_name); if ((ret <= 0) || (ret == (PATH_MAX - 1))) return O2CB_ET_INTERNAL_FAILURE; *global = 0; ret = o2cb_get_attribute(attr_path, attr_value, sizeof(attr_value) - 1); if (ret) { if (ret == O2CB_ET_SERVICE_UNAVAILABLE) ret = 0; return ret; } /* wipeout the last newline character */ do_strchomp(attr_value); if (!strcmp(attr_value, O2CB_GLOBAL_HEARTBEAT_TAG)) *global = 1; return 0; } /* * The hbmode validation is done in the kernel */ errcode_t o2cb_set_heartbeat_mode(char *cluster_name, char *hbmode) { char attr_path[PATH_MAX]; char _fake_cluster_name[NAME_MAX]; errcode_t ret; int local = 0; if (!cluster_name) { ret = _fake_default_cluster(_fake_cluster_name); if (ret) return ret; cluster_name = _fake_cluster_name; } if (!strcmp(hbmode, O2CB_LOCAL_HEARTBEAT_TAG)) local = 1; ret = snprintf(attr_path, PATH_MAX - 1, O2CB_FORMAT_HEARTBEAT_MODE, configfs_path, cluster_name); if ((ret <= 0) || (ret == (PATH_MAX - 1))) return O2CB_ET_INTERNAL_FAILURE; ret = o2cb_set_attribute(attr_path, hbmode); if (ret && ret == O2CB_ET_SERVICE_UNAVAILABLE && local) ret = 0; return ret; } static errcode_t dump_list_to_string(char **dump_list, char **debug) { int i; size_t len, count = 0; char *ptr; for (i = 0; dump_list[i]; i++) count += strlen(dump_list[i]); *debug = malloc(sizeof(char) * (count + 1)); if (!*debug) return O2CB_ET_NO_MEMORY; ptr = *debug; ptr[count] = '\0'; for (i = 0; dump_list[i]; i++) { len = strlen(dump_list[i]); memcpy(ptr, dump_list[i], len); ptr += len; } return 0; } errcode_t o2cb_control_daemon_debug(char **debug) { errcode_t err = O2CB_ET_SERVICE_UNAVAILABLE; int rc, fd = -1; char buf[OCFS2_CONTROLD_MAXLINE]; char **dump_list = NULL; rc = ocfs2_client_connect(); if (rc < 0) { /* fprintf(stderr, "Unable to connect to ocfs2_controld: %s\n", strerror(-rc)); */ switch (rc) { case -EACCES: case -EPERM: err = O2CB_ET_PERMISSION_DENIED; break; default: err = O2CB_ET_SERVICE_UNAVAILABLE; break; } goto out; } fd = rc; rc = send_message(fd, CM_DUMP); if (rc) { /* fprintf(stderr, "Unable to send DUMP message: %s\n", strerror(-rc)); */ err = O2CB_ET_IO; goto out; } rc = receive_list(fd, buf, &dump_list); if (rc) { /* fprintf(stderr, "Error reading from daemon: %s\n", strerror(-rc)); */ err = O2CB_ET_IO; goto out; } err = dump_list_to_string(dump_list, debug); o2cb_free_dir_list(dump_list); out: if (fd != -1) close(fd); return err; } errcode_t o2cb_get_hb_thread_pid (const char *cluster_name, const char *region_name, pid_t *pid) { char attr_path[PATH_MAX]; char _fake_cluster_name[NAME_MAX]; char attr_value[16]; errcode_t ret; if (!cluster_name) { ret = _fake_default_cluster(_fake_cluster_name); if (ret) return ret; cluster_name = _fake_cluster_name; } ret = snprintf(attr_path, PATH_MAX - 1, O2CB_FORMAT_HEARTBEAT_REGION_ATTR, configfs_path, cluster_name, region_name, "pid"); if ((ret <= 0) || (ret == (PATH_MAX - 1))) return O2CB_ET_INTERNAL_FAILURE; ret = o2cb_get_attribute(attr_path, attr_value, sizeof(attr_value) - 1); if (ret == 0) *pid = atoi (attr_value); return ret; } errcode_t o2cb_get_node_num(const char *cluster_name, const char *node_name, uint16_t *node_num) { char val[30]; char *p; errcode_t ret; ret = o2cb_get_node_attribute(cluster_name, node_name, "num", val, sizeof(val)); if (ret) return ret; *node_num = strtoul(val, &p, 0); if (!p || (*p && *p != '\n')) return O2CB_ET_INVALID_NODE_NUM; return 0; } errcode_t o2cb_get_node_port(const char *cluster_name, const char *node_name, uint32_t *ip_port) { char val[30]; char *p; errcode_t ret; ret = o2cb_get_node_attribute(cluster_name, node_name, "ipv4_port", val, sizeof(val)); if (ret) return ret; *ip_port = strtoul(val, &p, 0); if (!p || (*p && *p != '\n')) return O2CB_ET_SERVICE_UNAVAILABLE; return 0; } errcode_t o2cb_get_node_ip_string(const char *cluster_name, const char *node_name, char *ip_address, int count) { errcode_t ret; ret = o2cb_get_node_attribute(cluster_name, node_name, "ipv4_address", ip_address, count - 1); if (ret) return ret; /* wipeout the last newline character */ do_strchomp(ip_address); return 0; } errcode_t o2cb_get_node_local(const char *cluster_name, const char *node_name, uint32_t *local) { char val[30]; char *p; errcode_t ret; ret = o2cb_get_node_attribute(cluster_name, node_name, "local", val, sizeof(val)); if (ret) return ret; *local = strtoul(val, &p, 0); if (!p || (*p && *p != '\n')) return O2CB_ET_SERVICE_UNAVAILABLE; return 0; } /* * The handshake is pretty simple. We need to read all supported control * device protocols from the kernel. Once we've read them, we can write * the protocol we want to use. After that, we're good to go. * * Right now, we will just allow the T01 protocol and not write any * code to handle multiples. We'll add that later if and when it is * necessary. * * The versions read from the kernel are all 4 characers including the * newline. */ #define OCFS2_CONTROL_PROTO "T01\n" #define OCFS2_CONTROL_PROTO_LEN 4 #define OCFS2_CONTROL_MESSAGE_SETNODE_OP "SETN" #define OCFS2_CONTROL_MESSAGE_SETNODE_TOTAL_LEN 14 #define OCFS2_CONTROL_MESSAGE_SETVERSION_OP "SETV" #define OCFS2_CONTROL_MESSAGE_SETVERSION_TOTAL_LEN 11 #define OCFS2_CONTROL_MESSAGE_DOWN_OP "DOWN" #define OCFS2_CONTROL_MESSAGE_DOWN_TOTAL_LEN 47 #define OCFS2_CONTROL_MESSAGE_NODENUM_LEN 8 static errcode_t o2cb_control_handshake(unsigned int this_node, struct ocfs2_protocol_version *proto) { errcode_t err = 0; int found = 0; size_t ret; char buf[OCFS2_CONTROL_MESSAGE_SETNODE_TOTAL_LEN + 1]; if (control_device_fd == -1) { err = O2CB_ET_INTERNAL_FAILURE; goto out; } buf[OCFS2_CONTROL_PROTO_LEN] = '\0'; while (1) { ret = read(control_device_fd, buf, OCFS2_CONTROL_PROTO_LEN); if (ret == OCFS2_CONTROL_PROTO_LEN) { if (!found && !strcmp(buf, OCFS2_CONTROL_PROTO)) found = 1; continue; } if (ret != 0) err = O2CB_ET_IO; else if (!found) err = O2CB_ET_SERVICE_UNAVAILABLE; /* no match */ break; } if (err) goto out; ret = write(control_device_fd, OCFS2_CONTROL_PROTO, OCFS2_CONTROL_PROTO_LEN); if (ret != OCFS2_CONTROL_PROTO_LEN) { err = O2CB_ET_IO; goto out; } snprintf(buf, OCFS2_CONTROL_MESSAGE_SETNODE_TOTAL_LEN + 1, OCFS2_CONTROL_MESSAGE_SETNODE_OP " %08X\n", this_node); ret = write(control_device_fd, buf, OCFS2_CONTROL_MESSAGE_SETNODE_TOTAL_LEN); if (ret != OCFS2_CONTROL_MESSAGE_SETNODE_TOTAL_LEN) err = O2CB_ET_IO; snprintf(buf, OCFS2_CONTROL_MESSAGE_SETVERSION_TOTAL_LEN + 1, OCFS2_CONTROL_MESSAGE_SETVERSION_OP " %02X %02X\n", proto->pv_major, proto->pv_minor); ret = write(control_device_fd, buf, OCFS2_CONTROL_MESSAGE_SETVERSION_TOTAL_LEN); if (ret != OCFS2_CONTROL_MESSAGE_SETVERSION_TOTAL_LEN) err = O2CB_ET_IO; out: return err; } errcode_t o2cb_control_open(unsigned int this_node, struct ocfs2_protocol_version *proto) { errcode_t err = 0; int rc; if (!current_stack) { err = O2CB_ET_SERVICE_UNAVAILABLE; goto out; } if (control_device_fd != -1) goto out; rc = open(CONTROL_DEVICE, O_RDWR); if (rc < 0) { switch (errno) { default: err = O2CB_ET_INTERNAL_FAILURE; break; case ENOTDIR: case ENOENT: case EISDIR: err = O2CB_ET_SERVICE_UNAVAILABLE; break; case EACCES: case EPERM: case EROFS: err = O2CB_ET_PERMISSION_DENIED; break; } goto out; } control_device_fd = rc; err = o2cb_control_handshake(this_node, proto); if (err) { close(control_device_fd); control_device_fd = -1; } out: return err; } void o2cb_control_close(void) { if (control_device_fd != -1) { close(control_device_fd); control_device_fd = -1; } } errcode_t o2cb_control_node_down(const char *uuid, unsigned int nodeid) { errcode_t err = 0; size_t ret; char buf[OCFS2_CONTROL_MESSAGE_DOWN_TOTAL_LEN + 1]; if (control_device_fd == -1) return O2CB_ET_INTERNAL_FAILURE; snprintf(buf, OCFS2_CONTROL_MESSAGE_DOWN_TOTAL_LEN + 1, "DOWN %.32s %08X\n", uuid, nodeid); ret = write(control_device_fd, buf, OCFS2_CONTROL_MESSAGE_DOWN_TOTAL_LEN); if (ret != OCFS2_CONTROL_MESSAGE_DOWN_TOTAL_LEN) err = O2CB_ET_IO; return err; } errcode_t o2cb_get_hb_ctl_path(char *buf, int count) { int fd; int total = 0; int ret; #define HB_CTL_PATH "/proc/sys/fs/ocfs2/nm/hb_ctl_path" fd = open(HB_CTL_PATH, O_RDONLY); if (fd == -1) { if (errno == ENOENT) return O2CB_ET_MODULE_NOT_LOADED; else return errno; } while (total < count) { ret = read(fd, buf + total, count - total); if (ret < 0) { ret = -errno; if ((ret == -EAGAIN) || (ret == -EINTR)) continue; total = ret; break; } if (ret == 0) break; total += ret; } if (total < 0) { close(fd); return total; } buf[total] = '\0'; if (buf[total - 1] == '\n') buf[total - 1] = '\0'; close(fd); return 0; } #define MODPROBE_COMMAND "/sbin/modprobe" #define USER_KERNEL_MODULE "ocfs2_stack_user" #define O2CB_KERNEL_MODULE "ocfs2_stack_o2cb" static int perform_modprobe(char *module_name) { pid_t child; int child_status; char *argv[4]; argv[0] = MODPROBE_COMMAND; argv[1] = module_name; argv[2] = "-q"; argv[3] = NULL; child = fork(); if (child == 0) { execv(MODPROBE_COMMAND, argv); /* If execv fails, we have a problem */ return -EINVAL; } else wait(&child_status); return child_status; } errcode_t o2cb_setup_stack(char *stack_name) { char line[64]; int modprobe_performed = 0, write_performed = 0; errcode_t err = O2CB_ET_SERVICE_UNAVAILABLE; int len; redo: len = read_single_line_file(CLUSTER_STACK_FILE, line, sizeof(line)); if (len > 0) { if (line[len - 1] == '\n') { line[len - 1] = '\0'; len--; } if (len != OCFS2_STACK_LABEL_LEN) { err = O2CB_ET_INTERNAL_FAILURE; goto out; } if (!strncmp(line, stack_name, OCFS2_STACK_LABEL_LEN)) { err = 0; goto out; } if (!write_performed) { len = write_single_line_file(CLUSTER_STACK_FILE, stack_name, strlen(stack_name)); if (len < 0) goto out; write_performed = 1; goto redo; } } else if (len == -ENOENT) { if (!modprobe_performed) { perform_modprobe("ocfs2"); if ((!strncmp(stack_name, OCFS2_PCMK_CLUSTER_STACK, OCFS2_STACK_LABEL_LEN)) || (!strncmp(stack_name, OCFS2_CMAN_CLUSTER_STACK, OCFS2_STACK_LABEL_LEN))) perform_modprobe(USER_KERNEL_MODULE); else if (!strncmp(stack_name, classic_stack.s_name, OCFS2_STACK_LABEL_LEN)) perform_modprobe(O2CB_KERNEL_MODULE); modprobe_performed = 1; write_single_line_file(CLUSTER_STACK_FILE, stack_name, OCFS2_STACK_LABEL_LEN); write_performed = 1; goto redo; } else err = O2CB_ET_INTERNAL_FAILURE; } else { err = O2CB_ET_INTERNAL_FAILURE; goto out; } err = 0; out: return err; } ocfs2-tools-ocfs2-tools-1.8.6/libo2cb/o2cb_abi.h000066400000000000000000000034051347147137200212100ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * o2cb_abi.c * * Layout of configfs paths for O2CB cluster configuration. * * Copyright (C) 2005 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License, version 2, as published by the Free Software Foundation. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. */ #ifndef _O2CB_ABI_H #define _O2CB_ABI_H /* * The latest place is /sys/kernel/config, but older O2CB put it * at /config. So, libo2cb has to handle detection */ #define CONFIGFS_FORMAT_PATH "%s/config" #define O2CB_FORMAT_CLUSTER_DIR CONFIGFS_FORMAT_PATH "/cluster" #define O2CB_FORMAT_CLUSTER O2CB_FORMAT_CLUSTER_DIR "/%s" #define O2CB_FORMAT_NODE_DIR O2CB_FORMAT_CLUSTER "/node" #define O2CB_FORMAT_NODE O2CB_FORMAT_NODE_DIR "/%s" #define O2CB_FORMAT_NODE_ATTR O2CB_FORMAT_NODE "/%s" #define O2CB_FORMAT_HEARTBEAT_DIR O2CB_FORMAT_CLUSTER "/heartbeat" #define O2CB_FORMAT_HEARTBEAT_REGION O2CB_FORMAT_HEARTBEAT_DIR "/%s" #define O2CB_FORMAT_HEARTBEAT_REGION_ATTR O2CB_FORMAT_HEARTBEAT_REGION "/%s" #define O2CB_FORMAT_HEARTBEAT_MODE O2CB_FORMAT_HEARTBEAT_DIR "/mode" /* * Cluster info flags (ocfs2_cluster_info.ci_stackflags) */ #define OCFS2_CLUSTER_O2CB_GLOBAL_HEARTBEAT (0x01) #endif /* _O2CB_ABI_H */ ocfs2-tools-ocfs2-tools-1.8.6/libo2cb/o2cb_crc32.c000066400000000000000000000122001347147137200213550ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * o2cb_crc32.c * * Routines for computing a crc * * Crc code copied from scripts/genksyms/genksysms.c. Original header * from that file follows. */ #include "o2cb_crc32.h" /* Generate kernel symbol version hashes. Copyright 1996, 1997 Linux International. New implementation contributed by Richard Henderson Based on original work by Bjorn Ekwall This file was part of the Linux modutils 2.4.22: moved back into the kernel sources by Rusty Russell/Kai Germaschewski. 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. */ static const unsigned int crctab32[] = { 0x00000000U, 0x77073096U, 0xee0e612cU, 0x990951baU, 0x076dc419U, 0x706af48fU, 0xe963a535U, 0x9e6495a3U, 0x0edb8832U, 0x79dcb8a4U, 0xe0d5e91eU, 0x97d2d988U, 0x09b64c2bU, 0x7eb17cbdU, 0xe7b82d07U, 0x90bf1d91U, 0x1db71064U, 0x6ab020f2U, 0xf3b97148U, 0x84be41deU, 0x1adad47dU, 0x6ddde4ebU, 0xf4d4b551U, 0x83d385c7U, 0x136c9856U, 0x646ba8c0U, 0xfd62f97aU, 0x8a65c9ecU, 0x14015c4fU, 0x63066cd9U, 0xfa0f3d63U, 0x8d080df5U, 0x3b6e20c8U, 0x4c69105eU, 0xd56041e4U, 0xa2677172U, 0x3c03e4d1U, 0x4b04d447U, 0xd20d85fdU, 0xa50ab56bU, 0x35b5a8faU, 0x42b2986cU, 0xdbbbc9d6U, 0xacbcf940U, 0x32d86ce3U, 0x45df5c75U, 0xdcd60dcfU, 0xabd13d59U, 0x26d930acU, 0x51de003aU, 0xc8d75180U, 0xbfd06116U, 0x21b4f4b5U, 0x56b3c423U, 0xcfba9599U, 0xb8bda50fU, 0x2802b89eU, 0x5f058808U, 0xc60cd9b2U, 0xb10be924U, 0x2f6f7c87U, 0x58684c11U, 0xc1611dabU, 0xb6662d3dU, 0x76dc4190U, 0x01db7106U, 0x98d220bcU, 0xefd5102aU, 0x71b18589U, 0x06b6b51fU, 0x9fbfe4a5U, 0xe8b8d433U, 0x7807c9a2U, 0x0f00f934U, 0x9609a88eU, 0xe10e9818U, 0x7f6a0dbbU, 0x086d3d2dU, 0x91646c97U, 0xe6635c01U, 0x6b6b51f4U, 0x1c6c6162U, 0x856530d8U, 0xf262004eU, 0x6c0695edU, 0x1b01a57bU, 0x8208f4c1U, 0xf50fc457U, 0x65b0d9c6U, 0x12b7e950U, 0x8bbeb8eaU, 0xfcb9887cU, 0x62dd1ddfU, 0x15da2d49U, 0x8cd37cf3U, 0xfbd44c65U, 0x4db26158U, 0x3ab551ceU, 0xa3bc0074U, 0xd4bb30e2U, 0x4adfa541U, 0x3dd895d7U, 0xa4d1c46dU, 0xd3d6f4fbU, 0x4369e96aU, 0x346ed9fcU, 0xad678846U, 0xda60b8d0U, 0x44042d73U, 0x33031de5U, 0xaa0a4c5fU, 0xdd0d7cc9U, 0x5005713cU, 0x270241aaU, 0xbe0b1010U, 0xc90c2086U, 0x5768b525U, 0x206f85b3U, 0xb966d409U, 0xce61e49fU, 0x5edef90eU, 0x29d9c998U, 0xb0d09822U, 0xc7d7a8b4U, 0x59b33d17U, 0x2eb40d81U, 0xb7bd5c3bU, 0xc0ba6cadU, 0xedb88320U, 0x9abfb3b6U, 0x03b6e20cU, 0x74b1d29aU, 0xead54739U, 0x9dd277afU, 0x04db2615U, 0x73dc1683U, 0xe3630b12U, 0x94643b84U, 0x0d6d6a3eU, 0x7a6a5aa8U, 0xe40ecf0bU, 0x9309ff9dU, 0x0a00ae27U, 0x7d079eb1U, 0xf00f9344U, 0x8708a3d2U, 0x1e01f268U, 0x6906c2feU, 0xf762575dU, 0x806567cbU, 0x196c3671U, 0x6e6b06e7U, 0xfed41b76U, 0x89d32be0U, 0x10da7a5aU, 0x67dd4accU, 0xf9b9df6fU, 0x8ebeeff9U, 0x17b7be43U, 0x60b08ed5U, 0xd6d6a3e8U, 0xa1d1937eU, 0x38d8c2c4U, 0x4fdff252U, 0xd1bb67f1U, 0xa6bc5767U, 0x3fb506ddU, 0x48b2364bU, 0xd80d2bdaU, 0xaf0a1b4cU, 0x36034af6U, 0x41047a60U, 0xdf60efc3U, 0xa867df55U, 0x316e8eefU, 0x4669be79U, 0xcb61b38cU, 0xbc66831aU, 0x256fd2a0U, 0x5268e236U, 0xcc0c7795U, 0xbb0b4703U, 0x220216b9U, 0x5505262fU, 0xc5ba3bbeU, 0xb2bd0b28U, 0x2bb45a92U, 0x5cb36a04U, 0xc2d7ffa7U, 0xb5d0cf31U, 0x2cd99e8bU, 0x5bdeae1dU, 0x9b64c2b0U, 0xec63f226U, 0x756aa39cU, 0x026d930aU, 0x9c0906a9U, 0xeb0e363fU, 0x72076785U, 0x05005713U, 0x95bf4a82U, 0xe2b87a14U, 0x7bb12baeU, 0x0cb61b38U, 0x92d28e9bU, 0xe5d5be0dU, 0x7cdcefb7U, 0x0bdbdf21U, 0x86d3d2d4U, 0xf1d4e242U, 0x68ddb3f8U, 0x1fda836eU, 0x81be16cdU, 0xf6b9265bU, 0x6fb077e1U, 0x18b74777U, 0x88085ae6U, 0xff0f6a70U, 0x66063bcaU, 0x11010b5cU, 0x8f659effU, 0xf862ae69U, 0x616bffd3U, 0x166ccf45U, 0xa00ae278U, 0xd70dd2eeU, 0x4e048354U, 0x3903b3c2U, 0xa7672661U, 0xd06016f7U, 0x4969474dU, 0x3e6e77dbU, 0xaed16a4aU, 0xd9d65adcU, 0x40df0b66U, 0x37d83bf0U, 0xa9bcae53U, 0xdebb9ec5U, 0x47b2cf7fU, 0x30b5ffe9U, 0xbdbdf21cU, 0xcabac28aU, 0x53b39330U, 0x24b4a3a6U, 0xbad03605U, 0xcdd70693U, 0x54de5729U, 0x23d967bfU, 0xb3667a2eU, 0xc4614ab8U, 0x5d681b02U, 0x2a6f2b94U, 0xb40bbe37U, 0xc30c8ea1U, 0x5a05df1bU, 0x2d02ef8dU }; static inline unsigned long partial_crc32_one(unsigned char c, unsigned long crc) { return crctab32[(crc ^ c) & 0xff] ^ (crc >> 8); } static inline unsigned long partial_crc32(const char *s, unsigned long crc) { while (*s) crc = partial_crc32_one(*s++, crc); return crc; } static inline unsigned long crc32(const char *s) { return partial_crc32(s, 0xffffffff) ^ 0xffffffff; } unsigned long o2cb_crc32(const char *s) { return crc32(s); } ocfs2-tools-ocfs2-tools-1.8.6/libo2cb/o2cb_crc32.h000066400000000000000000000017251347147137200213740ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * o2cb_crc32.h * * libo2cb interface to crc32 functionality. * * Copyright (C) 2005 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License, version 2, as published by the Free Software Foundation. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * */ #ifndef _O2CB_CRC32_H #define _O2CB_CRC32_H unsigned long o2cb_crc32(const char *s); #endif /* _O2CB_CRC32_H */ ocfs2-tools-ocfs2-tools-1.8.6/libo2cb/o2cb_err.et000066400000000000000000000041341347147137200214260ustar00rootroot00000000000000# # o2cb_err.et # # Error codes for the O2CB library. # # Copyright (C) 2004 Oracle. All rights reserved. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public # License, version 2, as published by the Free Software Foundation. # # 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., 51 Franklin St, Fifth Floor, # Boston, MA 02110-1301 USA. # error_table o2cb ec O2CB_ET_INVALID_CLUSTER_NAME, "Cluster name is invalid" ec O2CB_ET_NO_MEMORY, "Memory allocation failed" ec O2CB_ET_IO, "I/O error on channel" ec O2CB_ET_SERVICE_UNAVAILABLE, "Unable to access cluster service" ec O2CB_ET_INTERNAL_FAILURE, "Internal logic failure" ec O2CB_ET_PERMISSION_DENIED, "Insufficient permissions to access cluster service" ec O2CB_ET_CLUSTER_EXISTS, "Cluster already exists" ec O2CB_ET_NODE_EXISTS, "Node already exists" ec O2CB_ET_REGION_EXISTS, "Heartbeat region already exists" ec O2CB_ET_REGION_IN_USE, "Heartbeat region in use" ec O2CB_ET_INVALID_BLOCK_SIZE, "Block size is invalid" ec O2CB_ET_INVALID_BLOCK_COUNT, "Block count is invalid" ec O2CB_ET_HOSTNAME_UNKNOWN, "Could not determine local host name" ec O2CB_ET_CONFIGURATION_ERROR, "Configuration error discovered" ec O2CB_ET_INVALID_NODE_NUM, "Node number is invalid" ec O2CB_ET_MODULE_NOT_LOADED, "Node manager kernel module is not loaded" ec O2CB_ET_BAD_SEM, "Could not access heartbeat region semaphore set" ec O2CB_ET_NO_SEM, "Region semaphore set destroyed" ec O2CB_ET_BAD_VERSION, "Revision of OCFS2-Tools is out of date." ec O2CB_ET_INVALID_STACK_NAME, "Cluster stack is invalid" ec O2CB_ET_UNKNOWN_REGION, "Heartbeat region could not be found" ec O2CB_ET_INVALID_HEARTBEAT_MODE, "Heartbeat mode is invalid" end ocfs2-tools-ocfs2-tools-1.8.6/libo2dlm/000077500000000000000000000000001347147137200175655ustar00rootroot00000000000000ocfs2-tools-ocfs2-tools-1.8.6/libo2dlm/.gitignore000066400000000000000000000001001347147137200215440ustar00rootroot00000000000000cscope.* libo2dlm.a o2dlm_err.* *.d o2dlm_test libdlm.h debug_* ocfs2-tools-ocfs2-tools-1.8.6/libo2dlm/Cscope.make000066400000000000000000000004521347147137200216410ustar00rootroot00000000000000.PHONY: cscope cscope: rm -f cscope.* echo "-k" >> cscope.files echo "-I inc" >> cscope.files find . -name '*.c' -print >>cscope.files find . -name '*.h' -print >>cscope.files find ../libocfs2/ -name '*.h' -print >>cscope.files find ../libocfs2/ -name '*.c' -print >>cscope.files cscope -b ocfs2-tools-ocfs2-tools-1.8.6/libo2dlm/Makefile000066400000000000000000000026721347147137200212340ustar00rootroot00000000000000TOPDIR = .. include $(TOPDIR)/Preamble.make INCLUDES = -I. -I$(TOPDIR)/include LIBRARIES = libo2dlm.a CFLAGS += -fPIC ifneq ($(BUILD_FSDLM_SUPPORT),) DEFINES += -DHAVE_FSDLM endif ifneq ($(OCFS2_DEBUG_EXE),) DEBUG_EXE_FILES = $(shell awk '/DEBUG_EXE/{if (k[FILENAME] == 0) {print FILENAME; k[FILENAME] = 1;}}' $(CFILES)) DEBUG_EXE_PROGRAMS = $(addprefix debug_,$(subst .c,,$(DEBUG_EXE_FILES))) .SECONDARY: UNINST_PROGRAMS += $(DEBUG_EXE_PROGRAMS) debug_%.o : %.c $(CC) $(CFLAGS) $(LOCAL_CFLAGS) $(CPPFLAGS) $(LOCAL_CPPFLAGS) \ $(INCLUDES) $(DEFINES) \ -DDEBUG_EXE -o $@ -c $< debug_%: debug_%.o libo2dlm.a $(LINK) $(COM_ERR_LIBS) endif CFILES = o2dlm.c capabilities.c HFILES = libdlm-compat.h TESTING_CFILES = o2dlm_test.c HFILES_GEN = o2dlm_err.h ifeq ($(LIBDLM_FOUND),) HFILES_GEN += libdlm.h CLEAN_RULES += clean-libdlm libdlm.h: libdlm-compat.h ln -s $< $@ clean-libdlm: rm -f libdlm.h endif OBJS = $(subst .c,.o,$(CFILES)) \ o2dlm_err.o $(OBJS): $(HFILES) $(HFILES_GEN) TESTING_OBJS = $(subst .c,.o,$(TESTING_CFILES)) o2dlm_err.c o2dlm_err.h: o2dlm_err.et compile_et o2dlm_err.et libo2dlm.a: $(OBJS) rm -f $@ $(AR) r $@ $^ $(RANLIB) $@ o2dlm_test: $(TESTING_OBJS) $(LIBRARIES) $(COM_ERR_LIBS) $(LINK) $(DL_LIBS) DIST_FILES = $(CFILES) $(HFILES) o2dlm_err.et $(TESTING_CFILES) CLEAN_RULES += clean-err clean-test clean-err: rm -f o2dlm_err.c o2dlm_err.h clean-test: rm -f o2dlm_test include $(TOPDIR)/Postamble.make ocfs2-tools-ocfs2-tools-1.8.6/libo2dlm/capabilities.c000066400000000000000000000052161347147137200223660ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * capabilities.c * * Read dlmfs capabilities. * * Copyright (C) 2010 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License version 2 as published by the Free Software Foundation. * * 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. */ #include #include #include #include #include #include "o2dlm/o2dlm.h" #define CAPABILITIES_FILE "/sys/module/ocfs2_dlmfs/parameters/capabilities" static ssize_t read_single_line_file(const char *file, char *line, size_t count) { ssize_t ret = 0; FILE *f; f = fopen(file, "r"); if (f) { if (fgets(line, count, f)) ret = strlen(line); fclose(f); } else ret = -errno; return ret; } static ssize_t o2dlm_read_capabilities(char *line, size_t count) { ssize_t got; got = read_single_line_file(CAPABILITIES_FILE, line, count); if ((got > 0) && (line[got - 1] == '\n')) { got--; line[got] = '\0'; } return got; } static errcode_t o2dlm_has_capability(const char *cap_name, int *found) { char line[PATH_MAX]; char *p; ssize_t got; got = o2dlm_read_capabilities(line, PATH_MAX); if (got == -ENOENT) { got = 0; line[0] = '\0'; } if (got < 0) return O2DLM_ET_SERVICE_UNAVAILABLE; *found = 0; p = strstr(line, cap_name); if (p) { p += strlen(cap_name); if (!*p || (*p == ' ')) *found = 1; } return 0; } errcode_t o2dlm_supports_bast(int *supports) { return o2dlm_has_capability("bast", supports); } errcode_t o2dlm_supports_stackglue(int *supports) { return o2dlm_has_capability("stackglue", supports); } #ifdef DEBUG_EXE int main(int argc, char *argv[]) { errcode_t ret; int i, rc = 0; char **caps; initialize_o2dl_error_table(); ret = o2dlm_supports_bast(&i); if (!ret) fprintf(stdout, "bast: %s\n", i ? "yes" : "no"); else { rc = 1; com_err("debug_capabilities", ret, "while testing bast capability"); } ret = o2dlm_supports_stackglue(&i); if (!ret) fprintf(stdout, "stackglue: %s\n", i ? "yes" : "no"); else { rc = 1; com_err("debug_capabilities", ret, "while testing stackglue capability"); } ret = o2dlm_has_capability("invalid", &i); if (!ret) fprintf(stdout, "invalid: %s\n", i ? "yes" : "no"); else { rc = 1; com_err("debug_capabilities", ret, "while testing invalid capability"); } return ret; } #endif ocfs2-tools-ocfs2-tools-1.8.6/libo2dlm/libdlm-compat.h000066400000000000000000000201371347147137200224650ustar00rootroot00000000000000/****************************************************************************** ******************************************************************************* ** ** Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. ** Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved. ** ** This library is free software; you can redistribute it and/or ** modify it under the terms of the GNU Lesser General Public ** License as published by the Free Software Foundation; either ** version 2 of the License, or (at your option) any later version. ** ** This library is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ** Lesser General Public License for more details. ** ** You should have received a copy of the GNU Lesser General Public ** License along with this library; if not, write to the Free Software ** Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ** ******************************************************************************* ******************************************************************************/ #ifndef __LIBDLM_H #define __LIBDLM_H /* * Typedefs for things that are compatible with the kernel but replicated here * so that users only need the libdlm include file. libdlm itself needs the * full kernel file so shouldn't use these. */ #define DLM_LVB_LEN 32 #ifndef BUILDING_LIBDLM #define DLM_RESNAME_MAXLEN 64 struct dlm_lksb { int sb_status; uint32_t sb_lkid; char sb_flags; char *sb_lvbptr; }; /* lksb flags */ #define DLM_SBF_DEMOTED 0x01 #define DLM_SBF_VALNOTVALID 0x02 #define DLM_SBF_ALTMODE 0x04 /* dlm_new_lockspace flags */ #define DLM_LSFL_NODIR 0x00000001 #define DLM_LSFL_TIMEWARN 0x00000002 #endif #if 0 /* Dummy definition to keep linkages */ struct dlm_queryinfo; #endif extern int dlm_kernel_version(uint32_t *maj, uint32_t *min, uint32_t *patch); extern void dlm_library_version(uint32_t *maj, uint32_t *min, uint32_t *patch); /* * Using the default lockspace * * lock_resource() - simple sync request or convert (requires pthreads) * unlock_resource() - simple sync unlock (requires pthreads) * dlm_lock() - async request or convert * dlm_unlock() - async unlock or cancel * dlm_lock_wait() - sync request or convert * dlm_unlock_wait() - sync unlock or cancel */ #ifdef _REENTRANT extern int lock_resource(const char *resource, int mode, int flags, int *lockid); extern int unlock_resource(int lockid); #endif extern int dlm_lock(uint32_t mode, struct dlm_lksb *lksb, uint32_t flags, const void *name, unsigned int namelen, uint32_t parent, /* unusued */ void (*astaddr) (void *astarg), void *astarg, void (*bastaddr) (void *astarg), void *range); /* unused */ extern int dlm_unlock(uint32_t lkid, uint32_t flags, struct dlm_lksb *lksb, void *astarg); extern int dlm_lock_wait(uint32_t mode, struct dlm_lksb *lksb, uint32_t flags, const void *name, unsigned int namelen, uint32_t parent, /* unused */ void *bastarg, void (*bastaddr) (void *bastarg), void *range); /* unused */ extern int dlm_unlock_wait(uint32_t lkid, uint32_t flags, struct dlm_lksb *lksb); /* * These two are for users that want to do their own FD handling * * dlm_get_fd() - returns fd for the default lockspace for polling and dispatch * dlm_dispatch() - dispatches pending asts and basts */ extern int dlm_get_fd(void); extern int dlm_dispatch(int fd); /* * Creating your own lockspace * * dlm_create_lockspace() - create and open a lockspace and return a handle * to it. Privileges are required to create/release. * dlm_new_lockspace() - same as create but allows flags * dlm_open_lockspace() - simply returns a handle for an existing lockspace and * may be called by ordinary users. * dlm_release_lockspace() * dlm_close_lockspace() * dlm_ls_get_fd() * * NOTE: that if you dlm_create_lockspace() then dlm_open_lockspace() you will * have two open files on the same device. Hardly a major problem but I thought * it worth pointing out. */ typedef void *dlm_lshandle_t; extern dlm_lshandle_t dlm_create_lockspace(const char *name, mode_t mode); extern int dlm_release_lockspace(const char *name, dlm_lshandle_t ls, int force); extern dlm_lshandle_t dlm_open_lockspace(const char *name); extern int dlm_close_lockspace(dlm_lshandle_t ls); extern int dlm_ls_get_fd(dlm_lshandle_t ls); extern dlm_lshandle_t dlm_new_lockspace(const char *name, mode_t mode, uint32_t flags); /* * Using your own lockspace * * dlm_ls_lock() * dlm_ls_lockx() * dlm_ls_unlock() * dlm_ls_lock_wait() * dlm_ls_unlock_wait() * dlm_ls_deadlock_cancel() * dlm_ls_purge() */ extern int dlm_ls_lock(dlm_lshandle_t lockspace, uint32_t mode, struct dlm_lksb *lksb, uint32_t flags, const void *name, unsigned int namelen, uint32_t parent, /* unused */ void (*astaddr) (void *astarg), void *astarg, void (*bastaddr) (void *astarg), void *range); /* unused */ extern int dlm_ls_lockx(dlm_lshandle_t lockspace, uint32_t mode, struct dlm_lksb *lksb, uint32_t flags, const void *name, unsigned int namelen, uint32_t parent, /* unused */ void (*astaddr) (void *astarg), void *astarg, void (*bastaddr) (void *astarg), uint64_t *xid, uint64_t *timeout); extern int dlm_ls_unlock(dlm_lshandle_t lockspace, uint32_t lkid, uint32_t flags, struct dlm_lksb *lksb, void *astarg); extern int dlm_ls_lock_wait(dlm_lshandle_t lockspace, uint32_t mode, struct dlm_lksb *lksb, uint32_t flags, const void *name, unsigned int namelen, uint32_t parent, /* unused */ void *bastarg, void (*bastaddr) (void *bastarg), void *range); /* unused */ extern int dlm_ls_unlock_wait(dlm_lshandle_t lockspace, uint32_t lkid, uint32_t flags, struct dlm_lksb *lksb); extern int dlm_ls_deadlock_cancel(dlm_lshandle_t ls, uint32_t lkid, uint32_t flags); extern int dlm_ls_purge(dlm_lshandle_t lockspace, int nodeid, int pid); /* * For threaded applications * * dlm_pthread_init() * dlm_ls_pthread_init() - call this before any locking operations and the ASTs * will be delivered in their own thread. * dlm_pthread_cleanup() - call the cleanup routine at application exit * (optional) or, if the locking functions are in a * shared library that is to be unloaded. * * dlm_close/release_lockspace() will tidy the threads for a non-default * lockspace */ #ifdef _REENTRANT extern int dlm_pthread_init(); extern int dlm_ls_pthread_init(dlm_lshandle_t lockspace); extern int dlm_pthread_cleanup(); #endif /* * Lock modes */ #define LKM_NLMODE 0 /* null lock */ #define LKM_CRMODE 1 /* concurrent read */ #define LKM_CWMODE 2 /* concurrent write */ #define LKM_PRMODE 3 /* protected read */ #define LKM_PWMODE 4 /* protected write */ #define LKM_EXMODE 5 /* exclusive */ /* * Locking flags - these match the ones in dlm.h */ #define LKF_NOQUEUE 0x00000001 #define LKF_CANCEL 0x00000002 #define LKF_CONVERT 0x00000004 #define LKF_VALBLK 0x00000008 #define LKF_QUECVT 0x00000010 #define LKF_IVVALBLK 0x00000020 #define LKF_CONVDEADLK 0x00000040 #define LKF_PERSISTENT 0x00000080 #define LKF_NODLCKWT 0x00000100 #define LKF_NODLCKBLK 0x00000200 #define LKF_EXPEDITE 0x00000400 #define LKF_NOQUEUEBAST 0x00000800 #define LKF_HEADQUE 0x00001000 #define LKF_NOORDER 0x00002000 #define LKF_ORPHAN 0x00004000 #define LKF_ALTPR 0x00008000 #define LKF_ALTCW 0x00010000 #define LKF_FORCEUNLOCK 0x00020000 #define LKF_TIMEOUT 0x00040000 #define LKF_WAIT 0x80000000 /* Userspace only, for sync API calls */ /* * Extra return codes used by the DLM */ #define ECANCEL 0x10001 #define EUNLOCK 0x10002 #define EINPROG 0x10003 /* lock operation is in progress */ #endif ocfs2-tools-ocfs2-tools-1.8.6/libo2dlm/o2dlm.c000066400000000000000000000724311347147137200207550ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * o2dlm.c * * Defines the userspace locking api * * Copyright (C) 2004 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License, version 2, as published by the Free Software Foundation. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * * Authors: Mark Fasheh */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "o2dlm/o2dlm.h" #include "ocfs2-kernel/kernel-list.h" #define USER_DLMFS_MAGIC 0x76a9f425 struct o2dlm_lock_bast { struct list_head b_bucket; /* to hang us off of the bast list */ int b_fd; /* the fd of the lock file */ void (*b_bast)(void *); void *b_arg; /* Argument to ->b_bast() */ }; struct o2dlm_lock_res { struct list_head l_bucket; /* to hang us off the locks list */ char l_id[O2DLM_LOCK_ID_MAX_LEN]; /* 32 byte, * null * terminated * string */ int l_flags; /* limited set of flags */ enum o2dlm_lock_level l_level; /* either PR or EX */ int l_fd; /* the fd returned by the open call */ #ifdef HAVE_FSDLM struct dlm_lksb l_lksb; /* lksb for fsdlm locking */ char l_lvb[DLM_LVB_LEN]; /* LVB for fsdlm */ #endif }; struct o2dlm_ctxt { int ct_classic; int ct_supports_bast; struct list_head *ct_hash; struct list_head *ct_bast_hash; unsigned int ct_hash_size; char ct_domain_path[O2DLM_MAX_FULL_DOMAIN_PATH]; /* domain * dir */ char ct_ctxt_lock_name[O2DLM_LOCK_ID_MAX_LEN]; #ifdef HAVE_FSDLM void *ct_lib_handle; dlm_lshandle_t ct_handle; #endif }; static errcode_t o2dlm_lock_nochecks(struct o2dlm_ctxt *ctxt, const char *lockid, int lockflags, enum o2dlm_lock_level level); static errcode_t o2dlm_unlock_lock_res(struct o2dlm_ctxt *ctxt, struct o2dlm_lock_res *lockres); static errcode_t o2dlm_generate_random_value(int64_t *value) { int randfd = 0; int readlen = sizeof(*value); if ((randfd = open("/dev/urandom", O_RDONLY)) == -1) return O2DLM_ET_RANDOM; if (read(randfd, value, readlen) != readlen) { close(randfd); return O2DLM_ET_RANDOM; } close(randfd); return 0; } static void o2dlm_free_ctxt(struct o2dlm_ctxt *ctxt) { if (ctxt->ct_hash) free(ctxt->ct_hash); if (ctxt->ct_bast_hash) free(ctxt->ct_bast_hash); free(ctxt); } #define O2DLM_DEFAULT_HASH_SIZE 4096 static errcode_t o2dlm_alloc_ctxt(const char *mnt_path, const char *dirname, struct o2dlm_ctxt **dlm_ctxt) { errcode_t err; struct o2dlm_ctxt *ctxt; int64_t rand; int len, i; err = o2dlm_generate_random_value(&rand); if (err) return err; ctxt = malloc(sizeof(*ctxt)); if (!ctxt) return O2DLM_ET_NO_MEMORY; memset(ctxt, 0, sizeof(*ctxt)); ctxt->ct_supports_bast = -1; ctxt->ct_hash_size = O2DLM_DEFAULT_HASH_SIZE; ctxt->ct_hash = calloc(ctxt->ct_hash_size, sizeof(struct list_head)); ctxt->ct_bast_hash = calloc(ctxt->ct_hash_size, sizeof(struct list_head)); if (!ctxt->ct_hash || !ctxt->ct_bast_hash) { err = O2DLM_ET_NO_MEMORY; goto exit_and_free; } for(i = 0; i < ctxt->ct_hash_size; i++) { INIT_LIST_HEAD(&ctxt->ct_hash[i]); INIT_LIST_HEAD(&ctxt->ct_bast_hash[i]); } len = snprintf(ctxt->ct_ctxt_lock_name, O2DLM_LOCK_ID_MAX_LEN, ".%016"PRIx64, rand); if (len == O2DLM_LOCK_ID_MAX_LEN) { err = O2DLM_ET_NAME_TOO_LONG; goto exit_and_free; } else if (len < 0) { err = O2DLM_ET_OUTPUT_ERROR; goto exit_and_free; } if (mnt_path) { ctxt->ct_classic = 1; len = snprintf(ctxt->ct_domain_path, PATH_MAX + 1, "%s/%s", mnt_path, dirname); } else { ctxt->ct_classic = 0; len = snprintf(ctxt->ct_domain_path, PATH_MAX + 1, "%s", dirname); } if (len == (PATH_MAX + 1)) { err = O2DLM_ET_BAD_DOMAIN_DIR; goto exit_and_free; } else if (len < 0) { err = O2DLM_ET_OUTPUT_ERROR; goto exit_and_free; } *dlm_ctxt = ctxt; err = 0; exit_and_free: if (err) o2dlm_free_ctxt(ctxt); return err; } static errcode_t o2dlm_check_user_dlmfs(const char *dlmfs_path) { struct statfs statfs_buf; struct stat stat_buf; int ret, fd; fd = open(dlmfs_path, O_RDONLY); if (fd < 0) return O2DLM_ET_OPEN_DLM_DIR; ret = fstat(fd, &stat_buf); if (ret) { close(fd); return O2DLM_ET_STATFS; } if (!S_ISDIR(stat_buf.st_mode)) { close(fd); return O2DLM_ET_NO_FS_DIR; } ret = fstatfs(fd, &statfs_buf); if (ret) { close(fd); return O2DLM_ET_STATFS; } close(fd); if (statfs_buf.f_type != USER_DLMFS_MAGIC) return O2DLM_ET_NO_FS; return 0; } static errcode_t o2dlm_check_domain_dir(struct o2dlm_ctxt *ctxt) { int status; char *dirpath = ctxt->ct_domain_path; struct stat st; status = stat(dirpath, &st); if (status) { if (errno == ENOENT) return O2DLM_ET_NO_DOMAIN_DIR; return O2DLM_ET_BAD_DOMAIN_DIR; } if (!S_ISDIR(st.st_mode)) return O2DLM_ET_BAD_DOMAIN_DIR; return 0; } #define O2DLM_DOMAIN_DIR_MODE 0755 static errcode_t o2dlm_create_domain(struct o2dlm_ctxt *ctxt) { int status; char *dirpath = ctxt->ct_domain_path; status = mkdir(dirpath, O2DLM_DOMAIN_DIR_MODE); if (status) return O2DLM_ET_DOMAIN_CREATE; return 0; } static errcode_t o2dlm_delete_domain_dir(struct o2dlm_ctxt *ctxt) { int ret; ret = rmdir(ctxt->ct_domain_path); if (ret) { if (errno == ENOTEMPTY) return O2DLM_ET_BUSY_DOMAIN_DIR; return O2DLM_ET_DOMAIN_DESTROY; } return 0; } static errcode_t o2dlm_full_path(char *path, struct o2dlm_ctxt *ctxt, const char *filename) { int ret; int len = PATH_MAX + 1; ret = snprintf(path, len, "%s/%s", ctxt->ct_domain_path, filename); if (ret == len) return O2DLM_ET_NAME_TOO_LONG; else if (ret < 0) return O2DLM_ET_OUTPUT_ERROR; return 0; } /* * Most of this hash function taken from dcache.h. It has the * following copyright line at the top: * * (C) Copyright 1997 Thomas Schoebel-Theuer, * with heavy changes by Linus Torvalds */ static inline unsigned long partial_name_hash(unsigned long c, unsigned long prevhash) { return (prevhash + (c << 4) + (c >> 4)) * 11; } static inline unsigned int o2dlm_hash_lockname(struct o2dlm_ctxt *ctxt, const char *name) { unsigned long hash = 0; unsigned int bucket; while(*name) hash = partial_name_hash(*name++, hash); bucket = (unsigned int) hash % ctxt->ct_hash_size; assert(bucket < ctxt->ct_hash_size); return bucket; } static struct o2dlm_lock_res *o2dlm_find_lock_res(struct o2dlm_ctxt *ctxt, const char *lockid) { struct o2dlm_lock_res *lockres; struct list_head *p; unsigned int bucket; bucket = o2dlm_hash_lockname(ctxt, lockid); list_for_each(p, &ctxt->ct_hash[bucket]) { lockres = list_entry(p, struct o2dlm_lock_res, l_bucket); if (!strcmp(lockid, lockres->l_id)) return lockres; } return NULL; } static void o2dlm_insert_lock_res(struct o2dlm_ctxt *ctxt, struct o2dlm_lock_res *lockres) { unsigned int bucket; bucket = o2dlm_hash_lockname(ctxt, lockres->l_id); list_add_tail(&lockres->l_bucket, &ctxt->ct_hash[bucket]); } static inline void o2dlm_remove_lock_res(struct o2dlm_lock_res *lockres) { list_del(&lockres->l_bucket); INIT_LIST_HEAD(&lockres->l_bucket); } static int o2dlm_translate_lock_flags(enum o2dlm_lock_level level, int lockflags) { int flags; switch (level) { case O2DLM_LEVEL_PRMODE: flags = O_RDONLY; break; case O2DLM_LEVEL_EXMODE: flags = O_RDWR; break; default: flags = 0; } if (lockflags & O2DLM_TRYLOCK) flags |= O_NONBLOCK; return flags; } static struct o2dlm_lock_res *o2dlm_new_lock_res(const char *id, enum o2dlm_lock_level level, int flags) { struct o2dlm_lock_res *lockres; lockres = malloc(sizeof(*lockres)); if (lockres) { memset(lockres, 0, sizeof(*lockres)); INIT_LIST_HEAD(&lockres->l_bucket); strncpy(lockres->l_id, id, O2DLM_LOCK_ID_MAX_LEN); lockres->l_flags = flags; lockres->l_level = level; lockres->l_fd = -1; #ifdef HAVE_FSDLM lockres->l_lksb.sb_lvbptr = lockres->l_lvb; memset(lockres->l_lksb.sb_lvbptr, 0, DLM_LVB_LEN); #endif } return lockres; } static inline unsigned int o2dlm_hash_bast(struct o2dlm_ctxt *ctxt, int fd) { return fd % ctxt->ct_hash_size; } static struct o2dlm_lock_bast *o2dlm_find_bast(struct o2dlm_ctxt *ctxt, int fd) { struct o2dlm_lock_bast *bast; struct list_head *p; unsigned int bucket; bucket = o2dlm_hash_bast(ctxt, fd); list_for_each(p, &ctxt->ct_bast_hash[bucket]) { bast = list_entry(p, struct o2dlm_lock_bast, b_bucket); if (fd == bast->b_fd) return bast; } return NULL; } static void o2dlm_insert_bast(struct o2dlm_ctxt *ctxt, struct o2dlm_lock_bast *bast) { unsigned int bucket; bucket = o2dlm_hash_bast(ctxt, bast->b_fd); list_add_tail(&bast->b_bucket, &ctxt->ct_bast_hash[bucket]); } static inline void o2dlm_remove_bast(struct o2dlm_lock_bast *bast) { list_del(&bast->b_bucket); INIT_LIST_HEAD(&bast->b_bucket); } static struct o2dlm_lock_bast *o2dlm_new_bast(int fd, void (*bast_func)(void *), void *bastarg) { struct o2dlm_lock_bast *bast; bast = malloc(sizeof(*bast)); if (bast) { memset(bast, 0, sizeof(*bast)); INIT_LIST_HEAD(&bast->b_bucket); bast->b_bast = bast_func; bast->b_arg = bastarg; bast->b_fd = fd; } return bast; } #define O2DLM_OPEN_MODE 0664 /* * Classic o2dlm */ static errcode_t o2dlm_lock_classic(struct o2dlm_ctxt *ctxt, const char *lockid, int lockflags, enum o2dlm_lock_level level) { int ret, flags, fd; char *path; struct o2dlm_lock_res *lockres; lockres = o2dlm_find_lock_res(ctxt, lockid); if (lockres) return O2DLM_ET_RECURSIVE_LOCK; path = malloc(PATH_MAX + 1); if (!path) return O2DLM_ET_NO_MEMORY; ret = o2dlm_full_path(path, ctxt, lockid); if (ret) { free(path); return ret; } lockflags &= O2DLM_VALID_FLAGS; flags = o2dlm_translate_lock_flags(level, lockflags); lockres = o2dlm_new_lock_res(lockid, level, flags); if (!lockres) { free(path); return O2DLM_ET_NO_MEMORY; } fd = open(path, flags|O_CREAT, O2DLM_OPEN_MODE); if (fd < 0) { free(path); free(lockres); if ((lockflags & O2DLM_TRYLOCK) && (errno == ETXTBSY)) return O2DLM_ET_TRYLOCK_FAILED; return O2DLM_ET_LOCKING; } lockres->l_flags = lockflags; lockres->l_fd = fd; o2dlm_insert_lock_res(ctxt, lockres); free(path); return 0; } static errcode_t o2dlm_drop_lock_classic(struct o2dlm_ctxt *ctxt, const char *lockid) { int ret, len = PATH_MAX + 1; char *path; /* * We're trying to unlink the lockres file from the dlm file * system. Note that EBUSY from unlink is not a fatal error here * -- it simply means that the lock is in use by some other * process. * */ path = malloc(len); if (!path) return O2DLM_ET_NO_MEMORY; ret = o2dlm_full_path(path, ctxt, lockid); if (ret) { free(path); return ret; } ret = unlink(path); free (path); if (ret) { if (errno == EBUSY) return O2DLM_ET_BUSY_LOCK; return O2DLM_ET_UNLINK; } return 0; } static errcode_t o2dlm_unlock_lock_res_classic(struct o2dlm_ctxt *ctxt, struct o2dlm_lock_res *lockres) { close(lockres->l_fd); return 0; } static errcode_t o2dlm_read_lvb_classic(struct o2dlm_ctxt *ctxt, char *lockid, char *lvb, unsigned int len, unsigned int *bytes_read) { int fd, ret; struct o2dlm_lock_res *lockres; lockres = o2dlm_find_lock_res(ctxt, lockid); if (!lockres) return O2DLM_ET_UNKNOWN_LOCK; fd = lockres->l_fd; ret = lseek(fd, 0, SEEK_SET); if (ret < 0) return O2DLM_ET_SEEK; ret = read(fd, lvb, len); if (ret < 0) return O2DLM_ET_LVB_READ; if (!ret) return O2DLM_ET_LVB_INVALID; if (bytes_read) *bytes_read = ret; return 0; } static errcode_t o2dlm_write_lvb_classic(struct o2dlm_ctxt *ctxt, char *lockid, const char *lvb, unsigned int len, unsigned int *bytes_written) { int fd, ret; struct o2dlm_lock_res *lockres; if (!ctxt || !lockid || !lvb) return O2DLM_ET_INVALID_ARGS; lockres = o2dlm_find_lock_res(ctxt, lockid); if (!lockres) return O2DLM_ET_UNKNOWN_LOCK; fd = lockres->l_fd; ret = lseek(fd, 0, SEEK_SET); if (ret < 0) return O2DLM_ET_SEEK; ret = write(fd, lvb, len); if (ret < 0) return O2DLM_ET_LVB_WRITE; if (bytes_written) *bytes_written = ret; return 0; } static errcode_t o2dlm_unlink_all(struct o2dlm_ctxt *ctxt) { int ret; char *name; DIR *dir; struct dirent *de; name = malloc(PATH_MAX + 1); if (!name) return O2DLM_ET_NO_MEMORY; dir = opendir(ctxt->ct_domain_path); if (!dir) { free(name); return O2DLM_ET_DOMAIN_DIR; } de = readdir(dir); while(de) { if ((strlen(de->d_name) == 1) && (de->d_name[0] == '.')) goto next; if ((strlen(de->d_name) == 2) && (de->d_name[0] == '.') && (de->d_name[1] == '.')) goto next; ret = o2dlm_full_path(name, ctxt, de->d_name); if (ret) goto close_and_free; ret = unlink(name); if (ret) { if (errno != EBUSY) { ret = O2DLM_ET_UNLINK; goto close_and_free; } } next: de = readdir(dir); } ret = 0; close_and_free: closedir(dir); free(name); return ret; } static errcode_t o2dlm_destroy_classic(struct o2dlm_ctxt *ctxt) { int ret, i; int error = 0; struct o2dlm_lock_res *lockres; struct o2dlm_lock_bast *bast; struct list_head *p, *n, *bucket; for(i = 0; i < ctxt->ct_hash_size; i++) { bucket = &ctxt->ct_bast_hash[i]; list_for_each_safe(p, n, bucket) { bast = list_entry(p, struct o2dlm_lock_bast, b_bucket); o2dlm_remove_bast(bast); free(bast); } bucket = &ctxt->ct_hash[i]; list_for_each_safe(p, n, bucket) { lockres = list_entry(p, struct o2dlm_lock_res, l_bucket); o2dlm_remove_lock_res(lockres); ret = o2dlm_unlock_lock_res(ctxt, lockres); if (ret && (ret != O2DLM_ET_BUSY_LOCK)) error = O2DLM_ET_FAILED_UNLOCKS; free(lockres); } } if (error) goto free_and_exit; ret = o2dlm_unlink_all(ctxt); if (ret && ret != O2DLM_ET_BUSY_LOCK) { error = ret; goto free_and_exit; } ret = o2dlm_delete_domain_dir(ctxt); if (ret && ret != O2DLM_ET_BUSY_DOMAIN_DIR) error = ret; free_and_exit: o2dlm_free_ctxt(ctxt); return error; } static errcode_t o2dlm_initialize_classic(const char *dlmfs_path, const char *domain_name, struct o2dlm_ctxt **dlm_ctxt) { errcode_t ret, dir_created = 0; struct o2dlm_ctxt *ctxt; if (!dlmfs_path) return O2DLM_ET_INVALID_ARGS; if (strlen(domain_name) >= O2DLM_DOMAIN_MAX_LEN) return O2DLM_ET_NAME_TOO_LONG; if ((strlen(dlmfs_path) + strlen(domain_name)) > O2DLM_MAX_FULL_DOMAIN_PATH) return O2DLM_ET_NAME_TOO_LONG; ret = o2dlm_check_user_dlmfs(dlmfs_path); if (ret) return ret; ret = o2dlm_alloc_ctxt(dlmfs_path, domain_name, &ctxt); if (ret) return ret; ret = o2dlm_check_domain_dir(ctxt); if (ret) { if (ret != O2DLM_ET_NO_DOMAIN_DIR) { o2dlm_free_ctxt(ctxt); return ret; } /* the domain does not yet exist - create it ourselves. */ ret = o2dlm_create_domain(ctxt); if (ret) { o2dlm_free_ctxt(ctxt); return ret; } dir_created = 1; } /* What we want to do here is create a lock which we'll hold * open for the duration of this context. This way if another * process won't be able to shut down this domain underneath * us. */ ret = o2dlm_lock_nochecks(ctxt, ctxt->ct_ctxt_lock_name, 0, O2DLM_LEVEL_PRMODE); if (ret) { if (dir_created) o2dlm_delete_domain_dir(ctxt); /* best effort * cleanup. */ o2dlm_free_ctxt(ctxt); return ret; } *dlm_ctxt = ctxt; return 0; } /* * fsdlm operations */ #ifdef HAVE_FSDLM /* Dynamic symbols */ static dlm_lshandle_t (*fsdlm_create_lockspace)(const char *name, mode_t mode); static int (*fsdlm_release_lockspace)(const char *name, dlm_lshandle_t ls, int force); static int (*fsdlm_ls_lock_wait)(dlm_lshandle_t ls, uint32_t mode, struct dlm_lksb *lksb, uint32_t flags, const void *name, unsigned int namelen, uint32_t parent, void *bastarg, void (*bastaddr)(void *bastarg), void *range); static int (*fsdlm_ls_unlock_wait)(dlm_lshandle_t ls, uint32_t lkid, uint32_t flags, struct dlm_lksb *lksb); static errcode_t load_fsdlm(struct o2dlm_ctxt *ctxt) { errcode_t ret = O2DLM_ET_SERVICE_UNAVAILABLE; if (ctxt->ct_lib_handle) { ret = 0; goto out; } ctxt->ct_lib_handle = dlopen("libdlm_lt.so.3", RTLD_NOW | RTLD_LOCAL); if (!ctxt->ct_lib_handle) goto out; #define load_sym(_sym) do { \ fs ## _sym = dlsym(ctxt->ct_lib_handle, #_sym); \ if (! fs ## _sym) \ goto out; \ } while (0) load_sym(dlm_create_lockspace); load_sym(dlm_release_lockspace); load_sym(dlm_ls_lock_wait); load_sym(dlm_ls_unlock_wait); ret = 0; out: if (ret && ctxt->ct_lib_handle) { dlclose(ctxt->ct_lib_handle); ctxt->ct_lib_handle = NULL; } return ret; } static void to_fsdlm_lock(enum o2dlm_lock_level level, int lockflags, uint32_t *fsdlm_mode, uint32_t *fsdlm_flags) { switch (level) { case O2DLM_LEVEL_PRMODE: *fsdlm_mode = LKM_PRMODE; break; case O2DLM_LEVEL_EXMODE: *fsdlm_mode = LKM_EXMODE; break; default: *fsdlm_mode = LKM_NLMODE; break; } if (lockflags & O2DLM_TRYLOCK) *fsdlm_flags = LKF_NOQUEUE; else *fsdlm_flags = 0; } static errcode_t o2dlm_lock_fsdlm(struct o2dlm_ctxt *ctxt, const char *lockid, int lockflags, enum o2dlm_lock_level level) { int rc; errcode_t ret = 0; struct o2dlm_lock_res *lockres; uint32_t mode, flags; if (!fsdlm_ls_lock_wait) return O2DLM_ET_SERVICE_UNAVAILABLE; lockres = o2dlm_find_lock_res(ctxt, lockid); if (lockres) return O2DLM_ET_RECURSIVE_LOCK; lockflags &= O2DLM_VALID_FLAGS; lockres = o2dlm_new_lock_res(lockid, level, lockflags); if (!lockres) return O2DLM_ET_NO_MEMORY; to_fsdlm_lock(level, lockflags, &mode, &flags); flags |= LKF_VALBLK; /* Always use LVBs */ rc = fsdlm_ls_lock_wait(ctxt->ct_handle, mode, &lockres->l_lksb, flags, lockid, strlen(lockid), 0, NULL, NULL, NULL); if (rc) rc = errno; else rc = lockres->l_lksb.sb_status; switch (rc) { case 0: /* Success! */ break; case EAGAIN: if (lockflags & O2DLM_TRYLOCK) ret = O2DLM_ET_TRYLOCK_FAILED; else ret = O2DLM_ET_LOCKING; break; case EINVAL: ret = O2DLM_ET_INVALID_ARGS; break; case ENOMEM: ret = O2DLM_ET_NO_MEMORY; break; case ECANCEL: ret = O2DLM_ET_LOCKING; break; default: ret = O2DLM_ET_INTERNAL_FAILURE; break; } if (!ret) o2dlm_insert_lock_res(ctxt, lockres); else free(lockres); return ret; } static errcode_t o2dlm_unlock_lock_res_fsdlm(struct o2dlm_ctxt *ctxt, struct o2dlm_lock_res *lockres) { int rc; errcode_t ret = 0; if (!fsdlm_ls_unlock_wait) return O2DLM_ET_SERVICE_UNAVAILABLE; rc = fsdlm_ls_unlock_wait(ctxt->ct_handle, lockres->l_lksb.sb_lkid, LKF_VALBLK, &lockres->l_lksb); if (rc) rc = errno; else rc = lockres->l_lksb.sb_status; switch (rc) { case 0: case EUNLOCK: /* Success! */ break; case ENOTCONN: ret = O2DLM_ET_SERVICE_UNAVAILABLE; break; case EINVAL: ret = O2DLM_ET_INVALID_ARGS; break; case ENOENT: ret = O2DLM_ET_UNKNOWN_LOCK; break; default: ret = O2DLM_ET_INTERNAL_FAILURE; break; } return ret; } static errcode_t o2dlm_write_lvb_fsdlm(struct o2dlm_ctxt *ctxt, char *lockid, const char *lvb, unsigned int len, unsigned int *bytes_written) { struct o2dlm_lock_res *lockres; if (!ctxt || !lockid || !lvb) return O2DLM_ET_INVALID_ARGS; lockres = o2dlm_find_lock_res(ctxt, lockid); if (!lockres) return O2DLM_ET_UNKNOWN_LOCK; /* fsdlm only supports DLM_LVB_LEN for userspace locks */ if (len > DLM_LVB_LEN) len = DLM_LVB_LEN; memcpy(lockres->l_lksb.sb_lvbptr, lvb, len); if (bytes_written) *bytes_written = len; return 0; } static errcode_t o2dlm_read_lvb_fsdlm(struct o2dlm_ctxt *ctxt, char *lockid, char *lvb, unsigned int len, unsigned int *bytes_read) { struct o2dlm_lock_res *lockres; lockres = o2dlm_find_lock_res(ctxt, lockid); if (!lockres) return O2DLM_ET_UNKNOWN_LOCK; /* fsdlm only supports DLM_LVB_LEN for userspace locks */ if (len > DLM_LVB_LEN) len = DLM_LVB_LEN; memcpy(lvb, lockres->l_lksb.sb_lvbptr, len); if (bytes_read) *bytes_read = len; return 0; } static errcode_t o2dlm_initialize_fsdlm(const char *domain_name, struct o2dlm_ctxt **dlm_ctxt) { errcode_t ret; struct o2dlm_ctxt *ctxt; if (strlen(domain_name) >= O2DLM_DOMAIN_MAX_LEN) return O2DLM_ET_NAME_TOO_LONG; ret = o2dlm_alloc_ctxt(NULL, domain_name, &ctxt); if (ret) return ret; ret = load_fsdlm(ctxt); if (ret) { o2dlm_free_ctxt(ctxt); return ret; } ctxt->ct_handle = fsdlm_create_lockspace(ctxt->ct_domain_path, 0600); if (!ctxt->ct_handle) { switch (errno) { case EINVAL: ret = O2DLM_ET_INVALID_ARGS; break; case ENOMEM: ret = O2DLM_ET_NO_MEMORY; break; case EEXIST: /* * This is a special case for older * versions of fs/dlm. */ ret = O2DLM_ET_DOMAIN_BUSY; break; case EACCES: case EPERM: ret = O2DLM_ET_BAD_DOMAIN_DIR; break; default: ret = O2DLM_ET_INTERNAL_FAILURE; break; } o2dlm_free_ctxt(ctxt); return ret; } /* What we want to do here is create a lock which we'll hold * open for the duration of this context. This way if another * process won't be able to shut down this domain underneath * us. */ ret = o2dlm_lock_nochecks(ctxt, ctxt->ct_ctxt_lock_name, 0, O2DLM_LEVEL_PRMODE); if (ret) { /* Ignore the error, we want ret to be propagated */ fsdlm_release_lockspace(ctxt->ct_domain_path, ctxt->ct_handle, 0); o2dlm_free_ctxt(ctxt); return ret; } *dlm_ctxt = ctxt; return 0; } static errcode_t o2dlm_destroy_fsdlm(struct o2dlm_ctxt *ctxt) { int i, rc; errcode_t ret, error = 0; struct o2dlm_lock_res *lockres; struct list_head *p, *n, *bucket; if (!fsdlm_release_lockspace) return O2DLM_ET_SERVICE_UNAVAILABLE; for(i = 0; i < ctxt->ct_hash_size; i++) { bucket = &ctxt->ct_hash[i]; list_for_each_safe(p, n, bucket) { lockres = list_entry(p, struct o2dlm_lock_res, l_bucket); o2dlm_remove_lock_res(lockres); ret = o2dlm_unlock_lock_res(ctxt, lockres); if (ret && (ret != O2DLM_ET_BUSY_LOCK)) error = O2DLM_ET_FAILED_UNLOCKS; free(lockres); } } if (error) goto free_and_exit; rc = fsdlm_release_lockspace(ctxt->ct_domain_path, ctxt->ct_handle, 0); if (!rc) goto free_and_exit; switch(errno) { case EBUSY: /* Do nothing */ break; case EINVAL: error = O2DLM_ET_INVALID_ARGS; break; case ENOMEM: error = O2DLM_ET_NO_MEMORY; break; case EACCES: case EPERM: error = O2DLM_ET_BAD_DOMAIN_DIR; break; default: error = O2DLM_ET_INTERNAL_FAILURE; break; } free_and_exit: o2dlm_free_ctxt(ctxt); return error; } #else /* HAVE_FSDLM */ static errcode_t o2dlm_lock_fsdlm(struct o2dlm_ctxt *ctxt, const char *lockid, int lockflags, enum o2dlm_lock_level level) { return O2DLM_ET_SERVICE_UNAVAILABLE; } static errcode_t o2dlm_unlock_lock_res_fsdlm(struct o2dlm_ctxt *ctxt, struct o2dlm_lock_res *lockres) { return O2DLM_ET_SERVICE_UNAVAILABLE; } static errcode_t o2dlm_read_lvb_fsdlm(struct o2dlm_ctxt *ctxt, char *lockid, char *lvb, unsigned int len, unsigned int *bytes_read) { return O2DLM_ET_SERVICE_UNAVAILABLE; } static errcode_t o2dlm_write_lvb_fsdlm(struct o2dlm_ctxt *ctxt, char *lockid, const char *lvb, unsigned int len, unsigned int *bytes_written) { return O2DLM_ET_SERVICE_UNAVAILABLE; } static errcode_t o2dlm_initialize_fsdlm(const char *domain_name, struct o2dlm_ctxt **dlm_ctxt) { return O2DLM_ET_SERVICE_UNAVAILABLE; } static errcode_t o2dlm_destroy_fsdlm(struct o2dlm_ctxt *ctxt) { return O2DLM_ET_SERVICE_UNAVAILABLE; } #endif /* HAVE_FSDLM */ /* * Public API */ /* Use this internally to avoid the check for a reserved name */ static errcode_t o2dlm_lock_nochecks(struct o2dlm_ctxt *ctxt, const char *lockid, int lockflags, enum o2dlm_lock_level level) { if (strlen(lockid) >= O2DLM_LOCK_ID_MAX_LEN) return O2DLM_ET_INVALID_LOCK_NAME; if (level != O2DLM_LEVEL_PRMODE && level != O2DLM_LEVEL_EXMODE) return O2DLM_ET_INVALID_LOCK_LEVEL; if (ctxt->ct_classic) return o2dlm_lock_classic(ctxt, lockid, lockflags, level); else return o2dlm_lock_fsdlm(ctxt, lockid, lockflags, level); } errcode_t o2dlm_lock(struct o2dlm_ctxt *ctxt, const char *lockid, int lockflags, enum o2dlm_lock_level level) { if (!ctxt || !lockid) return O2DLM_ET_INVALID_ARGS; /* names starting with '.' are reserved. */ if (lockid[0] == '.') return O2DLM_ET_INVALID_LOCK_NAME; return o2dlm_lock_nochecks(ctxt, lockid, lockflags, level); } static errcode_t o2dlm_ctxt_supports_bast(struct o2dlm_ctxt *ctxt) { int support; errcode_t ret = 0; if (ctxt->ct_supports_bast == -1) { ret = o2dlm_supports_bast(&support); if (!ret) { ctxt->ct_supports_bast = support; } } if (!ret && !ctxt->ct_supports_bast) ret = O2DLM_ET_BAST_UNSUPPORTED; return ret; } errcode_t o2dlm_lock_with_bast(struct o2dlm_ctxt *ctxt, const char *lockid, int lockflags, enum o2dlm_lock_level level, void (*bast_func)(void *bast_arg), void *bast_arg, int *poll_fd) { errcode_t ret; struct o2dlm_lock_res *lockres; struct o2dlm_lock_bast *bast; if (!ctxt->ct_classic) return O2DLM_ET_BAST_UNSUPPORTED; if (!bast_func || !poll_fd) return O2DLM_ET_INVALID_ARGS; ret = o2dlm_ctxt_supports_bast(ctxt); if (ret) return ret; ret = o2dlm_lock(ctxt, lockid, lockflags, level); if (ret) return ret; lockres = o2dlm_find_lock_res(ctxt, lockid); if (!lockres) { o2dlm_unlock(ctxt, lockid); return O2DLM_ET_INTERNAL_FAILURE; } bast = o2dlm_new_bast(lockres->l_fd, bast_func, bast_arg); if (!bast) { o2dlm_unlock(ctxt, lockid); return O2DLM_ET_NO_MEMORY; } o2dlm_insert_bast(ctxt, bast); *poll_fd = lockres->l_fd; return 0; } static errcode_t o2dlm_unlock_lock_res(struct o2dlm_ctxt *ctxt, struct o2dlm_lock_res *lockres) { if (ctxt->ct_classic) return o2dlm_unlock_lock_res_classic(ctxt, lockres); else return o2dlm_unlock_lock_res_fsdlm(ctxt, lockres); } /* * Dropping locks is only available on dlmfs. No one should be using * libdlm if they can help it. */ errcode_t o2dlm_drop_lock(struct o2dlm_ctxt *ctxt, const char *lockid) { if (!ctxt || !lockid) return O2DLM_ET_INVALID_ARGS; if (o2dlm_find_lock_res(ctxt, lockid)) return O2DLM_ET_BUSY_LOCK; if (ctxt->ct_classic) return o2dlm_drop_lock_classic(ctxt, lockid); else return O2DLM_ET_SERVICE_UNAVAILABLE; } errcode_t o2dlm_unlock(struct o2dlm_ctxt *ctxt, const char *lockid) { int ret; struct o2dlm_lock_res *lockres; struct o2dlm_lock_bast *bast; if (!ctxt || !lockid) return O2DLM_ET_INVALID_ARGS; lockres = o2dlm_find_lock_res(ctxt, lockid); if (!lockres) return O2DLM_ET_UNKNOWN_LOCK; bast = o2dlm_find_bast(ctxt, lockres->l_fd); if (bast) o2dlm_remove_bast(bast); o2dlm_remove_lock_res(lockres); ret = o2dlm_unlock_lock_res(ctxt, lockres); free(lockres); if (ret && (ret != O2DLM_ET_BUSY_LOCK)) return ret; return 0; } errcode_t o2dlm_read_lvb(struct o2dlm_ctxt *ctxt, char *lockid, char *lvb, unsigned int len, unsigned int *bytes_read) { if (!ctxt || !lockid || !lvb) return O2DLM_ET_INVALID_ARGS; if (ctxt->ct_classic) return o2dlm_read_lvb_classic(ctxt, lockid, lvb, len, bytes_read); else return o2dlm_read_lvb_fsdlm(ctxt, lockid, lvb, len, bytes_read); } errcode_t o2dlm_write_lvb(struct o2dlm_ctxt *ctxt, char *lockid, const char *lvb, unsigned int len, unsigned int *bytes_written) { if (!ctxt || !lockid || !lvb) return O2DLM_ET_INVALID_ARGS; if (ctxt->ct_classic) return o2dlm_write_lvb_classic(ctxt, lockid, lvb, len, bytes_written); else return o2dlm_write_lvb_fsdlm(ctxt, lockid, lvb, len, bytes_written); } void o2dlm_process_bast(struct o2dlm_ctxt *ctxt, int poll_fd) { struct o2dlm_lock_bast *bast; bast = o2dlm_find_bast(ctxt, poll_fd); if (bast) bast->b_bast(bast->b_arg); } /* NULL dlmfs_path means fsdlm */ errcode_t o2dlm_initialize(const char *dlmfs_path, const char *domain_name, struct o2dlm_ctxt **dlm_ctxt) { if (!domain_name || !dlm_ctxt) return O2DLM_ET_INVALID_ARGS; if (dlmfs_path) return o2dlm_initialize_classic(dlmfs_path, domain_name, dlm_ctxt); else return o2dlm_initialize_fsdlm(domain_name, dlm_ctxt); } errcode_t o2dlm_destroy(struct o2dlm_ctxt *ctxt) { if (!ctxt) return O2DLM_ET_INVALID_ARGS; if (ctxt->ct_classic) return o2dlm_destroy_classic(ctxt); else return o2dlm_destroy_fsdlm(ctxt); } ocfs2-tools-ocfs2-tools-1.8.6/libo2dlm/o2dlm_err.et000066400000000000000000000051311347147137200220040ustar00rootroot00000000000000# # o2dlm_err.et # # Error codes for the O2DLM library. # # Copyright (C) 2005 Oracle. All rights reserved. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public # License, version 2, as published by the Free Software Foundation. # # 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., 51 Franklin St, Fifth Floor, # Boston, MA 02110-1301 USA. # error_table o2dl ec O2DLM_ET_NO_MEMORY, "Memory allocation failed" ec O2DLM_ET_SERVICE_UNAVAILABLE, "Unable to access cluster service" ec O2DLM_ET_INVALID_LOCK_NAME, "Invalid name for a lock" ec O2DLM_ET_INVALID_LOCK_LEVEL, "Invalid locking level" ec O2DLM_ET_INTERNAL_FAILURE, "Internal logic failure" ec O2DLM_ET_NAME_TOO_LONG, "Name too long" ec O2DLM_ET_STATFS, "Could not stat user_dlmfs mountpoint" ec O2DLM_ET_NO_FS_DIR, "Dlmfs path specified is not a directory." ec O2DLM_ET_NO_FS, "ocfs2_dlmfs file system was not found" ec O2DLM_ET_OPEN_DLM_DIR, "Could not open dlm directory" ec O2DLM_ET_NO_DOMAIN_DIR, "No directory for domain was found" ec O2DLM_ET_BAD_DOMAIN_DIR, "Could not stat domain directory" ec O2DLM_ET_BUSY_DOMAIN_DIR, "Domain directory has active locks" ec O2DLM_ET_DOMAIN_DIR, "Could not read domain dir" ec O2DLM_ET_DOMAIN_DESTROY, "Could not destroy domain" ec O2DLM_ET_DOMAIN_CREATE, "Could not create domain" ec O2DLM_ET_RANDOM, "Could not generate random value" ec O2DLM_ET_BUSY_LOCK, "Lock resource is in use by another process" ec O2DLM_ET_OUTPUT_ERROR, "Output Error" ec O2DLM_ET_UNLINK, "Error unlinking lock resource" ec O2DLM_ET_UNKNOWN_LOCK, "Could not find lock resource" ec O2DLM_ET_RECURSIVE_LOCK, "Already have a lock on this resource" ec O2DLM_ET_LOCKING, "Could not take lock" ec O2DLM_ET_FAILED_UNLOCKS, "Could not drop all locks" ec O2DLM_ET_INVALID_ARGS, "Invalid arguments passed to function" ec O2DLM_ET_TRYLOCK_FAILED, "Trylock failed" ec O2DLM_ET_LVB_READ, "Error reading LVB" ec O2DLM_ET_LVB_WRITE, "Error writing LVB" ec O2DLM_ET_SEEK, "Could not seek within file descriptor" ec O2DLM_ET_DOMAIN_BUSY, "The domain is busy and cannot be accessed" ec O2DLM_ET_BAST_UNSUPPORTED, "This environment does not support BASTs" ec O2DLM_ET_LVB_INVALID, "The LVB is not valid" end ocfs2-tools-ocfs2-tools-1.8.6/libo2dlm/o2dlm_test.c000066400000000000000000000164111347147137200220100ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * Test driver for libo2dlm. * * Copyright (C) 2004 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License, version 2, as published by the Free Software Foundation. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * * Authors: Mark Fasheh */ #include #include #include #include #include #include #include #include #include #include "o2dlm/o2dlm.h" #define COMMAND_MAX_LEN 4096 static char cbuf[COMMAND_MAX_LEN]; #define DEFAULT_DLMFS_PATH "/dlm/" static char *dlmfs_path = NULL; static char *prog; static struct o2dlm_ctxt *dlm_ctxt = NULL; enum commands { REGISTER = 0, UNREGISTER, LOCK, TRYLOCK, UNLOCK, GETLVB, SETLVB, HELP, NUM_COMMANDS }; static char *command_strings[NUM_COMMANDS] = { [REGISTER] "REGISTER", [UNREGISTER] "UNREGISTER", [LOCK] "LOCK", [TRYLOCK] "TRYLOCK", [UNLOCK] "UNLOCK", [GETLVB] "GETLVB", [SETLVB] "SETLVB", [HELP] "HELP", }; #define NUM_PR_STRINGS 4 static char *pr_strings[NUM_PR_STRINGS] = { "PR", "PRMODE", "RO", "O2DLM_LEVEL_PRMODE" }; #define NUM_EX_STRINGS 4 static char *ex_strings[NUM_EX_STRINGS] = { "EX", "EXMODE", "EX", "O2DLM_LEVEL_EXMODE" }; struct command_s { enum commands c_type; char c_domain[O2DLM_DOMAIN_MAX_LEN]; char c_id[O2DLM_LOCK_ID_MAX_LEN]; enum o2dlm_lock_level c_level; char *c_lvb; }; static void print_commands(void) { printf("Domain Commands:\n"); printf("register \"domain\'\n"); printf("unregister \"domain\'\n"); printf("Locking Commands - \"level\" is one of PR, or EX. " "Some common variations are understood\n"); printf("lock \"level\" \"lockid\"\n"); printf("trylock \"level\" \"lockid\"\n"); printf("unlock \"lockid\"\n"); printf("getlvb \"lockid\"\n"); printf("setlvb \"lockid\" \"lvb\"\n"); } static int decode_type(struct command_s *c, char *buf) { int i; for(i = 0; i < NUM_COMMANDS; i++) { if (!strcasecmp(buf, command_strings[i])) { c->c_type = i; return 0; } } return -1; } static void kill_return(char *buf) { char *c; c = strchr(buf, '\n'); if (c) *c = '\0'; } static int decode_lock(struct command_s *c, char *buf) { kill_return(buf); memset(c->c_id, 0, O2DLM_LOCK_ID_MAX_LEN); strncpy(c->c_id, buf, O2DLM_LOCK_ID_MAX_LEN - 1); return 0; } static int decode_domain(struct command_s *c, char *buf) { kill_return(buf); memset(c->c_domain, 0, O2DLM_DOMAIN_MAX_LEN); strncpy(c->c_domain, buf, O2DLM_DOMAIN_MAX_LEN - 1); return 0; } static int decode_level(struct command_s *c, char *buf) { int i; kill_return(buf); for (i = 0; i < NUM_PR_STRINGS; i++) { if (!strcasecmp(buf, pr_strings[i])) { c->c_level = O2DLM_LEVEL_PRMODE; return 0; } } for (i = 0; i < NUM_EX_STRINGS; i++) { if (!strcasecmp(buf, ex_strings[i])) { c->c_level = O2DLM_LEVEL_EXMODE; return 0; } } return -1; } static void print_command(struct command_s *c, const char *str) { printf("Command: %s ", command_strings[c->c_type]); switch (c->c_type) { case REGISTER: case UNREGISTER: printf("\"%s\" %s\n", c->c_domain, str); break; case LOCK: case TRYLOCK: if (c->c_level == O2DLM_LEVEL_PRMODE) printf("O2DLM_LEVEL_PRMODE "); else printf("O2DLM_LEVEL_EXMODE "); /* fall through */ case GETLVB: case UNLOCK: printf("\"%s\" %s\n", c->c_id, str); break; case SETLVB: printf("\"%s\" \"%s\" %s\n", c->c_id, c->c_lvb, str); break; case HELP: printf("%s\n", str); break; default: printf("wha?!?!?\n"); } } static int get_command(struct command_s *command) { char *next; again: printf("command: "); if (!fgets(cbuf, COMMAND_MAX_LEN, stdin)) return -1; next = strtok(cbuf, " \n"); if (!next) goto again; if (decode_type(command, next)) { fprintf(stderr, "Invalid command type \"%s\"\n", next); goto again; } if (command->c_type == HELP) return 0; next = strtok(NULL, " "); if (!next) { fprintf(stderr, "invalid input!\n"); goto again; } switch (command->c_type) { case REGISTER: case UNREGISTER: if (decode_domain(command, next)) { fprintf(stderr, "Invalid domain \"%s\"\n", next); goto again; } break; case LOCK: case TRYLOCK: if (decode_level(command, next)) { fprintf(stderr, "Invalid lock level \"%s\"\n", next); goto again; } next = strtok(NULL, " "); if (!next) { fprintf(stderr, "invalid input!\n"); goto again; } /* fall through */ case SETLVB: case GETLVB: case UNLOCK: if (decode_lock(command, next)) { fprintf(stderr, "Invalid lock \"%s\"\n", next); goto again; } if (command->c_type == SETLVB) { /* for setlvb we want to get a pointer to the * start of the string to stuff */ next = strtok(NULL, "\n"); if (!next) { fprintf(stderr, "invalid input!\n"); goto again; } kill_return(next); command->c_lvb = next; } break; default: fprintf(stderr, "whoa, can't parse this\n"); } return 0; } #define LVB_LEN 64 static char lvb_buf[LVB_LEN]; static errcode_t exec_command(struct command_s *c) { errcode_t ret = 0; unsigned int bytes, len; switch (c->c_type) { case REGISTER: ret = o2dlm_initialize(dlmfs_path, c->c_domain, &dlm_ctxt); break; case UNREGISTER: ret = o2dlm_destroy(dlm_ctxt); dlm_ctxt = NULL; break; case LOCK: ret = o2dlm_lock(dlm_ctxt, c->c_id, 0, c->c_level); break; case UNLOCK: ret = o2dlm_unlock(dlm_ctxt, c->c_id); break; case TRYLOCK: ret = o2dlm_lock(dlm_ctxt, c->c_id, O2DLM_TRYLOCK, c->c_level); break; case GETLVB: ret = o2dlm_read_lvb(dlm_ctxt, c->c_id, lvb_buf, LVB_LEN, &bytes); if (!ret) { printf("%u bytes read. LVB begins on following " "line and is terminated by a newline\n", bytes); printf("%.*s\n", bytes, lvb_buf); } break; case SETLVB: len = strlen(c->c_lvb); ret = o2dlm_write_lvb(dlm_ctxt, c->c_id, c->c_lvb, len, &bytes); if (!ret) printf("%u bytes written.\n", bytes); break; case HELP: default: print_commands(); } return ret; } int main(int argc, char **argv) { errcode_t error; struct command_s c; initialize_o2dl_error_table(); prog = argv[0]; if (argc < 2) { dlmfs_path = DEFAULT_DLMFS_PATH; printf("No fs path provided, using %s\n", dlmfs_path); } else if (!strcmp(argv[1], "-u")) { dlmfs_path = NULL; printf("Using fsdlm\n"); } else { dlmfs_path = argv[1]; printf("Using fs at %s\n", dlmfs_path); } printf("Type \"help\" to see a list of commands\n"); while (!get_command(&c)) { error = exec_command(&c); if (error) { print_command(&c, "failed!"); com_err(prog, error, "returned\n"); } else print_command(&c, "succeeded!\n"); } return 0; } ocfs2-tools-ocfs2-tools-1.8.6/libocfs2/000077500000000000000000000000001347147137200175645ustar00rootroot00000000000000ocfs2-tools-ocfs2-tools-1.8.6/libocfs2/.gitignore000066400000000000000000000001251347147137200215520ustar00rootroot00000000000000cscope* stamp-md5 *.sw? *.cmd ocfs2_err.c ocfs2_err.h libocfs2.a debug_* *.d ocfs2.7 ocfs2-tools-ocfs2-tools-1.8.6/libocfs2/Cscope.make000066400000000000000000000006161347147137200216420ustar00rootroot00000000000000.PHONY: cscope cscope: rm -f cscope.* echo "-k" >> cscope.files echo "-I include" >> cscope.files find . -maxdepth 2 -name '*.c' -print >>cscope.files find . -maxdepth 2 -name '*.h' -print >>cscope.files find ../libo2dlm -name '*.c' >>cscope.files find ../libo2dlm -name '*.h' >>cscope.files find ../libo2cb -name '*.c' >>cscope.files find ../libo2cb -name '*.h' >>cscope.files cscope -b ocfs2-tools-ocfs2-tools-1.8.6/libocfs2/Makefile000066400000000000000000000041121347147137200212220ustar00rootroot00000000000000TOPDIR = .. include $(TOPDIR)/Preamble.make INCLUDES = -I$(TOPDIR)/include LIBRARIES = libocfs2.a LIBO2DLM_LIBS = -L$(TOPDIR)/libo2dlm -lo2dlm $(DL_LIBS) LIBO2DLM_DEPS = $(TOPDIR)/libo2dlm/libo2dlm.a ifneq ($(BUILD_FSDLM_SUPPORT),) LIBO2CB_LIBS = -L$(TOPDIR)/libo2cb -lo2cb -ldlm_lt else LIBO2CB_LIBS = -L$(TOPDIR)/libo2cb -lo2cb endif LIBO2CB_DEPS = $(TOPDIR)/libo2cb/libo2cb.a CFLAGS += -fPIC ifneq ($(OCFS2_DEBUG_EXE),) DEBUG_EXE_FILES = $(shell awk '/DEBUG_EXE/{if (k[FILENAME] == 0) {print FILENAME; k[FILENAME] = 1;}}' $(CFILES)) DEBUG_EXE_PROGRAMS = $(addprefix debug_,$(subst .c,,$(DEBUG_EXE_FILES))) .SECONDARY: UNINST_PROGRAMS += $(DEBUG_EXE_PROGRAMS) debug_%.o : %.c $(CC) $(CFLAGS) $(LOCAL_CFLAGS) $(CPPFLAGS) $(LOCAL_CPPFLAGS) \ $(INCLUDES) $(DEFINES) \ -DDEBUG_EXE -o $@ -c $< debug_%: debug_%.o libocfs2.a $(LIBO2DLM_DEPS) $(LIBO2CB_DEPS) $(LINK) $(COM_ERR_LIBS) $(LIBO2DLM_LIBS) $(LIBO2CB_LIBS) endif CFILES = \ alloc.c \ bitmap.c \ bitops.c \ blockcheck.c \ blocktype.c \ cached_inode.c \ chain.c \ chainalloc.c \ checkhb.c \ closefs.c \ dirblock.c \ dir_iterate.c \ dir_scan.c \ dlm.c \ fileio.c \ freefs.c \ expanddir.c \ extend_file.c \ extents.c \ extent_map.c \ getsectsize.c \ getsize.c \ heartbeat.c \ inode.c \ inode_scan.c \ ismounted.c \ kernel-rbtree.c \ link.c \ lookup.c \ memory.c \ mkjournal.c \ namei.c \ openfs.c \ slot_map.c \ sysfile.c \ truncate.c \ unix_io.c \ unlink.c \ lockid.c \ backup_super.c \ feature_string.c\ quota.c \ image.c \ xattr.c \ extent_tree.c \ refcount.c \ dir_indexed.c HFILES = \ bitmap.h \ crc32table.h \ dir_iterate.h \ dir_util.h \ extent_map.h \ extent_tree.h \ refcount.h HFILES_GEN = ocfs2_err.h OBJS = $(subst .c,.o,$(CFILES)) \ ocfs2_err.o $(OBJS): $(HFILES_GEN) ocfs2_err.c ocfs2_err.h: ocfs2_err.et compile_et ocfs2_err.et libocfs2.a: $(OBJS) rm -f $@ $(AR) r $@ $^ $(RANLIB) $@ MANS = ocfs2.7 DIST_FILES = $(CFILES) $(HFILES) ocfs2_err.et ocfs2.7.in CLEAN_RULES = clean-err clean-err: rm -f ocfs2_err.c ocfs2_err.h include $(TOPDIR)/Postamble.make ocfs2-tools-ocfs2-tools-1.8.6/libocfs2/alloc.c000066400000000000000000000524451347147137200210340ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * alloc.c * * Allocate inodes, extent_blocks, and actual data space. Part of the * OCFS2 userspace library. * * Copyright (C) 2004 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License, version 2, as published by the Free Software Foundation. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. */ #define _XOPEN_SOURCE 600 /* Triggers magic in features.h */ #define _LARGEFILE64_SOURCE #include #include #include "ocfs2/ocfs2.h" static errcode_t ocfs2_chain_alloc_with_io(ocfs2_filesys *fs, ocfs2_cached_inode *cinode, uint64_t *gd_blkno, uint16_t *suballoc_bit, uint64_t *bitno) { errcode_t ret; if (!cinode->ci_chains) { ret = ocfs2_load_chain_allocator(fs, cinode); if (ret) return ret; } ret = ocfs2_chain_alloc(fs, cinode, gd_blkno, suballoc_bit, bitno); if (ret) return ret; return ocfs2_write_chain_allocator(fs, cinode); } static errcode_t ocfs2_chain_free_with_io(ocfs2_filesys *fs, ocfs2_cached_inode *cinode, uint64_t bitno) { errcode_t ret; if (!cinode->ci_chains) { ret = ocfs2_load_chain_allocator(fs, cinode); if (ret) return ret; } ret = ocfs2_chain_free(fs, cinode, bitno); if (ret) return ret; return ocfs2_write_chain_allocator(fs, cinode); } static errcode_t ocfs2_load_allocator(ocfs2_filesys *fs, int type, int slot_num, ocfs2_cached_inode **alloc_cinode) { errcode_t ret; uint64_t blkno; if (!*alloc_cinode) { ret = ocfs2_lookup_system_inode(fs, type, slot_num, &blkno); if (ret) return ret; ret = ocfs2_read_cached_inode(fs, blkno, alloc_cinode); if (ret) return ret; /* Ignore error */ ocfs2_cache_chain_allocator_blocks(fs, (*alloc_cinode)->ci_inode); } if (!(*alloc_cinode)->ci_chains) { ret = ocfs2_load_chain_allocator(fs, *alloc_cinode); if (ret) return ret; } return 0; } /* * This function is duplicated in mkfs.ocfs2/mkfs.c. * Please keep them in sync. */ static int ocfs2_clusters_per_group(int block_size, int cluster_size_bits) { int megabytes; switch (block_size) { case 4096: case 2048: megabytes = 4; break; case 1024: megabytes = 2; break; case 512: default: megabytes = 1; break; } #define ONE_MB_SHIFT 20 return (megabytes << ONE_MB_SHIFT) >> cluster_size_bits; } static void ocfs2_zero_dinode_id2_with_xattr(int blocksize, struct ocfs2_dinode *di) { unsigned int xattrsize = di->i_xattr_inline_size; if (di->i_dyn_features & OCFS2_INLINE_XATTR_FL) memset(&di->id2, 0, blocksize - offsetof(struct ocfs2_dinode, id2) - xattrsize); else memset(&di->id2, 0, blocksize - offsetof(struct ocfs2_dinode, id2)); } void ocfs2_dinode_new_extent_list(ocfs2_filesys *fs, struct ocfs2_dinode *di) { ocfs2_zero_dinode_id2_with_xattr(fs->fs_blocksize, di); di->id2.i_list.l_tree_depth = 0; di->id2.i_list.l_next_free_rec = 0; di->id2.i_list.l_count = ocfs2_extent_recs_per_inode(fs->fs_blocksize); } void ocfs2_set_inode_data_inline(ocfs2_filesys *fs, struct ocfs2_dinode *di) { struct ocfs2_inline_data *idata = &di->id2.i_data; ocfs2_zero_dinode_id2_with_xattr(fs->fs_blocksize, di); idata->id_count = ocfs2_max_inline_data_with_xattr(fs->fs_blocksize, di); di->i_dyn_features |= OCFS2_INLINE_DATA_FL; } static void ocfs2_init_inode(ocfs2_filesys *fs, struct ocfs2_dinode *di, int16_t slot, uint64_t gd_blkno, uint16_t suballoc_bit, uint64_t blkno, uint16_t mode, uint32_t flags) { int cs_bits = OCFS2_RAW_SB(fs->fs_super)->s_clustersize_bits; unsigned int tl_recs; di->i_generation = fs->fs_super->i_generation; di->i_fs_generation = fs->fs_super->i_fs_generation; di->i_blkno = blkno; di->i_suballoc_slot = slot; di->i_suballoc_loc = gd_blkno; di->i_suballoc_bit = suballoc_bit; di->i_uid = di->i_gid = 0; di->i_mode = mode; if (S_ISDIR(di->i_mode)) di->i_links_count = 2; else di->i_links_count = 1; strcpy((char *)di->i_signature, OCFS2_INODE_SIGNATURE); di->i_atime = di->i_ctime = di->i_mtime = time(NULL); di->i_dtime = 0; di->i_flags = flags; if (flags & OCFS2_LOCAL_ALLOC_FL) { di->id2.i_lab.la_size = ocfs2_local_alloc_size(fs->fs_blocksize); return ; } if (flags & OCFS2_CHAIN_FL) { di->id2.i_chain.cl_count = ocfs2_chain_recs_per_inode(fs->fs_blocksize); di->id2.i_chain.cl_cpg = ocfs2_clusters_per_group(fs->fs_blocksize, cs_bits); di->id2.i_chain.cl_bpc = fs->fs_clustersize / fs->fs_blocksize; di->id2.i_chain.cl_next_free_rec = 0; return ; } if (flags & OCFS2_DEALLOC_FL) { tl_recs = ocfs2_truncate_recs_per_inode(fs->fs_blocksize); di->id2.i_dealloc.tl_count = tl_recs; return ; } if (flags & OCFS2_SUPER_BLOCK_FL) return ; if (ocfs2_support_inline_data(OCFS2_RAW_SB(fs->fs_super)) && S_ISDIR(di->i_mode)) ocfs2_set_inode_data_inline(fs, di); else ocfs2_dinode_new_extent_list(fs, di); } static void ocfs2_init_eb(ocfs2_filesys *fs, struct ocfs2_extent_block *eb, uint64_t gd_blkno, uint16_t suballoc_bit, uint64_t blkno) { strcpy((char *)eb->h_signature, OCFS2_EXTENT_BLOCK_SIGNATURE); eb->h_fs_generation = fs->fs_super->i_fs_generation; eb->h_blkno = blkno; eb->h_suballoc_slot = 0; eb->h_suballoc_loc = gd_blkno; eb->h_suballoc_bit = suballoc_bit; eb->h_list.l_count = ocfs2_extent_recs_per_eb(fs->fs_blocksize); } errcode_t ocfs2_new_inode(ocfs2_filesys *fs, uint64_t *ino, int mode) { errcode_t ret; char *buf; uint64_t gd_blkno; uint16_t suballoc_bit; struct ocfs2_dinode *di; ret = ocfs2_malloc_block(fs->fs_io, &buf); if (ret) return ret; ret = ocfs2_load_allocator(fs, INODE_ALLOC_SYSTEM_INODE, 0, &fs->fs_inode_allocs[0]); if (ret) goto out; ret = ocfs2_chain_alloc_with_io(fs, fs->fs_inode_allocs[0], &gd_blkno, &suballoc_bit, ino); if (ret == OCFS2_ET_BIT_NOT_FOUND) { ret = ocfs2_chain_add_group(fs, fs->fs_inode_allocs[0]); if (ret) goto out; ret = ocfs2_chain_alloc_with_io(fs, fs->fs_inode_allocs[0], &gd_blkno, &suballoc_bit, ino); if (ret) goto out; } else if (ret) goto out; memset(buf, 0, fs->fs_blocksize); di = (struct ocfs2_dinode *)buf; ocfs2_init_inode(fs, di, 0, gd_blkno, suballoc_bit, *ino, mode, OCFS2_VALID_FL); ret = ocfs2_write_inode(fs, *ino, buf); if (ret) ocfs2_delete_inode(fs, *ino); out: ocfs2_free(&buf); return ret; } errcode_t ocfs2_new_system_inode(ocfs2_filesys *fs, uint64_t *ino, int mode, int flags) { errcode_t ret; char *buf; uint64_t gd_blkno; uint16_t suballoc_bit; struct ocfs2_dinode *di; ret = ocfs2_malloc_block(fs->fs_io, &buf); if (ret) return ret; ret = ocfs2_load_allocator(fs, GLOBAL_INODE_ALLOC_SYSTEM_INODE, 0, &fs->fs_system_inode_alloc); if (ret) goto out; ret = ocfs2_chain_alloc_with_io(fs, fs->fs_system_inode_alloc, &gd_blkno, &suballoc_bit, ino); if (ret == OCFS2_ET_BIT_NOT_FOUND) { ret = ocfs2_chain_add_group(fs, fs->fs_system_inode_alloc); if (ret) goto out; ret = ocfs2_chain_alloc_with_io(fs, fs->fs_system_inode_alloc, &gd_blkno, &suballoc_bit, ino); if (ret) goto out; } else if (ret) goto out; memset(buf, 0, fs->fs_blocksize); di = (struct ocfs2_dinode *)buf; ocfs2_init_inode(fs, di, -1, gd_blkno, suballoc_bit, *ino, mode, (flags | OCFS2_VALID_FL | OCFS2_SYSTEM_FL)); ret = ocfs2_write_inode(fs, *ino, buf); out: ocfs2_free(&buf); return ret; } errcode_t ocfs2_delete_inode(ocfs2_filesys *fs, uint64_t ino) { errcode_t ret; char *buf; struct ocfs2_dinode *di; int16_t slot; ocfs2_cached_inode **inode_alloc; ret = ocfs2_malloc_block(fs->fs_io, &buf); if (ret) return ret; ret = ocfs2_read_inode(fs, ino, buf); if (ret) goto out; di = (struct ocfs2_dinode *)buf; slot = di->i_suballoc_slot; if (slot == OCFS2_INVALID_SLOT) { inode_alloc = &fs->fs_system_inode_alloc; ret = ocfs2_load_allocator(fs, GLOBAL_INODE_ALLOC_SYSTEM_INODE, 0, inode_alloc); } else { inode_alloc = &fs->fs_inode_allocs[slot]; ret = ocfs2_load_allocator(fs, INODE_ALLOC_SYSTEM_INODE, slot, inode_alloc); } if (ret) goto out; ret = ocfs2_chain_free_with_io(fs, *inode_alloc, ino); if (ret) goto out; di->i_flags &= ~(OCFS2_VALID_FL | OCFS2_ORPHANED_FL); di->i_dtime = time(NULL); ret = ocfs2_write_inode(fs, di->i_blkno, buf); out: ocfs2_free(&buf); return ret; } errcode_t ocfs2_test_inode_allocated(ocfs2_filesys *fs, uint64_t blkno, int *is_allocated) { int16_t slot; uint16_t max_slots = OCFS2_RAW_SB(fs->fs_super)->s_max_slots; ocfs2_cached_inode **ci; int type; errcode_t ret = OCFS2_ET_INTERNAL_FAILURE; for (slot = OCFS2_INVALID_SLOT; slot != max_slots; slot++) { if (slot == OCFS2_INVALID_SLOT) { type = GLOBAL_INODE_ALLOC_SYSTEM_INODE; ci = &fs->fs_system_inode_alloc; } else { type = INODE_ALLOC_SYSTEM_INODE; ci = &fs->fs_inode_allocs[slot]; } ret = ocfs2_load_allocator(fs, type, slot, ci); if (ret) break; ret = ocfs2_chain_test(fs, *ci, blkno, is_allocated); if (ret != OCFS2_ET_INVALID_BIT) break; } return ret; } errcode_t ocfs2_new_extent_block(ocfs2_filesys *fs, uint64_t *blkno) { errcode_t ret; char *buf; uint64_t gd_blkno; uint16_t suballoc_bit; struct ocfs2_extent_block *eb; ret = ocfs2_malloc_block(fs->fs_io, &buf); if (ret) return ret; ret = ocfs2_load_allocator(fs, EXTENT_ALLOC_SYSTEM_INODE, 0, &fs->fs_eb_allocs[0]); if (ret) goto out; ret = ocfs2_chain_alloc_with_io(fs, fs->fs_eb_allocs[0], &gd_blkno, &suballoc_bit, blkno); if (ret == OCFS2_ET_BIT_NOT_FOUND) { ret = ocfs2_chain_add_group(fs, fs->fs_eb_allocs[0]); if (ret) goto out; ret = ocfs2_chain_alloc_with_io(fs, fs->fs_eb_allocs[0], &gd_blkno, &suballoc_bit, blkno); if (ret) goto out; } else if (ret) goto out; memset(buf, 0, fs->fs_blocksize); eb = (struct ocfs2_extent_block *)buf; ocfs2_init_eb(fs, eb, gd_blkno, suballoc_bit, *blkno); ret = ocfs2_write_extent_block(fs, *blkno, buf); out: ocfs2_free(&buf); return ret; } errcode_t ocfs2_delete_xattr_block(ocfs2_filesys *fs, uint64_t blkno) { errcode_t ret; char *buf; int slot; struct ocfs2_xattr_block *xb = NULL; ret = ocfs2_malloc_block(fs->fs_io, &buf); if (ret) return ret; ret = ocfs2_read_xattr_block(fs, blkno, buf); if (ret) goto out; xb = (struct ocfs2_xattr_block *)buf; slot = xb->xb_suballoc_slot; ret = ocfs2_load_allocator(fs, EXTENT_ALLOC_SYSTEM_INODE, slot, &fs->fs_eb_allocs[slot]); if (ret) goto out; ret = ocfs2_chain_free_with_io(fs, fs->fs_eb_allocs[slot], blkno); if (ret) goto out; out: ocfs2_free(&buf); return ret; } errcode_t ocfs2_delete_extent_block(ocfs2_filesys *fs, uint64_t blkno) { errcode_t ret; char *buf; struct ocfs2_extent_block *eb; int slot; ret = ocfs2_malloc_block(fs->fs_io, &buf); if (ret) return ret; ret = ocfs2_read_extent_block(fs, blkno, buf); if (ret) goto out; eb = (struct ocfs2_extent_block *)buf; slot = eb->h_suballoc_slot; ret = ocfs2_load_allocator(fs, EXTENT_ALLOC_SYSTEM_INODE, slot, &fs->fs_eb_allocs[slot]); if (ret) goto out; ret = ocfs2_chain_free_with_io(fs, fs->fs_eb_allocs[slot], blkno); if (ret) goto out; ret = ocfs2_write_extent_block(fs, eb->h_blkno, buf); out: ocfs2_free(&buf); return ret; } errcode_t ocfs2_delete_refcount_block(ocfs2_filesys *fs, uint64_t blkno) { errcode_t ret; char *buf; struct ocfs2_refcount_block *rb; int slot; ret = ocfs2_malloc_block(fs->fs_io, &buf); if (ret) return ret; ret = ocfs2_read_refcount_block(fs, blkno, buf); if (ret) goto out; rb = (struct ocfs2_refcount_block *)buf; slot = rb->rf_suballoc_slot; ret = ocfs2_load_allocator(fs, EXTENT_ALLOC_SYSTEM_INODE, slot, &fs->fs_eb_allocs[slot]); if (ret) goto out; ret = ocfs2_chain_free_with_io(fs, fs->fs_eb_allocs[slot], blkno); out: ocfs2_free(&buf); return ret; } static void ocfs2_init_rb(ocfs2_filesys *fs, struct ocfs2_refcount_block *rb, uint64_t gd_blkno, uint16_t suballoc_bit, uint64_t blkno, uint64_t root_blkno, uint32_t rf_generation) { strcpy((void *)rb, OCFS2_REFCOUNT_BLOCK_SIGNATURE); rb->rf_fs_generation = fs->fs_super->i_fs_generation; rb->rf_blkno = blkno; rb->rf_suballoc_slot = 0; rb->rf_suballoc_loc = gd_blkno; rb->rf_suballoc_bit = suballoc_bit; rb->rf_parent = root_blkno; if (root_blkno) rb->rf_flags = OCFS2_REFCOUNT_LEAF_FL; rb->rf_records.rl_count = ocfs2_refcount_recs_per_rb(fs->fs_blocksize); rb->rf_generation = rf_generation; } errcode_t ocfs2_new_refcount_block(ocfs2_filesys *fs, uint64_t *blkno, uint64_t root_blkno, uint32_t rf_generation) { errcode_t ret; char *buf; uint64_t gd_blkno; uint16_t suballoc_bit; struct ocfs2_refcount_block *rb; ret = ocfs2_malloc_block(fs->fs_io, &buf); if (ret) return ret; ret = ocfs2_load_allocator(fs, EXTENT_ALLOC_SYSTEM_INODE, 0, &fs->fs_eb_allocs[0]); if (ret) goto out; ret = ocfs2_chain_alloc_with_io(fs, fs->fs_eb_allocs[0], &gd_blkno, &suballoc_bit, blkno); if (ret == OCFS2_ET_BIT_NOT_FOUND) { ret = ocfs2_chain_add_group(fs, fs->fs_eb_allocs[0]); if (ret) goto out; ret = ocfs2_chain_alloc_with_io(fs, fs->fs_eb_allocs[0], &gd_blkno, &suballoc_bit, blkno); if (ret) goto out; } else if (ret) goto out; memset(buf, 0, fs->fs_blocksize); rb = (struct ocfs2_refcount_block *)buf; ocfs2_init_rb(fs, rb, gd_blkno, suballoc_bit, *blkno, root_blkno, rf_generation); ret = ocfs2_write_refcount_block(fs, *blkno, buf); out: ocfs2_free(&buf); return ret; } errcode_t ocfs2_grow_chain_allocator(ocfs2_filesys *fs, int type, int slot_num, uint32_t num_clusters) { errcode_t ret = OCFS2_ET_INVALID_ARGUMENT; ocfs2_cached_inode *ci; int i, num_groups, cpg; switch (type) { case EXTENT_ALLOC_SYSTEM_INODE: ci = fs->fs_eb_allocs[slot_num]; break; case INODE_ALLOC_SYSTEM_INODE: ci = fs->fs_inode_allocs[slot_num]; break; case GLOBAL_INODE_ALLOC_SYSTEM_INODE: ci = fs->fs_system_inode_alloc; break; default: goto out; } ret = ocfs2_load_allocator(fs, type, slot_num, &ci); if (ret) goto out; cpg = ci->ci_inode->id2.i_chain.cl_cpg; num_groups = (num_clusters + cpg - 1) / cpg; for (i = 0; i < num_groups; ++i) { ret = ocfs2_chain_add_group(fs, ci); if (ret) goto out; } out: return ret; } /* only initiate part of dx_root: * dr_subllaoc_slot * dr_sbualloc_bit * dr_fs_generation * dr_blkno * dr_flags */ static void init_dx_root(ocfs2_filesys *fs, struct ocfs2_dx_root_block *dx_root, int slot, uint64_t gd_blkno, uint16_t suballoc_bit, uint64_t dr_blkno) { memset(dx_root, 0, fs->fs_blocksize); strcpy((char *)dx_root->dr_signature, OCFS2_DX_ROOT_SIGNATURE); dx_root->dr_suballoc_slot = slot; dx_root->dr_suballoc_loc = gd_blkno; dx_root->dr_suballoc_bit = suballoc_bit; dx_root->dr_fs_generation = fs->fs_super->i_fs_generation; dx_root->dr_blkno = dr_blkno; dx_root->dr_flags |= OCFS2_DX_FLAG_INLINE; } errcode_t ocfs2_new_dx_root(ocfs2_filesys *fs, struct ocfs2_dinode *di, uint64_t *dr_blkno) { errcode_t ret; char *buf = NULL; uint64_t gd_blkno; uint16_t suballoc_bit; struct ocfs2_dx_root_block *dx_root; int slot; ret = ocfs2_malloc_block(fs->fs_io, &buf); if (ret) goto out; slot = di->i_suballoc_slot; if (slot == (uint16_t)OCFS2_INVALID_SLOT) slot = 0; ret = ocfs2_load_allocator(fs, EXTENT_ALLOC_SYSTEM_INODE, slot, &fs->fs_eb_allocs[slot]); if (ret) goto out; ret = ocfs2_chain_alloc_with_io(fs, fs->fs_eb_allocs[slot], &gd_blkno, &suballoc_bit, dr_blkno); if (ret == OCFS2_ET_BIT_NOT_FOUND) { ret = ocfs2_chain_add_group(fs, fs->fs_eb_allocs[slot]); if (ret) goto out; ret = ocfs2_chain_alloc_with_io(fs, fs->fs_eb_allocs[slot], &gd_blkno, &suballoc_bit, dr_blkno); if (ret) goto out; } else if (ret) goto out; dx_root = (struct ocfs2_dx_root_block *)buf; init_dx_root(fs, dx_root, slot, gd_blkno, suballoc_bit, *dr_blkno); ret = ocfs2_write_dx_root(fs, *dr_blkno, (char *)dx_root); out: if (buf) ocfs2_free(&buf); return ret; } errcode_t ocfs2_delete_dx_root(ocfs2_filesys *fs, uint64_t dr_blkno) { errcode_t ret; char *buf = NULL; struct ocfs2_dx_root_block *dx_root; int slot; ret = ocfs2_malloc_block(fs->fs_io, &buf); if (ret) goto out; ret = ocfs2_read_dx_root(fs, dr_blkno, buf); if (ret) goto out; dx_root = (struct ocfs2_dx_root_block *)buf; slot = dx_root->dr_suballoc_slot; ret = ocfs2_load_allocator(fs, EXTENT_ALLOC_SYSTEM_INODE, slot, &fs->fs_eb_allocs[slot]); if (ret) goto out; ret = ocfs2_chain_free_with_io(fs, fs->fs_eb_allocs[slot], dr_blkno); out: if (buf) ocfs2_free(&buf); return ret; } /* XXX what to do about local allocs? * XXX Well, we shouldn't use local allocs to allocate, as we are * userspace and we have the entire bitmap in memory. However, this * doesn't solve the issue of "is space still in dirty local * allocs?" */ errcode_t ocfs2_new_clusters(ocfs2_filesys *fs, uint32_t min, uint32_t requested, uint64_t *start_blkno, uint32_t *clusters_found) { errcode_t ret; uint64_t start_bit; uint64_t found; ret = ocfs2_load_allocator(fs, GLOBAL_BITMAP_SYSTEM_INODE, 0, &fs->fs_cluster_alloc); if (ret) goto out; ret = ocfs2_chain_alloc_range(fs, fs->fs_cluster_alloc, min, requested, &start_bit, &found); if (ret) goto out; *start_blkno = ocfs2_clusters_to_blocks(fs, start_bit); /* We never have enough bits that can be allocated * contiguously to overflow this. The lower level API needs * fixing. */ *clusters_found = (uint32_t) found; ret = ocfs2_write_chain_allocator(fs, fs->fs_cluster_alloc); if (ret) ocfs2_free_clusters(fs, requested, *start_blkno); out: return ret; } errcode_t ocfs2_test_cluster_allocated(ocfs2_filesys *fs, uint32_t cpos, int *is_allocated) { errcode_t ret; ret = ocfs2_load_allocator(fs, GLOBAL_BITMAP_SYSTEM_INODE, 0, &fs->fs_cluster_alloc); if (!ret) { ret = ocfs2_chain_test(fs, fs->fs_cluster_alloc, cpos, is_allocated); } return ret; } errcode_t ocfs2_new_specific_cluster(ocfs2_filesys *fs, uint32_t cpos) { errcode_t ret; int allocatedp = 0; /* Loads the allocator if we need it */ ret = ocfs2_test_cluster_allocated(fs, cpos, &allocatedp); if (ret) goto out; if (allocatedp) { ret = OCFS2_ET_BIT_NOT_FOUND; goto out; } ocfs2_chain_force_val(fs, fs->fs_cluster_alloc, cpos, 1, NULL); ret = ocfs2_write_chain_allocator(fs, fs->fs_cluster_alloc); if (ret) ocfs2_free_clusters(fs, 1, ocfs2_blocks_to_clusters(fs, cpos)); out: return ret; } errcode_t ocfs2_free_clusters(ocfs2_filesys *fs, uint32_t len, uint64_t start_blkno) { errcode_t ret; ret = ocfs2_load_allocator(fs, GLOBAL_BITMAP_SYSTEM_INODE, 0, &fs->fs_cluster_alloc); if (ret) goto out; ret = ocfs2_chain_free_range(fs, fs->fs_cluster_alloc, len, ocfs2_blocks_to_clusters(fs, start_blkno)); if (ret) goto out; /* XXX OK, it's bad if we can't revert this after the io fails */ ret = ocfs2_write_chain_allocator(fs, fs->fs_cluster_alloc); out: return ret; } /* * Test whether clusters have the specified value in the bitmap. * test: expected value * matches: 1 if all bits match test, else 0 */ errcode_t ocfs2_test_clusters(ocfs2_filesys *fs, uint32_t len, uint64_t start_blkno, int test, int *matches) { errcode_t ret; uint32_t start_cluster; int set = 0; *matches = 0; if (!len) return 0; ret = ocfs2_load_allocator(fs, GLOBAL_BITMAP_SYSTEM_INODE, 0, &fs->fs_cluster_alloc); if (ret) goto out; start_cluster = ocfs2_blocks_to_clusters(fs, start_blkno); while (len) { ret = ocfs2_bitmap_test(fs->fs_cluster_alloc->ci_chains, start_cluster, &set); if (ret || set != test) goto out; len--; start_cluster++; } *matches = 1; out: return ret; } #ifdef DEBUG_EXE #include static void print_usage(void) { fprintf(stdout, "debug_alloc \n"); } int main(int argc, char *argv[]) { errcode_t ret; ocfs2_filesys *fs; char *buf; struct ocfs2_dinode *di; uint64_t blkno; if (argc < 3) { print_usage(); return 1; } initialize_ocfs_error_table(); ret = ocfs2_open(argv[2], OCFS2_FLAG_RW, 0, 0, &fs); if (ret) { com_err(argv[0], ret, "while opening \"%s\"", argv[2]); goto out; } ret = ocfs2_malloc_block(fs->fs_io, &buf); if (ret) { com_err(argv[0], ret, "while allocating buffer"); goto out_close; } di = (struct ocfs2_dinode *)buf; ret = ocfs2_new_inode(fs, &blkno, 0644 | S_IFREG); if (ret) { com_err(argv[0], ret, "while allocating a new inode"); goto out_free; } ret = ocfs2_link(fs, fs->fs_root_blkno, argv[1], blkno, OCFS2_FT_REG_FILE); if (ret) { com_err(argv[0], ret, "while linking inode %"PRIu64, blkno); } out_free: ocfs2_free(&buf); out_close: ret = ocfs2_close(fs); if (ret) { com_err(argv[0], ret, "while closing \"%s\"", argv[2]); } out: return ret; } #endif /* DEBUG_EXE */ ocfs2-tools-ocfs2-tools-1.8.6/libocfs2/backup_super.c000066400000000000000000000125011347147137200224120ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * backup_super.c * * Backup superblocks for an OCFS2 volume. * * Copyright (C) 2006 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License, version 2, as published by the Free Software Foundation. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * */ #include #include "ocfs2/ocfs2.h" /* In case we don't have fs_blocksize, we will return * byte offsets and let the caller calculate them by itself. */ int ocfs2_get_backup_super_offsets(ocfs2_filesys *fs, uint64_t *offsets, size_t len) { size_t i; uint64_t blkno; uint32_t blocksize; memset(offsets, 0, sizeof(uint64_t) * len); len = ocfs2_min(len, (size_t)OCFS2_MAX_BACKUP_SUPERBLOCKS); if (fs) blocksize = fs->fs_blocksize; else blocksize = 1; for (i = 0; i < len; i++) { blkno = ocfs2_backup_super_blkno(blocksize, i); if (fs && fs->fs_blocks <= blkno) break; offsets[i] = blkno; } return i; } errcode_t ocfs2_clear_backup_super_list(ocfs2_filesys *fs, uint64_t *blocks, size_t len) { size_t i; errcode_t ret = 0; if (!len || !blocks || !*blocks) goto bail; len = ocfs2_min(len,(size_t)OCFS2_MAX_BACKUP_SUPERBLOCKS); /* * Don't clear anything if backup superblocks aren't enabled - * there might be real data there! If backup superblocks are * enabled, we know these blocks are backups, and we're * safe to clear them. */ if (!OCFS2_HAS_COMPAT_FEATURE(OCFS2_RAW_SB(fs->fs_super), OCFS2_FEATURE_COMPAT_BACKUP_SB)) goto bail; for (i = 0; i < len; i++) { ret = ocfs2_free_clusters(fs, 1, blocks[i]); if (ret) break; } bail: return ret; } static errcode_t check_cluster(ocfs2_filesys *fs, uint32_t cpos) { errcode_t ret; int val; ret = ocfs2_test_cluster_allocated(fs, cpos, &val); if (ret) goto bail; if (val) { ret = ENOSPC; goto bail; } ret = 0; bail: return ret; } errcode_t ocfs2_set_backup_super_list(ocfs2_filesys *fs, uint64_t *blocks, size_t len) { size_t i; errcode_t ret = 0; char *buf = NULL; uint64_t *blkno = blocks; uint32_t cluster, bpc = fs->fs_clustersize / fs->fs_blocksize; if (!len || !blocks || !*blocks) goto bail; len = ocfs2_min(len,(size_t)OCFS2_MAX_BACKUP_SUPERBLOCKS); if (!OCFS2_HAS_COMPAT_FEATURE(OCFS2_RAW_SB(fs->fs_super), OCFS2_FEATURE_COMPAT_BACKUP_SB)) { /* check all the blkno to see whether it is used. */ for (i = 0; i < len; i++, blkno++) { ret = check_cluster(fs, ocfs2_blocks_to_clusters(fs, *blkno)); if (ret) goto bail; } } ret = ocfs2_malloc_blocks(fs->fs_io, bpc, &buf); if (ret) goto bail; memset(buf, 0, fs->fs_clustersize); /* zero all the clusters at first */ blkno = blocks; for (i = 0; i < len; i++, blkno++) { cluster = ocfs2_blocks_to_clusters(fs, *blkno); ret = io_write_block(fs->fs_io, cluster*bpc, bpc, buf); if (ret) goto bail; } ret = ocfs2_refresh_backup_super_list(fs, blocks, len); if (ret) goto bail; /* We just tested the clusters, so the allocation can't fail */ blkno = blocks; for (i = 0; i < len; i++, blkno++) ocfs2_new_specific_cluster(fs, ocfs2_blocks_to_clusters(fs, *blkno)); bail: if (buf) ocfs2_free(&buf); return ret; } errcode_t ocfs2_refresh_backup_super_list(ocfs2_filesys *fs, uint64_t *blocks, size_t len) { errcode_t ret = 0; size_t i; for (i = 0; i < len; i++, blocks++) { ret = ocfs2_write_backup_super(fs, *blocks); if (ret) goto bail; } bail: return ret; } errcode_t ocfs2_refresh_backup_supers(ocfs2_filesys *fs) { int num; uint64_t blocks[OCFS2_MAX_BACKUP_SUPERBLOCKS]; if (!OCFS2_HAS_COMPAT_FEATURE(OCFS2_RAW_SB(fs->fs_super), OCFS2_FEATURE_COMPAT_BACKUP_SB)) return 0; /* Do nothing */ num = ocfs2_get_backup_super_offsets(fs, blocks, ARRAY_SIZE(blocks)); return num ? ocfs2_refresh_backup_super_list(fs, blocks, num) : 0; } errcode_t ocfs2_read_backup_super(ocfs2_filesys *fs, int backup, char *sbbuf) { int numsb; uint64_t blocks[OCFS2_MAX_BACKUP_SUPERBLOCKS]; if (!OCFS2_HAS_COMPAT_FEATURE(OCFS2_RAW_SB(fs->fs_super), OCFS2_FEATURE_COMPAT_BACKUP_SB)) return OCFS2_ET_NO_BACKUP_SUPER; numsb = ocfs2_get_backup_super_offsets(fs, blocks, ARRAY_SIZE(blocks)); if (backup < 0 || backup >= numsb) return OCFS2_ET_NO_BACKUP_SUPER; return ocfs2_read_super(fs, blocks[backup], sbbuf); } /* These were terrible names, don't use them */ int ocfs2_get_backup_super_offset(ocfs2_filesys *fs, uint64_t *offsets, size_t len) { return ocfs2_get_backup_super_offsets(fs, offsets, len); } errcode_t ocfs2_refresh_backup_super(ocfs2_filesys *fs, uint64_t *blocks, size_t len) { return ocfs2_refresh_backup_super_list(fs, blocks, len); } errcode_t ocfs2_set_backup_super(ocfs2_filesys *fs, uint64_t *blocks, size_t len) { return ocfs2_set_backup_super_list(fs, blocks, len); } ocfs2-tools-ocfs2-tools-1.8.6/libocfs2/bitmap.c000066400000000000000000000677501347147137200212230ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * bitmap.c * * Basic bitmap routines for the OCFS2 userspace library. * * Copyright (C) 2004 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License, version 2, as published by the Free Software Foundation. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. */ #define _XOPEN_SOURCE 600 /* Triggers magic in features.h */ #define _LARGEFILE64_SOURCE #include #include #include #include "ocfs2/ocfs2.h" #include "ocfs2/bitops.h" #include "bitmap.h" /* The public API */ void ocfs2_bitmap_free(ocfs2_bitmap **bitmap) { struct rb_node *node; struct ocfs2_bitmap_region *br; /* * If the bitmap needs to do extra cleanup of region, * it should have done it in destroy_notify. Same with the * private pointers. */ if ((*bitmap)->b_ops->destroy_notify) (*(*bitmap)->b_ops->destroy_notify)(*bitmap); while ((node = rb_first(&(*bitmap)->b_regions)) != NULL) { br = rb_entry(node, struct ocfs2_bitmap_region, br_node); rb_erase(&br->br_node, &(*bitmap)->b_regions); ocfs2_bitmap_free_region(br); } ocfs2_free(&(*bitmap)->b_description); ocfs2_free(bitmap); } errcode_t ocfs2_bitmap_set(ocfs2_bitmap *bitmap, uint64_t bitno, int *oldval) { errcode_t ret; int old_tmp; if (bitno >= bitmap->b_total_bits) return OCFS2_ET_INVALID_BIT; ret = (*bitmap->b_ops->set_bit)(bitmap, bitno, &old_tmp); if (ret) return ret; if (!old_tmp) bitmap->b_set_bits++; if (oldval) *oldval = old_tmp; return 0; } errcode_t ocfs2_bitmap_clear(ocfs2_bitmap *bitmap, uint64_t bitno, int *oldval) { errcode_t ret; int old_tmp; if (bitno >= bitmap->b_total_bits) return OCFS2_ET_INVALID_BIT; ret = (*bitmap->b_ops->clear_bit)(bitmap, bitno, &old_tmp); if (ret) return ret; if (old_tmp) bitmap->b_set_bits--; if (oldval) *oldval = old_tmp; return 0; } errcode_t ocfs2_bitmap_test(ocfs2_bitmap *bitmap, uint64_t bitno, int *val) { errcode_t ret; if (bitno >= bitmap->b_total_bits) return OCFS2_ET_INVALID_BIT; ret = (*bitmap->b_ops->test_bit)(bitmap, bitno, val); return ret; } errcode_t ocfs2_bitmap_find_next_set(ocfs2_bitmap *bitmap, uint64_t start, uint64_t *found) { if (start >= bitmap->b_total_bits) return OCFS2_ET_INVALID_BIT; return (*bitmap->b_ops->find_next_set)(bitmap, start, found); } errcode_t ocfs2_bitmap_find_next_clear(ocfs2_bitmap *bitmap, uint64_t start, uint64_t *found) { if (start >= bitmap->b_total_bits) return OCFS2_ET_INVALID_BIT; return (*bitmap->b_ops->find_next_clear)(bitmap, start, found); } /* kind of poorly named, but I couldn't come up with something nicer */ errcode_t ocfs2_bitmap_alloc_range(ocfs2_bitmap *bitmap, uint64_t min, uint64_t len, uint64_t *first_bit, uint64_t *bits_found) { errcode_t ret; if (min == 0 || len == 0 || len >= bitmap->b_total_bits || min > len) return OCFS2_ET_INVALID_ARGUMENT; ret = (*bitmap->b_ops->alloc_range)(bitmap, min, len, first_bit, bits_found); if (ret == 0 && *bits_found < min) abort(); return ret; } errcode_t ocfs2_bitmap_clear_range(ocfs2_bitmap *bitmap, uint64_t len, uint64_t first_bit) { if (len == 0 || len + first_bit > bitmap->b_total_bits) return OCFS2_ET_INVALID_ARGUMENT; return (*bitmap->b_ops->clear_range)(bitmap, len, first_bit); } errcode_t ocfs2_bitmap_read(ocfs2_bitmap *bitmap) { if (!bitmap->b_ops->read_bitmap) return OCFS2_ET_INVALID_ARGUMENT; /* FIXME: Some sane error, or handle in ->read_bitmap() */ if (rb_first(&bitmap->b_regions)) return OCFS2_ET_INVALID_BIT; return (*bitmap->b_ops->read_bitmap)(bitmap); } errcode_t ocfs2_bitmap_write(ocfs2_bitmap *bitmap) { if (!bitmap->b_ops->write_bitmap) return OCFS2_ET_INVALID_ARGUMENT; return (*bitmap->b_ops->write_bitmap)(bitmap); } uint64_t ocfs2_bitmap_get_set_bits(ocfs2_bitmap *bitmap) { return bitmap->b_set_bits; } /* * The remaining functions are private to the library. */ /* * This function is private to the library. Bitmap subtypes will * use this to allocate their structure, but their b_ops will * determine how they work. */ errcode_t ocfs2_bitmap_new(ocfs2_filesys *fs, uint64_t total_bits, const char *description, struct ocfs2_bitmap_operations *ops, void *private_data, ocfs2_bitmap **ret_bitmap) { errcode_t ret; ocfs2_bitmap *bitmap; if (!ops->set_bit || !ops->clear_bit || !ops->test_bit) return OCFS2_ET_INVALID_ARGUMENT; ret = ocfs2_malloc0(sizeof(struct _ocfs2_bitmap), &bitmap); if (ret) return ret; bitmap->b_fs = fs; bitmap->b_total_bits = total_bits; bitmap->b_ops = ops; bitmap->b_regions = RB_ROOT; bitmap->b_private = private_data; if (description) { ret = ocfs2_malloc0(sizeof(char) * (strlen(description) + 1), &bitmap->b_description); if (ret) goto out_free; strcpy(bitmap->b_description, description); } *ret_bitmap = bitmap; return 0; out_free: ocfs2_free(&bitmap); return ret; } static size_t ocfs2_align_total(int total_bits) { return (total_bits + 7) / 8; } errcode_t ocfs2_bitmap_alloc_region(ocfs2_bitmap *bitmap, uint64_t start_bit, int bitmap_start, int total_bits, struct ocfs2_bitmap_region **ret_br) { errcode_t ret; struct ocfs2_bitmap_region *br; if (total_bits < 0) return OCFS2_ET_INVALID_BIT; ret = ocfs2_malloc0(sizeof(struct ocfs2_bitmap_region), &br); if (ret) return ret; br->br_start_bit = start_bit; br->br_bitmap_start = bitmap_start; br->br_valid_bits = total_bits; br->br_total_bits = total_bits + bitmap_start; br->br_bytes = ocfs2_align_total(br->br_total_bits); ret = ocfs2_malloc0(br->br_bytes, &br->br_bitmap); if (ret) ocfs2_free(&br); else *ret_br = br; return ret; } void ocfs2_bitmap_free_region(struct ocfs2_bitmap_region *br) { if (br->br_bitmap) ocfs2_free(&br->br_bitmap); ocfs2_free(&br); } errcode_t ocfs2_bitmap_realloc_region(ocfs2_bitmap *bitmap, struct ocfs2_bitmap_region *br, int total_bits) { errcode_t ret; size_t new_bytes; if ((br->br_start_bit + total_bits) > bitmap->b_total_bits) return OCFS2_ET_INVALID_BIT; new_bytes = ocfs2_align_total(total_bits + br->br_bitmap_start); if (new_bytes > br->br_bytes) { ret = ocfs2_realloc0(new_bytes, &br->br_bitmap, br->br_bytes); if (ret) return ret; br->br_bytes = new_bytes; } br->br_valid_bits = total_bits; br->br_total_bits = total_bits + br->br_bitmap_start; return 0; } errcode_t ocfs2_bitmap_foreach_region(ocfs2_bitmap *bitmap, ocfs2_bitmap_foreach_func func, void *private_data) { struct ocfs2_bitmap_region *br; struct rb_node *node; errcode_t ret = 0; for (node = rb_first(&bitmap->b_regions); node; node = rb_next(node)) { br = rb_entry(node, struct ocfs2_bitmap_region, br_node); ret = func(br, private_data); if (ret == OCFS2_ET_ITERATION_COMPLETE) { ret = 0; break; } if (ret) break; } return ret; } /* * Attempt to merge two regions. If the merge is successful, 0 will * be returned and prev will be the only valid region. Next will * be freed. */ static errcode_t ocfs2_bitmap_merge_region(ocfs2_bitmap *bitmap, struct ocfs2_bitmap_region *prev, struct ocfs2_bitmap_region *next) { errcode_t ret; uint64_t new_bits; size_t prev_bytes; uint8_t *pbm, *nbm, offset, diff; if ((prev->br_start_bit + prev->br_valid_bits) != next->br_start_bit) return OCFS2_ET_INVALID_BIT; if (bitmap->b_ops->merge_region && !(*bitmap->b_ops->merge_region)(bitmap, prev, next)) return OCFS2_ET_INVALID_BIT; /* XXX: We don't support merge if the bitmap isn't aligned now. */ if (prev->br_bitmap_start || next->br_bitmap_start) return OCFS2_ET_INVALID_BIT; new_bits = (uint64_t)(prev->br_total_bits) + (uint64_t)(next->br_total_bits); if (new_bits > INT_MAX) return OCFS2_ET_INVALID_BIT; /* grab before realloc changes them */ prev_bytes = prev->br_bytes; offset = prev->br_total_bits % 8; ret = ocfs2_bitmap_realloc_region(bitmap, prev, (int)new_bits); if (ret) return ret; /* if prev's last bit ends on a byte boundary then we can just * copy everything over */ if (offset == 0) { memcpy(prev->br_bitmap + prev_bytes, next->br_bitmap, next->br_bytes); goto done; } /* otherwise we have to shift next in. we're about to free * next, so we consume it as we go. */ pbm = &prev->br_bitmap[prev_bytes - 1]; nbm = &next->br_bitmap[0]; diff = 8 - offset; while(next->br_bytes-- && next->br_total_bits > 0) { /* mask off just the offset bytes in the prev */ *pbm &= ((1 << offset) - 1); /* move 'diff' LSB from next into prevs MSB */ *pbm |= (*nbm & ((1 << diff) - 1)) << offset; pbm++; next->br_total_bits -= diff; if (next->br_total_bits > 0) { /* then set prev's LSB to the next offset MSB. this relies * on 0s being shifted into the MSB */ *pbm = *nbm >> diff; nbm++; next->br_total_bits -= offset; } } done: prev->br_set_bits = prev->br_set_bits + next->br_set_bits; rb_erase(&next->br_node, &bitmap->b_regions); ocfs2_bitmap_free_region(next); return 0; } /* * Find a bitmap_region in the tree that intersects the bit region * that is passed in. * * _p and _parent are set so that callers can use rb_link_node and * rb_insert_color to insert a node after finding that their bit * wasn't found. * * _next is only used if a bitmap_region isn't found. it is set * to the next node in the tree greater than the bitmap range * that was searched. */ static struct ocfs2_bitmap_region *ocfs2_bitmap_lookup(ocfs2_bitmap *bitmap, uint64_t bitno, uint64_t total_bits, struct rb_node ***ret_p, struct rb_node **ret_parent, struct rb_node **ret_next) { struct rb_node **p = &bitmap->b_regions.rb_node; struct rb_node *parent = NULL, *last_left = NULL; struct ocfs2_bitmap_region *br = NULL; while (*p) { parent = *p; br = rb_entry(parent, struct ocfs2_bitmap_region, br_node); if (bitno + total_bits <= br->br_start_bit) { last_left = parent; p = &(*p)->rb_left; br = NULL; } else if (bitno >= (br->br_start_bit + br->br_valid_bits)) { p = &(*p)->rb_right; br = NULL; } else break; } if (ret_p != NULL) *ret_p = p; if (ret_parent != NULL) *ret_parent = parent; if (br == NULL && ret_next != NULL) *ret_next = last_left; return br; } errcode_t ocfs2_bitmap_insert_region(ocfs2_bitmap *bitmap, struct ocfs2_bitmap_region *br) { struct ocfs2_bitmap_region *br_tmp; struct rb_node **p, *parent, *node; if (br->br_start_bit > bitmap->b_total_bits) return OCFS2_ET_INVALID_BIT; /* we shouldn't find an existing region that intersects our new one */ br_tmp = ocfs2_bitmap_lookup(bitmap, br->br_start_bit, br->br_valid_bits, &p, &parent, NULL); if (br_tmp) return OCFS2_ET_INVALID_BIT; rb_link_node(&br->br_node, parent, p); rb_insert_color(&br->br_node, &bitmap->b_regions); /* try to merge our new extent with its neighbours in the tree */ node = rb_prev(&br->br_node); if (node) { br_tmp = rb_entry(node, struct ocfs2_bitmap_region, br_node); ocfs2_bitmap_merge_region(bitmap, br_tmp, br); br = br_tmp; } node = rb_next(&br->br_node); if (node != NULL) { br_tmp = rb_entry(node, struct ocfs2_bitmap_region, br_node); ocfs2_bitmap_merge_region(bitmap, br, br_tmp); } return 0; } static int set_generic_shared(ocfs2_bitmap *bitmap, struct ocfs2_bitmap_region *br, uint64_t bitno) { int old_tmp; old_tmp = ocfs2_set_bit(bitno - br->br_start_bit + br->br_bitmap_start, br->br_bitmap); if (!old_tmp) { br->br_set_bits++; if (bitmap->b_ops->bit_change_notify) (*bitmap->b_ops->bit_change_notify)(bitmap, br, bitno, 1); } return old_tmp; } /* * Helper functions for the most generic of bitmaps. If there is no * memory allocated for the bit, it fails. */ errcode_t ocfs2_bitmap_set_generic(ocfs2_bitmap *bitmap, uint64_t bitno, int *oldval) { struct ocfs2_bitmap_region *br; int old_tmp; br = ocfs2_bitmap_lookup(bitmap, bitno, 1, NULL, NULL, NULL); if (!br) return OCFS2_ET_INVALID_BIT; old_tmp = set_generic_shared(bitmap, br, bitno); if (oldval) *oldval = old_tmp; return 0; } static int clear_generic_shared(ocfs2_bitmap *bitmap, struct ocfs2_bitmap_region *br, uint64_t bitno) { int old_tmp; old_tmp = ocfs2_clear_bit(bitno - br->br_start_bit + br->br_bitmap_start, br->br_bitmap); if (old_tmp) { br->br_set_bits--; if (bitmap->b_ops->bit_change_notify) (*bitmap->b_ops->bit_change_notify)(bitmap, br, bitno, 0); } return old_tmp; } errcode_t ocfs2_bitmap_clear_generic(ocfs2_bitmap *bitmap, uint64_t bitno, int *oldval) { struct ocfs2_bitmap_region *br; int old_tmp; br = ocfs2_bitmap_lookup(bitmap, bitno, 1, NULL, NULL, NULL); if (!br) return OCFS2_ET_INVALID_BIT; old_tmp = clear_generic_shared(bitmap, br, bitno); if (oldval) *oldval = old_tmp; return 0; } errcode_t ocfs2_bitmap_test_generic(ocfs2_bitmap *bitmap, uint64_t bitno, int *val) { struct ocfs2_bitmap_region *br; br = ocfs2_bitmap_lookup(bitmap, bitno, 1, NULL, NULL, NULL); if (!br) return OCFS2_ET_INVALID_BIT; *val = ocfs2_test_bit(bitno - br->br_start_bit + br->br_bitmap_start, br->br_bitmap) ? 1 : 0; return 0; } errcode_t ocfs2_bitmap_find_next_set_generic(ocfs2_bitmap *bitmap, uint64_t start, uint64_t *found) { struct ocfs2_bitmap_region *br; struct rb_node *node = NULL; int offset, ret; /* start from either the node whose's br contains the bit or * the next greatest node in the tree */ br = ocfs2_bitmap_lookup(bitmap, start, 1, NULL, NULL, &node); if (br) node = &br->br_node; for (; node != NULL; node = rb_next(node)) { br = rb_entry(node, struct ocfs2_bitmap_region, br_node); if (start > br->br_start_bit) offset = start - br->br_start_bit; else offset = 0; ret = ocfs2_find_next_bit_set(br->br_bitmap, br->br_total_bits, offset + br->br_bitmap_start); if (ret != br->br_total_bits) { *found = br->br_start_bit + ret - br->br_bitmap_start; return 0; } } return OCFS2_ET_BIT_NOT_FOUND; } errcode_t ocfs2_bitmap_find_next_clear_generic(ocfs2_bitmap *bitmap, uint64_t start, uint64_t *found) { struct ocfs2_bitmap_region *br; struct rb_node *node = NULL; int offset, ret; /* start from either the node whose's br contains the bit or * the next greatest node in the tree */ br = ocfs2_bitmap_lookup(bitmap, start, 1, NULL, NULL, &node); if (br) node = &br->br_node; for (; node != NULL; node = rb_next(node)) { br = rb_entry(node, struct ocfs2_bitmap_region, br_node); if (start > br->br_start_bit) offset = start - br->br_start_bit; else offset = 0; ret = ocfs2_find_next_bit_clear(br->br_bitmap, br->br_total_bits, offset + br->br_bitmap_start); if (ret != br->br_total_bits) { *found = br->br_start_bit + ret - br->br_bitmap_start; return 0; } } return OCFS2_ET_BIT_NOT_FOUND; } struct alloc_range_args { ocfs2_bitmap *ar_bitmap; uint64_t ar_min_len; uint64_t ar_len; uint64_t ar_first_bit; uint64_t ar_bits_found; errcode_t ar_ret; }; /* Our strategy here to aid discontiguous allocation is to track the * largest free regions (which still fit within ar_min_len) and if the * max allocation fails, fall back to returning one of those. */ static errcode_t alloc_range_func(struct ocfs2_bitmap_region *br, void *private_data) { struct alloc_range_args *ar = private_data; errcode_t ret = 0; uint64_t best_start = UINT64_MAX, best_len = 0; int start, end; if ((br->br_valid_bits - br->br_set_bits) < ar->ar_min_len) goto out; for (start = br->br_bitmap_start; start + ar->ar_min_len <= br->br_total_bits;) { start = ocfs2_find_next_bit_clear(br->br_bitmap, br->br_total_bits, start); if (start == br->br_total_bits) break; /* avoiding start + 1 here so that start at total_bits - 1 * just works out */ end = ocfs2_find_next_bit_set(br->br_bitmap, br->br_total_bits, start); /* We've found a region large enough to hold our max. */ if ((end - start) >= ar->ar_len) { end = start + ar->ar_len; goto found; } if ((end - start) > best_len) { best_len = end - start; best_start = start; } start = end + 1; } /* Nothing found at all */ if (best_start == UINT64_MAX || best_len < ar->ar_min_len) goto out; /* Best fit works */ start = best_start; end = best_start + best_len; found: ar->ar_first_bit = br->br_start_bit + start - br->br_bitmap_start; ar->ar_bits_found = end - start; for (; start < end; start++) set_generic_shared(ar->ar_bitmap, br, br->br_start_bit + start - br->br_bitmap_start); ar->ar_ret = 0; ret = OCFS2_ET_ITERATION_COMPLETE; out: return ret; } errcode_t ocfs2_bitmap_alloc_range_generic(ocfs2_bitmap *bitmap, uint64_t min_len, uint64_t len, uint64_t *first_bit, uint64_t *bits_found) { errcode_t ret; struct alloc_range_args ar = { .ar_bitmap = bitmap, .ar_min_len = min_len, .ar_len = len, .ar_ret = OCFS2_ET_BIT_NOT_FOUND, }; ret = ocfs2_bitmap_foreach_region(bitmap, alloc_range_func, &ar); if (ret == 0) ret = ar.ar_ret; if (ret == 0) { *first_bit = ar.ar_first_bit; *bits_found = ar.ar_bits_found; } return ret; } errcode_t ocfs2_bitmap_clear_range_generic(ocfs2_bitmap *bitmap, uint64_t len, uint64_t first_bit) { struct ocfs2_bitmap_region *br; uint64_t end; br = ocfs2_bitmap_lookup(bitmap, first_bit, len, NULL, NULL, NULL); if (!br) return OCFS2_ET_INVALID_BIT; for (end = first_bit + len; first_bit < end; first_bit++) clear_generic_shared(bitmap, br, first_bit); return 0; } /* * Helper functions for a bitmap with holes in it. * If a bit doesn't have memory allocated for it, we allocate. */ errcode_t ocfs2_bitmap_set_holes(ocfs2_bitmap *bitmap, uint64_t bitno, int *oldval) { errcode_t ret; struct ocfs2_bitmap_region *br; if (!ocfs2_bitmap_set_generic(bitmap, bitno, oldval)) return 0; ret = ocfs2_bitmap_alloc_region(bitmap, bitno, 0, 1, &br); if (ret) return ret; ret = ocfs2_bitmap_insert_region(bitmap, br); if (ret) return ret; return ocfs2_bitmap_set_generic(bitmap, bitno, oldval); } errcode_t ocfs2_bitmap_clear_holes(ocfs2_bitmap *bitmap, uint64_t bitno, int *oldval) { errcode_t ret; struct ocfs2_bitmap_region *br; if (!ocfs2_bitmap_clear_generic(bitmap, bitno, oldval)) return 0; ret = ocfs2_bitmap_alloc_region(bitmap, bitno, 0, 1, &br); if (ret) return ret; ret = ocfs2_bitmap_insert_region(bitmap, br); return ret; } errcode_t ocfs2_bitmap_test_holes(ocfs2_bitmap *bitmap, uint64_t bitno, int *val) { if (ocfs2_bitmap_test_generic(bitmap, bitno, val)) *val = 0; return 0; } errcode_t ocfs2_bitmap_find_next_set_holes(ocfs2_bitmap *bitmap, uint64_t start, uint64_t *found) { return ocfs2_bitmap_find_next_set_generic(bitmap, start, found); } errcode_t ocfs2_bitmap_find_next_clear_holes(ocfs2_bitmap *bitmap, uint64_t start, uint64_t *found) { struct ocfs2_bitmap_region *br; struct rb_node *node = NULL; uint64_t seen; int offset, ret; /* start from either the node whose's br contains the bit or * the next greatest node in the tree */ br = ocfs2_bitmap_lookup(bitmap, start, 1, NULL, NULL, &node); if (br) node = &br->br_node; else if (!node) { /* There was nothing past start */ *found = start; return 0; } seen = start; for (; node != NULL; node = rb_next(node)) { br = rb_entry(node, struct ocfs2_bitmap_region, br_node); /* Did we find a hole? */ if (seen < br->br_start_bit) { *found = seen; return 0; } if (start > br->br_start_bit) offset = start - br->br_start_bit; else offset = 0; ret = ocfs2_find_next_bit_clear(br->br_bitmap, br->br_total_bits, offset + br->br_bitmap_start); if (ret != br->br_total_bits) { *found = br->br_start_bit + ret - br->br_bitmap_start; return 0; } seen = br->br_start_bit + br->br_valid_bits; } return OCFS2_ET_BIT_NOT_FOUND; } static struct ocfs2_bitmap_operations global_cluster_ops = { .set_bit = ocfs2_bitmap_set_generic, .clear_bit = ocfs2_bitmap_clear_generic, .test_bit = ocfs2_bitmap_test_generic, .find_next_set = ocfs2_bitmap_find_next_set_generic, .find_next_clear = ocfs2_bitmap_find_next_clear_generic, .alloc_range = ocfs2_bitmap_alloc_range_generic, .clear_range = ocfs2_bitmap_clear_range_generic, }; errcode_t ocfs2_cluster_bitmap_new(ocfs2_filesys *fs, const char *description, ocfs2_bitmap **ret_bitmap) { errcode_t ret; ocfs2_bitmap *bitmap; uint64_t max_bits, num_bits, bitoff, alloc_bits; struct ocfs2_bitmap_region *br; num_bits = fs->fs_clusters; ret = ocfs2_bitmap_new(fs, num_bits, description ? description : "Generic cluster bitmap", &global_cluster_ops, NULL, &bitmap); if (ret) return ret; bitoff = 0; max_bits = INT_MAX - (fs->fs_clustersize - 1); while (bitoff < num_bits) { alloc_bits = num_bits; if (num_bits > max_bits) alloc_bits = max_bits; ret = ocfs2_bitmap_alloc_region(bitmap, bitoff, 0, alloc_bits, &br); if (ret) { ocfs2_bitmap_free(&bitmap); return ret; } ret = ocfs2_bitmap_insert_region(bitmap, br); if (ret) { ocfs2_bitmap_free_region(br); ocfs2_bitmap_free(&bitmap); return ret; } bitoff += alloc_bits; } *ret_bitmap = bitmap; return 0; } static struct ocfs2_bitmap_operations global_block_ops = { .set_bit = ocfs2_bitmap_set_holes, .clear_bit = ocfs2_bitmap_clear_holes, .test_bit = ocfs2_bitmap_test_holes, .find_next_set = ocfs2_bitmap_find_next_set_holes, .find_next_clear = ocfs2_bitmap_find_next_clear_holes, /* XXX can't allocate a range yet, would need to fill holes and merge * with adjacent */ }; errcode_t ocfs2_block_bitmap_new(ocfs2_filesys *fs, const char *description, ocfs2_bitmap **ret_bitmap) { errcode_t ret; ocfs2_bitmap *bitmap; ret = ocfs2_bitmap_new(fs, fs->fs_blocks, description ? description : "Generic block bitmap", &global_block_ops, NULL, &bitmap); if (ret) return ret; *ret_bitmap = bitmap; return 0; } #ifdef DEBUG_EXE #include #include #include static uint64_t read_number(const char *num) { uint64_t val; char *ptr; val = strtoull(num, &ptr, 0); if (!ptr || *ptr) return 0; return val; } static void print_usage(void) { fprintf(stderr, "debug_bitmap [-a] \n"); } extern int opterr, optind; extern char *optarg; static void dump_regions(ocfs2_bitmap *bitmap) { struct ocfs2_bitmap_region *br; struct rb_node *node; fprintf(stdout, "Bitmap \"%s\": total = %"PRIu64", set = %"PRIu64"\n", bitmap->b_description, bitmap->b_total_bits, bitmap->b_set_bits); for (node = rb_first(&bitmap->b_regions);node; node = rb_next(node)) { br = rb_entry(node, struct ocfs2_bitmap_region, br_node); fprintf(stdout, "(start: %"PRIu64", n: %d, set: %d)\n", br->br_start_bit, br->br_valid_bits, br->br_set_bits); } } static void print_bitmap(ocfs2_bitmap *bitmap) { uint64_t bitno; uint64_t gap_start = 0; /* GCC is dumb */ errcode_t ret; int val, gap; gap = 0; for (bitno = 0; bitno < bitmap->b_total_bits; bitno++) { ret = ocfs2_bitmap_test(bitmap, bitno, &val); if (ret) { if (ret == OCFS2_ET_INVALID_BIT) { if (!gap) { gap = 1; gap_start = bitno; } continue; } com_err("print_bitmap", ret, "while testing bit %"PRIu64"\n", bitno); break; } if (gap) { fprintf(stdout, "\nGap of length %"PRIu64" at %"PRIu64"\n", bitno - gap_start, gap_start); gap = bitno % 72; gap += gap / 8; for (; gap; gap--) fprintf(stdout, " "); fflush(stdout); } else { if (bitno && !(bitno % 72)) fprintf(stdout, "\n"); else if (bitno && !(bitno % 8)) fprintf(stdout, " "); } fprintf(stdout, "%d", val); fflush(stdout); } if ((bitno - 1) % 72) fprintf(stdout, "\n"); } static int try_op(ocfs2_bitmap *bitmap, errcode_t (*func)(ocfs2_bitmap *bitmap, uint64_t bitno, int *val), char *bit_val, int *ret_val) { errcode_t ret; uint64_t bitno; char *ptr; if (!bit_val) { fprintf(stderr, "You must provide a bit offset\n"); return 1; } bitno = read_number(bit_val); if (!bitno) { for (ptr = bit_val; *ptr; ptr++) { if (*ptr != '0') break; } if ((ptr == bit_val) || *ptr) { fprintf(stderr, "Invalid bit offset: %s\n", bit_val); return 1; } } ret = (*func)(bitmap, bitno, ret_val); if (ret) { com_err("try_op", ret, "while setting bit %"PRIu64"\n", bitno); return 1; } return 0; } static int try_op64(ocfs2_bitmap *bitmap, errcode_t (*func)(ocfs2_bitmap *bitmap, uint64_t bitno, uint64_t *val), char *bit_val, uint64_t *ret_val) { errcode_t ret; uint64_t bitno; char *ptr; if (!bit_val) { fprintf(stderr, "You must provide a bit offset\n"); return 1; } bitno = read_number(bit_val); if (!bitno) { for (ptr = bit_val; *ptr; ptr++) { if (*ptr != '0') break; } if ((ptr == bit_val) || *ptr) { fprintf(stderr, "Invalid bit offset: %s\n", bit_val); return 1; } } ret = (*func)(bitmap, bitno, ret_val); if (ret) { com_err("try_op64", ret, "while setting bit %"PRIu64"\n", bitno); return 1; } return 0; } static void run_test(ocfs2_bitmap *bitmap) { char buf[256]; char *ptr, *cmd; uint64_t val64; int val; while (1) { fprintf(stdout, "Command: "); fflush(stdout); if (!fgets(buf, sizeof(buf), stdin)) break; ptr = buf + strlen(buf) - 1; if (*ptr == '\n') *ptr = '\0'; for (cmd = buf; (*cmd == ' ') || (*cmd == '\t'); cmd++); if (!(*cmd)) continue; ptr = strchr(cmd, ' '); if (ptr) { *ptr = '\0'; ptr++; } if (!strcmp(cmd, "set")) { try_op(bitmap, ocfs2_bitmap_set, ptr, NULL); } else if (!strcmp(cmd, "clear")) { try_op(bitmap, ocfs2_bitmap_clear, ptr, NULL); } else if (!strcmp(cmd, "test")) { if (!try_op(bitmap, ocfs2_bitmap_test, ptr, &val)) { fprintf(stdout, "Bit %s is %s\n", ptr, val ? "set" : "clear"); } } else if (!strcmp(cmd, "fns")) { if (!try_op64(bitmap, ocfs2_bitmap_find_next_set, ptr, &val64)) { fprintf(stdout, "Found %"PRIu64"\n", val64); } } else if (!strcmp(cmd, "fnc")) { if (!try_op64(bitmap, ocfs2_bitmap_find_next_clear, ptr, &val64)) { fprintf(stdout, "Found %"PRIu64"\n", val64); } } else if (!strcmp(cmd, "print")) { print_bitmap(bitmap); } else if (!strcmp(cmd, "dump")) { dump_regions(bitmap); } else if (!strcmp(cmd, "quit")) { break; } else { fprintf(stderr, "Invalid command: \"%s\"\n", cmd); } } } int main(int argc, char *argv[]) { errcode_t ret; int c; int alloc = 0; char *filename; ocfs2_filesys *fs; ocfs2_bitmap *bitmap; initialize_ocfs_error_table(); while ((c = getopt(argc, argv, "a")) != EOF) { switch (c) { case 'a': alloc = 1; break; default: print_usage(); return 1; break; } } if (optind >= argc) { fprintf(stderr, "Missing filename\n"); print_usage(); return 1; } filename = argv[optind]; ret = ocfs2_open(filename, OCFS2_FLAG_RO, 0, 0, &fs); if (ret) { com_err(argv[0], ret, "while opening file \"%s\"", filename); return 1; } if (alloc) ret = ocfs2_block_bitmap_new(fs, "Testing", &bitmap); else ret = ocfs2_cluster_bitmap_new(fs, "Testing", &bitmap); if (ret) { com_err(argv[0], ret, "while creating bitmap"); goto out_close; } run_test(bitmap); ocfs2_bitmap_free(&bitmap); out_close: ocfs2_close(fs); return ret; } #endif /* DEBUG_EXE */ ocfs2-tools-ocfs2-tools-1.8.6/libocfs2/bitmap.h000066400000000000000000000115561347147137200212210ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * bitmap.h * * Structures for allocation bitmaps for the OCFS2 userspace library. * * Copyright (C) 2004 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License, version 2, as published by the Free Software Foundation. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * * Authors: Joel Becker */ #ifndef _BITMAP_H #define _BITMAP_H #include "ocfs2/kernel-rbtree.h" struct ocfs2_bitmap_region { struct rb_node br_node; uint64_t br_start_bit; /* Bit offset. */ int br_bitmap_start; /* bit start in br_bitmap. */ int br_valid_bits; /* bit length valid in br_bitmap. */ int br_total_bits; /* set_bit() and friends can't handle bitmaps larger than int offsets */ size_t br_bytes; int br_set_bits; uint8_t *br_bitmap; void *br_private; }; struct ocfs2_bitmap_operations { errcode_t (*set_bit)(ocfs2_bitmap *bitmap, uint64_t bit, int *oldval); errcode_t (*clear_bit)(ocfs2_bitmap *bitmap, uint64_t bit, int *oldval); errcode_t (*test_bit)(ocfs2_bitmap *bitmap, uint64_t bit, int *val); errcode_t (*find_next_set)(ocfs2_bitmap *bitmap, uint64_t start, uint64_t *found); errcode_t (*find_next_clear)(ocfs2_bitmap *bitmap, uint64_t start, uint64_t *found); int (*merge_region)(ocfs2_bitmap *bitmap, struct ocfs2_bitmap_region *prev, struct ocfs2_bitmap_region *next); errcode_t (*read_bitmap)(ocfs2_bitmap *bitmap); errcode_t (*write_bitmap)(ocfs2_bitmap *bitmap); void (*destroy_notify)(ocfs2_bitmap *bitmap); void (*bit_change_notify)(ocfs2_bitmap *bitmap, struct ocfs2_bitmap_region *br, uint64_t bitno, int new_val); errcode_t (*alloc_range)(ocfs2_bitmap *bitmap, uint64_t min_len, uint64_t len, uint64_t *first_bit, uint64_t *bits_found); errcode_t (*clear_range)(ocfs2_bitmap *bitmap, uint64_t len, uint64_t first_bit); }; struct _ocfs2_bitmap { ocfs2_filesys *b_fs; uint64_t b_set_bits; uint64_t b_total_bits; char *b_description; struct ocfs2_bitmap_operations *b_ops; struct rb_root b_regions; void *b_private; }; errcode_t ocfs2_bitmap_new(ocfs2_filesys *fs, uint64_t total_bits, const char *description, struct ocfs2_bitmap_operations *ops, void *private_data, ocfs2_bitmap **ret_bitmap); errcode_t ocfs2_bitmap_alloc_region(ocfs2_bitmap *bitmap, uint64_t start_bit, int bitmap_start, int total_bits, struct ocfs2_bitmap_region **ret_br); void ocfs2_bitmap_free_region(struct ocfs2_bitmap_region *br); errcode_t ocfs2_bitmap_realloc_region(ocfs2_bitmap *bitmap, struct ocfs2_bitmap_region *br, int total_bits); errcode_t ocfs2_bitmap_insert_region(ocfs2_bitmap *bitmap, struct ocfs2_bitmap_region *br); typedef errcode_t (*ocfs2_bitmap_foreach_func)(struct ocfs2_bitmap_region *br, void *private_data); errcode_t ocfs2_bitmap_foreach_region(ocfs2_bitmap *bitmap, ocfs2_bitmap_foreach_func func, void *private_data); errcode_t ocfs2_bitmap_set_generic(ocfs2_bitmap *bitmap, uint64_t bitno, int *oldval); errcode_t ocfs2_bitmap_clear_generic(ocfs2_bitmap *bitmap, uint64_t bitno, int *oldval); errcode_t ocfs2_bitmap_test_generic(ocfs2_bitmap *bitmap, uint64_t bitno, int *val); errcode_t ocfs2_bitmap_find_next_set_generic(ocfs2_bitmap *bitmap, uint64_t start, uint64_t *found); errcode_t ocfs2_bitmap_find_next_clear_generic(ocfs2_bitmap *bitmap, uint64_t start, uint64_t *found); errcode_t ocfs2_bitmap_alloc_range_generic(ocfs2_bitmap *bitmap, uint64_t min_len, uint64_t len, uint64_t *first_bit, uint64_t *bits_found); errcode_t ocfs2_bitmap_clear_range_generic(ocfs2_bitmap *bitmap, uint64_t len, uint64_t first_bit); errcode_t ocfs2_bitmap_set_holes(ocfs2_bitmap *bitmap, uint64_t bitno, int *oldval); errcode_t ocfs2_bitmap_clear_holes(ocfs2_bitmap *bitmap, uint64_t bitno, int *oldval); errcode_t ocfs2_bitmap_test_holes(ocfs2_bitmap *bitmap, uint64_t bitno, int *val); errcode_t ocfs2_bitmap_find_next_set_holes(ocfs2_bitmap *bitmap, uint64_t start, uint64_t *found); errcode_t ocfs2_bitmap_find_next_clear_holes(ocfs2_bitmap *bitmap, uint64_t start, uint64_t *found); #endif /* _BITMAP_H */ ocfs2-tools-ocfs2-tools-1.8.6/libocfs2/bitops.c000066400000000000000000000117151347147137200212350ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * bitops.c * * Bitmap frobbing code for the OCFS2 userspace library. See bitops.h * for inlined versions. * * Copyright (C) 2004 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License, version 2, as published by the Free Software Foundation. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * * This code is a port of e2fsprogs/lib/ext2fs/bitops.c * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o. */ #include #include #include #include "ocfs2/bitops.h" /* * For the benefit of those who are trying to port Linux to another * architecture, here are some C-language equivalents. You should * recode these in the native assmebly language, if at all possible. * * C language equivalents written by Theodore Ts'o, 9/26/92. * Modified by Pete A. Zaitcev 7/14/95 to be portable to big endian * systems, as well as non-32 bit systems. */ int ocfs2_set_bit(int nr,void * addr) { int mask, retval; unsigned char *ADDR = (unsigned char *) addr; ADDR += nr >> 3; mask = 1 << (nr & 0x07); retval = (mask & *ADDR) != 0; *ADDR |= mask; return retval; } int ocfs2_clear_bit(int nr, void * addr) { int mask, retval; unsigned char *ADDR = (unsigned char *) addr; ADDR += nr >> 3; mask = 1 << (nr & 0x07); retval = (mask & *ADDR) != 0; *ADDR &= ~mask; return retval; } int ocfs2_test_bit(int nr, const void * addr) { int mask; const unsigned char *ADDR = (const unsigned char *) addr; ADDR += nr >> 3; mask = 1 << (nr & 0x07); return ((mask & *ADDR) != 0); } int ocfs2_find_first_bit_set(void *addr, int size) { return ocfs2_find_next_bit_set(addr, size, 0); } int ocfs2_find_first_bit_clear(void *addr, int size) { return ocfs2_find_next_bit_clear(addr, size, 0); } int ocfs2_find_next_bit_set(void *addr, int size, int offset) { unsigned char * p; int set = 0, d0; unsigned int bit = offset & 7, res = 0; unsigned char tilde = ~0; unsigned int mask = 0U | tilde; /* XXX care to check for null ADDR and <= 0 for int args? */ if (size == 0) return 0; res = offset >> 3; p = ((unsigned char *) addr) + res; res <<= 3; if (bit) { set = ffs(*p & ~((1 << bit) - 1)); if (set) return (offset & ~7) + set - 1; p++; res += 8; } while ((size > res) && (*p == 0)) { p++; res += 8; } if (res >= size) return size; if ((res + 8) > size) mask >>= 8 - (size - res); d0 = ffs(*p & mask); if (d0 == 0) return size; return (res + d0 - 1); } int ocfs2_find_next_bit_clear(void *addr, int size, int offset) { unsigned char * p; int set = 0, d0; unsigned int bit = offset & 7, res = 0; unsigned char tilde = ~0; unsigned int mask = 0U | tilde; if (size == 0) return 0; res = offset >> 3; p = ((unsigned char *) addr) + res; res <<= 3; if (bit) { set = ffs(~*p & ~((1 << bit) - 1) & mask); if (set) return (offset & ~7) + set - 1; p++; res += 8; } while ((size > res) && (*p == tilde)) { p++; res += 8; } if (res >= size) return size; if ((res + 8) > size) mask >>= 8 - (size - res); d0 = ffs(~(*p & mask)); if (d0 == 0) return size; return (res + d0 - 1); } int ocfs2_get_bits_set(void *addr, int size, int offset) { int set_bits = 0, found = 0; while (1) { found = ocfs2_find_next_bit_set(addr, size, offset); if (found < size) { set_bits++; offset = found + 1; } else break; } return set_bits; } #ifdef DEBUG_EXE #include #include #include #define bit_expect(expect, which, args...) do { \ int _ret = ocfs2_find_##which(bitmap, args); \ fprintf(stdout, #which "(" #args ") = %d (expected %d: %s)\n", \ _ret, expect, \ _ret == expect ? "correct" : "_incorrect_"); \ } while (0) int main(int argc, char *argv[]) { char bitmap[8 * sizeof(unsigned long)]; int size = sizeof(bitmap) * 8; /* Test an arbitrary size (not byte bounded) */ memset(bitmap, 0, sizeof(bitmap)); ocfs2_set_bit(size - 1, bitmap); bit_expect(size - 3, first_bit_set, size - 3); bit_expect(size - 1, first_bit_set, size); bit_expect(size - 1, next_bit_set, size, size - 1); bit_expect(size, next_bit_clear, size, size - 1); memset(bitmap, 0xFF, sizeof(bitmap)); ocfs2_clear_bit(size - 1, bitmap); bit_expect(size - 3, first_bit_clear, size - 3); bit_expect(size - 1, first_bit_clear, size); bit_expect(size - 1, next_bit_clear, size, size - 1); bit_expect(size, next_bit_set, size, size - 1); /* XXX add more tests */ return 0; } #endif ocfs2-tools-ocfs2-tools-1.8.6/libocfs2/blockcheck.c000066400000000000000000000600431347147137200220230ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * blockcheck.c * * Checksum and ECC codes for the OCFS2 userspace library. * * Copyright (C) 2006, 2008 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License, version 2, as published by the Free Software Foundation. * * 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. * * The 802.3 CRC32 algorithm is copied from the Linux kernel, lib/crc32.c. * Code was from the public domain, is now GPL, so no real copyright * attribution other than "The Linux Kernel". XXX: better text, anyone? */ #define _XOPEN_SOURCE 600 /* Triggers magic in features.h */ #define _LARGEFILE64_SOURCE #ifdef DEBUG_EXE # define _BSD_SOURCE /* For timersub() */ #endif #include #include "ocfs2/ocfs2.h" #include "ocfs2/bitops.h" #include "ocfs2/byteorder.h" #include "crc32table.h" static inline unsigned int hc_hweight32(unsigned int w) { unsigned int res = (w & 0x55555555) + ((w >> 1) & 0x55555555); res = (res & 0x33333333) + ((res >> 2) & 0x33333333); res = (res & 0x0F0F0F0F) + ((res >> 4) & 0x0F0F0F0F); res = (res & 0x00FF00FF) + ((res >> 8) & 0x00FF00FF); return (res & 0x0000FFFF) + ((res >> 16) & 0x0000FFFF); } /* * Calculate the bit offset in the hamming code buffer based on the bit's * offset in the data buffer. Since the hamming code reserves all * power-of-two bits for parity, the data bit number and the code bit * number are offest by all the parity bits beforehand. * * Recall that bit numbers in hamming code are 1-based. This function * takes the 0-based data bit from the caller. * * An example. Take bit 1 of the data buffer. 1 is a power of two (2^0), * so it's a parity bit. 2 is a power of two (2^1), so it's a parity bit. * 3 is not a power of two. So bit 1 of the data buffer ends up as bit 3 * in the code buffer. * * The caller passes in *p if it wants to keep track of the most recent * number of parity bits added. This allows the function to start the * calculation at the last place. */ static unsigned int calc_code_bit(unsigned int i, unsigned int *p_cache) { unsigned int b, p = 0; /* * Data bits are 0-based, but we're talking code bits, which * are 1-based. */ b = i + 1; /* Use the cache if it is there */ if (p_cache) p = *p_cache; b += p; /* * For every power of two below our bit number, bump our bit. * * We compare with (b + 1) because we have to compare with what b * would be _if_ it were bumped up by the parity bit. Capice? * * p is set above. */ for (; (1 << p) < (b + 1); p++) b++; if (p_cache) *p_cache = p; return b; } /* * This is the low level encoder function. It can be called across * multiple hunks just like the crc32 code. 'd' is the number of bits * _in_this_hunk_. nr is the bit offset of this hunk. So, if you had * two 512B buffers, you would do it like so: * * parity = ocfs2_hamming_encode(0, buf1, 512 * 8, 0); * parity = ocfs2_hamming_encode(parity, buf2, 512 * 8, 512 * 8); * * If you just have one buffer, use ocfs2_hamming_encode_block(). */ uint32_t ocfs2_hamming_encode(uint32_t parity, void *data, unsigned int d, unsigned int nr) { unsigned int i, b, p = 0; if (!d) abort(); /* * b is the hamming code bit number. Hamming code specifies a * 1-based array, but C uses 0-based. So 'i' is for C, and 'b' is * for the algorithm. * * The i++ in the for loop is so that the start offset passed * to ocfs2_find_next_bit_set() is one greater than the previously * found bit. */ for (i = 0; (i = ocfs2_find_next_bit_set(data, d, i)) < d; i++) { /* * i is the offset in this hunk, nr + i is the total bit * offset. */ b = calc_code_bit(nr + i, &p); /* * Data bits in the resultant code are checked by * parity bits that are part of the bit number * representation. Huh? * * * In other words, the parity bit at position 2^k * checks bits in positions having bit k set in * their binary representation. Conversely, for * instance, bit 13, i.e. 1101(2), is checked by * bits 1000(2) = 8, 0100(2)=4 and 0001(2) = 1. * * * Note that 'k' is the _code_ bit number. 'b' in * our loop. */ parity ^= b; } /* While the data buffer was treated as little endian, the * return value is in host endian. */ return parity; } uint32_t ocfs2_hamming_encode_block(void *data, unsigned int blocksize) { return ocfs2_hamming_encode(0, data, blocksize * 8, 0); } /* * Like ocfs2_hamming_encode(), this can handle hunks. nr is the bit * offset of the current hunk. If bit to be fixed is not part of the * current hunk, this does nothing. * * If you only have one hunk, use ocfs2_hamming_fix_block(). */ void ocfs2_hamming_fix(void *data, unsigned int d, unsigned int nr, unsigned int fix) { unsigned int i, b; if (!d) abort(); /* * If the bit to fix has an hweight of 1, it's a parity bit. One * busted parity bit is its own error. Nothing to do here. */ if (hc_hweight32(fix) == 1) return; /* * nr + d is the bit right past the data hunk we're looking at. * If fix after that, nothing to do */ if (fix >= calc_code_bit(nr + d, NULL)) return; /* * nr is the offset in the data hunk we're starting at. Let's * start b at the offset in the code buffer. See hamming_encode() * for a more detailed description of 'b'. */ b = calc_code_bit(nr, NULL); /* If the fix is before this hunk, nothing to do */ if (fix < b) return; for (i = 0; i < d; i++, b++) { /* Skip past parity bits */ while (hc_hweight32(b) == 1) b++; /* * i is the offset in this data hunk. * nr + i is the offset in the total data buffer. * b is the offset in the total code buffer. * * Thus, when b == fix, bit i in the current hunk needs * fixing. */ if (b == fix) { if (ocfs2_test_bit(i, data)) ocfs2_clear_bit(i, data); else ocfs2_set_bit(i, data); break; } } } void ocfs2_hamming_fix_block(void *data, unsigned int blocksize, unsigned int fix) { ocfs2_hamming_fix(data, blocksize * 8, 0, fix); } /* * table-based crc32_le() stolen from the kernel. This is the one we know * the filesystem is using. * * RFC 3385 shows that the 802.3 crc32 (this one) has the same properties * and probabilities as crc32c (which iSCSI uses) for data blocks < 2^16 * bits. We fit. */ /** * crc32_le() - Calculate bitwise little-endian Ethernet AUTODIN II CRC32 * @crc - seed value for computation. ~0 for Ethernet, sometimes 0 for * other uses, or the previous crc32 value if computing incrementally. * @p - pointer to buffer over which CRC is run * @len - length of buffer @p * */ uint32_t crc32_le(uint32_t crc, unsigned char const *p, size_t len) { const uint32_t *b =(uint32_t *)p; const uint32_t *tab = crc32table_le; #if __BYTE_ORDER == __LITTLE_ENDIAN # define DO_CRC(x) crc = tab[ (crc ^ (x)) & 255 ] ^ (crc>>8) #else # define DO_CRC(x) crc = tab[ ((crc >> 24) ^ (x)) & 255] ^ (crc<<8) #endif crc = cpu_to_le32(crc); /* Align it */ if(((long)b)&3 && len){ do { uint8_t *p = (uint8_t *)b; DO_CRC(*p++); b = (void *)p; } while ((--len) && ((long)b)&3 ); } if(len >= 4){ /* load data 32 bits wide, xor data 32 bits wide. */ size_t save_len = len & 3; len = len >> 2; --b; /* use pre increment below(*++b) for speed */ do { crc ^= *++b; DO_CRC(0); DO_CRC(0); DO_CRC(0); DO_CRC(0); } while (--len); b++; /* point to next byte(s) */ len = save_len; } /* And the last few bytes */ if(len){ do { uint8_t *p = (uint8_t *)b; DO_CRC(*p++); b = (void *)p; } while (--len); } return le32_to_cpu(crc); #undef DO_CRC } /* * This function generates check information for a block. * data is the block to be checked. bc is a pointer to the * ocfs2_block_check structure describing the crc32 and the ecc. * * bc should be a pointer inside data, as the function will * take care of zeroing it before calculating the check information. If * bc does not point inside data, the caller must make sure any inline * ocfs2_block_check structures are zeroed. * * The data buffer must be in on-disk endian (little endian for ocfs2). * bc will be filled with little-endian values and will be ready to go to * disk. */ void ocfs2_block_check_compute(void *data, size_t blocksize, struct ocfs2_block_check *bc) { uint32_t crc; uint16_t ecc; memset(bc, 0, sizeof(struct ocfs2_block_check)); crc = crc32_le(~0, data, blocksize); /* We know this will return max 16 bits */ ecc = (uint16_t)ocfs2_hamming_encode_block(data, blocksize); bc->bc_crc32e = cpu_to_le32(crc); bc->bc_ecc = cpu_to_le16(ecc); /* We know it's max 16 bits */ } /* * This function validates existing check information. Like _compute, * the function will take care of zeroing bc before calculating check codes. * If bc is not a pointer inside data, the caller must have zeroed any * inline ocfs2_block_check structures. * * Again, the data passed in should be the on-disk endian. */ errcode_t ocfs2_block_check_validate(void *data, size_t blocksize, struct ocfs2_block_check *bc) { errcode_t err = 0; struct ocfs2_block_check check; uint32_t crc, ecc; check.bc_crc32e = le32_to_cpu(bc->bc_crc32e); check.bc_ecc = le16_to_cpu(bc->bc_ecc); memset(bc, 0, sizeof(struct ocfs2_block_check)); /* Fast path - if the crc32 validates, we're good to go */ crc = crc32_le(~0, data, blocksize); if (crc == check.bc_crc32e) goto out; /* Ok, try ECC fixups */ ecc = ocfs2_hamming_encode_block(data, blocksize); ocfs2_hamming_fix_block(data, blocksize, ecc ^ check.bc_ecc); /* And check the crc32 again */ crc = crc32_le(~0, data, blocksize); if (crc == check.bc_crc32e) goto out; err = OCFS2_ET_BAD_CRC32; out: bc->bc_crc32e = cpu_to_le32(check.bc_crc32e); bc->bc_ecc = cpu_to_le16(check.bc_ecc); return err; } /* * These are the main API. They check the superblock flag before * calling the underlying operations. * * They expect the buffer to be in disk format. */ void ocfs2_compute_meta_ecc(ocfs2_filesys *fs, void *data, struct ocfs2_block_check *bc) { if (ocfs2_meta_ecc(OCFS2_RAW_SB(fs->fs_super))) ocfs2_block_check_compute(data, fs->fs_blocksize, bc); } errcode_t ocfs2_validate_meta_ecc(ocfs2_filesys *fs, void *data, struct ocfs2_block_check *bc) { errcode_t err = 0; if (ocfs2_meta_ecc(OCFS2_RAW_SB(fs->fs_super)) && !(fs->fs_flags & OCFS2_FLAG_NO_ECC_CHECKS)) err = ocfs2_block_check_validate(data, fs->fs_blocksize, bc); return err; } #ifdef DEBUG_EXE #include #include #include #include #include #include #include #include #include #include #include /* * The function hamming_encode_orig() is my original, tested version. It's * slow. We work from it to make a faster one. */ /* * We use the following conventions: * * d = # data bits * p = # parity bits * c = # total code bits (d + p) */ static int calc_parity_bits_orig(unsigned int d) { unsigned int p; /* * Bits required for Single Error Correction is as follows: * * d + p + 1 <= 2^p * * We're restricting ourselves to 31 bits of parity, that should be * sufficient. */ for (p = 1; p < 32; p++) { if ((d + p + 1) <= (1 << p)) return p; } return 0; } static unsigned int calc_code_bit_orig(unsigned int i) { unsigned int b, p; /* * Data bits are 0-based, but we're talking code bits, which * are 1-based. */ b = i + 1; /* * For every power of two below our bit number, bump our bit. * * We compare with (b + 1) because we have to compare with what b * would be _if_ it were bumped up by the parity bit. Capice? */ for (p = 0; (1 << p) < (b + 1); p++) b++; return b; } /* * Find the log base 2 of 32-bit v. * * Algorithm found on http://graphics.stanford.edu/~seander/bithacks.html, * by Sean Eron Anderson. Code on the page is in the public domain unless * otherwise noted. * * This particular algorithm is credited to Eric Cole. */ static int find_highest_bit_set(unsigned int v) { static const int MultiplyDeBruijnBitPosition[32] = { 0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8, 31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9 }; v |= v >> 1; /* first round down to power of 2 */ v |= v >> 2; v |= v >> 4; v |= v >> 8; v |= v >> 16; v = (v >> 1) + 1; return MultiplyDeBruijnBitPosition[(uint32_t)(v * 0x077CB531UL) >> 27]; } static unsigned int calc_code_bit_cheat(unsigned int i) { unsigned int b, p; /* * Data bits are 0-based, but we're talking code bits, which * are 1-based. */ b = i + 1; /* * As a cheat, we know that all bits below b's highest bit must be * parity bits, so we can start there. */ p = find_highest_bit_set(b); b += p; /* * For every power of two below our bit number, bump our bit. * * We compare with (b + 1) because we have to compare with what b * would be _if_ it were bumped up by the parity bit. Capice? * * We start p at 2^p because of the cheat above. */ for (p = (1 << p); p < (b + 1); p <<= 1) b++; return b; } /* * This is the low level encoder function. It can be called across * multiple hunks just like the crc32 code. 'd' is the number of bits * _in_this_hunk_. nr is the bit offset of this hunk. So, if you had * two 512B buffers, you would do it like so: * * parity = ocfs2_hamming_encode(0, buf1, 512 * 8, 0); * parity = ocfs2_hamming_encode(parity, buf2, 512 * 8, 512 * 8); * * If you just have one buffer, use ocfs2_hamming_encode_block(). */ static uint32_t hamming_encode_orig(uint32_t parity, void *data, unsigned int d, unsigned int nr) { unsigned int p = calc_parity_bits_orig(d); unsigned int i, j, b; if (!p) abort(); /* * b is the hamming code bit number. Hamming code specifies a * 1-based array, but C uses 0-based. So 'i' is for C, and 'b' is * for the algorithm. * * The i++ in the for loop is so that the start offset passed * to ocfs2_find_next_bit_set() is one greater than the previously * found bit. */ for (i = 0; (i = ocfs2_find_next_bit_set(data, d, i)) < d; i++) { /* * i is the offset in this hunk, nr + i is the total bit * offset. */ b = calc_code_bit_orig(nr + i); for (j = 0; j < p; j++) { /* * Data bits in the resultant code are checked by * parity bits that are part of the bit number * representation. Huh? * * * In other words, the parity bit at position 2^k * checks bits in positions having bit k set in * their binary representation. Conversely, for * instance, bit 13, i.e. 1101(2), is checked by * bits 1000(2) = 8, 0100(2)=4 and 0001(2) = 1. * * * Note that 'k' is the _code_ bit number. 'b' in * our loop. */ if (b & (1 << j)) parity ^= (1 << j); } } /* While the data buffer was treated as little endian, the * return value is in host endian. */ return parity; } /* * This version uses the direct parity ^= b, but the original * calc_parity_bits() and calc_code_bit(). */ static uint32_t ocfs2_hamming_encode_orig_bits(uint32_t parity, void *data, unsigned int d, unsigned int nr) { unsigned int p = calc_parity_bits_orig(d); unsigned int i, b; if (!p) abort(); /* * b is the hamming code bit number. Hamming code specifies a * 1-based array, but C uses 0-based. So 'i' is for C, and 'b' is * for the algorithm. * * The i++ in the for loop is so that the start offset passed * to ocfs2_find_next_bit_set() is one greater than the previously * found bit. */ for (i = 0; (i = ocfs2_find_next_bit_set(data, d, i)) < d; i++) { /* * i is the offset in this hunk, nr + i is the total bit * offset. */ b = calc_code_bit_orig(nr + i); /* * Data bits in the resultant code are checked by * parity bits that are part of the bit number * representation. Huh? * * * In other words, the parity bit at position 2^k * checks bits in positions having bit k set in * their binary representation. Conversely, for * instance, bit 13, i.e. 1101(2), is checked by * bits 1000(2) = 8, 0100(2)=4 and 0001(2) = 1. * * * Note that 'k' is the _code_ bit number. 'b' in * our loop. */ parity ^= b; } /* While the data buffer was treated as little endian, the * return value is in host endian. */ return parity; } /* * This version uses the direct parity ^= b, but the original * calc_code_bit() */ static uint32_t ocfs2_hamming_encode_orig_code_bit(uint32_t parity, void *data, unsigned int d, unsigned int nr) { unsigned int i, b; if (!d) abort(); /* * b is the hamming code bit number. Hamming code specifies a * 1-based array, but C uses 0-based. So 'i' is for C, and 'b' is * for the algorithm. * * The i++ in the for loop is so that the start offset passed * to ocfs2_find_next_bit_set() is one greater than the previously * found bit. */ for (i = 0; (i = ocfs2_find_next_bit_set(data, d, i)) < d; i++) { /* * i is the offset in this hunk, nr + i is the total bit * offset. */ b = calc_code_bit_orig(nr + i); /* * Data bits in the resultant code are checked by * parity bits that are part of the bit number * representation. Huh? * * * In other words, the parity bit at position 2^k * checks bits in positions having bit k set in * their binary representation. Conversely, for * instance, bit 13, i.e. 1101(2), is checked by * bits 1000(2) = 8, 0100(2)=4 and 0001(2) = 1. * * * Note that 'k' is the _code_ bit number. 'b' in * our loop. */ parity ^= b; } /* While the data buffer was treated as little endian, the * return value is in host endian. */ return parity; } /* * This version uses the direct parity ^= b, but the cheating * calc_code_bit(). */ static uint32_t ocfs2_hamming_encode_cheat_code_bit(uint32_t parity, void *data, unsigned int d, unsigned int nr) { unsigned int i, b; if (!d) abort(); /* * b is the hamming code bit number. Hamming code specifies a * 1-based array, but C uses 0-based. So 'i' is for C, and 'b' is * for the algorithm. * * The i++ in the for loop is so that the start offset passed * to ocfs2_find_next_bit_set() is one greater than the previously * found bit. */ for (i = 0; (i = ocfs2_find_next_bit_set(data, d, i)) < d; i++) { /* * i is the offset in this hunk, nr + i is the total bit * offset. */ b = calc_code_bit_cheat(nr + i); /* * Data bits in the resultant code are checked by * parity bits that are part of the bit number * representation. Huh? * * * In other words, the parity bit at position 2^k * checks bits in positions having bit k set in * their binary representation. Conversely, for * instance, bit 13, i.e. 1101(2), is checked by * bits 1000(2) = 8, 0100(2)=4 and 0001(2) = 1. * * * Note that 'k' is the _code_ bit number. 'b' in * our loop. */ parity ^= b; } /* While the data buffer was treated as little endian, the * return value is in host endian. */ return parity; } struct run_context { char *rc_name; void *rc_data; int rc_size; int rc_count; void (*rc_func)(struct run_context *ct, int nr); }; static void timeme(struct run_context *ct) { int i; struct rusage start; struct rusage stop; struct timeval sys_diff, usr_diff; assert(!getrusage(RUSAGE_SELF, &start)); for (i = 0; i < ct->rc_count; i++) ct->rc_func(ct, i); assert(!getrusage(RUSAGE_SELF, &stop)); timersub(&stop.ru_utime, &start.ru_utime, &usr_diff); timersub(&stop.ru_stime, &start.ru_stime, &sys_diff); fprintf(stderr, "Time for %s: %ld.%06ld user, %ld.%06ld system\n", ct->rc_name, usr_diff.tv_sec, usr_diff.tv_usec, sys_diff.tv_sec, sys_diff.tv_usec); } static void crc32_func(struct run_context *ct, int nr) { uint32_t crc = ~0; crc = crc32_le(crc, ct->rc_data, ct->rc_size); } static void run_crc32(char *buf, int size, int count) { struct run_context ct = { .rc_name = "CRC32", .rc_data = buf, .rc_size = size, .rc_count = count, .rc_func = crc32_func, }; timeme(&ct); } struct hamming_context { struct run_context hc_rc; uint32_t hc_ecc; int hc_ecc_valid; uint32_t (*hc_encode)(uint32_t parity, void *data, unsigned int d, unsigned int nr); }; #define rc_to_hc(_rc) ((struct hamming_context *)(_rc)) static void hamming_func(struct run_context *ct, int nr) { uint32_t ecc = 0; struct hamming_context *hc = rc_to_hc(ct); ecc = hc->hc_encode(ecc, ct->rc_data, ct->rc_size * 8, 0); if (hc->hc_ecc_valid) { if (hc->hc_ecc != ecc) { fprintf(stderr, "Calculated ecc %"PRIu32" != saved ecc %"PRIu32"\n", ecc, hc->hc_ecc); exit(1); } } else { assert(!nr); hc->hc_ecc = ecc; hc->hc_ecc_valid = 1; }; } static void run_hamming(char *buf, int size, int count) { struct hamming_context hc = { .hc_rc = { .rc_name = "Original hamming code", .rc_data = buf, .rc_size = size, .rc_count = count, .rc_func = hamming_func, }, .hc_encode = hamming_encode_orig, }; timeme(&hc.hc_rc); hc.hc_rc.rc_name = "Current hamming code"; hc.hc_encode = ocfs2_hamming_encode; timeme(&hc.hc_rc); hc.hc_rc.rc_name = "Parity xor with orig calc bits"; hc.hc_encode = ocfs2_hamming_encode_orig_bits; timeme(&hc.hc_rc); hc.hc_rc.rc_name = "Parity xor with orig calc code bit"; hc.hc_encode = ocfs2_hamming_encode_orig_code_bit; timeme(&hc.hc_rc); hc.hc_rc.rc_name = "Parity xor with cheating calc code bit"; hc.hc_encode = ocfs2_hamming_encode_cheat_code_bit; timeme(&hc.hc_rc); hc.hc_rc.rc_name = "Current hamming code"; hc.hc_encode = ocfs2_hamming_encode; timeme(&hc.hc_rc); } static uint64_t read_number(const char *num) { uint64_t val; char *ptr; val = strtoull(num, &ptr, 0); if (!ptr || *ptr) return 0; return val; } static void get_file(char *filename, char **buf, int *size) { int rc, fd, tot = 0; char *b; struct stat stat_buf; rc = stat(filename, &stat_buf); if (rc) { fprintf(stderr, "Unable to stat \"%s\": %s\n", filename, strerror(errno)); exit(1); } if (!S_ISREG(stat_buf.st_mode)) { fprintf(stderr, "File \"%s\" is not a regular file\n", filename); exit(1); } b = malloc(stat_buf.st_size * sizeof(char)); if (!b) { fprintf(stderr, "Unable to allocate buffer: %s\n", strerror(errno)); exit(1); } fd = open64(filename, O_RDONLY); if (fd < 0) { fprintf(stderr, "Unable to open \"%s\": %s\n", filename, strerror(errno)); exit(1); } while (tot < stat_buf.st_size) { rc = read(fd, b + tot, stat_buf.st_size - tot); if (rc < 0) { fprintf(stderr, "Error reading from \"%s\": %s\n", filename, strerror(errno)); exit(1); } if (!rc) { fprintf(stderr, "Unexpected EOF while reading from \"%s\"\n", filename); exit(1); } tot += rc; } close(fd); *size = stat_buf.st_size; *buf = b; } static void print_usage(void) { fprintf(stderr, "Usage: blockcheck []\n"); } int main(int argc, char *argv[]) { int size, count = 1; char *filename, *buf; initialize_ocfs_error_table(); if (argc < 2) { fprintf(stderr, "Missing filename\n"); print_usage(); return 1; } filename = argv[1]; if (argc > 2) { count = read_number(argv[2]); if (count < 1) { fprintf(stderr, "Invalid count: %d\n", count); print_usage(); return 1; } } get_file(filename, &buf, &size); run_crc32(buf, size, count); run_hamming(buf, size, count); #if 0 ocfs2_block_check_compute(buf, size, &check); fprintf(stdout, "crc32le: %"PRIu32", ecc: %"PRIu16"\n", le32_to_cpu(check.bc_crc32e), le16_to_cpu(check.bc_ecc)); #endif free(buf); return 0; } #endif ocfs2-tools-ocfs2-tools-1.8.6/libocfs2/blocktype.c000066400000000000000000000077611347147137200217370ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * blocktype.c * * Detect various metadata block types, etc. * * Copyright (C) 2011 Oracle. All rights reserved. * * 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. */ #define _XOPEN_SOURCE 600 /* Triggers magic in features.h */ #define _LARGEFILE64_SOURCE #include #include "ocfs2/ocfs2.h" struct block_types { enum ocfs2_block_type bt_type; char bt_signature[8]; int bt_offset; }; static struct block_types bts[] = { { OCFS2_BLOCK_INODE, OCFS2_INODE_SIGNATURE, offsetof(struct ocfs2_dinode, i_signature), }, { OCFS2_BLOCK_SUPERBLOCK, OCFS2_SUPER_BLOCK_SIGNATURE, offsetof(struct ocfs2_dinode, i_signature), }, { OCFS2_BLOCK_EXTENT_BLOCK, OCFS2_EXTENT_BLOCK_SIGNATURE, offsetof(struct ocfs2_extent_block, h_signature), }, { OCFS2_BLOCK_GROUP_DESCRIPTOR, OCFS2_GROUP_DESC_SIGNATURE, offsetof(struct ocfs2_group_desc, bg_signature), }, { OCFS2_BLOCK_DIR_BLOCK, OCFS2_DIR_TRAILER_SIGNATURE, offsetof(struct ocfs2_dir_block_trailer, db_signature), }, { OCFS2_BLOCK_XATTR, OCFS2_XATTR_BLOCK_SIGNATURE, offsetof(struct ocfs2_xattr_block, xb_signature), }, { OCFS2_BLOCK_REFCOUNT, OCFS2_REFCOUNT_BLOCK_SIGNATURE, offsetof(struct ocfs2_refcount_block, rf_signature), }, { OCFS2_BLOCK_DXROOT, OCFS2_DX_ROOT_SIGNATURE, offsetof(struct ocfs2_dx_root_block, dr_signature), }, { OCFS2_BLOCK_DXLEAF, OCFS2_DX_LEAF_SIGNATURE, offsetof(struct ocfs2_dx_leaf, dl_signature), }, }; enum ocfs2_block_type ocfs2_detect_block(char *buf) { int i, numtypes = sizeof(bts)/sizeof(struct block_types); for (i = 0; i < numtypes; ++i) { if (!strncmp(buf + bts[i].bt_offset, bts[i].bt_signature, strlen(bts[i].bt_signature))) return bts[i].bt_type; } return OCFS2_BLOCK_UNKNOWN; } static void ocfs2_swap_block(ocfs2_filesys *fs, void *block, int to_cpu) { enum ocfs2_block_type bt = ocfs2_detect_block(block); switch (bt) { case OCFS2_BLOCK_INODE: case OCFS2_BLOCK_SUPERBLOCK: if (to_cpu) ocfs2_swap_inode_to_cpu(fs, block); else ocfs2_swap_inode_from_cpu(fs, block); break; case OCFS2_BLOCK_EXTENT_BLOCK: if (to_cpu) ocfs2_swap_extent_block_to_cpu(fs, block); else ocfs2_swap_extent_block_from_cpu(fs, block); break; case OCFS2_BLOCK_GROUP_DESCRIPTOR: if (to_cpu) ocfs2_swap_group_desc_to_cpu(fs, block); else ocfs2_swap_group_desc_from_cpu(fs, block); break; case OCFS2_BLOCK_DIR_BLOCK: if (to_cpu) ocfs2_swap_dir_entries_to_cpu(block, fs->fs_blocksize); else ocfs2_swap_dir_entries_from_cpu(block, fs->fs_blocksize); break; case OCFS2_BLOCK_XATTR: if (to_cpu) ocfs2_swap_xattr_block_to_cpu(fs, block); else ocfs2_swap_xattr_block_from_cpu(fs, block); break; case OCFS2_BLOCK_REFCOUNT: if (to_cpu) ocfs2_swap_refcount_block_to_cpu(fs, block); else ocfs2_swap_refcount_block_from_cpu(fs, block); break; case OCFS2_BLOCK_DXROOT: if (to_cpu) ocfs2_swap_dx_root_to_cpu(fs, block); else ocfs2_swap_dx_root_from_cpu(fs, block); break; case OCFS2_BLOCK_DXLEAF: if (to_cpu) ocfs2_swap_dx_leaf_to_cpu(block); else ocfs2_swap_dx_leaf_from_cpu(block); break; default: break; } return ; } /* * ocfs2_swap_block...() silently ignores unknown block types. The caller * needs to detect unknown blocks. */ void ocfs2_swap_block_from_cpu(ocfs2_filesys *fs, void *block) { ocfs2_swap_block(fs, block, 0); } void ocfs2_swap_block_to_cpu(ocfs2_filesys *fs, void *block) { ocfs2_swap_block(fs, block, 1); } ocfs2-tools-ocfs2-tools-1.8.6/libocfs2/cached_inode.c000066400000000000000000000051601347147137200223170ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * cached_inode.c * * Cache inode structure for the OCFS2 userspace library. * * Copyright (C) 2004 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License, version 2, as published by the Free Software Foundation. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. */ #define _XOPEN_SOURCE 600 /* Triggers XOPEN2K in features.h */ #define _LARGEFILE64_SOURCE #include #include "ocfs2/ocfs2.h" errcode_t ocfs2_read_cached_inode(ocfs2_filesys *fs, uint64_t blkno, ocfs2_cached_inode **ret_ci) { errcode_t ret; char *blk; ocfs2_cached_inode *cinode; if ((blkno < OCFS2_SUPER_BLOCK_BLKNO) || (blkno > fs->fs_blocks)) return OCFS2_ET_BAD_BLKNO; ret = ocfs2_malloc0(sizeof(ocfs2_cached_inode), &cinode); if (ret) return ret; cinode->ci_fs = fs; cinode->ci_blkno = blkno; ret = ocfs2_malloc_block(fs->fs_io, &blk); if (ret) goto cleanup; cinode->ci_inode = (struct ocfs2_dinode *)blk; ret = ocfs2_read_inode(fs, blkno, blk); if (ret) goto cleanup; *ret_ci = cinode; return 0; cleanup: ocfs2_free_cached_inode(fs, cinode); return ret; } errcode_t ocfs2_free_cached_inode(ocfs2_filesys *fs, ocfs2_cached_inode *cinode) { if (!cinode) return OCFS2_ET_INVALID_ARGUMENT; if (cinode->ci_chains) ocfs2_bitmap_free(&cinode->ci_chains); if (cinode->ci_inode) ocfs2_free(&cinode->ci_inode); ocfs2_free(&cinode); return 0; } errcode_t ocfs2_write_cached_inode(ocfs2_filesys *fs, ocfs2_cached_inode *cinode) { errcode_t ret; if (!(fs->fs_flags & OCFS2_FLAG_RW)) return OCFS2_ET_RO_FILESYS; if ((cinode->ci_blkno < OCFS2_SUPER_BLOCK_BLKNO) || (cinode->ci_blkno > fs->fs_blocks)) return OCFS2_ET_BAD_BLKNO; ret = ocfs2_write_inode(fs, cinode->ci_blkno, (char *)cinode->ci_inode); return ret; } errcode_t ocfs2_refresh_cached_inode(ocfs2_filesys *fs, ocfs2_cached_inode *cinode) { if (cinode->ci_chains) { ocfs2_bitmap_free(&cinode->ci_chains); cinode->ci_chains = NULL; } return ocfs2_read_inode(fs, cinode->ci_blkno, (char *)cinode->ci_inode); } ocfs2-tools-ocfs2-tools-1.8.6/libocfs2/chain.c000066400000000000000000000276201347147137200210210ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * chain.c * * Iterate over allocation chains. Part of the OCFS2 userspace library. * * Copyright (C) 2004 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License, version 2, as published by the Free Software Foundation. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. */ #define _XOPEN_SOURCE 600 /* Triggers XOPEN2K in features.h */ #define _LARGEFILE64_SOURCE #include #include "ocfs2/ocfs2.h" #include "ocfs2/byteorder.h" static void ocfs2_swap_group_desc_header(struct ocfs2_group_desc *gd) { gd->bg_size = bswap_16(gd->bg_size); gd->bg_bits = bswap_16(gd->bg_bits); gd->bg_free_bits_count = bswap_16(gd->bg_free_bits_count); gd->bg_chain = bswap_16(gd->bg_chain); gd->bg_generation = bswap_32(gd->bg_generation); gd->bg_next_group = bswap_64(gd->bg_next_group); gd->bg_parent_dinode = bswap_64(gd->bg_parent_dinode); gd->bg_blkno = bswap_64(gd->bg_blkno); } void ocfs2_swap_group_desc_from_cpu(ocfs2_filesys *fs, struct ocfs2_group_desc *gd) { if (cpu_is_little_endian) return; if (ocfs2_gd_is_discontig(gd)) ocfs2_swap_extent_list_from_cpu(fs, gd, &gd->bg_list); ocfs2_swap_group_desc_header(gd); } void ocfs2_swap_group_desc_to_cpu(ocfs2_filesys *fs, struct ocfs2_group_desc *gd) { if (cpu_is_little_endian) return; ocfs2_swap_group_desc_header(gd); if (ocfs2_gd_is_discontig(gd)) ocfs2_swap_extent_list_to_cpu(fs, gd, &gd->bg_list); } errcode_t ocfs2_read_group_desc(ocfs2_filesys *fs, uint64_t blkno, char *gd_buf) { errcode_t ret; char *blk; struct ocfs2_group_desc *gd; if ((blkno < OCFS2_SUPER_BLOCK_BLKNO) || (blkno > fs->fs_blocks)) return OCFS2_ET_BAD_BLKNO; ret = ocfs2_malloc_block(fs->fs_io, &blk); if (ret) return ret; ret = ocfs2_read_blocks(fs, blkno, 1, blk); if (ret) goto out; gd = (struct ocfs2_group_desc *)blk; ret = ocfs2_validate_meta_ecc(fs, blk, &gd->bg_check); if (ret) goto out; ret = OCFS2_ET_BAD_GROUP_DESC_MAGIC; if (memcmp(gd->bg_signature, OCFS2_GROUP_DESC_SIGNATURE, strlen(OCFS2_GROUP_DESC_SIGNATURE))) goto out; memcpy(gd_buf, blk, fs->fs_blocksize); gd = (struct ocfs2_group_desc *)gd_buf; ocfs2_swap_group_desc_to_cpu(fs, gd); ret = 0; out: ocfs2_free(&blk); return ret; } errcode_t ocfs2_write_group_desc(ocfs2_filesys *fs, uint64_t blkno, char *gd_buf) { errcode_t ret; char *blk; struct ocfs2_group_desc *gd; if (!(fs->fs_flags & OCFS2_FLAG_RW)) return OCFS2_ET_RO_FILESYS; if ((blkno < OCFS2_SUPER_BLOCK_BLKNO) || (blkno > fs->fs_blocks)) return OCFS2_ET_BAD_BLKNO; ret = ocfs2_malloc_block(fs->fs_io, &blk); if (ret) return ret; memcpy(blk, gd_buf, fs->fs_blocksize); gd = (struct ocfs2_group_desc *)blk; ocfs2_swap_group_desc_from_cpu(fs, gd); ocfs2_compute_meta_ecc(fs, blk, &gd->bg_check); ret = io_write_block(fs->fs_io, blkno, 1, blk); if (ret) goto out; fs->fs_flags |= OCFS2_FLAG_CHANGED; ret = 0; out: ocfs2_free(&blk); return ret; } struct chain_context { ocfs2_filesys *fs; int (*func)(ocfs2_filesys *fs, uint64_t gd_blkno, int chain_num, void *priv_data); errcode_t errcode; char *gd_buf; void *priv_data; }; static int chain_iterate_gd(struct ocfs2_chain_rec *c_rec, int chain_num, struct chain_context *ctxt) { int iret = 0; uint64_t blkno; struct ocfs2_group_desc *gd; blkno = c_rec->c_blkno; while (blkno) { iret = (*ctxt->func)(ctxt->fs, blkno, chain_num, ctxt->priv_data); if (iret & OCFS2_CHAIN_ABORT) break; ctxt->errcode = ocfs2_read_group_desc(ctxt->fs, blkno, ctxt->gd_buf); if (ctxt->errcode) { iret |= OCFS2_CHAIN_ERROR; break; } gd = (struct ocfs2_group_desc *)ctxt->gd_buf; if ((gd->bg_blkno != blkno) || (gd->bg_chain != chain_num)) { ctxt->errcode = OCFS2_ET_CORRUPT_GROUP_DESC; iret |= OCFS2_CHAIN_ERROR; break; } blkno = gd->bg_next_group; } return iret; } static int chain_iterate_cl(struct ocfs2_chain_list *cl, struct chain_context *ctxt) { int iret = 0; int i; for (i = 0; i < cl->cl_next_free_rec; i++) { iret |= chain_iterate_gd(&cl->cl_recs[i], i, ctxt); if (iret & (OCFS2_CHAIN_ABORT | OCFS2_CHAIN_ERROR)) break; } if (iret & OCFS2_CHAIN_CHANGED) { /* Something here ? */ } return iret; } errcode_t ocfs2_chain_iterate(ocfs2_filesys *fs, uint64_t blkno, int (*func)(ocfs2_filesys *fs, uint64_t gd_blkno, int chain_num, void *priv_data), void *priv_data) { int iret = 0; char *buf; struct ocfs2_dinode *inode; errcode_t ret; struct chain_context ctxt; ret = ocfs2_malloc_block(fs->fs_io, &buf); if (ret) return ret; ret = ocfs2_read_inode(fs, blkno, buf); if (ret) goto out_buf; inode = (struct ocfs2_dinode *)buf; ret = OCFS2_ET_INODE_NOT_VALID; if (!(inode->i_flags & OCFS2_VALID_FL)) goto out_buf; ret = OCFS2_ET_INODE_CANNOT_BE_ITERATED; if (!(inode->i_flags & OCFS2_CHAIN_FL)) goto out_buf; ret = ocfs2_malloc0(fs->fs_blocksize, &ctxt.gd_buf); if (ret) goto out_gd_buf; ctxt.fs = fs; ctxt.func = func; ctxt.priv_data = priv_data; ret = 0; iret |= chain_iterate_cl(&inode->id2.i_chain, &ctxt); if (iret & OCFS2_EXTENT_ERROR) ret = ctxt.errcode; if (iret & OCFS2_EXTENT_CHANGED) { /* Do something */ } out_gd_buf: if (ctxt.gd_buf) ocfs2_free(&ctxt.gd_buf); out_buf: ocfs2_free(&buf); return ret; } uint64_t ocfs2_get_block_from_group(ocfs2_filesys *fs, struct ocfs2_group_desc *grp, int bpc, int bit_offset) { int cpos, i; struct ocfs2_extent_rec *rec = NULL; int block_per_bit = ocfs2_clusters_to_blocks(fs, 1) / bpc; if (!ocfs2_gd_is_discontig(grp)) return grp->bg_blkno + bit_offset * block_per_bit; /* handle discontiguous group. */ cpos = bit_offset / bpc; for (i = 0; i < grp->bg_list.l_next_free_rec; i++) { rec = &grp->bg_list.l_recs[i]; if (rec->e_cpos <= cpos && rec->e_cpos + rec->e_leaf_clusters > cpos) break; } if (!rec || i == grp->bg_list.l_next_free_rec) abort(); return rec->e_blkno + (bit_offset * block_per_bit - ocfs2_clusters_to_blocks(fs, rec->e_cpos)); } errcode_t ocfs2_cache_chain_allocator_blocks(ocfs2_filesys *fs, struct ocfs2_dinode *di) { struct io_vec_unit *ivus = NULL; char *buf = NULL; errcode_t ret = 0; int i, j, count; struct ocfs2_chain_list *cl; struct ocfs2_chain_rec *cr; struct ocfs2_group_desc *gd; io_channel *channel = fs->fs_io; int blocksize = fs->fs_blocksize; int64_t group_size; unsigned int num_gds, max_chain_len; int depth = 0; if (!(di->i_flags & OCFS2_CHAIN_FL)) { ret = OCFS2_ET_INODE_NOT_VALID; goto out; } if (!channel) goto out; if (!di->i_clusters) goto out; group_size = (int64_t)di->i_clusters / di->id2.i_chain.cl_cpg; group_size *= blocksize; if (group_size > io_get_cache_size(channel)) goto out; cl = &(di->id2.i_chain); count = cl->cl_next_free_rec; num_gds = (di->i_clusters + cl->cl_cpg)/cl->cl_cpg; max_chain_len = (num_gds + cl->cl_count)/cl->cl_count; ret = ocfs2_malloc_blocks(channel, count, &buf); if (ret) goto out; memset(buf, 0, count * blocksize); ret = ocfs2_malloc(sizeof(struct io_vec_unit) * count, &ivus); if (ret) goto out; for (i = 0; i < count; ++i) { cr = &(cl->cl_recs[i]); ivus[i].ivu_blkno = cr->c_blkno; ivus[i].ivu_buf = buf + (i * blocksize); ivus[i].ivu_buflen = blocksize; } while (count) { ret = io_vec_read_blocks(channel, ivus, count); if (ret) goto out; for (i = 0, j = 0; i < count; ++i) { gd = (struct ocfs2_group_desc *)ivus[i].ivu_buf; ret = ocfs2_validate_meta_ecc(fs, ivus[i].ivu_buf, &gd->bg_check); if (ret) goto out; if (memcmp(gd->bg_signature, OCFS2_GROUP_DESC_SIGNATURE, strlen(OCFS2_GROUP_DESC_SIGNATURE))) { ret = OCFS2_ET_BAD_GROUP_DESC_MAGIC; goto out; } ocfs2_swap_group_desc_to_cpu(fs, gd); if ((gd->bg_next_group > OCFS2_SUPER_BLOCK_BLKNO) && (gd->bg_next_group < fs->fs_blocks)) { ivus[j].ivu_blkno = gd->bg_next_group; memset(ivus[j].ivu_buf, 0, blocksize); ivus[j].ivu_buflen = blocksize; j++; } } count = j; if (++depth >= max_chain_len) goto out; } out: ocfs2_free(&ivus); ocfs2_free(&buf); return ret; } #ifdef DEBUG_EXE #include #include #include static uint64_t read_number(const char *num) { uint64_t val; char *ptr; val = strtoull(num, &ptr, 0); if (!ptr || *ptr) return 0; return val; } static void print_usage(void) { fprintf(stderr, "Usage: debug_chain -i \n"); } struct walk_it { struct ocfs2_dinode *di; char *gd_buf; int last_chain; int count_free; int count_total; }; static int walk_chain_func(ocfs2_filesys *fs, uint64_t gd_blkno, int chain_num, void *priv_data) { struct walk_it *wi = priv_data; struct ocfs2_group_desc *gd; errcode_t ret; if (wi->last_chain != chain_num) { fprintf(stdout, "CHAIN[%02d]: %d/%d\n", chain_num, wi->di->id2.i_chain.cl_recs[chain_num].c_free, wi->di->id2.i_chain.cl_recs[chain_num].c_total); wi->last_chain = chain_num; wi->count_free = wi->count_total = 0; } ret = ocfs2_read_group_desc(fs, gd_blkno, wi->gd_buf); if (ret) return OCFS2_CHAIN_ERROR; gd = (struct ocfs2_group_desc *)wi->gd_buf; wi->count_free += gd->bg_free_bits_count; wi->count_total += gd->bg_bits; fprintf(stdout, " %16"PRIu64": %05d/%05d = %05d/%05d\n", gd->bg_blkno, gd->bg_free_bits_count, gd->bg_bits, wi->count_free, wi->count_total); return 0; } extern int opterr, optind; extern char *optarg; int main(int argc, char *argv[]) { errcode_t ret; uint64_t blkno; int c; char *filename, *buf; ocfs2_filesys *fs; struct ocfs2_dinode *di; struct walk_it wi; blkno = OCFS2_SUPER_BLOCK_BLKNO; initialize_ocfs_error_table(); while ((c = getopt(argc, argv, "bei:")) != EOF) { switch (c) { case 'i': blkno = read_number(optarg); if (blkno <= OCFS2_SUPER_BLOCK_BLKNO) { fprintf(stderr, "Invalid inode block: %s\n", optarg); print_usage(); return 1; } break; default: print_usage(); return 1; break; } } if (blkno == OCFS2_SUPER_BLOCK_BLKNO) { fprintf(stderr, "You must specify an inode block\n"); print_usage(); return 1; } if (optind >= argc) { fprintf(stderr, "Missing filename\n"); print_usage(); return 1; } filename = argv[optind]; ret = ocfs2_open(filename, OCFS2_FLAG_RO, 0, 0, &fs); if (ret) { com_err(argv[0], ret, "while opening file \"%s\"", filename); goto out; } ret = ocfs2_malloc_block(fs->fs_io, &buf); if (ret) { com_err(argv[0], ret, "while allocating inode buffer"); goto out_close; } memset(&wi, 0, sizeof(wi)); ret = ocfs2_read_inode(fs, blkno, buf); if (ret) { com_err(argv[0], ret, "while reading inode %"PRIu64, blkno); goto out_free; } di = (struct ocfs2_dinode *)buf; fprintf(stdout, "OCFS2 inode %"PRIu64" on \"%s\"\n", blkno, filename); ret = ocfs2_malloc_block(fs->fs_io, &wi.gd_buf); if (ret) { com_err(argv[0], ret, "while allocating gd buffer"); goto out_free; } wi.di = di; wi.last_chain = -1; ret = ocfs2_chain_iterate(fs, blkno, walk_chain_func, &wi); if (ret) { com_err(argv[0], ret, "while walking extents"); } out_free: if (wi.gd_buf) ocfs2_free(&wi.gd_buf); ocfs2_free(&buf); out_close: ret = ocfs2_close(fs); if (ret) { com_err(argv[0], ret, "while closing file \"%s\"", filename); } out: return 0; } #endif /* DEBUG_EXE */ ocfs2-tools-ocfs2-tools-1.8.6/libocfs2/chainalloc.c000066400000000000000000000600301347147137200220240ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * chainalloc.c * * Functions to use the chain allocators for the OCFS2 userspace * library. * * Copyright (C) 2004 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License, version 2, as published by the Free Software Foundation. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. */ #define _XOPEN_SOURCE 600 /* Triggers magic in features.h */ #define _LARGEFILE64_SOURCE #include #include #include "ocfs2/ocfs2.h" #include "bitmap.h" #include "ocfs2/bitops.h" struct chainalloc_bitmap_private { ocfs2_cached_inode *cb_cinode; errcode_t cb_errcode; int cb_dirty; int cb_suballoc; }; struct chainalloc_region_private { struct chainalloc_bitmap_private *cr_cb; struct ocfs2_group_desc *cr_ag; int cr_dirty; /* In discontiguous group block, it is set as * the bit offset of this region in the whole group. * As for contiguous group, it is set to 0. */ int bit_offset; }; static void chainalloc_destroy_notify(ocfs2_bitmap *bitmap) { struct rb_node *node = NULL; struct ocfs2_bitmap_region *br; struct chainalloc_region_private *cr; for (node = rb_first(&bitmap->b_regions); node; node = rb_next(node)) { br = rb_entry(node, struct ocfs2_bitmap_region, br_node); cr = br->br_private; /* * For discontiguous block group, only destroy * cr_ag once when we meet with the head. */ if (cr->cr_ag && !cr->bit_offset) ocfs2_free(&cr->cr_ag); ocfs2_free(&br->br_private); } ocfs2_free(&bitmap->b_private); } static uint64_t chainalloc_scale_start_bit(ocfs2_filesys *fs, uint64_t blkno, int bpc) { int bitsize = fs->fs_clustersize / bpc; if (bitsize == fs->fs_blocksize) return blkno; if (bitsize < fs->fs_blocksize) return blkno * (fs->fs_blocksize / bitsize); else return blkno / (bitsize / fs->fs_blocksize); } /* * Find the next bitmap_region we could add in the block group. * For contiguous group, the return bits will contains the whole group. * For a discontiguous one, every leaf extent record will become an * individual ocfs2_bitmap_region. So the 'bits' are set properly. */ static void chainalloc_get_next_region(ocfs2_filesys *fs, struct ocfs2_group_desc *gd, struct chainalloc_bitmap_private *cb, uint64_t *start_bit, int bit_offset, int *region_bits, int *set_bits) { int i; int bpc = cb->cb_cinode->ci_inode->id2.i_chain.cl_bpc; uint32_t cpos; uint64_t blkno = gd->bg_blkno; struct ocfs2_extent_rec *rec = NULL; if (!ocfs2_gd_is_discontig(gd) || !gd->bg_list.l_next_free_rec) { /* OK, a contiguous group. */ if (bit_offset) abort(); if (blkno == OCFS2_RAW_SB(fs->fs_super)->s_first_cluster_group) blkno = 0; *start_bit = chainalloc_scale_start_bit(fs, blkno, bpc); *region_bits = gd->bg_bits; *set_bits = gd->bg_bits - gd->bg_free_bits_count; return; } /* handle discontiguous group. */ cpos = bit_offset / bpc; for (i = 0; i < gd->bg_list.l_next_free_rec; i++) { rec = &gd->bg_list.l_recs[i]; if (rec->e_cpos == cpos) break; } if (i == gd->bg_list.l_next_free_rec) abort(); *start_bit = chainalloc_scale_start_bit(fs, rec->e_blkno, bpc); *region_bits = rec->e_leaf_clusters * bpc; *set_bits = ocfs2_get_bits_set(gd->bg_bitmap, bit_offset + *region_bits, bit_offset); return; } /* * Create bitmap regions for the group. * For contiguous group, create one region for the whole group. * For a discontiguous one, every leaf extent record will become an * individual ocfs2_bitmap_region and be inserted. */ static errcode_t create_chainalloc_region(ocfs2_filesys *fs, ocfs2_bitmap *bitmap, struct ocfs2_group_desc *gd, struct chainalloc_bitmap_private *cb) { errcode_t ret = 0; int total_bits = gd->bg_bits; int region_bits = 0, bit_offset = 0, set_bits = 0; uint64_t start_bit; struct chainalloc_region_private *cr = NULL; struct ocfs2_bitmap_region *br = NULL; while (total_bits) { chainalloc_get_next_region(fs, gd, cb, &start_bit, bit_offset, ®ion_bits, &set_bits); ret = ocfs2_malloc0(sizeof(struct chainalloc_region_private), &cr); if (ret) break; cr->cr_cb = cb; cr->cr_ag = gd; cr->bit_offset = bit_offset; /* * In case bit_offset isn't aligned to byte, * We have to alloc/copy some addiontal bits in * the head. */ ret = ocfs2_bitmap_alloc_region(bitmap, start_bit, bit_offset % 8, region_bits, &br); if (ret) break; br->br_private = cr; memcpy(br->br_bitmap, cr->cr_ag->bg_bitmap + bit_offset / 8, br->br_bytes); br->br_set_bits = set_bits; ret = ocfs2_bitmap_insert_region(bitmap, br); if (ret) break; br = NULL; cr = NULL; total_bits -= region_bits; bit_offset += region_bits; } if (br) ocfs2_bitmap_free_region(br); if (cr) ocfs2_free(&cr); return ret; } static int chainalloc_process_group(ocfs2_filesys *fs, uint64_t gd_blkno, int chain_num, void *priv_data) { ocfs2_bitmap *bitmap = priv_data; struct chainalloc_bitmap_private *cb = bitmap->b_private; char *gd_buf; struct ocfs2_group_desc *gd; cb->cb_errcode = ocfs2_malloc_block(fs->fs_io, &gd_buf); if (cb->cb_errcode) return OCFS2_CHAIN_ABORT; cb->cb_errcode = ocfs2_read_group_desc(fs, gd_blkno, gd_buf); if (cb->cb_errcode) goto out_free_buf; gd = (struct ocfs2_group_desc *)gd_buf; cb->cb_errcode = create_chainalloc_region(fs, bitmap, gd, cb); if (cb->cb_errcode) goto out_free_buf; return 0; out_free_buf: ocfs2_free(&gd_buf); return OCFS2_CHAIN_ABORT; } static errcode_t chainalloc_read_bitmap(ocfs2_bitmap *bitmap) { errcode_t ret; struct chainalloc_bitmap_private *cb = bitmap->b_private; if (!cb->cb_cinode) return OCFS2_ET_INVALID_ARGUMENT; ret = ocfs2_chain_iterate(bitmap->b_fs, cb->cb_cinode->ci_blkno, chainalloc_process_group, bitmap); return ret; } static errcode_t chainalloc_write_group(struct ocfs2_bitmap_region *br, void *private_data) { struct chainalloc_region_private *cr = br->br_private; ocfs2_filesys *fs = private_data; errcode_t ret = 0; uint8_t *bm, *gbm; int offset, end; if (!cr->cr_dirty) return 0; if (cr->bit_offset) { /* * Discontiguous block group. * The lower bits of bg_bitmap[0] isn't controled by this br, * so we should copy them from the original group bitmap. */ offset = cr->bit_offset % 8; gbm = &cr->cr_ag->bg_bitmap[cr->bit_offset / 8]; bm = &br->br_bitmap[0]; *bm &= 0xFF << offset; *bm |= *gbm & (0xFF >> (8 - offset)); } if (br->br_total_bits % 8 != 0) { end = cr->bit_offset + br->br_valid_bits; offset = end % 8; gbm = &cr->cr_ag->bg_bitmap[end / 8]; bm = &br->br_bitmap[br->br_total_bits / 8]; *bm &= 0xFF >> (8 - offset); *bm |= *gbm & (0xFF << offset); } memcpy(cr->cr_ag->bg_bitmap + cr->bit_offset / 8, br->br_bitmap, br->br_bytes); ret = ocfs2_write_group_desc(fs, cr->cr_ag->bg_blkno, (char *)cr->cr_ag); if (ret == 0) cr->cr_dirty = 0; return ret; } static errcode_t chainalloc_write_bitmap(ocfs2_bitmap *bitmap) { struct chainalloc_bitmap_private *cb = bitmap->b_private; ocfs2_filesys *fs; errcode_t ret; if (!cb->cb_cinode) return OCFS2_ET_INVALID_ARGUMENT; if (!cb->cb_dirty) return 0; fs = cb->cb_cinode->ci_fs; ret = ocfs2_bitmap_foreach_region(bitmap, chainalloc_write_group, fs); if (ret) goto out; ret = ocfs2_write_cached_inode(fs, cb->cb_cinode); if (ret == 0) cb->cb_dirty = 0; out: return ret; } static int chainalloc_merge_region(ocfs2_bitmap *bitmap, struct ocfs2_bitmap_region *prev, struct ocfs2_bitmap_region *next) { /* Can't merge */ return 0; } /* update the free bit counts in the alloc group, chain rec, and inode so * that they are valid if we're asked to write in the future */ static void chainalloc_bit_change_notify(ocfs2_bitmap *bitmap, struct ocfs2_bitmap_region *br, uint64_t bitno, int new_val) { struct chainalloc_bitmap_private *cb = bitmap->b_private; struct chainalloc_region_private *cr = br->br_private; struct ocfs2_dinode *di = cb->cb_cinode->ci_inode; struct ocfs2_group_desc *ag = cr->cr_ag; struct ocfs2_chain_rec *rec = &di->id2.i_chain.cl_recs[ag->bg_chain]; if (new_val) { ag->bg_free_bits_count--; rec->c_free--; di->id1.bitmap1.i_used++; } else { ag->bg_free_bits_count++; rec->c_free++; di->id1.bitmap1.i_used--; } cr->cr_dirty = 1; cb->cb_dirty = 1; } static struct ocfs2_bitmap_operations chainalloc_bitmap_ops = { .set_bit = ocfs2_bitmap_set_generic, .clear_bit = ocfs2_bitmap_clear_generic, .test_bit = ocfs2_bitmap_test_generic, .find_next_set = ocfs2_bitmap_find_next_set_generic, .find_next_clear = ocfs2_bitmap_find_next_clear_generic, .merge_region = chainalloc_merge_region, .read_bitmap = chainalloc_read_bitmap, .write_bitmap = chainalloc_write_bitmap, .destroy_notify = chainalloc_destroy_notify, .bit_change_notify = chainalloc_bit_change_notify, .alloc_range = ocfs2_bitmap_alloc_range_generic, .clear_range = ocfs2_bitmap_clear_range_generic, }; static errcode_t ocfs2_chainalloc_bitmap_new(ocfs2_filesys *fs, const char *description, uint64_t total_bits, ocfs2_bitmap **ret_bitmap) { errcode_t ret; ocfs2_bitmap *bitmap; struct chainalloc_bitmap_private *cb; ret = ocfs2_malloc0(sizeof(struct chainalloc_bitmap_private), &cb); if (ret) return ret; ret = ocfs2_bitmap_new(fs, total_bits, description ? description : "Generic chain allocator bitmap", &chainalloc_bitmap_ops, cb, &bitmap); if (ret) return ret; *ret_bitmap = bitmap; return 0; } static void ocfs2_chainalloc_set_private(ocfs2_bitmap *bitmap, ocfs2_cached_inode *cinode, uint64_t gb_blkno) { struct chainalloc_bitmap_private *cb = bitmap->b_private; cb->cb_cinode = cinode; cb->cb_suballoc = (gb_blkno != cinode->ci_inode->i_blkno); } errcode_t ocfs2_load_chain_allocator(ocfs2_filesys *fs, ocfs2_cached_inode *cinode) { errcode_t ret; uint64_t total_bits, gb_blkno; char name[256]; if (cinode->ci_chains) ocfs2_bitmap_free(&cinode->ci_chains); total_bits = (uint64_t)fs->fs_clusters * cinode->ci_inode->id2.i_chain.cl_bpc; ret = ocfs2_lookup_system_inode(fs, GLOBAL_BITMAP_SYSTEM_INODE, 0, &gb_blkno); if (ret) return ret; snprintf(name, sizeof(name), "Chain allocator inode %"PRIu64, cinode->ci_blkno); ret = ocfs2_chainalloc_bitmap_new(fs, name, total_bits, &cinode->ci_chains); if (ret) return ret; ocfs2_chainalloc_set_private(cinode->ci_chains, cinode, gb_blkno); ret = ocfs2_bitmap_read(cinode->ci_chains); if (ret) { ocfs2_bitmap_free(&cinode->ci_chains); return ret; } return 0; } errcode_t ocfs2_write_chain_allocator(ocfs2_filesys *fs, ocfs2_cached_inode *cinode) { if (!cinode->ci_chains) return OCFS2_ET_INVALID_ARGUMENT; return ocfs2_bitmap_write(cinode->ci_chains); } /* FIXME: should take a hint, no? */ /* FIXME: Better name, too */ errcode_t ocfs2_chain_alloc_range(ocfs2_filesys *fs, ocfs2_cached_inode *cinode, uint64_t min, uint64_t requested, uint64_t *start_bit, uint64_t *bits_found) { if (!cinode->ci_chains) return OCFS2_ET_INVALID_ARGUMENT; return ocfs2_bitmap_alloc_range(cinode->ci_chains, min, requested, start_bit, bits_found); } errcode_t ocfs2_chain_free_range(ocfs2_filesys *fs, ocfs2_cached_inode *cinode, uint64_t len, uint64_t start_bit) { if (!cinode->ci_chains) return OCFS2_ET_INVALID_ARGUMENT; return ocfs2_bitmap_clear_range(cinode->ci_chains, len, start_bit); } struct find_gd_state { ocfs2_filesys *fs; uint64_t bitno; uint64_t gd_blkno; uint64_t suballoc_bit; int found; }; static errcode_t chainalloc_find_gd(struct ocfs2_bitmap_region *br, void *private_data) { struct chainalloc_region_private *cr = br->br_private; struct find_gd_state *state = private_data; if ((state->bitno >= br->br_start_bit) && (state->bitno < (br->br_start_bit + br->br_valid_bits))) { state->found = 1; state->gd_blkno = cr->cr_ag->bg_blkno; state->suballoc_bit = state->bitno - br->br_start_bit + cr->bit_offset; if (state->gd_blkno == OCFS2_RAW_SB(state->fs->fs_super)->s_first_cluster_group) state->gd_blkno = 0; return OCFS2_ET_ITERATION_COMPLETE; } return 0; } errcode_t ocfs2_chain_alloc(ocfs2_filesys *fs, ocfs2_cached_inode *cinode, uint64_t *gd_blkno, uint16_t *suballoc_bit, uint64_t *bitno) { errcode_t ret; int oldval; struct find_gd_state state; if (!cinode->ci_chains) return OCFS2_ET_INVALID_ARGUMENT; ret = ocfs2_bitmap_find_next_clear(cinode->ci_chains, 0, bitno); if (ret) return ret; ret = ocfs2_bitmap_set(cinode->ci_chains, *bitno, &oldval); if (ret) return ret; if (oldval) return OCFS2_ET_INTERNAL_FAILURE; state = (struct find_gd_state) { .fs = fs, .bitno = *bitno, }; ret = ocfs2_bitmap_foreach_region(cinode->ci_chains, chainalloc_find_gd, &state); if (!ret) { if (state.found) { *gd_blkno = state.gd_blkno; *suballoc_bit = state.suballoc_bit; } else ret = OCFS2_ET_INTERNAL_FAILURE; } return ret; } errcode_t ocfs2_chain_free(ocfs2_filesys *fs, ocfs2_cached_inode *cinode, uint64_t bitno) { errcode_t ret; int oldval; if (!cinode->ci_chains) return OCFS2_ET_INVALID_ARGUMENT; ret = ocfs2_bitmap_clear(cinode->ci_chains, bitno, &oldval); if (ret) return ret; if (!oldval) return OCFS2_ET_FREEING_UNALLOCATED_REGION; return 0; } /* just a variant that won't return failure if it tried to set what * was already set */ errcode_t ocfs2_chain_force_val(ocfs2_filesys *fs, ocfs2_cached_inode *cinode, uint64_t bitno, int newval, int *oldval) { errcode_t ret; if (!cinode->ci_chains) return OCFS2_ET_INVALID_ARGUMENT; if (newval) ret = ocfs2_bitmap_set(cinode->ci_chains, bitno, oldval); else ret = ocfs2_bitmap_clear(cinode->ci_chains, bitno, oldval); return ret; } errcode_t ocfs2_chain_test(ocfs2_filesys *fs, ocfs2_cached_inode *cinode, uint64_t bitno, int *oldval) { if (!cinode->ci_chains) return OCFS2_ET_INVALID_ARGUMENT; return ocfs2_bitmap_test(cinode->ci_chains, bitno, oldval); } void ocfs2_init_group_desc(ocfs2_filesys *fs, struct ocfs2_group_desc *gd, uint64_t blkno, uint32_t generation, uint64_t parent_inode, uint16_t bits, uint16_t chain, int suballoc) { memset(gd, 0, fs->fs_blocksize); strcpy((char *)gd->bg_signature, OCFS2_GROUP_DESC_SIGNATURE); gd->bg_generation = generation; gd->bg_size = ocfs2_group_bitmap_size(fs->fs_blocksize, suballoc, OCFS2_RAW_SB(fs->fs_super)->s_feature_incompat); gd->bg_bits = bits; gd->bg_chain = chain; gd->bg_parent_dinode = parent_inode; gd->bg_blkno = blkno; /* First bit set to account for the descriptor block */ ocfs2_set_bit(0, gd->bg_bitmap); gd->bg_free_bits_count = gd->bg_bits - 1; } errcode_t ocfs2_chain_add_group(ocfs2_filesys *fs, ocfs2_cached_inode *cinode) { errcode_t ret; uint64_t blkno = 0, old_blkno = 0; uint32_t found; uint16_t chain_num; struct ocfs2_group_desc *gd; char *buf = NULL; struct ocfs2_chain_rec *rec = NULL; struct chainalloc_bitmap_private *cb = cinode->ci_chains->b_private; ret = ocfs2_malloc_block(fs->fs_io, &buf); if (ret) return ret; gd = (struct ocfs2_group_desc *)buf; ret = ocfs2_new_clusters(fs, cinode->ci_inode->id2.i_chain.cl_cpg, cinode->ci_inode->id2.i_chain.cl_cpg, &blkno, &found); if (ret) goto out; if (found != cinode->ci_inode->id2.i_chain.cl_cpg) abort(); /* pick chain to add to */ if (cinode->ci_inode->id2.i_chain.cl_next_free_rec < cinode->ci_inode->id2.i_chain.cl_count) chain_num = cinode->ci_inode->id2.i_chain.cl_next_free_rec; else chain_num = (cinode->ci_inode->i_clusters / cinode->ci_inode->id2.i_chain.cl_cpg) % cinode->ci_inode->id2.i_chain.cl_count; ocfs2_init_group_desc(fs, gd, blkno, fs->fs_super->i_fs_generation, cinode->ci_inode->i_blkno, cinode->ci_inode->id2.i_chain.cl_cpg * cinode->ci_inode->id2.i_chain.cl_bpc, chain_num, cb->cb_suballoc); rec = &cinode->ci_inode->id2.i_chain.cl_recs[chain_num]; old_blkno = rec->c_blkno; gd->bg_next_group = old_blkno; ret = ocfs2_write_group_desc(fs, blkno, (char *)gd); if (ret) goto out; /* XXX could be a helper? */ rec->c_free += gd->bg_free_bits_count; rec->c_total += gd->bg_bits; rec->c_blkno = blkno; cinode->ci_inode->i_clusters += cinode->ci_inode->id2.i_chain.cl_cpg; cinode->ci_inode->i_size = (uint64_t)cinode->ci_inode->i_clusters * fs->fs_clustersize; cinode->ci_inode->id1.bitmap1.i_total += gd->bg_bits; cinode->ci_inode->id1.bitmap1.i_used += gd->bg_bits - gd->bg_free_bits_count; if (cinode->ci_inode->id2.i_chain.cl_next_free_rec == chain_num) cinode->ci_inode->id2.i_chain.cl_next_free_rec = chain_num + 1; ret = ocfs2_write_cached_inode(fs, cinode); if (ret) goto out; /* XXX this is probably too clever by half */ ret = chainalloc_process_group(fs, blkno, chain_num, cinode->ci_chains); if (ret) { ret = cb->cb_errcode; goto out; } /* ok, it's official */ blkno = 0; rec = NULL; out: if (rec != NULL) { /* XXX also could be a helper */ rec->c_free -= gd->bg_free_bits_count; rec->c_total -= gd->bg_bits; rec->c_blkno = old_blkno; cinode->ci_inode->i_clusters -= cinode->ci_inode->id2.i_chain.cl_cpg; cinode->ci_inode->i_size = (uint64_t)cinode->ci_inode->i_clusters * fs->fs_clustersize; cinode->ci_inode->id1.bitmap1.i_total -= gd->bg_bits; cinode->ci_inode->id1.bitmap1.i_used -= gd->bg_bits - gd->bg_free_bits_count; if (cinode->ci_inode->id2.i_chain.cl_next_free_rec == (chain_num + 1) && old_blkno == 0) cinode->ci_inode->id2.i_chain.cl_next_free_rec = chain_num; ocfs2_write_cached_inode(fs, cinode); } if (blkno != 0) ocfs2_free_clusters(fs, cinode->ci_inode->id2.i_chain.cl_cpg, blkno); if (buf) ocfs2_free(&buf); return ret; } #ifdef DEBUG_EXE #include #include #include static uint64_t read_number(const char *num) { uint64_t val; char *ptr; val = strtoull(num, &ptr, 0); if (!ptr || *ptr) return 0; return val; } static void print_usage(void) { fprintf(stderr, "debug_bitmap [-i ] \n"); } extern int opterr, optind; extern char *optarg; static void dump_regions(ocfs2_bitmap *bitmap) { struct ocfs2_bitmap_region *br; struct rb_node *node; fprintf(stdout, "Bitmap \"%s\": total = %"PRIu64", set = %"PRIu64"\n", bitmap->b_description, bitmap->b_total_bits, bitmap->b_set_bits); for (node = rb_first(&bitmap->b_regions);node; node = rb_next(node)) { br = rb_entry(node, struct ocfs2_bitmap_region, br_node); fprintf(stdout, "(start: %"PRIu64", n: %d, set: %d)\n", br->br_start_bit, br->br_valid_bits, br->br_set_bits); } } static void print_bitmap(ocfs2_bitmap *bitmap) { uint64_t bitno; uint64_t gap_start = 0; /* GCC is dumb */ errcode_t ret; int val, gap; gap = 0; for (bitno = 0; bitno < bitmap->b_total_bits; bitno++) { ret = ocfs2_bitmap_test(bitmap, bitno, &val); if (ret) { if (ret == OCFS2_ET_INVALID_BIT) { if (!gap) { gap = 1; gap_start = bitno; } continue; } com_err("print_bitmap", ret, "while testing bit %"PRIu64"\n", bitno); break; } if (gap) { fprintf(stdout, "\nGap of length %"PRIu64" at %"PRIu64"\n", bitno - gap_start, gap_start); gap = bitno % 72; gap += gap / 8; for (; gap; gap--) fprintf(stdout, " "); fflush(stdout); } else { if (bitno && !(bitno % 72)) fprintf(stdout, "\n"); else if (bitno && !(bitno % 8)) fprintf(stdout, " "); } fprintf(stdout, "%d", val); fflush(stdout); } if ((bitno - 1) % 72) fprintf(stdout, "\n"); } static int try_op(ocfs2_bitmap *bitmap, errcode_t (*func)(ocfs2_bitmap *bitmap, uint64_t bitno, int *val), char *bit_val, int *ret_val) { errcode_t ret; uint64_t bitno; char *ptr; if (!bit_val) { fprintf(stderr, "You must provide a bit offset\n"); return 1; } bitno = read_number(bit_val); if (!bitno) { for (ptr = bit_val; *ptr; ptr++) { if (*ptr != '0') break; } if ((ptr == bit_val) || *ptr) { fprintf(stderr, "Invalid bit offset: %s\n", bit_val); return 1; } } ret = (*func)(bitmap, bitno, ret_val); if (ret) { com_err("try_op", ret, "while setting bit %"PRIu64"\n", bitno); return 1; } return 0; } static int try_op64(ocfs2_bitmap *bitmap, errcode_t (*func)(ocfs2_bitmap *bitmap, uint64_t bitno, uint64_t *val), char *bit_val, uint64_t *ret_val) { errcode_t ret; uint64_t bitno; char *ptr; if (!bit_val) { fprintf(stderr, "You must provide a bit offset\n"); return 1; } bitno = read_number(bit_val); if (!bitno) { for (ptr = bit_val; *ptr; ptr++) { if (*ptr != '0') break; } if ((ptr == bit_val) || *ptr) { fprintf(stderr, "Invalid bit offset: %s\n", bit_val); return 1; } } ret = (*func)(bitmap, bitno, ret_val); if (ret) { com_err("try_op64", ret, "while setting bit %"PRIu64"\n", bitno); return 1; } return 0; } static void run_test(ocfs2_bitmap *bitmap) { char buf[256]; char *ptr, *cmd; uint64_t val64; int val; while (1) { fprintf(stdout, "Command: "); fflush(stdout); if (!fgets(buf, sizeof(buf), stdin)) break; ptr = buf + strlen(buf) - 1; if (*ptr == '\n') *ptr = '\0'; for (cmd = buf; (*cmd == ' ') || (*cmd == '\t'); cmd++); if (!(*cmd)) continue; ptr = strchr(cmd, ' '); if (ptr) { *ptr = '\0'; ptr++; } if (!strcmp(cmd, "set")) { try_op(bitmap, ocfs2_bitmap_set, ptr, NULL); } else if (!strcmp(cmd, "clear")) { try_op(bitmap, ocfs2_bitmap_clear, ptr, NULL); } else if (!strcmp(cmd, "test")) { if (!try_op(bitmap, ocfs2_bitmap_test, ptr, &val)) { fprintf(stdout, "Bit %s is %s\n", ptr, val ? "set" : "clear"); } } else if (!strcmp(cmd, "fns")) { if (!try_op64(bitmap, ocfs2_bitmap_find_next_set, ptr, &val64)) { fprintf(stdout, "Found %"PRIu64"\n", val64); } } else if (!strcmp(cmd, "fnc")) { if (!try_op64(bitmap, ocfs2_bitmap_find_next_clear, ptr, &val64)) { fprintf(stdout, "Found %"PRIu64"\n", val64); } } else if (!strcmp(cmd, "print")) { print_bitmap(bitmap); } else if (!strcmp(cmd, "dump")) { dump_regions(bitmap); } else if (!strcmp(cmd, "quit")) { break; } else { fprintf(stderr, "Invalid command: \"%s\"\n", cmd); } } } int main(int argc, char *argv[]) { errcode_t ret; int c; char *filename; uint64_t blkno = 0; ocfs2_filesys *fs; ocfs2_cached_inode *cinode; initialize_ocfs_error_table(); while ((c = getopt(argc, argv, "i:")) != EOF) { switch (c) { case 'i': blkno = read_number(optarg); if (!blkno) { print_usage(); return 1; } break; default: print_usage(); return 1; break; } } if (optind >= argc) { fprintf(stderr, "Missing filename\n"); print_usage(); return 1; } filename = argv[optind]; ret = ocfs2_open(filename, OCFS2_FLAG_RO, 0, 0, &fs); if (ret) { com_err(argv[0], ret, "while opening file \"%s\"", filename); return 1; } ret = ocfs2_read_cached_inode(fs, blkno, &cinode); if (ret) { com_err(argv[0], ret, "while reading inode %"PRIu64, blkno); goto out_close; } ret = ocfs2_load_chain_allocator(fs, cinode); if (ret) { com_err(argv[0], ret, "while loading chain allocator"); goto out_free_inode; } run_test(cinode->ci_chains); out_free_inode: ocfs2_free_cached_inode(fs, cinode); out_close: ocfs2_close(fs); return ret; } #endif /* DEBUG_EXE */ ocfs2-tools-ocfs2-tools-1.8.6/libocfs2/checkhb.c000066400000000000000000000100771347147137200213240ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * checkhb.c * * ocfs2 check heartbeat function * * Copyright (C) 2004 Oracle. All rights reserved. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. */ #define _LARGEFILE64_SOURCE #define _GNU_SOURCE /* Because libc really doesn't want us using O_DIRECT? */ #include #include #include #include #include #include #include #include #include #include "ocfs2/byteorder.h" #include "ocfs2/ocfs2.h" #include "ocfs2-kernel/ocfs1_fs_compat.h" /* * ocfs2_check_heartbeats() check if the list of ocfs2 devices are * mounted on the cluster or not * * Return: * mounted_flags set to ==> * OCFS2_MF_MOUNTED if mounted locally * OCFS2_MF_ISROOT * OCFS2_MF_READONLY * OCFS2_MF_SWAP * OCFS2_MF_BUSY * OCFS2_MF_MOUNTED_CLUSTER if mounted on cluster */ errcode_t ocfs2_check_heartbeats(struct list_head *dev_list, int ignore_local) { ocfs2_filesys *fs = NULL; struct ocfs2_super_block *osb; errcode_t ret = 0; struct list_head *pos; ocfs2_devices *dev = NULL; char *device= NULL; int open_flags, i; list_for_each(pos, dev_list) { dev = list_entry(pos, ocfs2_devices, list); device = dev->dev_name; /* open fs */ fs = NULL; open_flags = OCFS2_FLAG_RO | OCFS2_FLAG_HEARTBEAT_DEV_OK; ret = ocfs2_open(device, open_flags, 0, 0, &fs); if (ret) { ret = 0; continue; } else dev->fs_type = 2; osb = OCFS2_RAW_SB(fs->fs_super); if (OCFS2_HAS_INCOMPAT_FEATURE(osb, OCFS2_FEATURE_INCOMPAT_HEARTBEAT_DEV)) dev->hb_dev = 1; /* is it locally mounted */ if (!ignore_local || !dev->hb_dev) { ret = ocfs2_check_mount_point(device, &dev->mount_flags, NULL, 0); if (ret) goto bail; } /* get label/uuid for ocfs2 */ memcpy(dev->label, osb->s_label, sizeof(dev->label)); memcpy(dev->uuid, osb->s_uuid, sizeof(dev->uuid)); if (ocfs2_mount_local(fs)) snprintf(dev->stack, sizeof(dev->stack), "%s", "None"); else if (ocfs2_clusterinfo_valid(osb)) { snprintf(dev->stack, sizeof(dev->stack), "%.*s", OCFS2_STACK_LABEL_LEN, osb->s_cluster_info.ci_stack); snprintf(dev->cluster, sizeof(dev->cluster), "%.*s", OCFS2_CLUSTER_NAME_LEN, osb->s_cluster_info.ci_cluster); dev->stackflags = osb->s_cluster_info.ci_stackflags; } else snprintf(dev->stack, sizeof(dev->stack), "%s", OCFS2_CLASSIC_CLUSTER_STACK); if (dev->hb_dev) goto close; /* read slotmap to get nodes on which the volume is mounted */ ret = ocfs2_load_slot_map(fs, &dev->map); if (ret) { dev->errcode = ret; ret = 0; } else { for (i = 0; i < dev->map->md_num_slots; i++) { if (dev->map->md_slots[i].sd_valid) { dev->mount_flags |= OCFS2_MF_MOUNTED_CLUSTER; break; } } } close: ocfs2_close(fs); } bail: return ret; } /* * ocfs2_get_ocfs1_label() * */ errcode_t ocfs2_get_ocfs1_label(char *device, uint8_t *label, uint16_t label_len, uint8_t *uuid, uint16_t uuid_len) { int fd = -1; int ret = OCFS2_ET_IO; char buf[512]; struct ocfs1_vol_label *v1_lbl; fd = open64(device, O_RDONLY); if (fd == -1) goto bail; if (pread(fd, buf, sizeof(buf), 512) == -1) goto bail; v1_lbl = (struct ocfs1_vol_label *)buf; memcpy(label, v1_lbl->label, label_len); memcpy(uuid, v1_lbl->vol_id, uuid_len); ret = 0; bail: if (fd >= 0) close(fd); return ret; } ocfs2-tools-ocfs2-tools-1.8.6/libocfs2/closefs.c000066400000000000000000000031771347147137200213760ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * closefs.c * * Close an OCFS2 filesystem. Part of the OCFS2 userspace library. * * Copyright (C) 2004 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License, version 2, as published by the Free Software Foundation. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * * Ideas taken from e2fsprogs/lib/ext2fs/closefs.c * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o. */ #define _XOPEN_SOURCE 600 /* Triggers XOPEN2K in features.h */ #define _LARGEFILE64_SOURCE #include "ocfs2/ocfs2.h" errcode_t ocfs2_flush(ocfs2_filesys *fs) { int type; errcode_t ret; for (type = 0; type < MAXQUOTAS; type++) if (fs->qinfo[type].flags & OCFS2_QF_INFO_DIRTY) { ret = ocfs2_write_global_quota_info(fs, type); if (ret) return ret; ret = ocfs2_write_cached_inode(fs, fs->qinfo[type].qi_inode); if (ret) return ret; } return 0; } errcode_t ocfs2_close(ocfs2_filesys *fs) { errcode_t ret; if (fs->fs_flags & OCFS2_FLAG_DIRTY) { ret = ocfs2_flush(fs); if (ret) return ret; } ocfs2_freefs(fs); return 0; } ocfs2-tools-ocfs2-tools-1.8.6/libocfs2/crc32table.h000066400000000000000000000243361347147137200216710ustar00rootroot00000000000000/* this file is generated - do not edit */ /* * This file is generated in the kernel sources by lib/gen_crc32table.c. * The following includes and defines are for our usage. */ #include #include #if __BYTE_ORDER == __LITTLE_ENDIAN # define tole(x) ((uint32_t)(x)) # define tobe(x) ((uint32_t)__bswap_constant_32(x)) #elif __BYTE_ORDER == __BIG_ENDIAN # define tole(x) ((uint32_t)__bswap_constant_32(x)) # define tobe(x) ((uint32_t)(x)) #else # error Invalid byte order __BYTE_ORDER #endif static const uint32_t crc32table_le[] = { tole(0x00000000L), tole(0x77073096L), tole(0xee0e612cL), tole(0x990951baL), tole(0x076dc419L), tole(0x706af48fL), tole(0xe963a535L), tole(0x9e6495a3L), tole(0x0edb8832L), tole(0x79dcb8a4L), tole(0xe0d5e91eL), tole(0x97d2d988L), tole(0x09b64c2bL), tole(0x7eb17cbdL), tole(0xe7b82d07L), tole(0x90bf1d91L), tole(0x1db71064L), tole(0x6ab020f2L), tole(0xf3b97148L), tole(0x84be41deL), tole(0x1adad47dL), tole(0x6ddde4ebL), tole(0xf4d4b551L), tole(0x83d385c7L), tole(0x136c9856L), tole(0x646ba8c0L), tole(0xfd62f97aL), tole(0x8a65c9ecL), tole(0x14015c4fL), tole(0x63066cd9L), tole(0xfa0f3d63L), tole(0x8d080df5L), tole(0x3b6e20c8L), tole(0x4c69105eL), tole(0xd56041e4L), tole(0xa2677172L), tole(0x3c03e4d1L), tole(0x4b04d447L), tole(0xd20d85fdL), tole(0xa50ab56bL), tole(0x35b5a8faL), tole(0x42b2986cL), tole(0xdbbbc9d6L), tole(0xacbcf940L), tole(0x32d86ce3L), tole(0x45df5c75L), tole(0xdcd60dcfL), tole(0xabd13d59L), tole(0x26d930acL), tole(0x51de003aL), tole(0xc8d75180L), tole(0xbfd06116L), tole(0x21b4f4b5L), tole(0x56b3c423L), tole(0xcfba9599L), tole(0xb8bda50fL), tole(0x2802b89eL), tole(0x5f058808L), tole(0xc60cd9b2L), tole(0xb10be924L), tole(0x2f6f7c87L), tole(0x58684c11L), tole(0xc1611dabL), tole(0xb6662d3dL), tole(0x76dc4190L), tole(0x01db7106L), tole(0x98d220bcL), tole(0xefd5102aL), tole(0x71b18589L), tole(0x06b6b51fL), tole(0x9fbfe4a5L), tole(0xe8b8d433L), tole(0x7807c9a2L), tole(0x0f00f934L), tole(0x9609a88eL), tole(0xe10e9818L), tole(0x7f6a0dbbL), tole(0x086d3d2dL), tole(0x91646c97L), tole(0xe6635c01L), tole(0x6b6b51f4L), tole(0x1c6c6162L), tole(0x856530d8L), tole(0xf262004eL), tole(0x6c0695edL), tole(0x1b01a57bL), tole(0x8208f4c1L), tole(0xf50fc457L), tole(0x65b0d9c6L), tole(0x12b7e950L), tole(0x8bbeb8eaL), tole(0xfcb9887cL), tole(0x62dd1ddfL), tole(0x15da2d49L), tole(0x8cd37cf3L), tole(0xfbd44c65L), tole(0x4db26158L), tole(0x3ab551ceL), tole(0xa3bc0074L), tole(0xd4bb30e2L), tole(0x4adfa541L), tole(0x3dd895d7L), tole(0xa4d1c46dL), tole(0xd3d6f4fbL), tole(0x4369e96aL), tole(0x346ed9fcL), tole(0xad678846L), tole(0xda60b8d0L), tole(0x44042d73L), tole(0x33031de5L), tole(0xaa0a4c5fL), tole(0xdd0d7cc9L), tole(0x5005713cL), tole(0x270241aaL), tole(0xbe0b1010L), tole(0xc90c2086L), tole(0x5768b525L), tole(0x206f85b3L), tole(0xb966d409L), tole(0xce61e49fL), tole(0x5edef90eL), tole(0x29d9c998L), tole(0xb0d09822L), tole(0xc7d7a8b4L), tole(0x59b33d17L), tole(0x2eb40d81L), tole(0xb7bd5c3bL), tole(0xc0ba6cadL), tole(0xedb88320L), tole(0x9abfb3b6L), tole(0x03b6e20cL), tole(0x74b1d29aL), tole(0xead54739L), tole(0x9dd277afL), tole(0x04db2615L), tole(0x73dc1683L), tole(0xe3630b12L), tole(0x94643b84L), tole(0x0d6d6a3eL), tole(0x7a6a5aa8L), tole(0xe40ecf0bL), tole(0x9309ff9dL), tole(0x0a00ae27L), tole(0x7d079eb1L), tole(0xf00f9344L), tole(0x8708a3d2L), tole(0x1e01f268L), tole(0x6906c2feL), tole(0xf762575dL), tole(0x806567cbL), tole(0x196c3671L), tole(0x6e6b06e7L), tole(0xfed41b76L), tole(0x89d32be0L), tole(0x10da7a5aL), tole(0x67dd4accL), tole(0xf9b9df6fL), tole(0x8ebeeff9L), tole(0x17b7be43L), tole(0x60b08ed5L), tole(0xd6d6a3e8L), tole(0xa1d1937eL), tole(0x38d8c2c4L), tole(0x4fdff252L), tole(0xd1bb67f1L), tole(0xa6bc5767L), tole(0x3fb506ddL), tole(0x48b2364bL), tole(0xd80d2bdaL), tole(0xaf0a1b4cL), tole(0x36034af6L), tole(0x41047a60L), tole(0xdf60efc3L), tole(0xa867df55L), tole(0x316e8eefL), tole(0x4669be79L), tole(0xcb61b38cL), tole(0xbc66831aL), tole(0x256fd2a0L), tole(0x5268e236L), tole(0xcc0c7795L), tole(0xbb0b4703L), tole(0x220216b9L), tole(0x5505262fL), tole(0xc5ba3bbeL), tole(0xb2bd0b28L), tole(0x2bb45a92L), tole(0x5cb36a04L), tole(0xc2d7ffa7L), tole(0xb5d0cf31L), tole(0x2cd99e8bL), tole(0x5bdeae1dL), tole(0x9b64c2b0L), tole(0xec63f226L), tole(0x756aa39cL), tole(0x026d930aL), tole(0x9c0906a9L), tole(0xeb0e363fL), tole(0x72076785L), tole(0x05005713L), tole(0x95bf4a82L), tole(0xe2b87a14L), tole(0x7bb12baeL), tole(0x0cb61b38L), tole(0x92d28e9bL), tole(0xe5d5be0dL), tole(0x7cdcefb7L), tole(0x0bdbdf21L), tole(0x86d3d2d4L), tole(0xf1d4e242L), tole(0x68ddb3f8L), tole(0x1fda836eL), tole(0x81be16cdL), tole(0xf6b9265bL), tole(0x6fb077e1L), tole(0x18b74777L), tole(0x88085ae6L), tole(0xff0f6a70L), tole(0x66063bcaL), tole(0x11010b5cL), tole(0x8f659effL), tole(0xf862ae69L), tole(0x616bffd3L), tole(0x166ccf45L), tole(0xa00ae278L), tole(0xd70dd2eeL), tole(0x4e048354L), tole(0x3903b3c2L), tole(0xa7672661L), tole(0xd06016f7L), tole(0x4969474dL), tole(0x3e6e77dbL), tole(0xaed16a4aL), tole(0xd9d65adcL), tole(0x40df0b66L), tole(0x37d83bf0L), tole(0xa9bcae53L), tole(0xdebb9ec5L), tole(0x47b2cf7fL), tole(0x30b5ffe9L), tole(0xbdbdf21cL), tole(0xcabac28aL), tole(0x53b39330L), tole(0x24b4a3a6L), tole(0xbad03605L), tole(0xcdd70693L), tole(0x54de5729L), tole(0x23d967bfL), tole(0xb3667a2eL), tole(0xc4614ab8L), tole(0x5d681b02L), tole(0x2a6f2b94L), tole(0xb40bbe37L), tole(0xc30c8ea1L), tole(0x5a05df1bL), tole(0x2d02ef8dL) }; static const uint32_t crc32table_be[] = { tobe(0x00000000L), tobe(0x04c11db7L), tobe(0x09823b6eL), tobe(0x0d4326d9L), tobe(0x130476dcL), tobe(0x17c56b6bL), tobe(0x1a864db2L), tobe(0x1e475005L), tobe(0x2608edb8L), tobe(0x22c9f00fL), tobe(0x2f8ad6d6L), tobe(0x2b4bcb61L), tobe(0x350c9b64L), tobe(0x31cd86d3L), tobe(0x3c8ea00aL), tobe(0x384fbdbdL), tobe(0x4c11db70L), tobe(0x48d0c6c7L), tobe(0x4593e01eL), tobe(0x4152fda9L), tobe(0x5f15adacL), tobe(0x5bd4b01bL), tobe(0x569796c2L), tobe(0x52568b75L), tobe(0x6a1936c8L), tobe(0x6ed82b7fL), tobe(0x639b0da6L), tobe(0x675a1011L), tobe(0x791d4014L), tobe(0x7ddc5da3L), tobe(0x709f7b7aL), tobe(0x745e66cdL), tobe(0x9823b6e0L), tobe(0x9ce2ab57L), tobe(0x91a18d8eL), tobe(0x95609039L), tobe(0x8b27c03cL), tobe(0x8fe6dd8bL), tobe(0x82a5fb52L), tobe(0x8664e6e5L), tobe(0xbe2b5b58L), tobe(0xbaea46efL), tobe(0xb7a96036L), tobe(0xb3687d81L), tobe(0xad2f2d84L), tobe(0xa9ee3033L), tobe(0xa4ad16eaL), tobe(0xa06c0b5dL), tobe(0xd4326d90L), tobe(0xd0f37027L), tobe(0xddb056feL), tobe(0xd9714b49L), tobe(0xc7361b4cL), tobe(0xc3f706fbL), tobe(0xceb42022L), tobe(0xca753d95L), tobe(0xf23a8028L), tobe(0xf6fb9d9fL), tobe(0xfbb8bb46L), tobe(0xff79a6f1L), tobe(0xe13ef6f4L), tobe(0xe5ffeb43L), tobe(0xe8bccd9aL), tobe(0xec7dd02dL), tobe(0x34867077L), tobe(0x30476dc0L), tobe(0x3d044b19L), tobe(0x39c556aeL), tobe(0x278206abL), tobe(0x23431b1cL), tobe(0x2e003dc5L), tobe(0x2ac12072L), tobe(0x128e9dcfL), tobe(0x164f8078L), tobe(0x1b0ca6a1L), tobe(0x1fcdbb16L), tobe(0x018aeb13L), tobe(0x054bf6a4L), tobe(0x0808d07dL), tobe(0x0cc9cdcaL), tobe(0x7897ab07L), tobe(0x7c56b6b0L), tobe(0x71159069L), tobe(0x75d48ddeL), tobe(0x6b93dddbL), tobe(0x6f52c06cL), tobe(0x6211e6b5L), tobe(0x66d0fb02L), tobe(0x5e9f46bfL), tobe(0x5a5e5b08L), tobe(0x571d7dd1L), tobe(0x53dc6066L), tobe(0x4d9b3063L), tobe(0x495a2dd4L), tobe(0x44190b0dL), tobe(0x40d816baL), tobe(0xaca5c697L), tobe(0xa864db20L), tobe(0xa527fdf9L), tobe(0xa1e6e04eL), tobe(0xbfa1b04bL), tobe(0xbb60adfcL), tobe(0xb6238b25L), tobe(0xb2e29692L), tobe(0x8aad2b2fL), tobe(0x8e6c3698L), tobe(0x832f1041L), tobe(0x87ee0df6L), tobe(0x99a95df3L), tobe(0x9d684044L), tobe(0x902b669dL), tobe(0x94ea7b2aL), tobe(0xe0b41de7L), tobe(0xe4750050L), tobe(0xe9362689L), tobe(0xedf73b3eL), tobe(0xf3b06b3bL), tobe(0xf771768cL), tobe(0xfa325055L), tobe(0xfef34de2L), tobe(0xc6bcf05fL), tobe(0xc27dede8L), tobe(0xcf3ecb31L), tobe(0xcbffd686L), tobe(0xd5b88683L), tobe(0xd1799b34L), tobe(0xdc3abdedL), tobe(0xd8fba05aL), tobe(0x690ce0eeL), tobe(0x6dcdfd59L), tobe(0x608edb80L), tobe(0x644fc637L), tobe(0x7a089632L), tobe(0x7ec98b85L), tobe(0x738aad5cL), tobe(0x774bb0ebL), tobe(0x4f040d56L), tobe(0x4bc510e1L), tobe(0x46863638L), tobe(0x42472b8fL), tobe(0x5c007b8aL), tobe(0x58c1663dL), tobe(0x558240e4L), tobe(0x51435d53L), tobe(0x251d3b9eL), tobe(0x21dc2629L), tobe(0x2c9f00f0L), tobe(0x285e1d47L), tobe(0x36194d42L), tobe(0x32d850f5L), tobe(0x3f9b762cL), tobe(0x3b5a6b9bL), tobe(0x0315d626L), tobe(0x07d4cb91L), tobe(0x0a97ed48L), tobe(0x0e56f0ffL), tobe(0x1011a0faL), tobe(0x14d0bd4dL), tobe(0x19939b94L), tobe(0x1d528623L), tobe(0xf12f560eL), tobe(0xf5ee4bb9L), tobe(0xf8ad6d60L), tobe(0xfc6c70d7L), tobe(0xe22b20d2L), tobe(0xe6ea3d65L), tobe(0xeba91bbcL), tobe(0xef68060bL), tobe(0xd727bbb6L), tobe(0xd3e6a601L), tobe(0xdea580d8L), tobe(0xda649d6fL), tobe(0xc423cd6aL), tobe(0xc0e2d0ddL), tobe(0xcda1f604L), tobe(0xc960ebb3L), tobe(0xbd3e8d7eL), tobe(0xb9ff90c9L), tobe(0xb4bcb610L), tobe(0xb07daba7L), tobe(0xae3afba2L), tobe(0xaafbe615L), tobe(0xa7b8c0ccL), tobe(0xa379dd7bL), tobe(0x9b3660c6L), tobe(0x9ff77d71L), tobe(0x92b45ba8L), tobe(0x9675461fL), tobe(0x8832161aL), tobe(0x8cf30badL), tobe(0x81b02d74L), tobe(0x857130c3L), tobe(0x5d8a9099L), tobe(0x594b8d2eL), tobe(0x5408abf7L), tobe(0x50c9b640L), tobe(0x4e8ee645L), tobe(0x4a4ffbf2L), tobe(0x470cdd2bL), tobe(0x43cdc09cL), tobe(0x7b827d21L), tobe(0x7f436096L), tobe(0x7200464fL), tobe(0x76c15bf8L), tobe(0x68860bfdL), tobe(0x6c47164aL), tobe(0x61043093L), tobe(0x65c52d24L), tobe(0x119b4be9L), tobe(0x155a565eL), tobe(0x18197087L), tobe(0x1cd86d30L), tobe(0x029f3d35L), tobe(0x065e2082L), tobe(0x0b1d065bL), tobe(0x0fdc1becL), tobe(0x3793a651L), tobe(0x3352bbe6L), tobe(0x3e119d3fL), tobe(0x3ad08088L), tobe(0x2497d08dL), tobe(0x2056cd3aL), tobe(0x2d15ebe3L), tobe(0x29d4f654L), tobe(0xc5a92679L), tobe(0xc1683bceL), tobe(0xcc2b1d17L), tobe(0xc8ea00a0L), tobe(0xd6ad50a5L), tobe(0xd26c4d12L), tobe(0xdf2f6bcbL), tobe(0xdbee767cL), tobe(0xe3a1cbc1L), tobe(0xe760d676L), tobe(0xea23f0afL), tobe(0xeee2ed18L), tobe(0xf0a5bd1dL), tobe(0xf464a0aaL), tobe(0xf9278673L), tobe(0xfde69bc4L), tobe(0x89b8fd09L), tobe(0x8d79e0beL), tobe(0x803ac667L), tobe(0x84fbdbd0L), tobe(0x9abc8bd5L), tobe(0x9e7d9662L), tobe(0x933eb0bbL), tobe(0x97ffad0cL), tobe(0xafb010b1L), tobe(0xab710d06L), tobe(0xa6322bdfL), tobe(0xa2f33668L), tobe(0xbcb4666dL), tobe(0xb8757bdaL), tobe(0xb5365d03L), tobe(0xb1f740b4L) }; ocfs2-tools-ocfs2-tools-1.8.6/libocfs2/dir_indexed.c000066400000000000000000001053761347147137200222220ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * Copyright (C) 2009, 2010 Novell. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License, version 2, as published by the Free Software Foundation. * * 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. */ #include #include #include #include #include "ocfs2_err.h" #include "extent_tree.h" errcode_t ocfs2_dx_dir_truncate(ocfs2_filesys *fs, uint64_t dir) { struct ocfs2_dx_root_block *dx_root; char *dx_root_buf = NULL, *di_buf = NULL; struct ocfs2_dinode *di; uint64_t dx_root_blk; errcode_t ret = 0; ret = ocfs2_malloc_block(fs->fs_io, &di_buf); if (ret) goto out; ret = ocfs2_read_inode(fs, dir, di_buf); if (ret) goto out; di = (struct ocfs2_dinode *)di_buf; /* we have to trust i_dyn_features */ if (!S_ISDIR(di->i_mode) || !ocfs2_dir_indexed(di) || di->i_dyn_features & OCFS2_INLINE_DATA_FL) goto out; dx_root_blk = di->i_dx_root; di->i_dyn_features &= ~OCFS2_INDEXED_DIR_FL; di->i_dx_root = 0; /* update inode firstly */ ret = ocfs2_write_inode(fs, di->i_blkno, (char *)di); if (ret) goto out; /* inode is updated, the rested errors are not fatal */ ret = ocfs2_malloc_block(fs->fs_io, &dx_root_buf); if (ret) goto out; ret = ocfs2_read_dx_root(fs, dx_root_blk, dx_root_buf); if (ret) goto out; dx_root = (struct ocfs2_dx_root_block *)dx_root_buf; if (dx_root->dr_flags & OCFS2_DX_FLAG_INLINE) goto remove_index; ret = ocfs2_dir_indexed_tree_truncate(fs, dx_root); /* * even ocfs2_dir_indexed_tree_truncate() failed, * we still want to call ocfs2_delete_dx_root(). */ remove_index: ret = ocfs2_delete_dx_root(fs, dx_root->dr_blkno); out: if (di_buf) ocfs2_free(&di_buf); if (dx_root_buf) ocfs2_free(&dx_root_buf); return ret; } static unsigned int ocfs2_figure_dirent_hole(struct ocfs2_dir_entry *de) { unsigned int hole; if (de->inode == 0) hole = de->rec_len; else hole = de->rec_len - OCFS2_DIR_REC_LEN(de->name_len); return hole; } int ocfs2_find_max_rec_len(ocfs2_filesys *fs, char *buf) { int size, this_hole, largest_hole = 0; char *de_buf, *limit; struct ocfs2_dir_entry *de; size = ocfs2_dir_trailer_blk_off(fs); limit = buf + size; de_buf = buf; de = (struct ocfs2_dir_entry *)de_buf; do { this_hole = ocfs2_figure_dirent_hole(de); if (this_hole > largest_hole) largest_hole = this_hole; de_buf += de->rec_len; de = (struct ocfs2_dir_entry *)de_buf; } while (de_buf < limit); if (largest_hole >= OCFS2_DIR_MIN_REC_LEN) return largest_hole; return 0; } struct trailer_ctxt { struct ocfs2_dx_root_block *dx_root; struct ocfs2_dinode *di; errcode_t err; }; /* make sure the space for trailer is reserved */ static errcode_t ocfs2_check_dir_trailer_space(ocfs2_filesys *fs, struct ocfs2_dinode *di, uint64_t blkno, char *blk) { errcode_t ret = 0; struct ocfs2_dir_entry *dirent; unsigned int offset = 0; unsigned int toff = ocfs2_dir_trailer_blk_off(fs); unsigned int real_rec_len = 0; while(offset < fs->fs_blocksize) { dirent = (struct ocfs2_dir_entry *)(blk + offset); if (!ocfs2_check_dir_entry(fs, dirent, blk, offset)) { ret = OCFS2_ET_DIR_CORRUPTED; break; } real_rec_len = dirent->inode ? OCFS2_DIR_REC_LEN(dirent->name_len) : OCFS2_DIR_REC_LEN(1); if ((offset + real_rec_len) <= toff) goto next; if (dirent->inode) { ret = OCFS2_ET_DIR_NO_SPACE; break; } next: offset += dirent->rec_len; } return ret; } static int dir_trailer_func(ocfs2_filesys *fs, uint64_t blkno, uint64_t bcount, uint16_t ext_flags, void *priv_data) { struct trailer_ctxt *ctxt = (struct trailer_ctxt *)priv_data; struct ocfs2_dinode *di = ctxt->di; struct ocfs2_dx_root_block *dx_root = ctxt->dx_root; struct ocfs2_dir_block_trailer *trailer; int max_rec_len = 0, ret = 0; errcode_t err; char *blk = NULL; ret = ocfs2_malloc_block(fs->fs_io, &blk); if (ret) goto out; /* here we don't trust trailer, cannot use * ocfs2_read_dir_block() */ err = ocfs2_read_blocks(fs, blkno, 1, blk); if (err) { ctxt->err = err; ret = OCFS2_EXTENT_ERROR; goto out; } ocfs2_swap_dir_entries_to_cpu(blk, fs->fs_blocksize); err = ocfs2_check_dir_trailer_space(fs, di, blkno, blk); if (err) { ctxt->err = err; ret = OCFS2_EXTENT_ERROR; goto out; } ocfs2_init_dir_trailer(fs, di, blkno, blk); max_rec_len = ocfs2_find_max_rec_len(fs, blk); trailer = ocfs2_dir_trailer_from_block(fs, blk); trailer->db_free_rec_len = max_rec_len; if (max_rec_len) { trailer->db_free_next = dx_root->dr_free_blk; dx_root->dr_free_blk = blkno; } /* comput trailer->db_check here, after writes out, * trailer is trustable */ err = ocfs2_write_dir_block(fs, di, blkno, blk); if (err) { ctxt->err = err; ret = OCFS2_EXTENT_ERROR; } out: if (blk) ocfs2_free(&blk); return ret; } static errcode_t ocfs2_init_dir_trailers(ocfs2_filesys *fs, struct ocfs2_dinode *di, struct ocfs2_dx_root_block *dx_root) { errcode_t ret = 0; struct trailer_ctxt ctxt; if (di->i_dyn_features & OCFS2_INLINE_DATA_FL) { ret = OCFS2_ET_INODE_NOT_VALID; goto out; } ctxt.di = di; ctxt.dx_root = dx_root; ctxt.err = 0; ret = ocfs2_block_iterate_inode(fs, di, 0, dir_trailer_func, &ctxt); /* callback dir_trailer_func() may have error which can not * return to its caller directly. If dir_trailer_func() sets * error in ctxt.err, we should take this REAL error other * than the value returned by ocfs2_block_iterate_inode(). */ if (ctxt.err) ret = ctxt.err; out: return ret; } static void ocfs2_dx_entry_list_insert(struct ocfs2_dx_entry_list *entry_list, struct ocfs2_dx_hinfo *hinfo, uint64_t dirent_blk) { int i; struct ocfs2_dx_entry *dx_entry; i = entry_list->de_num_used; dx_entry = &entry_list->de_entries[i]; memset(dx_entry, 0, sizeof(struct ocfs2_dx_entry)); dx_entry->dx_major_hash = hinfo->major_hash; dx_entry->dx_minor_hash = hinfo->minor_hash; dx_entry->dx_dirent_blk = dirent_blk; entry_list->de_num_used += 1; } struct dx_insert_ctxt { uint64_t dir_blkno; uint64_t dx_root_blkno; ocfs2_filesys *fs; errcode_t err; }; inline static int ocfs2_inline_dx_has_space(struct ocfs2_dx_root_block *dx_root) { struct ocfs2_dx_entry_list *entry_list; entry_list = &dx_root->dr_entries; if (entry_list->de_num_used >= entry_list->de_count) return 0; return 1; } static struct ocfs2_dx_leaf **ocfs2_dx_dir_alloc_leaves(ocfs2_filesys *fs, int *ret_num_leaves) { errcode_t num_dx_leaves = ocfs2_clusters_to_blocks(fs, 1); char **dx_leaves_buf = NULL; dx_leaves_buf = calloc(num_dx_leaves, sizeof (void *)); if (dx_leaves_buf && ret_num_leaves) *ret_num_leaves = num_dx_leaves; return (struct ocfs2_dx_leaf **)dx_leaves_buf; } static errcode_t ocfs2_dx_dir_format_cluster(ocfs2_filesys *fs, struct ocfs2_dx_leaf **dx_leaves, int num_dx_leaves, uint64_t start_blk) { errcode_t ret; int i; struct ocfs2_dx_leaf *dx_leaf; char *blk; for (i = 0; i < num_dx_leaves; i++) { ret = ocfs2_malloc_block(fs->fs_io, &blk); if (ret) goto out; dx_leaves[i] = (struct ocfs2_dx_leaf *)blk; dx_leaf = (struct ocfs2_dx_leaf *)blk; memset(dx_leaf, 0, fs->fs_blocksize); strcpy((char *)dx_leaf->dl_signature, OCFS2_DX_LEAF_SIGNATURE); dx_leaf->dl_fs_generation = fs->fs_super->i_fs_generation; dx_leaf->dl_blkno = start_blk + i; dx_leaf->dl_list.de_count = ocfs2_dx_entries_per_leaf(fs->fs_blocksize); ret = ocfs2_write_dx_leaf(fs, dx_leaf->dl_blkno, dx_leaf); if (ret) goto out; } ret = 0; out: return ret; } static inline unsigned int __ocfs2_dx_dir_hash_idx(ocfs2_filesys *fs, uint32_t minor_hash) { unsigned int cbits, bbits, dx_mask; cbits = OCFS2_RAW_SB(fs->fs_super)->s_clustersize_bits; bbits = OCFS2_RAW_SB(fs->fs_super)->s_blocksize_bits; dx_mask = (1 << (cbits - bbits)) -1; return (minor_hash & dx_mask); } static inline unsigned int ocfs2_dx_dir_hash_idx(ocfs2_filesys *fs, struct ocfs2_dx_hinfo *hinfo) { return __ocfs2_dx_dir_hash_idx(fs, hinfo->minor_hash); } static void ocfs2_dx_dir_leaf_insert_tail(struct ocfs2_dx_leaf *dx_leaf, struct ocfs2_dx_entry *dx_new_entry) { int i; i = dx_leaf->dl_list.de_num_used; dx_leaf->dl_list.de_entries[i] = *dx_new_entry; dx_leaf->dl_list.de_num_used += 1; } static errcode_t ocfs2_expand_inline_dx_root(ocfs2_filesys *fs, struct ocfs2_dx_root_block *dx_root) { errcode_t ret; int num_dx_leaves, i, j; uint64_t start_blkno = 0; uint32_t clusters_found = 0; struct ocfs2_dx_leaf **dx_leaves = NULL; struct ocfs2_dx_leaf *target_leaf; struct ocfs2_dx_entry_list *entry_list; struct ocfs2_extent_tree et; struct ocfs2_dx_entry *dx_entry; dx_leaves = ocfs2_dx_dir_alloc_leaves(fs, &num_dx_leaves); if (!dx_leaves) { ret = OCFS2_ET_NO_MEMORY; goto out; } ret = ocfs2_new_clusters(fs, 1, 1, &start_blkno, &clusters_found); if (ret) goto out; assert(clusters_found == 1); ret = ocfs2_dx_dir_format_cluster(fs, dx_leaves, num_dx_leaves, start_blkno); if (ret) goto out; /* * Transfer the entries from inline dx_root into the appropriate * block */ entry_list = &dx_root->dr_entries; for (i = 0; i < entry_list->de_num_used; i++) { dx_entry = &entry_list->de_entries[i]; j = __ocfs2_dx_dir_hash_idx(fs, dx_entry->dx_minor_hash); target_leaf = (struct ocfs2_dx_leaf *)dx_leaves[j]; ocfs2_dx_dir_leaf_insert_tail(target_leaf, dx_entry); } /* * Write out all leaves. * If ocfs2_write_dx_leaf() failed, since dx_root is not cleared * yet, and the leaves are not inserted into indexed tree yet, * this cluster will be recoganized as orphan in blocks scan of * fsck.ocfs2 */ for (i = 0; i < num_dx_leaves; i ++) { target_leaf = (struct ocfs2_dx_leaf *)dx_leaves[i]; ret = ocfs2_write_dx_leaf(fs, target_leaf->dl_blkno, target_leaf); if (ret) goto out; } dx_root->dr_flags &= ~OCFS2_DX_FLAG_INLINE; memset(&dx_root->dr_list, 0, fs->fs_blocksize - offsetof(struct ocfs2_dx_root_block, dr_list)); dx_root->dr_list.l_count = ocfs2_extent_recs_per_dx_root(fs->fs_blocksize); /* This should never fail considering we start with an empty * dx_root */ ocfs2_init_dx_root_extent_tree(&et, fs, (char *)dx_root, dx_root->dr_blkno); ret = ocfs2_tree_insert_extent(fs, &et, 0, start_blkno, 1, 0); if (ret) goto out; out: ocfs2_free(&dx_leaves); return ret; } static errcode_t ocfs2_dx_dir_lookup_rec(ocfs2_filesys *fs, struct ocfs2_dx_root_block *dx_root, struct ocfs2_extent_list *el, uint32_t major_hash, uint32_t *ret_cpos, uint64_t *ret_phys_blkno, unsigned int *ret_clen) { errcode_t ret = 0; int i, found; struct ocfs2_extent_block *eb; struct ocfs2_extent_rec *rec = NULL; char *eb_buf = NULL; if (el->l_tree_depth) { ret = ocfs2_tree_find_leaf(fs, &dx_root->dr_list, dx_root->dr_blkno, (char *)dx_root, major_hash, &eb_buf); if (ret) goto out; eb = (struct ocfs2_extent_block *)eb_buf; el = &eb->h_list; if (el->l_tree_depth) { ret = OCFS2_ET_CORRUPT_EXTENT_BLOCK; goto out; } } found = 0; for (i = el->l_next_free_rec - 1; i >= 0; i--) { rec = &el->l_recs[i]; if (rec->e_cpos <= major_hash) { found = 1; break; } } if (!found) { ret = OCFS2_ET_CORRUPT_EXTENT_BLOCK; goto out; } if (ret_phys_blkno) *ret_phys_blkno = rec->e_blkno; if (ret_cpos) *ret_cpos = rec->e_cpos; if (ret_clen) *ret_clen = rec->e_leaf_clusters; out: if (eb_buf) ocfs2_free(&eb_buf); return ret; } errcode_t ocfs2_dx_dir_lookup(ocfs2_filesys *fs, struct ocfs2_dx_root_block *dx_root, struct ocfs2_extent_list *el, struct ocfs2_dx_hinfo *hinfo, uint32_t *ret_cpos, uint64_t *ret_phys_blkno) { errcode_t ret = 0; unsigned int cend = 0, clen = 0; uint32_t cpos = 0; uint64_t blkno = 0; uint32_t name_hash = hinfo->major_hash; ret = ocfs2_dx_dir_lookup_rec(fs, dx_root, el, name_hash, &cpos, &blkno, &clen); if (ret) goto out; cend = cpos + clen; if (name_hash >= cend) { blkno += ocfs2_clusters_to_blocks(fs, clen - 1); cpos += clen - 1; } else { blkno += ocfs2_clusters_to_blocks(fs, name_hash - cpos); cpos = name_hash; } blkno += ocfs2_dx_dir_hash_idx(fs, hinfo); if (ret_phys_blkno) *ret_phys_blkno = blkno; if (ret_cpos) *ret_cpos = cpos; out: return ret; } static int dx_leaf_sort_cmp(const void *a, const void *b) { const struct ocfs2_dx_entry *e1 = a; const struct ocfs2_dx_entry *e2 = b; uint32_t major_hash1 = e1->dx_major_hash; uint32_t major_hash2 = e2->dx_major_hash; uint32_t minor_hash1 = e1->dx_minor_hash; uint32_t minor_hash2 = e2->dx_minor_hash; if (major_hash1 > major_hash2) return 1; if (major_hash1 < major_hash2) return -1; /* it is not strictly necessary to sort by minor */ if (minor_hash1 > minor_hash2) return 1; if (minor_hash1 < minor_hash2) return -1; return 0; } static void dx_leaf_sort_swap(void *a, void *b, int size) { struct ocfs2_dx_entry *e1 = a; struct ocfs2_dx_entry *e2 = b; struct ocfs2_dx_entry tmp; assert(size == sizeof (struct ocfs2_dx_entry)); tmp = *e1; *e1 = *e2; *e2 = tmp; } static int ocfs2_dx_leaf_same_major(struct ocfs2_dx_leaf *dx_leaf) { struct ocfs2_dx_entry_list *dl_list = &dx_leaf->dl_list; int i, num = dl_list->de_num_used; for (i = 0; i < (num - 1); i++) { if (dl_list->de_entries[i].dx_major_hash != dl_list->de_entries[i + 1].dx_major_hash) return 0; } return 1; } /* * Find the optimal value to split this leaf on. This expects the leaf * entries to be in sorted order. * * leaf_cpos is the cpos of the leaf we're splitting. insert_hash is * the hash we want to insert. * * This function is only concerned with the major hash - that which * determines which cluster an item belongs to. */ static int ocfs2_dx_dir_find_leaf_split(struct ocfs2_dx_leaf *dx_leaf, uint32_t leaf_cpos, uint32_t insert_hash, uint32_t *split_hash) { struct ocfs2_dx_entry_list *dl_list = &dx_leaf->dl_list; int i, num_used = dl_list->de_num_used; int allsame; /* * There's a couple rare, but nasty corner cases we have to * check for here. All of them involve a leaf where all value * have the same hash, which is what we look for first. * * Most of the time, all of the above is false, and we simply * pick the median value for a split. */ allsame = ocfs2_dx_leaf_same_major(dx_leaf); if (allsame) { uint32_t val = dl_list->de_entries[0].dx_major_hash; if (val == insert_hash) { /* * No matter where we would choose to split, * the new entry would want to occupy the same * block as these. Since there's no space left * in their existing block, we know there * won't be space after the split. */ return OCFS2_ET_DIR_NO_SPACE; } if (val == leaf_cpos) { /* * Because val is the same as leaf_cpos (which * is the smallest value this leaf can have), * yet is not equal to insert_hash, then we * know that insert_hash *must* be larger than * val (and leaf_cpos). At least cpos+1 in value. * * We also know then, that there cannot be an * adjacent extent (otherwise we'd be looking * at it). Choosing this value gives us a * chance to get some continguousness. */ *split_hash = leaf_cpos + 1; return 0; } if (val > insert_hash) { /* * val can not be the same as insert_hash, and * also must be larger than leaf_cpos. Also, * we know that there can't be a leaf between * cpos and val, otherwise the entries with * hash 'val' would be there. */ *split_hash = val; return 0; } *split_hash = insert_hash; return 0; } /* * Since the records are sorted and the checks above * guaranteed that not all records in this block are the same, * we simple travel forward, from the median, and pick the 1st * record whose value is larger than leaf_cpos. */ for (i = (num_used /2); i < num_used; i++) { if (dl_list->de_entries[i].dx_major_hash > leaf_cpos) break; } assert(i < num_used); /* Should be impossible */ *split_hash = dl_list->de_entries[i].dx_major_hash; return 0; } static errcode_t ocfs2_read_dx_leaves(ocfs2_filesys *fs, uint64_t start, int num, struct ocfs2_dx_leaf **dx_leaves) { errcode_t ret = 0; int i; struct ocfs2_dx_leaf *dx_leaf; for (i = 0; i < num; i++) { assert(!dx_leaves[i]); ret = ocfs2_malloc_block(fs->fs_io, (char **)&dx_leaf); if (ret) goto bail; ret = ocfs2_read_dx_leaf(fs, start + i, (char *)dx_leaf); if (ret) goto bail; dx_leaves[i] = dx_leaf; } goto out; bail: for (; i >= 0; i--) { if (dx_leaves[i]) ocfs2_free(&dx_leaves[i]); } out: return ret; } static errcode_t __ocfs2_dx_dir_new_cluster(ocfs2_filesys *fs, uint32_t cpos, struct ocfs2_dx_leaf **dx_leaves, int num_dx_leaves, uint64_t *ret_phys_blkno) { errcode_t ret; uint32_t num; uint64_t phys; ret = ocfs2_new_clusters(fs, 1, 1, &phys, &num); if (ret) goto out; assert(num == 1); ret = ocfs2_dx_dir_format_cluster(fs, dx_leaves, num_dx_leaves, phys); if (ret) goto out; *ret_phys_blkno = phys; out: return ret; } static errcode_t ocfs2_dx_dir_new_cluster(ocfs2_filesys *fs, struct ocfs2_extent_tree *et, uint32_t cpos, uint64_t *phys_blocknr, struct ocfs2_dx_leaf **dx_leaves, int num_dx_leaves) { errcode_t ret; uint64_t blkno; ret = __ocfs2_dx_dir_new_cluster(fs, cpos, dx_leaves, num_dx_leaves, &blkno); if (ret) goto out; *phys_blocknr = blkno; ret = ocfs2_tree_insert_extent(fs, et, cpos, blkno, 1, 0); out: return ret; } static errcode_t ocfs2_dx_dir_transfer_leaf(ocfs2_filesys *fs, uint32_t split_hash, struct ocfs2_dx_leaf *tmp_dx_leaf, struct ocfs2_dx_leaf **orig_dx_leaves, uint64_t orig_dx_leaves_blkno, struct ocfs2_dx_leaf **new_dx_leaves, uint64_t new_dx_leaves_blkno, int num_dx_leaves) { errcode_t ret = 0; int i, j, num_used; uint32_t major_hash; struct ocfs2_dx_leaf *orig_dx_leaf, *new_dx_leaf; struct ocfs2_dx_entry_list *orig_list, *tmp_list; struct ocfs2_dx_entry *dx_entry; tmp_list = &tmp_dx_leaf->dl_list; for (i = 0; i < num_dx_leaves; i++) { orig_dx_leaf = orig_dx_leaves[i]; orig_list = &orig_dx_leaf->dl_list; new_dx_leaf = new_dx_leaves[i]; num_used = orig_list->de_num_used; memcpy(tmp_dx_leaf, orig_dx_leaf, fs->fs_blocksize); tmp_list->de_num_used = 0; memset(&tmp_list->de_entries, 0, sizeof(struct ocfs2_dx_entry) * num_used); for (j = 0; j < num_used; j++) { dx_entry = &orig_list->de_entries[j]; major_hash = dx_entry->dx_major_hash; if (major_hash >= split_hash) ocfs2_dx_dir_leaf_insert_tail(new_dx_leaf, dx_entry); else ocfs2_dx_dir_leaf_insert_tail(tmp_dx_leaf, dx_entry); } memcpy(orig_dx_leaf, tmp_dx_leaf, fs->fs_blocksize); ret = ocfs2_write_dx_leaf(fs, orig_dx_leaves_blkno + i, (char *)orig_dx_leaf); if (ret) goto out; ret = ocfs2_write_dx_leaf(fs, new_dx_leaves_blkno + i, (char *)new_dx_leaf); if (ret) goto out; } out: return ret; } static int ocfs2_dx_dir_free_leaves(ocfs2_filesys *fs, struct ocfs2_dx_leaf **dx_leaves) { int i, num; num = ocfs2_clusters_to_blocks(fs, 1); for (i = 0; i < num; i++) { if (dx_leaves[i]) ocfs2_free(&dx_leaves[i]); } free(dx_leaves); return 0; } /* from Linux kernel lib/sort.c */ static void ocfs2_sort(void *base, size_t num, size_t size, int (*cmp_func)(const void *, const void *), void (*swap_func)(void *, void *, int size)) { /* pre-scale counters for performance */ int i = (num/2 - 1) * size, n = num * size, c, r; /* heapify */ for (; i >= 0; i -= size) { for (r = i; r * 2 + size < n; r = c) { c = r * 2 + size; if (c < n - size && cmp_func(base + c, base + c + size) < 0) c += size; if (cmp_func(base + r, base + c) >= 0) break; swap_func(base + r, base + c, size); } } /* sort */ for (i = n - size; i > 0; i -= size) { swap_func(base, base + i, size); for (r = 0; r * 2 + size < i; r = c) { c = r * 2 + size; if (c < i - size && cmp_func(base + c, base + c + size) < 0) c += size; if (cmp_func(base + r, base + c) >= 0) break; swap_func(base + r, base + c, size); } } } static errcode_t ocfs2_dx_dir_rebalance(ocfs2_filesys *fs, struct ocfs2_dx_root_block *dx_root, struct ocfs2_dx_leaf *dx_leaf, struct ocfs2_dx_hinfo *hinfo, uint32_t leaf_cpos, uint64_t leaf_blkno) { struct ocfs2_extent_tree et; struct ocfs2_dx_leaf **orig_dx_leaves = NULL; struct ocfs2_dx_leaf **new_dx_leaves = NULL; struct ocfs2_dx_leaf *tmp_dx_leaf = NULL; uint32_t insert_hash = hinfo->major_hash; uint32_t split_hash, cpos; uint64_t orig_leaves_start, new_leaves_start; errcode_t ret; int num_used, num_dx_leaves; ocfs2_init_dx_root_extent_tree(&et, fs, (char *)dx_root, dx_root->dr_blkno); if (dx_root->dr_clusters == UINT_MAX) { ret = OCFS2_ET_DIR_NO_SPACE; goto out; } num_used = dx_leaf->dl_list.de_num_used; if (num_used < dx_leaf->dl_list.de_count) { ret = OCFS2_ET_DX_BALANCE_EMPTY_LEAF; goto out; } orig_dx_leaves = ocfs2_dx_dir_alloc_leaves(fs, &num_dx_leaves); if (!orig_dx_leaves) { ret = OCFS2_ET_NO_MEMORY; goto out; } new_dx_leaves = ocfs2_dx_dir_alloc_leaves(fs, NULL); if (!new_dx_leaves) { ret = OCFS2_ET_NO_MEMORY; goto out; } ocfs2_sort(dx_leaf->dl_list.de_entries, num_used, sizeof(struct ocfs2_dx_entry), dx_leaf_sort_cmp, dx_leaf_sort_swap); ret = ocfs2_dx_dir_find_leaf_split(dx_leaf, leaf_cpos, insert_hash, &split_hash); if (ret) goto out; ret = ocfs2_malloc_block(fs->fs_io, (char **)(&tmp_dx_leaf)); if (ret) goto out; orig_leaves_start = ocfs2_block_to_cluster_start(fs, leaf_blkno); ret = ocfs2_read_dx_leaves(fs, orig_leaves_start, num_dx_leaves, orig_dx_leaves); if (ret) goto out; cpos = split_hash; ret = ocfs2_dx_dir_new_cluster(fs, &et, cpos, &new_leaves_start, new_dx_leaves, num_dx_leaves); if (ret) goto out; ret = ocfs2_dx_dir_transfer_leaf(fs, split_hash, tmp_dx_leaf, orig_dx_leaves, orig_leaves_start, new_dx_leaves, new_leaves_start, num_dx_leaves); out: if (tmp_dx_leaf) ocfs2_free((char **)(&tmp_dx_leaf)); if (orig_dx_leaves) ocfs2_dx_dir_free_leaves(fs, orig_dx_leaves); if (new_dx_leaves) ocfs2_dx_dir_free_leaves(fs, new_dx_leaves); return ret; } static errcode_t ocfs2_find_dir_space_dx(ocfs2_filesys *fs, struct ocfs2_dx_root_block *dx_root, const char *name, int namelen, struct ocfs2_dir_lookup_result *lookup) { errcode_t ret; int rebalanced = 0; struct ocfs2_dx_leaf *dx_leaf; char *dx_leaf_buf = NULL; uint64_t blkno; uint32_t leaf_cpos; ret = ocfs2_malloc_block(fs->fs_io, &dx_leaf_buf); if (ret) goto out; restart_search: ret = ocfs2_dx_dir_lookup(fs, dx_root, &dx_root->dr_list, &lookup->dl_hinfo, &leaf_cpos, &blkno); if (ret) goto out; ret = ocfs2_read_dx_leaf(fs, blkno, dx_leaf_buf); if (ret) goto out; dx_leaf = (struct ocfs2_dx_leaf *)dx_leaf_buf; if (dx_leaf->dl_list.de_num_used >= dx_leaf->dl_list.de_count) { if (rebalanced) { /* * Rebalancing should have provided us with * space in an appropriate leaf. */ ret = OCFS2_ET_DIR_NO_SPACE; goto out; } ret = ocfs2_dx_dir_rebalance(fs, dx_root, dx_leaf, &lookup->dl_hinfo, leaf_cpos, blkno); if (ret) goto out; rebalanced = 1; goto restart_search; } lookup->dl_dx_leaf_blkno = blkno; out: if (dx_leaf_buf) ocfs2_free(&dx_leaf_buf); return ret; } /* * Hashing code adapted from ext3 */ #define DELTA 0x9E3779B9 static void TEA_transform(uint32_t buf[4], uint32_t const in[]) { uint32_t sum = 0; uint32_t b0 = buf[0], b1 = buf[1]; uint32_t a = in[0], b = in[1], c = in[2], d = in[3]; int n = 16; do { sum += DELTA; b0 += ((b1 << 4)+a) ^ (b1+sum) ^ ((b1 >> 5)+b); b1 += ((b0 << 4)+c) ^ (b0+sum) ^ ((b0 >> 5)+d); } while (--n); buf[0] += b0; buf[1] += b1; } static void str2hashbuf(const char *msg, int len, uint32_t *buf, int num) { uint32_t pad, val; int i; pad = (uint32_t)len | ((uint32_t)len << 8); pad |= pad << 16; val = pad; if (len > (num * 4)) len = num * 4; for (i = 0; i < len; i++) { if ((i % 4) == 0) val = pad; val = msg[i] + (val << 8); if ((i % 4) == 3) { *buf++ = val; val = pad; num --; } } if (--num >= 0) *buf++ = val; while(--num >= 0) *buf++ = pad; } void ocfs2_dx_dir_name_hash(ocfs2_filesys *fs, const char *name, int len, struct ocfs2_dx_hinfo *hinfo) { const char *p; uint32_t in[8], buf[4]; /* * XXX: Is this really necessary, if the index is never looked * at by readdir? Is a hash value of '0' a bad idea ? */ if ((len == 1 && !strncmp(".", name, 1)) || (len == 2 && !strncmp("..", name, 2))) { buf[0] = buf[1] = 0; goto out; } memcpy(buf, OCFS2_RAW_SB(fs->fs_super)->s_dx_seed, sizeof(buf)); p = name; while(len > 0) { str2hashbuf(p, len, in, 4); TEA_transform(buf, in); len -= 16; p += 16; } out: hinfo->major_hash = buf[0]; hinfo->minor_hash = buf[1]; } static int ocfs2_dx_dir_insert(struct ocfs2_dir_entry *dentry, uint64_t blocknr, int offset, int blocksize, char *buf, void *priv_data) { int ret = 0; errcode_t err; char *dx_buf = NULL; char *dx_leaf_buf = NULL; struct ocfs2_dx_root_block *dx_root = NULL; struct ocfs2_dx_leaf *dx_leaf = NULL; struct ocfs2_dir_lookup_result lookup; struct ocfs2_dx_entry_list *entry_list; struct dx_insert_ctxt *ctxt = (struct dx_insert_ctxt *)priv_data; ocfs2_filesys *fs = ctxt->fs; uint64_t dx_root_blkno = ctxt->dx_root_blkno; int write_dx_leaf = 0; err = ocfs2_malloc_block(fs->fs_io, &dx_buf); if (err) goto set_err; err = ocfs2_malloc_block(fs->fs_io, &dx_leaf_buf); if (err) goto set_err; err = ocfs2_read_dx_root(fs, dx_root_blkno, dx_buf); if (err) goto set_err; dx_root = (struct ocfs2_dx_root_block *)dx_buf; memset(&lookup, 0, sizeof(struct ocfs2_dir_lookup_result)); ocfs2_dx_dir_name_hash(fs, dentry->name, dentry->name_len, &lookup.dl_hinfo); if (dx_root->dr_flags & OCFS2_DX_FLAG_INLINE) { if (ocfs2_inline_dx_has_space(dx_root)) { entry_list = &dx_root->dr_entries; goto insert_into_entries; } else { /* root block is full, expand it to an extent */ err = ocfs2_expand_inline_dx_root(fs, dx_root); if (err) goto set_err; } } err = ocfs2_find_dir_space_dx(fs, dx_root, dentry->name, dentry->name_len, &lookup); if (err) goto set_err; err = ocfs2_read_dx_leaf(fs, lookup.dl_dx_leaf_blkno, dx_leaf_buf); if (err) goto set_err; dx_leaf = (struct ocfs2_dx_leaf *)dx_leaf_buf; entry_list = &dx_leaf->dl_list; write_dx_leaf = 1; insert_into_entries: ocfs2_dx_entry_list_insert(entry_list, &lookup.dl_hinfo, blocknr); if (write_dx_leaf) { err = ocfs2_write_dx_leaf(fs, dx_leaf->dl_blkno, dx_leaf); if (err) goto set_err; } dx_root->dr_num_entries += 1; err = ocfs2_write_dx_root(fs, dx_root_blkno, dx_buf); if (!err) goto out; set_err: ctxt->err = err; ret = OCFS2_DIRENT_ABORT; out: if (dx_leaf_buf) ocfs2_free(&dx_leaf_buf); if (dx_buf) ocfs2_free(&dx_buf); return ret; } errcode_t ocfs2_dx_dir_insert_entry(ocfs2_filesys *fs, uint64_t dir, const char *name, uint64_t ino, uint64_t blkno) { struct ocfs2_dir_entry dummy_de; struct dx_insert_ctxt dummy_ctxt; char *di_buf = NULL; struct ocfs2_dinode *di; errcode_t ret = 0; if (!ocfs2_supports_indexed_dirs(OCFS2_RAW_SB(fs->fs_super))) goto out; assert(name); memset(&dummy_de, 0, sizeof(struct ocfs2_dir_entry)); memcpy(dummy_de.name, name, strlen(name)); dummy_de.name_len = strlen(name); ret = ocfs2_malloc_block(fs->fs_io, &di_buf); if (ret) goto out; ret = ocfs2_read_inode(fs, dir, di_buf); if (ret) goto out; di = (struct ocfs2_dinode *)di_buf; if (!(di->i_dyn_features & OCFS2_INDEXED_DIR_FL)) goto out; memset(&dummy_ctxt, 0, sizeof(struct dx_insert_ctxt)); dummy_ctxt.dir_blkno = dir; dummy_ctxt.fs = fs; dummy_ctxt.dx_root_blkno = di->i_dx_root; ret = ocfs2_dx_dir_insert(&dummy_de, blkno, 0, fs->fs_blocksize, NULL, &dummy_ctxt); if (ret) ret = dummy_ctxt.err; out: if (di_buf) ocfs2_free(&di_buf); return ret; } /* * This function overwite the indexed dir attribute of * the given inode. The caller should make sure the dir's * indexed tree is truncated. * Currently tunefs.ocfs2 is the only user, before calling * this function, tunefs.ocfs2 makes sure there is space * for directory trailer. So directory entry moves here. */ errcode_t ocfs2_dx_dir_build(ocfs2_filesys *fs, uint64_t dir) { errcode_t ret = 0, err; uint64_t dr_blkno; char *dx_buf = NULL, *di_buf = NULL; struct ocfs2_dinode *di; struct ocfs2_dx_root_block *dx_root; struct dx_insert_ctxt ctxt; ocfs2_quota_hash *usrhash = NULL, *grphash = NULL; uint32_t uid, gid; long long change; ret = ocfs2_load_fs_quota_info(fs); if (ret) goto out; ret = ocfs2_init_quota_change(fs, &usrhash, &grphash); if (ret) goto out; ret = ocfs2_malloc_block(fs->fs_io, &di_buf); if (ret) goto out; ret = ocfs2_read_inode(fs, dir, di_buf); if (ret) goto out; di = (struct ocfs2_dinode *)di_buf; if ((ocfs2_dir_indexed(di)) || (di->i_dyn_features & OCFS2_INLINE_DATA_FL)) goto out; ret = ocfs2_new_dx_root(fs, di, &dr_blkno); if (ret) goto out; ret = ocfs2_malloc_block(fs->fs_io, &dx_buf); if (ret) goto out; ret = ocfs2_read_dx_root(fs, dr_blkno, dx_buf); if (ret) goto out; dx_root = (struct ocfs2_dx_root_block *)dx_buf; /* set inode to use indexed-dirs */ di->i_dyn_features |= OCFS2_INDEXED_DIR_FL; ret = ocfs2_init_dir_trailers(fs, di, dx_root); if (ret) goto out; dx_root->dr_dir_blkno = di->i_blkno; dx_root->dr_num_entries = 0; dx_root->dr_entries.de_count = ocfs2_dx_entries_per_root(fs->fs_blocksize); di->i_dx_root = dr_blkno; ret = ocfs2_write_dx_root(fs, dr_blkno, dx_buf); if (ret) goto out; ret = ocfs2_write_inode(fs, dir, di_buf); if (ret) goto out; ctxt.dir_blkno = dir; ctxt.dx_root_blkno = dr_blkno; ctxt.fs = fs; ctxt.err = 0; ret = ocfs2_dir_iterate(fs, dir, 0, NULL, ocfs2_dx_dir_insert, &ctxt); if (ctxt.err) ret = ctxt.err; if (ret) goto out; ret = ocfs2_read_dx_root(fs, dr_blkno, dx_buf); if (ret) goto out; ret = ocfs2_read_inode(fs, dir, di_buf); if (ret) goto out; ret = ocfs2_write_inode(fs, dir, di_buf); if(ret) goto out; /* check quota for dx_leaf */ change = ocfs2_clusters_to_bytes(fs, dx_root->dr_clusters); uid = di->i_uid; gid = di->i_gid; ret = ocfs2_apply_quota_change(fs, usrhash, grphash, uid, gid, change, 0); if (ret) { /* exceed quota, truncate the indexed tree */ ret = ocfs2_dx_dir_truncate(fs, dir); } out: err = ocfs2_finish_quota_change(fs, usrhash, grphash); if (!ret) ret = err; if (di_buf) ocfs2_free(&di_buf); if (dx_buf) ocfs2_free(&dx_buf); return ret; } void ocfs2_dx_list_remove_entry(struct ocfs2_dx_entry_list *entry_list, int index) { int num_used = entry_list->de_num_used; if (num_used == 1 || index == (num_used - 1)) goto clear; memmove(&entry_list->de_entries[index], &entry_list->de_entries[index + 1], (num_used - index - 1)*sizeof(struct ocfs2_dx_entry)); clear: num_used --; memset(&entry_list->de_entries[num_used], 0, sizeof(struct ocfs2_dx_entry)); entry_list->de_num_used = num_used; } static int ocfs2_match(int len, const char *name, struct ocfs2_dir_entry *de) { if (len != de->name_len) return 0; if (!de->inode) return 0; return !memcmp((char *)name, de->name, len); } int ocfs2_check_dir_entry(ocfs2_filesys *fs, struct ocfs2_dir_entry *de, char *dir_buf, unsigned int offset) { int rlen = de->rec_len; int ret = 1; if ((rlen < OCFS2_DIR_REC_LEN(1)) || (rlen % 4 != 0) || (rlen < OCFS2_DIR_REC_LEN(de->name_len)) || (((char *)de - dir_buf) > fs->fs_blocksize)) ret = 0; return ret; } int ocfs2_search_dirblock(ocfs2_filesys *fs, char *dir_buf, const char *name, int namelen, unsigned int bytes, struct ocfs2_dir_entry **res_dir) { struct ocfs2_dir_entry *de; char *dlimit, *de_buf; int de_len, offset = 0; int ret = 0; de_buf = (char *)dir_buf; dlimit = de_buf + bytes; while(de_buf < dlimit) { de = (struct ocfs2_dir_entry *)de_buf; if ((de_buf + namelen <= dlimit) && ocfs2_match(namelen, name, de)) { if (!ocfs2_check_dir_entry(fs, de, dir_buf, offset)) { ret = -1; goto out; } if (res_dir) *res_dir = de; ret = 1; goto out; } de_len = de->rec_len; if (de_len <= 0) { ret = -1; goto out; } de_buf += de_len; offset += de_len; } out: return ret; } errcode_t ocfs2_dx_dir_search(ocfs2_filesys *fs, const char *name, int namelen, struct ocfs2_dx_root_block *dx_root, struct ocfs2_dir_lookup_result *lookup) { errcode_t ret; char *di_buf = NULL, *dir_buf = NULL, *dx_leaf_buf = NULL; struct ocfs2_dx_entry_list *entry_list; struct ocfs2_dx_leaf *dx_leaf; struct ocfs2_dx_entry *dx_entry; struct ocfs2_dir_entry *dir_ent; uint32_t leaf_cpos; uint64_t blkno = 0; int i, found; if (dx_root->dr_flags & OCFS2_DX_FLAG_INLINE) entry_list = &dx_root->dr_entries; else { ret = ocfs2_dx_dir_lookup(fs, dx_root, &dx_root->dr_list, &lookup->dl_hinfo, &leaf_cpos, &blkno); if (ret) goto out; ret = ocfs2_malloc_block(fs->fs_io, &dx_leaf_buf); if (ret) goto out; ret = ocfs2_read_dx_leaf(fs, blkno, dx_leaf_buf); if (ret) goto out; dx_leaf = (struct ocfs2_dx_leaf *)dx_leaf_buf; entry_list = &dx_leaf->dl_list; } if ((entry_list->de_count == 0) || (entry_list->de_num_used == 0) || (dx_root->dr_num_entries == 0)) { ret = OCFS2_ET_DIR_CORRUPTED; goto out; } ret = ocfs2_malloc_block(fs->fs_io, &dir_buf); if (ret) goto out; found = 0; for (i = 0; i < entry_list->de_num_used; i++) { dx_entry = &entry_list->de_entries[i]; if ((lookup->dl_hinfo.major_hash != dx_entry->dx_major_hash) || (lookup->dl_hinfo.minor_hash != dx_entry->dx_minor_hash)) continue; ret = ocfs2_read_blocks(fs, dx_entry->dx_dirent_blk, 1, dir_buf); if (ret) goto out; ocfs2_swap_dir_entries_to_cpu(dir_buf, fs->fs_blocksize); found = ocfs2_search_dirblock(fs, dir_buf, name, namelen, fs->fs_blocksize, &dir_ent); if (found == 1) break; if (found == -1) { ret = OCFS2_ET_DIR_CORRUPTED; goto out; } } if (found <= 0) { ret = OCFS2_ET_FILE_NOT_FOUND; goto out; } lookup->dl_leaf = dir_buf; lookup->dl_leaf_blkno = dx_entry->dx_dirent_blk; lookup->dl_entry = dir_ent; lookup->dl_dx_entry = dx_entry; lookup->dl_dx_entry_idx = i; if (!(dx_root->dr_flags & OCFS2_DX_FLAG_INLINE)) { lookup->dl_dx_leaf = (struct ocfs2_dx_leaf *)dx_leaf_buf; lookup->dl_dx_leaf_blkno = blkno; } ret = 0; out: if (di_buf) ocfs2_free(&di_buf); if (ret) { if (dir_buf) ocfs2_free(&dir_buf); if (dx_leaf_buf) ocfs2_free(&dx_leaf_buf); } return ret; } void release_lookup_res(struct ocfs2_dir_lookup_result *res) { if (res->dl_leaf) ocfs2_free(&res->dl_leaf); if (res->dl_dx_leaf) ocfs2_free(&res->dl_dx_leaf); } ocfs2-tools-ocfs2-tools-1.8.6/libocfs2/dir_iterate.c000066400000000000000000000335561347147137200222370ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * dirblock.c * * Directory block routines for the OCFS2 userspace library. * * Copyright (C) 2004 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License, version 2, as published by the Free Software Foundation. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * * This code is a port of e2fsprogs/lib/ext2fs/dir_iterate.c * Copyright (C) 1993, 1994, 1994, 1995, 1996, 1997 Theodore Ts'o. */ #define _XOPEN_SOURCE 600 /* Triggers magic in features.h */ #define _LARGEFILE64_SOURCE #include #include "ocfs2/ocfs2.h" #include "dir_iterate.h" #include "dir_util.h" static int ocfs2_inline_dir_iterate(ocfs2_filesys *fs, struct ocfs2_dinode *di, struct dir_context *ctx); /* * This function checks to see whether or not a potential deleted * directory entry looks valid. What we do is check the deleted entry * and each successive entry to make sure that they all look valid and * that the last deleted entry ends at the beginning of the next * undeleted entry. Returns 1 if the deleted entry looks valid, zero * if not valid. */ static int ocfs2_validate_entry(char *buf, int offset, int final_offset) { struct ocfs2_dir_entry *dirent; while (offset < final_offset) { dirent = (struct ocfs2_dir_entry *)(buf + offset); offset += dirent->rec_len; if ((dirent->rec_len < 8) || ((dirent->rec_len % 4) != 0) || (((dirent->name_len & 0xFF)+8) > dirent->rec_len)) return 0; } return (offset == final_offset); } errcode_t ocfs2_dir_iterate2(ocfs2_filesys *fs, uint64_t dir, int flags, char *block_buf, int (*func)(uint64_t dir, int entry, struct ocfs2_dir_entry *dirent, uint64_t blocknr, int offset, int blocksize, char *buf, void *priv_data), void *priv_data) { struct ocfs2_dinode *di; struct dir_context ctx; errcode_t retval; retval = ocfs2_check_directory(fs, dir); if (retval) return retval; ctx.dir = dir; ctx.flags = flags; if (block_buf) ctx.buf = block_buf; else { retval = ocfs2_malloc_block(fs->fs_io, &ctx.buf); if (retval) return retval; } ctx.func = func; ctx.priv_data = priv_data; ctx.errcode = 0; retval = ocfs2_malloc_block(fs->fs_io, &ctx.di); if (retval) goto out; retval = ocfs2_read_inode(fs, dir, ctx.buf); if (retval) goto out; /* * Save off the inode - some paths use the buffer for dirent * data. */ memcpy(ctx.di, ctx.buf, fs->fs_blocksize); di = (struct ocfs2_dinode *)ctx.buf; if (ocfs2_support_inline_data(OCFS2_RAW_SB(fs->fs_super)) && di->i_dyn_features & OCFS2_INLINE_DATA_FL) retval = ocfs2_inline_dir_iterate(fs, di, &ctx); else retval = ocfs2_block_iterate(fs, dir, 0, ocfs2_process_dir_block, &ctx); out: if (!block_buf) ocfs2_free(&ctx.buf); if (ctx.di) ocfs2_free(&ctx.di); if (retval) return retval; return ctx.errcode; } struct xlate { int (*func)(struct ocfs2_dir_entry *dirent, uint64_t blocknr, int offset, int blocksize, char *buf, void *priv_data); void *real_private; }; static int xlate_func(uint64_t dir, int entry, struct ocfs2_dir_entry *dirent, uint64_t blocknr, int offset, int blocksize, char *buf, void *priv_data) { struct xlate *xl = (struct xlate *) priv_data; return (*xl->func)(dirent, blocknr, offset, blocksize, buf, xl->real_private); } extern errcode_t ocfs2_dir_iterate(ocfs2_filesys *fs, uint64_t dir, int flags, char *block_buf, int (*func)(struct ocfs2_dir_entry *dirent, uint64_t blocknr, int offset, int blocksize, char *buf, void *priv_data), void *priv_data) { struct xlate xl; xl.real_private = priv_data; xl.func = func; return ocfs2_dir_iterate2(fs, dir, flags, block_buf, xlate_func, &xl); } static int ocfs2_process_dir_entry(ocfs2_filesys *fs, uint64_t blocknr, unsigned int offset, int entry, int *changed, int *do_abort, struct dir_context *ctx) { errcode_t ret; struct ocfs2_dir_entry *dirent; unsigned int next_real_entry = 0; int size; while (offset < fs->fs_blocksize) { dirent = (struct ocfs2_dir_entry *) (ctx->buf + offset); if (((offset + dirent->rec_len) > fs->fs_blocksize) || (dirent->rec_len < 8) || ((dirent->rec_len % 4) != 0) || (((dirent->name_len & 0xFF)+8) > dirent->rec_len)) { ctx->errcode = OCFS2_ET_DIR_CORRUPTED; return OCFS2_BLOCK_ABORT; } if (ocfs2_skip_dir_trailer(fs, ctx->di, dirent, offset)) { if (!(ctx->flags & OCFS2_DIRENT_FLAG_INCLUDE_TRAILER)) goto next; } else if (!dirent->inode && !(ctx->flags & OCFS2_DIRENT_FLAG_INCLUDE_EMPTY)) { goto next; } else if ((ctx->flags & OCFS2_DIRENT_FLAG_EXCLUDE_DOTS) && is_dots(dirent->name, dirent->name_len)) { goto next; } ret = (ctx->func)(ctx->dir, (next_real_entry > offset) ? OCFS2_DIRENT_DELETED_FILE : entry, dirent, blocknr, offset, fs->fs_blocksize, ctx->buf, ctx->priv_data); if (entry < OCFS2_DIRENT_OTHER_FILE) entry++; if (ret & OCFS2_DIRENT_CHANGED) *changed += 1; if (ret & OCFS2_DIRENT_ABORT) { *do_abort += 1; break; } next: if (next_real_entry == offset) next_real_entry += dirent->rec_len; if (ctx->flags & OCFS2_DIRENT_FLAG_INCLUDE_REMOVED) { size = ((dirent->name_len & 0xFF) + 11) & ~3; if (dirent->rec_len != size) { unsigned int final_offset; final_offset = offset + dirent->rec_len; offset += size; while (offset < final_offset && !ocfs2_validate_entry(ctx->buf, offset, final_offset)) offset += 4; continue; } } offset += dirent->rec_len; } return 0; } static int ocfs2_inline_dir_iterate(ocfs2_filesys *fs, struct ocfs2_dinode *di, struct dir_context *ctx) { unsigned int offset = offsetof(struct ocfs2_dinode, id2.i_data.id_data); int ret = 0, changed = 0, do_abort = 0, entry; entry = OCFS2_DIRENT_DOT_FILE; ret = ocfs2_process_dir_entry(fs, di->i_blkno, offset, entry, &changed, &do_abort, ctx); if (ret) return ret; if (changed) { ctx->errcode = ocfs2_write_inode(fs, di->i_blkno, ctx->buf); if (ctx->errcode) return OCFS2_BLOCK_ABORT; } return 0; } /* * Helper function which is private to this module. Used by * ocfs2_dir_iterate() and ocfs2_dblist_dir_iterate() */ int ocfs2_process_dir_block(ocfs2_filesys *fs, uint64_t blocknr, uint64_t blockcnt, uint16_t ext_flags, void *priv_data) { struct dir_context *ctx = (struct dir_context *) priv_data; unsigned int offset = 0; int ret = 0; int changed = 0; int do_abort = 0; int entry; if (blockcnt < 0) return 0; entry = blockcnt ? OCFS2_DIRENT_OTHER_FILE : OCFS2_DIRENT_DOT_FILE; ctx->errcode = ocfs2_read_dir_block(fs, ctx->di, blocknr, ctx->buf); if (ctx->errcode) return OCFS2_BLOCK_ABORT; ret = ocfs2_process_dir_entry(fs, blocknr, offset, entry, &changed, &do_abort, ctx); if (ret) return ret; if (changed) { ctx->errcode = ocfs2_write_dir_block(fs, ctx->di, blocknr, ctx->buf); if (ctx->errcode) return OCFS2_BLOCK_ABORT; } if (do_abort) return OCFS2_BLOCK_ABORT; return 0; } struct dx_iterator_data { int (*dx_func)(ocfs2_filesys *fs, struct ocfs2_dx_entry_list *entry_list, struct ocfs2_dx_root_block *dx_root, struct ocfs2_dx_leaf *dx_leaf, void *priv_data); void *dx_priv_data; char *leaf_buf; struct ocfs2_dx_root_block *dx_root; errcode_t err; }; static int dx_iterator(ocfs2_filesys *fs, struct ocfs2_extent_rec *rec, int tree_depth, uint32_t ccount, uint64_t ref_blkno, int ref_recno, void *priv_data) { errcode_t err; int i; struct ocfs2_dx_leaf *dx_leaf; struct dx_iterator_data *iter = priv_data; uint64_t blkno, count; count = ocfs2_clusters_to_blocks(fs, rec->e_leaf_clusters); blkno = rec->e_blkno; for (i = 0; i < count; i++) { err = ocfs2_read_dx_leaf(fs, blkno, iter->leaf_buf); if (err) { iter->err = err; return OCFS2_EXTENT_ERROR; } dx_leaf = (struct ocfs2_dx_leaf *)iter->leaf_buf; err = iter->dx_func(fs, &dx_leaf->dl_list, iter->dx_root, dx_leaf, iter->dx_priv_data); /* callback dx_func() is defined by users, the return value does not * follow libocfs2 error codes. Don't touch iter->err and just stop * the iteration here.*/ if (err) return OCFS2_EXTENT_ERROR; blkno++; } return 0; } extern errcode_t ocfs2_dx_entries_iterate(ocfs2_filesys *fs, struct ocfs2_dinode *dir, int flags, int (*func)(ocfs2_filesys *fs, struct ocfs2_dx_entry_list *entry_list, struct ocfs2_dx_root_block *dx_root, struct ocfs2_dx_leaf *dx_leaf, void *priv_data), void *priv_data) { errcode_t ret = 0; struct ocfs2_dx_root_block *dx_root; uint64_t dx_blkno; char *buf = NULL, *eb_buf = NULL, *leaf_buf = NULL; struct dx_iterator_data data; if (!S_ISDIR(dir->i_mode) && !ocfs2_dir_indexed(dir)) { goto out; } ret = ocfs2_malloc_block(fs->fs_io, &buf); if (ret) goto out; dx_blkno = (uint64_t) dir->i_dx_root; ret = ocfs2_read_dx_root(fs, dx_blkno, buf); if (ret) goto out; dx_root = (struct ocfs2_dx_root_block *)buf; if (dx_root->dr_flags & OCFS2_DX_FLAG_INLINE) { ret = func(fs, &dx_root->dr_entries, dx_root, NULL, priv_data); goto out; } ret = ocfs2_malloc_block(fs->fs_io, &eb_buf); if (ret) goto out; ret = ocfs2_malloc_block(fs->fs_io, &leaf_buf); if (ret) goto out; data.dx_func = func; data.dx_priv_data = priv_data; data.leaf_buf = leaf_buf; data.dx_root = dx_root; data.err = 0; ret = ocfs2_extent_iterate_dx_root(fs, dx_root, OCFS2_EXTENT_FLAG_DATA_ONLY, eb_buf, dx_iterator, &data); /* dx_iterator may set the error code for non-extents-related * errors. If the error code is set by dx_iterator, no matter * what ocfs2_extent_iterate_dx_root() returns, we should take * data.err as retured error code. */ if (data.err) ret = data.err; out: if (buf) ocfs2_free(&buf); if (eb_buf) ocfs2_free(&eb_buf); if (leaf_buf) ocfs2_free(&leaf_buf); return ret; } extern errcode_t ocfs2_dx_frees_iterate(ocfs2_filesys *fs, struct ocfs2_dinode *dir, struct ocfs2_dx_root_block *dx_root, int flags, int (*func)(ocfs2_filesys *fs, uint64_t blkno, struct ocfs2_dir_block_trailer *trailer, char *dirblock, void *priv_data), void *priv_data) { errcode_t ret = 0; uint64_t blkno; char *buf = NULL; struct ocfs2_dir_block_trailer *trailer; if (!S_ISDIR(dir->i_mode) || !(ocfs2_dir_indexed(dir))) { goto out; } if (dx_root->dr_flags & OCFS2_DX_FLAG_INLINE) { goto out; } ret = ocfs2_malloc_block(fs->fs_io, &buf); if (ret) goto out; blkno = dx_root->dr_free_blk; while (blkno) { ret = ocfs2_read_dir_block(fs, dir, blkno, buf); if (ret) goto out; trailer = ocfs2_dir_trailer_from_block(fs, buf); func(fs, blkno, trailer, buf, priv_data); blkno = trailer->db_free_next; } out: if (buf) ocfs2_free(&buf); return ret; } #ifdef DEBUG_EXE #include #include #include static uint64_t read_number(const char *num) { uint64_t val; char *ptr; val = strtoull(num, &ptr, 0); if (!ptr || *ptr) return 0; return val; } static void print_usage(void) { fprintf(stderr, "Usage: dir_iterate -i \n"); } static int walk_names_func(struct ocfs2_dir_entry *dentry, int offset, int blocksize, char *buf, void *priv_data) { char name[256]; memcpy(name, dentry->name, dentry->name_len); name[dentry->name_len] = '\0'; fprintf(stdout, "%20"PRIu64" %s\n", dentry->inode, name); return 0; } extern int opterr, optind; extern char *optarg; int main(int argc, char *argv[]) { errcode_t ret; uint64_t blkno; int c; char *filename, *buf; ocfs2_filesys *fs; struct ocfs2_dinode *di; blkno = 0; initialize_ocfs_error_table(); while ((c = getopt(argc, argv, "i:")) != EOF) { switch (c) { case 'i': blkno = read_number(optarg); if (blkno <= OCFS2_SUPER_BLOCK_BLKNO) { fprintf(stderr, "Invalid inode block: %s\n", optarg); print_usage(); return 1; } break; default: print_usage(); return 1; break; } } if (optind >= argc) { fprintf(stderr, "Missing filename\n"); print_usage(); return 1; } filename = argv[optind]; ret = ocfs2_open(filename, OCFS2_FLAG_RO, 0, 0, &fs); if (ret) { com_err(argv[0], ret, "while opening file \"%s\"", filename); goto out; } ret = ocfs2_malloc_block(fs->fs_io, &buf); if (ret) { com_err(argv[0], ret, "while allocating inode buffer"); goto out_close; } if (blkno == 0) blkno = fs->fs_root_blkno; ret = ocfs2_read_inode(fs, blkno, buf); if (ret) { com_err(argv[0], ret, "while reading inode %"PRIu64, blkno); goto out_free; } di = (struct ocfs2_dinode *)buf; fprintf(stdout, "OCFS2 inode %"PRIu64" on \"%s\"\n", blkno, filename); ret = ocfs2_dir_iterate(fs, blkno, 0, NULL, walk_names_func, NULL); if (ret) { com_err(argv[0], ret, "while listing inode %"PRIu64" on \"%s\"\n", blkno, filename); goto out_free; } out_free: ocfs2_free(&buf); out_close: ret = ocfs2_close(fs); if (ret) { com_err(argv[0], ret, "while closing file \"%s\"", filename); } out: return 0; } #endif /* DEBUG_EXE */ ocfs2-tools-ocfs2-tools-1.8.6/libocfs2/dir_iterate.h000066400000000000000000000034641347147137200222370ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * dir_iterate.h * * Structures for dir iteration for the OCFS2 userspace library. * * Copyright (C) 2004 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License, version 2, as published by the Free Software Foundation. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * * Authors: Joel Becker */ #ifndef _DIR_ITERATE_H #define _DIR_ITERATE_H struct dir_context { uint64_t dir; int flags; struct ocfs2_dinode *di; char *buf; int (*func)(uint64_t dir, int entry, struct ocfs2_dir_entry *dirent, uint64_t blocknr, int offset, int blocksize, char *buf, void *priv_data); void *priv_data; errcode_t errcode; }; extern int ocfs2_process_dir_block(ocfs2_filesys *fs, uint64_t blocknr, uint64_t blockcnt, uint16_t ext_flags, void *priv_data); #define OCFS2_DIR_PAD 4 #define OCFS2_DIR_ROUND (OCFS2_DIR_PAD - 1) #define OCFS2_DIR_MEMBER_LEN offsetof(struct ocfs2_dir_entry, name) #define OCFS2_DIR_REC_LEN(name_len) (((name_len) + OCFS2_DIR_MEMBER_LEN + \ OCFS2_DIR_ROUND) & \ ~OCFS2_DIR_ROUND) #endif /* _DIR_ITERATE_H */ ocfs2-tools-ocfs2-tools-1.8.6/libocfs2/dir_scan.c000066400000000000000000000137531347147137200215230ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * dir_scan.c * * Read all the entries in a directory. For the OCFS2 userspace * library. * * Copyright (C) 2005 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License, version 2, as published by the Free Software Foundation. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * * Authors: Manish Singh */ #include #include #include "ocfs2/ocfs2.h" #include "dir_util.h" struct _ocfs2_dir_scan { ocfs2_filesys *fs; int flags; char *buf; unsigned int bufsize; unsigned int total_bufsize; ocfs2_cached_inode *inode; uint64_t total_blocks; uint64_t blocks_read; unsigned int offset; }; static errcode_t get_more_dir_blocks(ocfs2_dir_scan *scan) { errcode_t ret; uint64_t blkno; uint64_t cblocks; if (scan->blocks_read == scan->total_blocks) return OCFS2_ET_ITERATION_COMPLETE; ret = ocfs2_extent_map_get_blocks(scan->inode, scan->blocks_read, 1, &blkno, &cblocks, NULL); if (ret) return ret; ret = ocfs2_read_dir_block(scan->fs, scan->inode->ci_inode, blkno, scan->buf); if (ret) return ret; scan->blocks_read++; scan->bufsize = scan->total_bufsize; scan->offset = 0; return 0; } static inline int valid_dirent(ocfs2_dir_scan *scan, struct ocfs2_dir_entry *dirent) { if (dirent->inode) { if ((scan->flags & OCFS2_DIR_SCAN_FLAG_EXCLUDE_DOTS) && is_dots(dirent->name, dirent->name_len)) return 0; else return 1; } return 0; } errcode_t ocfs2_get_next_dir_entry(ocfs2_dir_scan *scan, struct ocfs2_dir_entry *out_dirent) { errcode_t ret; struct ocfs2_dir_entry *dirent; do { if (scan->offset == scan->bufsize) { ret = get_more_dir_blocks(scan); if (ret == OCFS2_ET_ITERATION_COMPLETE) { memset(out_dirent, 0, sizeof(struct ocfs2_dir_entry)); return 0; } if (ret) return ret; } dirent = (struct ocfs2_dir_entry *) (scan->buf + scan->offset); if (((scan->offset + dirent->rec_len) > scan->fs->fs_blocksize) || (dirent->rec_len < 8) || ((dirent->rec_len % 4) != 0) || (((dirent->name_len & 0xFF)+8) > dirent->rec_len)) return OCFS2_ET_DIR_CORRUPTED; scan->offset += dirent->rec_len; } while (!valid_dirent(scan, dirent) || ocfs2_skip_dir_trailer(scan->fs, scan->inode->ci_inode, dirent, scan->offset)); memcpy(out_dirent, dirent, sizeof(struct ocfs2_dir_entry)); return 0; } errcode_t ocfs2_open_dir_scan(ocfs2_filesys *fs, uint64_t dir, int flags, ocfs2_dir_scan **ret_scan) { ocfs2_dir_scan *scan; errcode_t ret; ret = ocfs2_check_directory(fs, dir); if (ret) return ret; ret = ocfs2_malloc0(sizeof(struct _ocfs2_dir_scan), &scan); if (ret) return ret; scan->fs = fs; scan->flags = flags; ret = ocfs2_malloc_block(fs->fs_io, &scan->buf); if (ret) goto bail_scan; ret = ocfs2_read_cached_inode(fs, dir, &scan->inode); if (ret) goto bail_dir_block; scan->total_blocks = scan->inode->ci_inode->i_size / fs->fs_blocksize; /* * Should we check i_size % blocksize? * total_blocks <= i_clusters? */ scan->total_bufsize = fs->fs_blocksize; *ret_scan = scan; return 0; bail_dir_block: ocfs2_free(&scan->buf); bail_scan: ocfs2_free(&scan); return ret; } void ocfs2_close_dir_scan(ocfs2_dir_scan *scan) { if (!scan) return; ocfs2_free_cached_inode(scan->fs, scan->inode); ocfs2_free(&scan->buf); ocfs2_free(&scan); return; } #ifdef DEBUG_EXE #include #include #include static uint64_t read_number(const char *num) { uint64_t val; char *ptr; val = strtoull(num, &ptr, 0); if (!ptr || *ptr) return 0; return val; } static void print_usage(void) { fprintf(stderr, "Usage: dir_scan -i \n"); } extern int opterr, optind; extern char *optarg; int main(int argc, char *argv[]) { errcode_t ret; char *filename; uint64_t blkno; int c; ocfs2_filesys *fs; ocfs2_dir_scan *scan; int done; struct ocfs2_dir_entry *dirent; blkno = 0; initialize_ocfs_error_table(); while ((c = getopt(argc, argv, "i:")) != EOF) { switch (c) { case 'i': blkno = read_number(optarg); if (blkno <= OCFS2_SUPER_BLOCK_BLKNO) { fprintf(stderr, "Invalid inode block: %s\n", optarg); print_usage(); return 1; } break; default: print_usage(); return 1; break; } } if (optind >= argc) { fprintf(stderr, "Missing filename\n"); print_usage(); return 1; } filename = argv[optind]; ret = ocfs2_open(filename, OCFS2_FLAG_RO, 0, 0, &fs); if (ret) { com_err(argv[0], ret, "while opening file \"%s\"", filename); goto out; } ret = ocfs2_malloc0(sizeof(struct ocfs2_dir_entry), &dirent); if (ret) { com_err(argv[0], ret, "while allocating dirent buffer"); goto out_close; } if (blkno == 0) blkno = fs->fs_root_blkno; ret = ocfs2_open_dir_scan(fs, blkno, 0, &scan); if (ret) { com_err(argv[0], ret, "while opening dir scan"); goto out_free; } done = 0; while (!done) { ret = ocfs2_get_next_dir_entry(scan, dirent); if (ret) { com_err(argv[0], ret, "while getting next dirent"); goto out_close_scan; } if (dirent->rec_len) { dirent->name[dirent->name_len] = '\0'; fprintf(stdout, "%s\n", dirent->name); } else done = 1; } out_close_scan: ocfs2_close_dir_scan(scan); out_free: ocfs2_free(&dirent); out_close: ret = ocfs2_close(fs); if (ret) { com_err(argv[0], ret, "while closing file \"%s\"", filename); } out: return 0; } #endif /* DEBUG_EXE */ ocfs2-tools-ocfs2-tools-1.8.6/libocfs2/dir_util.h000066400000000000000000000022401347147137200215460ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * dir_util.h * * Structures for dir iteration for the OCFS2 userspace library. * * Copyright (C) 2004 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License, version 2, as published by the Free Software Foundation. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * * Authors: Joel Becker */ #ifndef _DIR_UTIL_H #define _DIR_UTIL_H static inline int is_dots(const char *name, unsigned int len) { if (len == 0) return 0; if (name[0] == '.') { if (len == 1) return 1; if (len == 2 && name[1] == '.') return 1; } return 0; } #endif /* _DIR_UTIL_H */ ocfs2-tools-ocfs2-tools-1.8.6/libocfs2/dirblock.c000066400000000000000000000316421347147137200215270ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * dirblock.c * * Directory block routines for the OCFS2 userspace library. * * Copyright (C) 2004 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License, version 2, as published by the Free Software Foundation. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * * This code is a port of e2fsprogs/lib/ext2fs/dirblock.c * Copyright (C) 1995, 1996 Theodore Ts'o. */ #define _XOPEN_SOURCE 600 /* Triggers magic in features.h */ #define _LARGEFILE64_SOURCE #include #include "ocfs2/byteorder.h" #include "ocfs2/ocfs2.h" unsigned int ocfs2_dir_trailer_blk_off(ocfs2_filesys *fs) { return fs->fs_blocksize - sizeof(struct ocfs2_dir_block_trailer); } struct ocfs2_dir_block_trailer *ocfs2_dir_trailer_from_block(ocfs2_filesys *fs, void *data) { char *p = data; p += ocfs2_dir_trailer_blk_off(fs); return (struct ocfs2_dir_block_trailer *)p; } int ocfs2_dir_has_trailer(ocfs2_filesys *fs, struct ocfs2_dinode *di) { if (ocfs2_support_inline_data(OCFS2_RAW_SB(fs->fs_super)) && (di->i_dyn_features & OCFS2_INLINE_DATA_FL)) return 0; if (ocfs2_supports_indexed_dirs(OCFS2_RAW_SB(fs->fs_super)) && di->i_dyn_features & OCFS2_INDEXED_DIR_FL) return 1; return ocfs2_meta_ecc(OCFS2_RAW_SB(fs->fs_super)); } int ocfs2_supports_dir_trailer(ocfs2_filesys *fs) { return ocfs2_meta_ecc(OCFS2_RAW_SB(fs->fs_super)) || ocfs2_supports_indexed_dirs(OCFS2_RAW_SB(fs->fs_super)); } int ocfs2_skip_dir_trailer(ocfs2_filesys *fs, struct ocfs2_dinode *di, struct ocfs2_dir_entry *de, unsigned long offset) { if (!ocfs2_dir_has_trailer(fs, di)) return 0; if (offset != ocfs2_dir_trailer_blk_off(fs)) return 0; return 1; } /* * We are sure there is prepared space for the trailer, no directory * entry will overlap with the trailer: * - if we rebuild the indexed tree for a directory, no dir entry * will overwrite the trailer's space. * - if we build the indexed tree by tunefs.ocfs2, it will enable * meta ecc feature before enable indexed dirs feature. Which * means space for each trailer is well prepared already. */ void ocfs2_init_dir_trailer(ocfs2_filesys *fs, struct ocfs2_dinode *di, uint64_t blkno, void *buf) { struct ocfs2_dir_block_trailer *trailer = ocfs2_dir_trailer_from_block(fs, buf); memset(trailer, 0, sizeof(struct ocfs2_dir_block_trailer)); memcpy(trailer->db_signature, OCFS2_DIR_TRAILER_SIGNATURE, strlen(OCFS2_DIR_TRAILER_SIGNATURE)); trailer->db_compat_rec_len = sizeof(struct ocfs2_dir_block_trailer); trailer->db_blkno = blkno; trailer->db_parent_dinode = di->i_blkno; } static void ocfs2_swap_dir_entry(struct ocfs2_dir_entry *dirent) { if (cpu_is_little_endian) return; dirent->inode = bswap_64(dirent->inode); dirent->rec_len = bswap_16(dirent->rec_len); } static errcode_t ocfs2_swap_dir_entries_direction(void *buf, uint64_t bytes, int to_cpu) { char *p, *end; struct ocfs2_dir_entry *dirent; unsigned int name_len, rec_len; errcode_t retval = 0; p = (char *) buf; end = (char *) buf + bytes; while (p < end-12) { dirent = (struct ocfs2_dir_entry *) p; if (to_cpu) ocfs2_swap_dir_entry(dirent); name_len = dirent->name_len; rec_len = dirent->rec_len; if (!to_cpu) ocfs2_swap_dir_entry(dirent); if ((rec_len < 12) || (rec_len % 4)) { rec_len = 12; retval = OCFS2_ET_DIR_CORRUPTED; } if (((name_len & 0xFF) + 12) > rec_len) retval = OCFS2_ET_DIR_CORRUPTED; p += rec_len; } return retval; } errcode_t ocfs2_swap_dir_entries_from_cpu(void *buf, uint64_t bytes) { return ocfs2_swap_dir_entries_direction(buf, bytes, 0); } errcode_t ocfs2_swap_dir_entries_to_cpu(void *buf, uint64_t bytes) { return ocfs2_swap_dir_entries_direction(buf, bytes, 1); } void ocfs2_swap_dir_trailer(struct ocfs2_dir_block_trailer *trailer) { if (cpu_is_little_endian) return; trailer->db_compat_inode = bswap_64(trailer->db_compat_inode); trailer->db_compat_rec_len = bswap_16(trailer->db_compat_rec_len); trailer->db_blkno = bswap_64(trailer->db_blkno); trailer->db_parent_dinode = bswap_64(trailer->db_parent_dinode); trailer->db_free_rec_len = bswap_16(trailer->db_free_rec_len); trailer->db_free_next = bswap_64(trailer->db_free_next); } errcode_t ocfs2_read_dir_block(ocfs2_filesys *fs, struct ocfs2_dinode *di, uint64_t block, void *buf) { errcode_t retval; int end = fs->fs_blocksize; struct ocfs2_dir_block_trailer *trailer = NULL; retval = ocfs2_read_blocks(fs, block, 1, buf); if (retval) goto out; if (ocfs2_dir_has_trailer(fs, di)) { end = ocfs2_dir_trailer_blk_off(fs); trailer = ocfs2_dir_trailer_from_block(fs, buf); retval = ocfs2_validate_meta_ecc(fs, buf, &trailer->db_check); if (retval) goto out; if (memcmp(trailer->db_signature, OCFS2_DIR_TRAILER_SIGNATURE, strlen(OCFS2_DIR_TRAILER_SIGNATURE))) { retval = OCFS2_ET_BAD_DIR_BLOCK_MAGIC; goto out; } } retval = ocfs2_swap_dir_entries_to_cpu(buf, end); if (retval) goto out; if (trailer) ocfs2_swap_dir_trailer(trailer); out: return retval; } errcode_t ocfs2_write_dir_block(ocfs2_filesys *fs, struct ocfs2_dinode *di, uint64_t block, void *inbuf) { errcode_t retval; char *buf = NULL; int end = fs->fs_blocksize; struct ocfs2_dir_block_trailer *trailer = NULL; retval = ocfs2_malloc_block(fs->fs_io, &buf); if (retval) return retval; memcpy(buf, inbuf, fs->fs_blocksize); if (ocfs2_dir_has_trailer(fs, di)) end = ocfs2_dir_trailer_blk_off(fs); retval = ocfs2_swap_dir_entries_from_cpu(buf, end); if (retval) goto out; /* * We can always set trailer - ocfs2_compute_meta_ecc() does * nothing if the filesystem doesn't have the feature turned on */ trailer = ocfs2_dir_trailer_from_block(fs, buf); if (ocfs2_dir_has_trailer(fs, di)) ocfs2_swap_dir_trailer(trailer); ocfs2_compute_meta_ecc(fs, buf, &trailer->db_check); retval = io_write_block(fs->fs_io, block, 1, buf); out: ocfs2_free(&buf); return retval; } static void ocfs2_swap_dx_entry(struct ocfs2_dx_entry *dx_entry) { dx_entry->dx_major_hash = bswap_32(dx_entry->dx_major_hash); dx_entry->dx_minor_hash = bswap_32(dx_entry->dx_minor_hash); dx_entry->dx_dirent_blk = bswap_64(dx_entry->dx_dirent_blk); } static void ocfs2_swap_dx_entry_list_to_cpu(struct ocfs2_dx_entry_list *dl_list) { int i; if (cpu_is_little_endian) return; dl_list->de_count = bswap_16(dl_list->de_count); dl_list->de_num_used = bswap_16(dl_list->de_num_used); for (i = 0; i < dl_list->de_count; i++) ocfs2_swap_dx_entry(&dl_list->de_entries[i]); } static void ocfs2_swap_dx_entry_list_from_cpu(struct ocfs2_dx_entry_list *dl_list) { int i; if (cpu_is_little_endian) return; for (i = 0; i < dl_list->de_count; i++) ocfs2_swap_dx_entry(&dl_list->de_entries[i]); dl_list->de_count = bswap_16(dl_list->de_count); dl_list->de_num_used = bswap_16(dl_list->de_num_used); } void ocfs2_swap_dx_root_to_cpu(ocfs2_filesys *fs, struct ocfs2_dx_root_block *dx_root) { if (cpu_is_little_endian) return; dx_root->dr_suballoc_slot = bswap_16(dx_root->dr_suballoc_slot); dx_root->dr_suballoc_bit = bswap_16(dx_root->dr_suballoc_bit); dx_root->dr_fs_generation = bswap_32(dx_root->dr_fs_generation); dx_root->dr_blkno = bswap_64(dx_root->dr_blkno); dx_root->dr_last_eb_blk = bswap_64(dx_root->dr_last_eb_blk); dx_root->dr_clusters = bswap_32(dx_root->dr_clusters); dx_root->dr_dir_blkno = bswap_64(dx_root->dr_dir_blkno); dx_root->dr_num_entries = bswap_32(dx_root->dr_num_entries); dx_root->dr_free_blk = bswap_64(dx_root->dr_free_blk); if (dx_root->dr_flags & OCFS2_DX_FLAG_INLINE) ocfs2_swap_dx_entry_list_to_cpu(&dx_root->dr_entries); else ocfs2_swap_extent_list_to_cpu(fs, dx_root, &dx_root->dr_list); } void ocfs2_swap_dx_root_from_cpu(ocfs2_filesys *fs, struct ocfs2_dx_root_block *dx_root) { if (cpu_is_little_endian) return; dx_root->dr_suballoc_slot = bswap_16(dx_root->dr_suballoc_slot); dx_root->dr_suballoc_bit = bswap_16(dx_root->dr_suballoc_bit); dx_root->dr_fs_generation = bswap_32(dx_root->dr_fs_generation); dx_root->dr_blkno = bswap_64(dx_root->dr_blkno); dx_root->dr_last_eb_blk = bswap_64(dx_root->dr_last_eb_blk); dx_root->dr_clusters = bswap_32(dx_root->dr_clusters); dx_root->dr_dir_blkno = bswap_64(dx_root->dr_dir_blkno); dx_root->dr_num_entries = bswap_32(dx_root->dr_num_entries); dx_root->dr_free_blk = bswap_64(dx_root->dr_free_blk); if (dx_root->dr_flags & OCFS2_DX_FLAG_INLINE) ocfs2_swap_dx_entry_list_from_cpu(&dx_root->dr_entries); else ocfs2_swap_extent_list_from_cpu(fs, dx_root, &dx_root->dr_list); } /* XXX: should use the errcode_t return value */ errcode_t ocfs2_read_dx_root(ocfs2_filesys *fs, uint64_t block, void *buf) { errcode_t ret; struct ocfs2_dx_root_block *dx_root; char *dx_root_buf = NULL; ret = ocfs2_malloc_block(fs->fs_io, &dx_root_buf); if (ret) goto out; ret = ocfs2_read_blocks(fs, block, 1, dx_root_buf); if (ret) goto out; dx_root = (struct ocfs2_dx_root_block *)dx_root_buf; ret = ocfs2_validate_meta_ecc(fs, dx_root_buf, &dx_root->dr_check); if (ret) goto out; if (memcmp(dx_root->dr_signature, OCFS2_DX_ROOT_SIGNATURE, strlen(OCFS2_DX_ROOT_SIGNATURE))) { ret = OCFS2_ET_DIR_CORRUPTED; goto out; } ocfs2_swap_dx_root_to_cpu(fs, dx_root); memcpy(buf, dx_root_buf, fs->fs_blocksize); ret = 0; out: if (dx_root_buf) ocfs2_free(&dx_root_buf); return ret; } errcode_t ocfs2_write_dx_root(ocfs2_filesys *fs, uint64_t block, char *buf) { errcode_t ret; char *dx_root_buf = NULL; struct ocfs2_dx_root_block *dx_root; if (!(fs->fs_flags & OCFS2_FLAG_RW)) return OCFS2_ET_RO_FILESYS; if ((block < OCFS2_SUPER_BLOCK_BLKNO) || (block > fs->fs_blocks)) return OCFS2_ET_BAD_BLKNO; ret = ocfs2_malloc_block(fs->fs_io, &dx_root_buf); if (ret) goto out; memcpy(dx_root_buf, buf, fs->fs_blocksize); dx_root = (struct ocfs2_dx_root_block *)dx_root_buf; ocfs2_swap_dx_root_from_cpu(fs, dx_root); ocfs2_compute_meta_ecc(fs, dx_root_buf, &dx_root->dr_check); ret = io_write_block(fs->fs_io, block, 1, dx_root_buf); if (!ret) fs->fs_flags |= OCFS2_FLAG_CHANGED; out: if (dx_root_buf) ocfs2_free(&dx_root_buf); return ret; } void ocfs2_swap_dx_leaf_to_cpu(struct ocfs2_dx_leaf *dx_leaf) { if (cpu_is_little_endian) return; dx_leaf->dl_blkno = bswap_64(dx_leaf->dl_blkno); dx_leaf->dl_fs_generation = bswap_32(dx_leaf->dl_fs_generation); ocfs2_swap_dx_entry_list_to_cpu(&dx_leaf->dl_list); } void ocfs2_swap_dx_leaf_from_cpu(struct ocfs2_dx_leaf *dx_leaf) { if (cpu_is_little_endian) return; dx_leaf->dl_blkno = bswap_64(dx_leaf->dl_blkno); dx_leaf->dl_fs_generation = bswap_32(dx_leaf->dl_fs_generation); ocfs2_swap_dx_entry_list_from_cpu(&dx_leaf->dl_list); } errcode_t ocfs2_read_dx_leaf(ocfs2_filesys *fs, uint64_t block, void *buf) { errcode_t ret; struct ocfs2_dx_leaf *dx_leaf; ret = ocfs2_read_blocks(fs, block, 1, buf); if (ret) return ret; dx_leaf = (struct ocfs2_dx_leaf *)buf; ret = ocfs2_validate_meta_ecc(fs, buf, &dx_leaf->dl_check); if (ret) return ret; if (memcmp(dx_leaf->dl_signature, OCFS2_DX_LEAF_SIGNATURE, strlen(OCFS2_DX_LEAF_SIGNATURE))) return OCFS2_ET_DIR_CORRUPTED; ocfs2_swap_dx_leaf_to_cpu(dx_leaf); return 0; } errcode_t ocfs2_write_dx_leaf(ocfs2_filesys *fs, uint64_t block, void *buf) { errcode_t ret; char *dx_leaf_buf = NULL; struct ocfs2_dx_leaf *dx_leaf; if (!(fs->fs_flags & OCFS2_FLAG_RW)) return OCFS2_ET_RO_FILESYS; if ((block < OCFS2_SUPER_BLOCK_BLKNO) || (block > fs->fs_blocks)) return OCFS2_ET_BAD_BLKNO; ret = ocfs2_malloc_block(fs->fs_io, &dx_leaf_buf); if (ret) goto out; memcpy(dx_leaf_buf, buf, fs->fs_blocksize); dx_leaf = (struct ocfs2_dx_leaf *)dx_leaf_buf; ocfs2_swap_dx_leaf_from_cpu(dx_leaf); ocfs2_compute_meta_ecc(fs, dx_leaf_buf, &dx_leaf->dl_check); ret = io_write_block(fs->fs_io, block, 1, dx_leaf_buf); if (ret) goto out; fs->fs_flags |= OCFS2_FLAG_CHANGED; out: if (dx_leaf_buf) ocfs2_free(&dx_leaf_buf); return ret; } int ocfs2_dir_indexed(struct ocfs2_dinode *di) { if (di->i_dyn_features & OCFS2_INDEXED_DIR_FL) return 1; return 0; } /* * Only use this when we already know the directory is indexed. */ static int __ocfs2_is_dir_trailer(ocfs2_filesys *fs, unsigned long de_off) { if (de_off == ocfs2_dir_trailer_blk_off(fs)) return 1; return 0; } int ocfs2_is_dir_trailer(ocfs2_filesys *fs, struct ocfs2_dinode *di, unsigned long de_off) { if (ocfs2_dir_has_trailer(fs, di)) { return __ocfs2_is_dir_trailer(fs, de_off); } return 0; } ocfs2-tools-ocfs2-tools-1.8.6/libocfs2/dlm.c000066400000000000000000000247361347147137200205200ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * dlm.c * * Interface the OCFS2 userspace library to the userspace DLM library * * Copyright (C) 2005 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License, version 2, as published by the Free Software Foundation. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * */ #define _XOPEN_SOURCE 600 /* Triggers magic in features.h */ #define _LARGEFILE64_SOURCE #include "ocfs2/ocfs2.h" #define DEFAULT_DLMFS_PATH "/dlm/" static errcode_t ocfs2_get_journal_blkno(ocfs2_filesys *fs, uint64_t *jrnl_blkno) { struct ocfs2_super_block *sb = OCFS2_RAW_SB(fs->fs_super); char sysfile[OCFS2_MAX_FILENAME_LEN]; int i; errcode_t ret = 0; for (i = 0; i < sb->s_max_slots; ++i) { snprintf (sysfile, sizeof(sysfile), ocfs2_system_inodes[JOURNAL_SYSTEM_INODE].si_name, i); ret = ocfs2_lookup(fs, fs->fs_sysdir_blkno, sysfile, strlen(sysfile), NULL, &(jrnl_blkno[i])); if (ret) goto bail; } bail: return ret; } errcode_t ocfs2_lock_down_cluster(ocfs2_filesys *fs) { struct ocfs2_super_block *sb = OCFS2_RAW_SB(fs->fs_super); uint64_t jrnl_blkno[OCFS2_MAX_SLOTS]; ocfs2_cached_inode *ci; errcode_t ret = 0; int i; ret = ocfs2_get_journal_blkno(fs, jrnl_blkno); if (ret) goto bail; ret = ocfs2_super_lock(fs); if (ret) goto bail; for (i = 0; i < sb->s_max_slots; ++i) { ret = ocfs2_read_cached_inode(fs, jrnl_blkno[i], &ci); if (ret) { ocfs2_super_unlock(fs); goto bail; } ret = ocfs2_meta_lock(fs, ci, O2DLM_LEVEL_EXMODE, O2DLM_TRYLOCK); if (ret) { ocfs2_super_unlock(fs); ocfs2_free_cached_inode(fs, ci); goto bail; } ocfs2_meta_unlock(fs, ci); ocfs2_free_cached_inode(fs, ci); } bail: return ret; } errcode_t ocfs2_release_cluster(ocfs2_filesys *fs) { errcode_t ret = 0; ret = ocfs2_super_unlock(fs); if (ret) goto bail; bail: return ret; } errcode_t ocfs2_fill_cluster_desc(ocfs2_filesys *fs, struct o2cb_cluster_desc *desc) { errcode_t ret = 0; struct ocfs2_super_block *sb = OCFS2_RAW_SB(fs->fs_super); if (!ocfs2_clusterinfo_valid(sb)) { desc->c_stack = NULL; desc->c_cluster = NULL; desc->c_flags = 0; return 0; } ret = ocfs2_malloc0(OCFS2_STACK_LABEL_LEN + 1, &desc->c_stack); if (ret) return ret; ret = ocfs2_malloc0(OCFS2_CLUSTER_NAME_LEN + 1, &desc->c_cluster); if (ret) { ocfs2_free(&desc->c_stack); return ret; } memcpy(desc->c_stack, sb->s_cluster_info.ci_stack, OCFS2_STACK_LABEL_LEN); memcpy(desc->c_cluster, sb->s_cluster_info.ci_cluster, OCFS2_CLUSTER_NAME_LEN); desc->c_flags = sb->s_cluster_info.ci_stackflags; return 0; } static errcode_t ocfs2_set_cluster_flags(ocfs2_filesys *fs, struct o2cb_cluster_desc *desc) { struct ocfs2_super_block *sb = OCFS2_RAW_SB(fs->fs_super); sb->s_cluster_info.ci_stackflags = 0; /* exit if classic o2cb (default cluster stack) */ if (!desc->c_stack) goto out; if (strcmp(desc->c_stack, OCFS2_CLASSIC_CLUSTER_STACK)) goto out; if ((desc->c_flags & OCFS2_CLUSTER_O2CB_GLOBAL_HEARTBEAT)) sb->s_cluster_info.ci_stackflags |= OCFS2_CLUSTER_O2CB_GLOBAL_HEARTBEAT; out: return 0; } static errcode_t ocfs2_set_cluster_incompats(ocfs2_filesys *fs, struct o2cb_cluster_desc *desc) { errcode_t ret = 0; struct ocfs2_super_block *sb = OCFS2_RAW_SB(fs->fs_super); /* if default stack, then disable both clusterinfo and userspace */ if (!desc->c_stack) { sb->s_feature_incompat &= ~OCFS2_FEATURE_INCOMPAT_USERSPACE_STACK; sb->s_feature_incompat &= ~OCFS2_FEATURE_INCOMPAT_CLUSTERINFO; goto out; } /* extended slot map has to be enabled for non default stack */ if (!ocfs2_uses_extended_slot_map(sb)) { sb->s_feature_incompat |= OCFS2_FEATURE_INCOMPAT_EXTENDED_SLOT_MAP; ret = ocfs2_format_slot_map(fs); if (ret) goto out; } /* if o2cb, then enable clusterinfo and disable userspace */ if (!strcmp(desc->c_stack, OCFS2_CLASSIC_CLUSTER_STACK)) { sb->s_feature_incompat |= OCFS2_FEATURE_INCOMPAT_CLUSTERINFO; sb->s_feature_incompat &= ~OCFS2_FEATURE_INCOMPAT_USERSPACE_STACK; goto out; } /* if not o2cb, then enable userspace only if clusterinfo disabled */ if (!(sb->s_feature_incompat & OCFS2_FEATURE_INCOMPAT_CLUSTERINFO)) sb->s_feature_incompat |= OCFS2_FEATURE_INCOMPAT_USERSPACE_STACK; else sb->s_feature_incompat &= ~OCFS2_FEATURE_INCOMPAT_USERSPACE_STACK; out: return ret; } errcode_t ocfs2_set_cluster_desc(ocfs2_filesys *fs, struct o2cb_cluster_desc *desc) { errcode_t ret = OCFS2_ET_INVALID_ARGUMENT; struct ocfs2_super_block *sb = OCFS2_RAW_SB(fs->fs_super); if (!desc->c_stack) { memset(sb->s_cluster_info.ci_stack, 0, OCFS2_STACK_LABEL_LEN); memset(sb->s_cluster_info.ci_cluster, 0, OCFS2_CLUSTER_NAME_LEN); } else { if (!o2cb_valid_stack_name(desc->c_stack)) goto out; if (!strcmp(desc->c_stack, OCFS2_CLASSIC_CLUSTER_STACK)) { if (!o2cb_valid_o2cb_cluster_name(desc->c_cluster)) goto out; } else { if (!o2cb_valid_cluster_name(desc->c_cluster)) goto out; } memcpy(sb->s_cluster_info.ci_stack, desc->c_stack, OCFS2_STACK_LABEL_LEN); memcpy(sb->s_cluster_info.ci_cluster, desc->c_cluster, OCFS2_CLUSTER_NAME_LEN); } ret = ocfs2_set_cluster_flags(fs, desc); if (ret) goto out; ret = ocfs2_set_cluster_incompats(fs, desc); if (ret) goto out; ret = ocfs2_write_super(fs); out: return ret; } errcode_t ocfs2_initialize_dlm(ocfs2_filesys *fs, const char *service) { struct o2dlm_ctxt *dlm_ctxt = NULL; errcode_t ret = 0; int stackglue_support; struct o2cb_cluster_desc cluster; struct o2cb_region_desc desc; char *stack_path; ret = ocfs2_fill_cluster_desc(fs, &cluster); if (ret) goto bail; ret = ocfs2_fill_heartbeat_desc(fs, &desc); if (ret) goto bail; ret = o2dlm_supports_stackglue(&stackglue_support); if (ret) goto bail; desc.r_service = (char *)service; desc.r_persist = 0; ret = o2cb_begin_group_join(&cluster, &desc); if (ret) goto bail; /* * We want to use dlmfs if we can, as it provides the full feature * set of libo2dlm. Any dlmfs with the 'stackglue' capability will * support all cluster stacks. An empty cluster.c_stack means * o2cb, which always supports dlmfs. * * If we're unlucky enough to have older userspace stack code, * we pass NULL to avoid dlmfs. */ if (stackglue_support || !cluster.c_stack) stack_path = DEFAULT_DLMFS_PATH; else stack_path = NULL; ret = o2dlm_initialize(stack_path, fs->uuid_str, &dlm_ctxt); if (ret) { /* What to do with an error code? */ /* Ignore the result of complete_group_join, as we want * to propagate our o2dlm_initialize() error */ o2cb_complete_group_join(&cluster, &desc, ret); goto bail; } ret = o2cb_complete_group_join(&cluster, &desc, 0); if (!ret) fs->fs_dlm_ctxt = dlm_ctxt; else o2dlm_destroy(dlm_ctxt); bail: return ret; } errcode_t ocfs2_shutdown_dlm(ocfs2_filesys *fs, const char *service) { errcode_t ret; struct o2cb_cluster_desc cluster; struct o2cb_region_desc desc; ret = o2dlm_destroy(fs->fs_dlm_ctxt); if (ret) goto bail; fs->fs_dlm_ctxt = NULL; ret = ocfs2_fill_cluster_desc(fs, &cluster); if (ret) goto bail; ret = ocfs2_fill_heartbeat_desc(fs, &desc); if (ret) goto bail; desc.r_service = (char *)service; desc.r_persist = 0; ret = o2cb_group_leave(&cluster, &desc); bail: return ret; } errcode_t ocfs2_super_lock(ocfs2_filesys *fs) { char lock_name[OCFS2_LOCK_ID_MAX_LEN]; errcode_t ret; ocfs2_encode_lockres(OCFS2_LOCK_TYPE_SUPER, OCFS2_SUPER_BLOCK_BLKNO, 0, 0, lock_name); ret = o2dlm_lock(fs->fs_dlm_ctxt, lock_name, O2DLM_TRYLOCK, O2DLM_LEVEL_EXMODE); return ret; } errcode_t ocfs2_super_unlock(ocfs2_filesys *fs) { char lock_name[OCFS2_LOCK_ID_MAX_LEN]; errcode_t ret; ocfs2_encode_lockres(OCFS2_LOCK_TYPE_SUPER, OCFS2_SUPER_BLOCK_BLKNO, 0, 0, lock_name); ret = o2dlm_unlock(fs->fs_dlm_ctxt, lock_name); return ret; } errcode_t ocfs2_meta_lock(ocfs2_filesys *fs, ocfs2_cached_inode *ci, enum o2dlm_lock_level level, int flags) { char lock_name[OCFS2_LOCK_ID_MAX_LEN]; errcode_t ret; ocfs2_encode_lockres(OCFS2_LOCK_TYPE_META, ci->ci_blkno, ci->ci_inode->i_generation, 0, lock_name); ret = o2dlm_lock(fs->fs_dlm_ctxt, lock_name, flags, level); return ret; } errcode_t ocfs2_meta_unlock(ocfs2_filesys *fs, ocfs2_cached_inode *ci) { char lock_name[OCFS2_LOCK_ID_MAX_LEN]; errcode_t ret; ocfs2_encode_lockres(OCFS2_LOCK_TYPE_META, ci->ci_blkno, ci->ci_inode->i_generation, 0, lock_name); ret = o2dlm_unlock(fs->fs_dlm_ctxt, lock_name); return ret; } #ifdef DEBUG_EXE #include #include #include #include #define DEBUG_SERVICE "debug" static void print_usage(void) { fprintf(stderr, "Usage: dlm \n"); } extern int opterr, optind; extern char *optarg; int main(int argc, char *argv[]) { errcode_t ret; int c; char *filename; ocfs2_filesys *fs = NULL; char *progname; initialize_ocfs_error_table(); initialize_o2dl_error_table(); if (argc < 2) { print_usage(); exit(1); } filename = argv[1]; progname = basename(argv[0]); ret = ocfs2_open(filename, OCFS2_FLAG_RO, 0, 0, &fs); if (ret) { com_err(progname, ret, "while opening file \"%s\"", filename); goto out; } ret = ocfs2_initialize_dlm(fs, DEBUG_SERVICE); if (ret) { com_err(progname, ret, "while initializing dlm"); goto out; } printf("DLM initialized\n"); ret = ocfs2_lock_down_cluster(fs); if (ret) { com_err(progname, ret, "while locking cluster"); goto out; } printf("Cluster is locked\nPress any key to continue..."); c = getchar(); ret = ocfs2_release_cluster(fs); if (ret) { com_err(progname, ret, "while releasing cluster"); goto out; } printf("Cluster released\n"); out: if (fs->fs_dlm_ctxt) { ret = ocfs2_shutdown_dlm(fs, DEBUG_SERVICE); if (ret) com_err(progname, ret, "while shutting down dlm"); } if (fs) { ret = ocfs2_close(fs); if (ret) com_err(progname, ret, "while closing file \"%s\"", filename); } return 0; } #endif /* DEBUG_EXE */ ocfs2-tools-ocfs2-tools-1.8.6/libocfs2/expanddir.c000066400000000000000000000155051347147137200217140ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * expanddir.c * * Expands an OCFS2 directory. For the OCFS2 userspace library. * * Copyright (C) 2004 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License, version 2, as published by the Free Software Foundation. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * * This code is a port of e2fsprogs/lib/ext2fs/expanddir.c * Copyright (C) 1993, 1999 Theodore Ts'o. */ #define _XOPEN_SOURCE 600 /* Triggers magic in features.h */ #define _LARGEFILE64_SOURCE #include #include #include #if HAVE_UNISTD_H #include #endif #include "ocfs2/ocfs2.h" /* * ocfs2_expand_dir() * */ errcode_t ocfs2_expand_dir(ocfs2_filesys *fs, uint64_t dir) { errcode_t ret = 0; ocfs2_cached_inode *cinode = NULL; uint64_t used_blks; uint64_t totl_blks; uint64_t new_blk; uint64_t contig; char *buf = NULL; struct ocfs2_dir_entry *de; if (!(fs->fs_flags & OCFS2_FLAG_RW)) return OCFS2_ET_RO_FILESYS; /* ensure it is a dir */ ret = ocfs2_check_directory(fs, dir); if (ret) goto bail; /* read inode */ ret = ocfs2_read_cached_inode(fs, dir, &cinode); if (ret) goto bail; if (ocfs2_support_inline_data(OCFS2_RAW_SB(fs->fs_super)) && cinode->ci_inode->i_dyn_features & OCFS2_INLINE_DATA_FL) { ret = ocfs2_convert_inline_data_to_extents(cinode); if ((ret == 0) && ocfs2_supports_indexed_dirs(OCFS2_RAW_SB(fs->fs_super))) { ret = ocfs2_dx_dir_build(fs, dir); } goto bail; } /* This relies on the fact that i_size of a directory is a * multiple of blocksize */ used_blks = cinode->ci_inode->i_size >> OCFS2_RAW_SB(fs->fs_super)->s_blocksize_bits; totl_blks = ocfs2_clusters_to_blocks(fs, cinode->ci_inode->i_clusters); if (used_blks >= totl_blks) { ocfs2_free_cached_inode(fs, cinode); cinode = NULL; /* extend the directory */ ret = ocfs2_extend_allocation(fs, dir, 1); if (ret) goto bail; ret = ocfs2_read_cached_inode(fs, dir, &cinode); if (ret) goto bail; } /* get the next free block */ ret = ocfs2_extent_map_get_blocks(cinode, used_blks, 1, &new_blk, &contig, NULL); if (ret) goto bail; ret = ocfs2_malloc_block(fs->fs_io, &buf); if (ret) goto bail; memset(buf, 0, fs->fs_blocksize); de = (struct ocfs2_dir_entry *)buf; if (ocfs2_dir_has_trailer(fs, cinode->ci_inode)) { de->rec_len = ocfs2_dir_trailer_blk_off(fs); ocfs2_init_dir_trailer(fs, cinode->ci_inode, new_blk, buf); } else de->rec_len = fs->fs_blocksize; /* write new dir block */ ret = ocfs2_write_dir_block(fs, cinode->ci_inode, new_blk, buf); if (ret) goto bail; /* increase the size */ cinode->ci_inode->i_size += fs->fs_blocksize; /* update the size of the inode */ ret = ocfs2_write_cached_inode(fs, cinode); if (ret) goto bail; bail: if (buf) ocfs2_free(&buf); if (cinode) ocfs2_free_cached_inode(fs, cinode); return ret; } static void ocfs2_fill_initial_dirents(uint64_t dir, uint64_t parent, char *start, uint16_t size) { struct ocfs2_dir_entry *de = (struct ocfs2_dir_entry *)start; de->inode = dir; de->name_len = 1; de->rec_len = OCFS2_DIR_REC_LEN(de->name_len); de->name[0] = '.'; de->file_type = OCFS2_FT_DIR; de = (struct ocfs2_dir_entry *) ((char *)de + de->rec_len); de->inode = parent; de->rec_len = size - OCFS2_DIR_REC_LEN(1); de->name_len = 2; strcpy(de->name, ".."); de->file_type = OCFS2_FT_DIR; } errcode_t ocfs2_init_dir(ocfs2_filesys *fs, uint64_t dir, uint64_t parent_dir) { errcode_t ret = 0; ocfs2_cached_inode *cinode = NULL; struct ocfs2_dinode *parent; uint16_t size; uint64_t blkno, contig; char *buf = NULL, *data = NULL; struct ocfs2_inline_data *inline_data; if (!(fs->fs_flags & OCFS2_FLAG_RW)) return OCFS2_ET_RO_FILESYS; /* ensure it is a dir */ ret = ocfs2_check_directory(fs, dir); if (ret) goto bail; /* read inode */ ret = ocfs2_read_cached_inode(fs, dir, &cinode); if (ret) goto bail; ret = ocfs2_malloc_block(fs->fs_io, &buf); if (ret) goto bail; if (ocfs2_support_inline_data(OCFS2_RAW_SB(fs->fs_super))) { if (!(cinode->ci_inode->i_dyn_features & OCFS2_INLINE_DATA_FL)) return OCFS2_ET_DIR_CORRUPTED; inline_data = &cinode->ci_inode->id2.i_data; data = (char *)inline_data->id_data; size = inline_data->id_count; } else { if (cinode->ci_inode->i_dyn_features & OCFS2_INLINE_DATA_FL) return OCFS2_ET_DIR_CORRUPTED; ret = ocfs2_expand_dir(fs, dir); if (ret) goto bail; ocfs2_free_cached_inode(fs, cinode); cinode = NULL; ret = ocfs2_read_cached_inode(fs, dir, &cinode); if (ret) goto bail; ret = ocfs2_extent_map_get_blocks(cinode, 0, 1, &blkno, &contig, NULL); if (ret) goto bail; data = buf; size = fs->fs_blocksize; if (ocfs2_supports_dir_trailer(fs)) size = ocfs2_dir_trailer_blk_off(fs); } /* set '..' and '.' in dir. */ ocfs2_fill_initial_dirents(dir, parent_dir, data, size); /* And set the trailer if necessary */ if (!(cinode->ci_inode->i_dyn_features & OCFS2_INLINE_DATA_FL) && ocfs2_supports_dir_trailer(fs)) ocfs2_init_dir_trailer(fs, cinode->ci_inode, blkno, data); if (!(cinode->ci_inode->i_dyn_features & OCFS2_INLINE_DATA_FL)) { ret = ocfs2_write_dir_block(fs, cinode->ci_inode, blkno, buf); if (ret) goto bail; } /* * Only build indexed tree if the directory is initiated as non-inline. * Otherwise, the indexed tree will be build when convert the inlined * directory to extent in ocfs2_expand_dir() */ if (ocfs2_supports_indexed_dirs(OCFS2_RAW_SB(fs->fs_super)) && !(cinode->ci_inode->i_dyn_features & OCFS2_INLINE_DATA_FL)) { ret = ocfs2_dx_dir_build(fs, dir); if (ret) goto bail; /* * Re-read the 'cached inode' as ocfs2_dx_dir_build() * may have written out changes which won't be * reflected in our copy. */ ret = ocfs2_read_cached_inode(fs, dir, &cinode); if (ret) goto bail; } /* set link count of the parent */ ret = ocfs2_read_inode(fs, parent_dir, buf); if (ret) goto bail; parent = (struct ocfs2_dinode *)buf; parent->i_links_count++; ret = ocfs2_write_inode(fs, parent_dir, buf); if (ret) goto bail; /* increase the size */ cinode->ci_inode->i_size = size; /* update the inode */ ret = ocfs2_write_cached_inode(fs, cinode); bail: if (buf) ocfs2_free(&buf); if (cinode) ocfs2_free_cached_inode(fs, cinode); return ret; } ocfs2-tools-ocfs2-tools-1.8.6/libocfs2/extend_file.c000066400000000000000000000206721347147137200222250ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * extend_file.c * * Adds extents to an OCFS2 inode. For the OCFS2 userspace library. * * Copyright (C) 2004 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License, version 2, as published by the Free Software Foundation. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. */ #define _XOPEN_SOURCE 600 /* Triggers magic in features.h */ #define _LARGEFILE64_SOURCE #include #if HAVE_UNISTD_H #include #endif #include #include #include #include "ocfs2/ocfs2.h" #include "extent_tree.h" /* * Insert an extent into an inode btree. */ errcode_t ocfs2_inode_insert_extent(ocfs2_filesys *fs, uint64_t ino, uint32_t cpos, uint64_t c_blkno, uint32_t clusters, uint16_t flag) { errcode_t ret; ocfs2_cached_inode *ci = NULL; ret = ocfs2_read_cached_inode(fs, ino, &ci); if (ret) goto bail; ret = ocfs2_cached_inode_insert_extent(ci, cpos, c_blkno, clusters, flag); if (ret) goto bail; ret = ocfs2_write_cached_inode(fs, ci); bail: if (ci) ocfs2_free_cached_inode(fs, ci); return ret; } errcode_t ocfs2_cached_inode_insert_extent(ocfs2_cached_inode *ci, uint32_t cpos, uint64_t c_blkno, uint32_t clusters, uint16_t flag) { struct ocfs2_extent_tree et; ocfs2_init_dinode_extent_tree(&et, ci->ci_fs, (char *)ci->ci_inode, ci->ci_inode->i_blkno); return ocfs2_tree_insert_extent(ci->ci_fs, &et, cpos, c_blkno, clusters, flag); } errcode_t ocfs2_cached_inode_extend_allocation(ocfs2_cached_inode *ci, uint32_t new_clusters) { errcode_t ret = 0; uint32_t n_clusters = 0, cpos; uint64_t blkno, file_size; ocfs2_filesys *fs = ci->ci_fs; file_size = ci->ci_inode->i_size; cpos = (file_size + fs->fs_clustersize - 1) / fs->fs_clustersize; while (new_clusters) { n_clusters = 1; ret = ocfs2_new_clusters(fs, 1, new_clusters, &blkno, &n_clusters); if (ret) break; ret = ocfs2_cached_inode_insert_extent(ci, cpos, blkno, n_clusters, 0); if (ret) { /* XXX: We don't wan't to overwrite the error * from insert_extent(). But we probably need * to BE LOUDLY UPSET. */ ocfs2_free_clusters(fs, n_clusters, blkno); break; } new_clusters -= n_clusters; cpos += n_clusters; } return ret; } errcode_t ocfs2_extend_allocation(ocfs2_filesys *fs, uint64_t ino, uint32_t new_clusters) { errcode_t ret; ocfs2_cached_inode *ci = NULL; ret = ocfs2_read_cached_inode(fs, ino, &ci); if (ret) goto bail; ret = ocfs2_cached_inode_extend_allocation(ci, new_clusters); if (ret) goto bail; ret = ocfs2_write_cached_inode(fs, ci); bail: if (ci) ocfs2_free_cached_inode(fs, ci); return ret; } errcode_t ocfs2_extend_file(ocfs2_filesys *fs, uint64_t ino, uint64_t new_size) { errcode_t ret = 0; char *buf = NULL; struct ocfs2_dinode* di = NULL; if (!(fs->fs_flags & OCFS2_FLAG_RW)) return OCFS2_ET_RO_FILESYS; ret = ocfs2_malloc_block(fs->fs_io, &buf); if (ret) return ret; ret = ocfs2_read_inode(fs, ino, buf); if (ret) goto out_free_buf; di = (struct ocfs2_dinode *)buf; if (di->i_size >= new_size) { ret = EINVAL; goto out_free_buf; } di->i_size = new_size; ret = ocfs2_write_inode(fs, ino, buf); out_free_buf: if (buf) ocfs2_free(&buf); return ret; } errcode_t ocfs2_allocate_unwritten_extents(ocfs2_filesys *fs, uint64_t ino, uint64_t offset, uint64_t len) { errcode_t ret = 0; uint32_t n_clusters = 0, cpos; uint64_t p_blkno = 0, v_blkno, v_end, contig_blocks, wanted_blocks; ocfs2_cached_inode *ci = NULL; if (!(fs->fs_flags & OCFS2_FLAG_RW)) return OCFS2_ET_RO_FILESYS; if (!ocfs2_writes_unwritten_extents(OCFS2_RAW_SB(fs->fs_super))) return OCFS2_ET_RO_UNSUPP_FEATURE; ret = ocfs2_read_cached_inode(fs, ino, &ci); if (ret) goto out; if (!(ci->ci_inode->i_flags & OCFS2_VALID_FL)) return OCFS2_ET_INODE_NOT_VALID; if (ci->ci_inode->i_flags & OCFS2_SYSTEM_FL) return OCFS2_ET_INVALID_ARGUMENT; if (!S_ISREG(ci->ci_inode->i_mode)) return OCFS2_ET_INVALID_ARGUMENT; v_blkno = offset / fs->fs_blocksize; v_end = (offset + len - 1) / fs->fs_blocksize; while (v_blkno <= v_end) { ret = ocfs2_extent_map_get_blocks(ci, v_blkno, 1, &p_blkno, &contig_blocks, NULL); if (ret) continue; if (p_blkno) { v_blkno += contig_blocks; continue; } /* * There is a hole, so we have to allocate the space and * insert the unwritten extents. */ wanted_blocks = ocfs2_min(contig_blocks, v_end - v_blkno + 1); n_clusters = ocfs2_clusters_in_blocks(fs, wanted_blocks); ret = ocfs2_new_clusters(fs, 1, n_clusters, &p_blkno, &n_clusters); if (ret || n_clusters == 0) break; cpos = ocfs2_blocks_to_clusters(fs, v_blkno); ret = ocfs2_cached_inode_insert_extent(ci, cpos, p_blkno, n_clusters, OCFS2_EXT_UNWRITTEN); if (ret) { /* * XXX: We don't wan't to overwrite the error * from insert_extent(). But we probably need * to BE LOUDLY UPSET. */ ocfs2_free_clusters(fs, n_clusters, p_blkno); goto out; } /* save up what we have done. */ ret = ocfs2_write_cached_inode(fs, ci); if (ret) goto out; v_blkno = ocfs2_clusters_to_blocks(fs, cpos + n_clusters); } if (ci->ci_inode->i_size <= offset + len) { ci->ci_inode->i_size = offset + len; ret = ocfs2_write_cached_inode(fs, ci); } out: if (ci) ocfs2_free_cached_inode(fs, ci); return ret; } /* * Mark the already-existing extent at cpos as written for len clusters. * * If the existing extent is larger than the request, initiate a * split. An attempt will be made at merging with adjacent extents. * */ int ocfs2_mark_extent_written(ocfs2_filesys *fs, struct ocfs2_dinode *di, uint32_t cpos, uint32_t len, uint64_t p_blkno) { struct ocfs2_extent_tree et; if (!ocfs2_writes_unwritten_extents(OCFS2_RAW_SB(fs->fs_super))) return OCFS2_ET_UNSUPP_FEATURE; ocfs2_init_dinode_extent_tree(&et, fs, (char *)di, di->i_blkno); return ocfs2_change_extent_flag(fs, &et, cpos, len, p_blkno, 0, OCFS2_EXT_UNWRITTEN); } #ifdef DEBUG_EXE #include #include #include #include static void print_usage(void) { fprintf(stdout, "debug_extend_file -i -c \n"); } static uint64_t read_number(const char *num) { uint64_t val; char *ptr; val = strtoull(num, &ptr, 0); if (!ptr || *ptr) return 0; return val; } extern int opterr, optind; extern char *optarg; int main(int argc, char *argv[]) { errcode_t ret; char *filename; ocfs2_filesys *fs; uint64_t ino = 0; uint32_t new_clusters = 0; int c; initialize_ocfs_error_table(); while ((c = getopt(argc, argv, "i:c:")) != EOF) { switch (c) { case 'i': ino = read_number(optarg); if (ino <= OCFS2_SUPER_BLOCK_BLKNO) { fprintf(stderr, "Invalid inode block: %s\n", optarg); print_usage(); return 1; } break; case 'c': new_clusters = read_number(optarg); if (!new_clusters) { fprintf(stderr, "Invalid cluster count: %s\n", optarg); print_usage(); return 1; } break; default: print_usage(); return 1; break; } } if (!ino) { fprintf(stderr, "You must specify an inode block\n"); print_usage(); return 1; } if (!new_clusters) { fprintf(stderr, "You must specify how many clusters to extend\n"); print_usage(); return 1; } if (optind >= argc) { fprintf(stderr, "Missing filename\n"); print_usage(); return 1; } filename = argv[optind]; ret = ocfs2_open(filename, OCFS2_FLAG_RW, 0, 0, &fs); if (ret) { com_err(argv[0], ret, "while opening file \"%s\"", filename); goto out; } ret = ocfs2_extend_allocation(fs, ino, new_clusters); if (ret) { com_err(argv[0], ret, "while extending inode %"PRIu64, ino); } ret = ocfs2_close(fs); if (ret) { com_err(argv[0], ret, "while closing file \"%s\"", filename); } out: return !!ret; } #endif /* DEBUG_EXE */ ocfs2-tools-ocfs2-tools-1.8.6/libocfs2/extent_map.c000066400000000000000000000250361347147137200221020ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * extent_map.c * * In-memory extent map for the OCFS2 userspace library. * * Copyright (C) 2004 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License, version 2, as published by the Free Software Foundation. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. */ #define _XOPEN_SOURCE 600 /* Triggers magic in features.h */ #define _LARGEFILE64_SOURCE #include #include #include #include "ocfs2/ocfs2.h" #include "extent_map.h" /* * Return the 1st index within el which contains an extent start * larger than v_cluster. */ static int ocfs2_search_for_hole_index(struct ocfs2_extent_list *el, uint32_t v_cluster) { int i; struct ocfs2_extent_rec *rec; for(i = 0; i < el->l_next_free_rec; i++) { rec = &el->l_recs[i]; if (v_cluster < rec->e_cpos) break; } return i; } /* * Figure out the size of a hole which starts at v_cluster within the given * extent list. * * If there is no more allocation past v_cluster, we return the maximum * cluster size minus v_cluster. * * If we have in-inode extents, then el points to the dinode list and * eb_buf is NULL. Otherwise, eb_buf should point to the extent block * containing el. */ static int ocfs2_figure_hole_clusters(ocfs2_cached_inode *cinode, struct ocfs2_extent_list *el, char *eb_buf, uint32_t v_cluster, uint32_t *num_clusters) { int ret, i; char *next_eb_buf = NULL; struct ocfs2_extent_block *eb, *next_eb; i = ocfs2_search_for_hole_index(el, v_cluster); if (i == el->l_next_free_rec && eb_buf) { eb = (struct ocfs2_extent_block *)eb_buf; /* * Check the next leaf for any extents. */ if (eb->h_next_leaf_blk == 0) goto no_more_extents; ret = ocfs2_malloc_block(cinode->ci_fs->fs_io, &next_eb_buf); if (ret) goto out; ret = ocfs2_read_extent_block(cinode->ci_fs, eb->h_next_leaf_blk, next_eb_buf); if (ret) goto out; next_eb = (struct ocfs2_extent_block *)next_eb_buf; el = &next_eb->h_list; i = ocfs2_search_for_hole_index(el, v_cluster); if (i > 0) { if ((i > 1) || ocfs2_rec_clusters(el->l_tree_depth, &el->l_recs[0])) { ret = OCFS2_ET_CORRUPT_EXTENT_BLOCK; goto out; } } } no_more_extents: if (i == el->l_next_free_rec) { /* * We're at the end of our existing allocation. Just * return the maximum number of clusters we could * possibly allocate. */ *num_clusters = UINT32_MAX - v_cluster; } else *num_clusters = el->l_recs[i].e_cpos - v_cluster; ret = 0; out: if (next_eb_buf) ocfs2_free(&next_eb_buf); return ret; } errcode_t ocfs2_get_clusters(ocfs2_cached_inode *cinode, uint32_t v_cluster, uint32_t *p_cluster, uint32_t *num_clusters, uint16_t *extent_flags) { int i; uint16_t flags = 0; errcode_t ret = 0; ocfs2_filesys *fs = cinode->ci_fs; struct ocfs2_dinode *di; struct ocfs2_extent_block *eb; struct ocfs2_extent_list *el; struct ocfs2_extent_rec *rec; char *eb_buf = NULL; uint32_t coff; di = cinode->ci_inode; el = &di->id2.i_list; if (el->l_tree_depth) { ret = ocfs2_find_leaf(fs, di, v_cluster, &eb_buf); if (ret) goto out; eb = (struct ocfs2_extent_block *) eb_buf; el = &eb->h_list; if (el->l_tree_depth) { ret = OCFS2_ET_CORRUPT_EXTENT_BLOCK; goto out; } } i = ocfs2_search_extent_list(el, v_cluster); if (i == -1) { /* * A hole was found. Return some canned values that * callers can key on. If asked for, num_clusters will * be populated with the size of the hole. */ *p_cluster = 0; if (num_clusters) { ret = ocfs2_figure_hole_clusters(cinode, el, eb_buf, v_cluster, num_clusters); if (ret) goto out; } } else { rec = &el->l_recs[i]; assert(v_cluster >= rec->e_cpos); if (!rec->e_blkno) { ret = OCFS2_ET_BAD_BLKNO; goto out; } coff = v_cluster - rec->e_cpos; *p_cluster = ocfs2_blocks_to_clusters(fs, rec->e_blkno); *p_cluster = *p_cluster + coff; if (num_clusters) *num_clusters = ocfs2_rec_clusters(el->l_tree_depth, rec) - coff; flags = rec->e_flags; } if (extent_flags) *extent_flags = flags; out: if (eb_buf) ocfs2_free(&eb_buf); return ret; } errcode_t ocfs2_xattr_get_clusters(ocfs2_filesys *fs, struct ocfs2_extent_list *el, uint64_t el_blkno, char *el_blk, uint32_t v_cluster, uint32_t *p_cluster, uint32_t *num_clusters, uint16_t *extent_flags) { int i; errcode_t ret = 0; struct ocfs2_extent_block *eb; struct ocfs2_extent_rec *rec; char *eb_buf = NULL; uint32_t coff; if (el->l_tree_depth) { ret = ocfs2_tree_find_leaf(fs, el, el_blkno, el_blk, v_cluster, &eb_buf); if (ret) goto out; eb = (struct ocfs2_extent_block *)eb_buf; el = &eb->h_list; if (el->l_tree_depth) { ret = OCFS2_ET_CORRUPT_EXTENT_BLOCK; goto out; } } i = ocfs2_search_extent_list(el, v_cluster); if (i == -1) { ret = -1; goto out; } else { rec = &el->l_recs[i]; assert(v_cluster >= rec->e_cpos); if (!rec->e_blkno) { ret = OCFS2_ET_BAD_BLKNO; goto out; } coff = v_cluster - rec->e_cpos; *p_cluster = ocfs2_blocks_to_clusters(fs, rec->e_blkno); *p_cluster = *p_cluster + coff; if (num_clusters) *num_clusters = ocfs2_rec_clusters(el->l_tree_depth, rec) - coff; if (extent_flags) *extent_flags = rec->e_flags; } out: if (eb_buf) ocfs2_free(&eb_buf); return ret; } errcode_t ocfs2_extent_map_get_blocks(ocfs2_cached_inode *cinode, uint64_t v_blkno, int count, uint64_t *p_blkno, uint64_t *ret_count, uint16_t *extent_flags) { errcode_t ret; int bpc; uint32_t cpos, num_clusters = -1, p_cluster = -1; uint64_t boff = 0; ocfs2_filesys *fs = cinode->ci_fs; bpc = ocfs2_clusters_to_blocks(fs, 1); cpos = ocfs2_blocks_to_clusters(fs, v_blkno); ret = ocfs2_get_clusters(cinode, cpos, &p_cluster, &num_clusters, extent_flags); if (ret) goto out; /* * p_cluster == 0 indicates a hole. */ if (p_cluster) { boff = ocfs2_clusters_to_blocks(fs, p_cluster); boff += (v_blkno & (uint64_t)(bpc - 1)); } *p_blkno = boff; if (ret_count) { *ret_count = ocfs2_clusters_to_blocks(fs, num_clusters); *ret_count -= v_blkno & (uint64_t)(bpc - 1); } out: return ret; } errcode_t ocfs2_get_last_cluster_offset(ocfs2_filesys *fs, struct ocfs2_dinode *di, uint32_t *v_cluster) { errcode_t ret = 0; char *buf = NULL; struct ocfs2_extent_list *el = NULL; struct ocfs2_extent_rec *er = NULL; el = &di->id2.i_list; *v_cluster = 0; if (!el->l_next_free_rec) return 0; if (el->l_tree_depth) { ret = ocfs2_malloc_block(fs->fs_io, &buf); if (ret) goto bail; ret = ocfs2_read_extent_block(fs, di->i_last_eb_blk, buf); if (ret) goto bail; el = &((struct ocfs2_extent_block *)buf)->h_list; if (!el->l_next_free_rec || (el->l_next_free_rec == 1 && ocfs2_is_empty_extent(&el->l_recs[0]))) { ret = OCFS2_ET_CORRUPT_EXTENT_BLOCK; goto bail; } } er = &el->l_recs[el->l_next_free_rec - 1]; *v_cluster = er->e_cpos + er->e_leaf_clusters - 1; bail: if (buf) ocfs2_free(&buf); return ret; } #ifdef DEBUG_EXE #include #include #include enum debug_op { OP_NONE = 0, OP_LOOKUP_BLOCK, }; static uint64_t read_number(const char *num) { uint64_t val; char *ptr; val = strtoull(num, &ptr, 0); if (!ptr || *ptr) return 0; return val; } static int read_b_numbers(const char *num, uint64_t *blkno, int *count) { uint64_t val; char *ptr; val = strtoull(num, &ptr, 0); if (!ptr) return 1; if (*ptr != ':') return 1; *blkno = val; ptr++; val = strtoull(ptr, &ptr, 0); if (!ptr || *ptr) return 1; if (val > INT_MAX) return 1; *count = (int)val; return 0; } static void print_usage(void) { fprintf(stderr, "Usage: extent_map -i -b : \n"); } extern int opterr, optind; extern char *optarg; int main(int argc, char *argv[]) { errcode_t ret; uint64_t blkno, contig, blkoff = 0; uint16_t ext_flags; int count = 0; int c, op = 0; char *filename; ocfs2_filesys *fs; ocfs2_cached_inode *cinode; blkno = OCFS2_SUPER_BLOCK_BLKNO; initialize_ocfs_error_table(); while ((c = getopt(argc, argv, "i:b:")) != EOF) { switch (c) { case 'i': blkno = read_number(optarg); if (blkno <= OCFS2_SUPER_BLOCK_BLKNO) { fprintf(stderr, "Invalid inode block: %s\n", optarg); print_usage(); return 1; } break; case 'b': if (op) { fprintf(stderr, "Cannot specify more than one operation\n"); print_usage(); return 1; } if (read_b_numbers(optarg, &blkoff, &count)) { fprintf(stderr, "Invalid block range: %s\n", optarg); print_usage(); return 1; } op = OP_LOOKUP_BLOCK; break; default: print_usage(); return 1; break; } } if (!op) { fprintf(stderr, "Missing operation\n"); print_usage(); return 1; } if (optind >= argc) { fprintf(stderr, "Missing filename\n"); print_usage(); return 1; } filename = argv[optind]; ret = ocfs2_open(filename, OCFS2_FLAG_RO, 0, 0, &fs); if (ret) { com_err(argv[0], ret, "while opening file \"%s\"", filename); goto out; } ret = ocfs2_read_cached_inode(fs, blkno, &cinode); if (ret) { com_err(argv[0], ret, "while reading inode %"PRIu64, blkno); goto out_close; } fprintf(stdout, "OCFS2 inode %"PRIu64" on \"%s\" has depth %"PRId16"\n", blkno, filename, cinode->ci_inode->id2.i_list.l_tree_depth); ret = ocfs2_extent_map_get_blocks(cinode, blkoff, count, &blkno, &contig, &ext_flags); if (ret) { com_err(argv[0], ret, "looking up block range %"PRIu64":%d", blkoff, count); goto out_free; } fprintf(stdout, "Lookup of block range %"PRIu64":%d returned %"PRIu64":%"PRIu64"\n", blkoff, count, blkno, contig); out_free: ocfs2_free_cached_inode(fs, cinode); out_close: ret = ocfs2_close(fs); if (ret) { com_err(argv[0], ret, "while closing file \"%s\"", filename); } out: return 0; } #endif /* DEBUG_EXE */ ocfs2-tools-ocfs2-tools-1.8.6/libocfs2/extent_map.h000066400000000000000000000023751347147137200221100ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * extent_map.h * * Internal extent map structures for the OCFS2 userspace library. * * Copyright (C) 2004 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License, version 2, as published by the Free Software Foundation. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * * Authors: Joel Becker */ #ifndef _EXTENT_MAP_H #define _EXTENT_MAP_H #include "ocfs2/kernel-rbtree.h" typedef struct _ocfs2_extent_map_entry ocfs2_extent_map_entry; struct _ocfs2_extent_map { struct rb_root em_extents; uint32_t em_clusters; }; struct _ocfs2_extent_map_entry { struct rb_node e_node; int e_tree_depth; struct ocfs2_extent_rec e_rec; }; #endif /* _EXTENT_MAP_H */ ocfs2-tools-ocfs2-tools-1.8.6/libocfs2/extent_tree.c000066400000000000000000003247261347147137200222740ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * extent_tree.c * * Copyright (C) 2009 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License version 2 as published by the Free Software Foundation. * * 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. */ #include #include #include #include #include #include "ocfs2/byteorder.h" #include "ocfs2/ocfs2.h" #include "extent_tree.h" static void ocfs2_dinode_set_last_eb_blk(struct ocfs2_extent_tree *et, uint64_t blkno) { struct ocfs2_dinode *di = et->et_object; di->i_last_eb_blk = blkno; } static uint64_t ocfs2_dinode_get_last_eb_blk(struct ocfs2_extent_tree *et) { struct ocfs2_dinode *di = et->et_object; return di->i_last_eb_blk; } static void ocfs2_dinode_update_clusters(struct ocfs2_extent_tree *et, uint32_t clusters) { struct ocfs2_dinode *di = et->et_object; di->i_clusters += clusters; } static void ocfs2_dinode_fill_root_el(struct ocfs2_extent_tree *et) { struct ocfs2_dinode *di = et->et_object; et->et_root_el = &di->id2.i_list; } static struct ocfs2_extent_tree_operations ocfs2_dinode_et_ops = { .eo_set_last_eb_blk = ocfs2_dinode_set_last_eb_blk, .eo_get_last_eb_blk = ocfs2_dinode_get_last_eb_blk, .eo_update_clusters = ocfs2_dinode_update_clusters, .eo_fill_root_el = ocfs2_dinode_fill_root_el, }; static void ocfs2_refcount_tree_fill_root_el(struct ocfs2_extent_tree *et) { struct ocfs2_refcount_block *rb = et->et_object; et->et_root_el = &rb->rf_list; } static void ocfs2_refcount_tree_set_last_eb_blk(struct ocfs2_extent_tree *et, uint64_t blkno) { struct ocfs2_refcount_block *rb = et->et_object; rb->rf_last_eb_blk = blkno; } static uint64_t ocfs2_refcount_tree_get_last_eb_blk(struct ocfs2_extent_tree *et) { struct ocfs2_refcount_block *rb = et->et_object; return rb->rf_last_eb_blk; } static void ocfs2_refcount_tree_update_clusters(struct ocfs2_extent_tree *et, uint32_t clusters) { struct ocfs2_refcount_block *rb = et->et_object; rb->rf_clusters += clusters; } static enum ocfs2_contig_type ocfs2_refcount_tree_extent_contig(ocfs2_filesys *fs, struct ocfs2_extent_tree *et, struct ocfs2_extent_rec *ext, struct ocfs2_extent_rec *insert_rec) { return CONTIG_NONE; } static struct ocfs2_extent_tree_operations ocfs2_refcount_tree_et_ops = { .eo_set_last_eb_blk = ocfs2_refcount_tree_set_last_eb_blk, .eo_get_last_eb_blk = ocfs2_refcount_tree_get_last_eb_blk, .eo_update_clusters = ocfs2_refcount_tree_update_clusters, .eo_fill_root_el = ocfs2_refcount_tree_fill_root_el, .eo_extent_contig = ocfs2_refcount_tree_extent_contig, }; static void ocfs2_xattr_value_fill_root_el(struct ocfs2_extent_tree *et) { struct ocfs2_xattr_value_root *xv = et->et_object; et->et_root_el = &xv->xr_list; } static void ocfs2_xattr_value_set_last_eb_blk(struct ocfs2_extent_tree *et, uint64_t blkno) { struct ocfs2_xattr_value_root *xv = et->et_object; xv->xr_last_eb_blk = blkno; } static uint64_t ocfs2_xattr_value_get_last_eb_blk(struct ocfs2_extent_tree *et) { struct ocfs2_xattr_value_root *xv = et->et_object; return xv->xr_last_eb_blk; } static void ocfs2_xattr_value_update_clusters(struct ocfs2_extent_tree *et, uint32_t clusters) { struct ocfs2_xattr_value_root *xv = et->et_object; xv->xr_clusters += clusters; } static uint32_t ocfs2_xattr_value_get_clusters(struct ocfs2_extent_tree *et) { struct ocfs2_xattr_value_root *xv = et->et_object; return xv->xr_clusters; } static struct ocfs2_extent_tree_operations ocfs2_xattr_value_et_ops = { .eo_set_last_eb_blk = ocfs2_xattr_value_set_last_eb_blk, .eo_get_last_eb_blk = ocfs2_xattr_value_get_last_eb_blk, .eo_update_clusters = ocfs2_xattr_value_update_clusters, .eo_get_clusters = ocfs2_xattr_value_get_clusters, .eo_fill_root_el = ocfs2_xattr_value_fill_root_el, }; static void ocfs2_dx_root_set_last_eb_blk (struct ocfs2_extent_tree *et, uint64_t blkno) { struct ocfs2_dx_root_block *dx_root = et->et_object; dx_root->dr_last_eb_blk = blkno; } static uint64_t ocfs2_dx_root_get_last_eb_blk (struct ocfs2_extent_tree *et) { struct ocfs2_dx_root_block *dx_root = et->et_object; return dx_root->dr_last_eb_blk; } static void ocfs2_dx_root_update_clusters(struct ocfs2_extent_tree *et, uint32_t clusters) { struct ocfs2_dx_root_block *dx_root = et->et_object; dx_root->dr_clusters += clusters; } static int ocfs2_dx_root_sanity_check(struct ocfs2_extent_tree *et) { struct ocfs2_dx_root_block *dx_root = (struct ocfs2_dx_root_block *)et->et_object; assert(OCFS2_IS_VALID_DX_ROOT(dx_root)); return 0; } static void ocfs2_dx_root_fill_root_el (struct ocfs2_extent_tree *et) { struct ocfs2_dx_root_block *dx_root = et->et_object; et->et_root_el = &dx_root->dr_list; } static struct ocfs2_extent_tree_operations ocfs2_dx_root_et_ops = { .eo_set_last_eb_blk = ocfs2_dx_root_set_last_eb_blk, .eo_get_last_eb_blk = ocfs2_dx_root_get_last_eb_blk, .eo_update_clusters = ocfs2_dx_root_update_clusters, .eo_sanity_check = ocfs2_dx_root_sanity_check, .eo_fill_root_el = ocfs2_dx_root_fill_root_el, }; static void __ocfs2_init_extent_tree(struct ocfs2_extent_tree *et, ocfs2_filesys *fs, char *buf, uint64_t blkno, ocfs2_root_write_func write, void *obj, struct ocfs2_extent_tree_operations *ops) { et->et_ops = ops; et->et_root_buf = buf; et->et_root_blkno = blkno; et->et_root_write = write; et->et_object = obj; et->et_ops->eo_fill_root_el(et); if (!et->et_ops->eo_fill_max_leaf_clusters) et->et_max_leaf_clusters = 0; else et->et_ops->eo_fill_max_leaf_clusters(fs, et); } void ocfs2_init_dinode_extent_tree(struct ocfs2_extent_tree *et, ocfs2_filesys *fs, char *buf, uint64_t blkno) { __ocfs2_init_extent_tree(et, fs, buf, blkno, ocfs2_write_inode, buf, &ocfs2_dinode_et_ops); } void ocfs2_init_refcount_extent_tree(struct ocfs2_extent_tree *et, ocfs2_filesys *fs, char *buf, uint64_t blkno) { __ocfs2_init_extent_tree(et, fs, buf, blkno, ocfs2_write_refcount_block, buf, &ocfs2_refcount_tree_et_ops); } void ocfs2_init_xattr_value_extent_tree(struct ocfs2_extent_tree *et, ocfs2_filesys *fs, char *buf, uint64_t blkno, ocfs2_root_write_func write, struct ocfs2_xattr_value_root *xv) { __ocfs2_init_extent_tree(et, fs, buf, blkno, write, xv, &ocfs2_xattr_value_et_ops); } void ocfs2_init_dx_root_extent_tree(struct ocfs2_extent_tree *et, ocfs2_filesys *fs, char *buf, uint64_t blkno) { __ocfs2_init_extent_tree(et, fs, buf, blkno, ocfs2_write_dx_root, buf, &ocfs2_dx_root_et_ops); } static inline void ocfs2_et_set_last_eb_blk(struct ocfs2_extent_tree *et, uint64_t new_last_eb_blk) { et->et_ops->eo_set_last_eb_blk(et, new_last_eb_blk); } static inline uint64_t ocfs2_et_get_last_eb_blk(struct ocfs2_extent_tree *et) { return et->et_ops->eo_get_last_eb_blk(et); } static inline void ocfs2_et_update_clusters(struct ocfs2_extent_tree *et, uint32_t clusters) { return et->et_ops->eo_update_clusters(et, clusters); } struct insert_ctxt { ocfs2_filesys *fs; struct ocfs2_extent_tree *et; struct ocfs2_extent_rec rec; }; /* * Reset the actual path elements so that we can re-use the structure * to build another path. Generally, this involves freeing the buffer * heads. */ static void ocfs2_reinit_path(struct ocfs2_path *path, int keep_root) { int i, start = 0, depth = 0; struct ocfs2_path_item *node; if (keep_root) start = 1; for(i = start; i < path_num_items(path); i++) { node = &path->p_node[i]; if (!node->buf) continue; ocfs2_free(&node->buf); node->blkno = 0; node->buf = NULL; node->el = NULL; } /* * Tree depth may change during truncate, or insert. If we're * keeping the root extent list, then make sure that our path * structure reflects the proper depth. */ if (keep_root) depth = path_root_el(path)->l_tree_depth; path->p_tree_depth = depth; } void ocfs2_free_path(struct ocfs2_path *path) { /* We don't free the root because often in libocfs2 the root is a * shared buffer such as the inode. Caller must be responsible for * handling the root of the path. */ if (path) { ocfs2_reinit_path(path, 1); ocfs2_free(&path); } } static enum ocfs2_contig_type ocfs2_extent_rec_contig(ocfs2_filesys *fs, struct ocfs2_extent_rec *ext, struct ocfs2_extent_rec *insert_rec); static inline enum ocfs2_contig_type ocfs2_et_extent_contig(ocfs2_filesys *fs, struct ocfs2_extent_tree *et, struct ocfs2_extent_rec *rec, struct ocfs2_extent_rec *insert_rec) { if (et->et_ops->eo_extent_contig) return et->et_ops->eo_extent_contig(fs, et, rec, insert_rec); return ocfs2_extent_rec_contig(fs, rec, insert_rec); } /* * All the elements of src into dest. After this call, src could be freed * without affecting dest. * * Both paths should have the same root. Any non-root elements of dest * will be freed. */ static void ocfs2_cp_path(ocfs2_filesys *fs, struct ocfs2_path *dest, struct ocfs2_path *src) { int i; struct ocfs2_extent_block *eb = NULL; assert(path_root_blkno(dest) == path_root_blkno(src)); dest->p_tree_depth = src->p_tree_depth; for(i = 1; i < OCFS2_MAX_PATH_DEPTH; i++) { if (!src->p_node[i].buf) { if (dest->p_node[i].buf) ocfs2_free(dest->p_node[i].buf); dest->p_node[i].blkno = 0; dest->p_node[i].buf = NULL; dest->p_node[i].el = NULL; continue; } if (!dest->p_node[i].buf) ocfs2_malloc_block(fs->fs_io, &dest->p_node[i].buf); assert(dest->p_node[i].buf); memcpy(dest->p_node[i].buf, src->p_node[i].buf, fs->fs_blocksize); eb = (struct ocfs2_extent_block *)dest->p_node[i].buf; dest->p_node[i].el = &eb->h_list; dest->p_node[i].blkno = src->p_node[i].blkno; } } /* * Make the *dest path the same as src and re-initialize src path to * have a root only. */ static void ocfs2_mv_path(struct ocfs2_path *dest, struct ocfs2_path *src) { int i; assert(path_root_blkno(dest) == path_root_blkno(src)); for(i = 1; i < OCFS2_MAX_PATH_DEPTH; i++) { ocfs2_free(&dest->p_node[i].buf); dest->p_node[i].blkno = src->p_node[i].blkno; dest->p_node[i].buf = src->p_node[i].buf; dest->p_node[i].el = src->p_node[i].el; src->p_node[i].blkno = 0; src->p_node[i].buf = NULL; src->p_node[i].el = NULL; } } /* * Insert an extent block at given index. * * Note: * This buf will be inserted into the path, so the caller shouldn't free it. */ static inline void ocfs2_path_insert_eb(struct ocfs2_path *path, int index, char *buf) { struct ocfs2_extent_block *eb = (struct ocfs2_extent_block *) buf; /* * Right now, no root buf is an extent block, so this helps * catch code errors with dinode trees. The assertion can be * safely removed if we ever need to insert extent block * structures at the root. */ assert(index); path->p_node[index].blkno = eb->h_blkno; path->p_node[index].buf = (char *)buf; path->p_node[index].el = &eb->h_list; } static struct ocfs2_path *ocfs2_new_path(char *buf, struct ocfs2_extent_list *root_el, uint64_t blkno) { struct ocfs2_path *path = NULL; assert(root_el->l_tree_depth < OCFS2_MAX_PATH_DEPTH); ocfs2_malloc0(sizeof(*path), &path); if (path) { path->p_tree_depth = root_el->l_tree_depth; path->p_node[0].blkno = blkno; path->p_node[0].buf = buf; path->p_node[0].el = root_el; } return path; } static struct ocfs2_path *ocfs2_new_path_from_path(struct ocfs2_path *path) { return ocfs2_new_path(path_root_buf(path), path_root_el(path), path_root_blkno(path)); } struct ocfs2_path *ocfs2_new_path_from_et(struct ocfs2_extent_tree *et) { return ocfs2_new_path(et->et_root_buf, et->et_root_el, et->et_root_blkno); } /* Write all the extent block information to the disk. * We write all paths furthur down than subtree_index. * The caller will handle writing the sub_index. */ static errcode_t ocfs2_write_path_eb(ocfs2_filesys *fs, struct ocfs2_path *path, int sub_index) { errcode_t ret; int i; for (i = path->p_tree_depth; i > sub_index; i--) { ret = ocfs2_write_extent_block(fs, path->p_node[i].blkno, path->p_node[i].buf); if (ret) return ret; } return 0; } /* some extent blocks is modified and we need to synchronize them to the disk * accordingly. * * We will not update the inode if subtree_index is "0" since it should be * updated by the caller. * * left_path or right_path can be NULL, but they can't be NULL in the same time. * And if they are both not NULL, we will treat subtree_index in the right_path * as the right extent block information. */ static errcode_t ocfs2_sync_path_to_disk(ocfs2_filesys *fs, struct ocfs2_path *left_path, struct ocfs2_path *right_path, int subtree_index) { errcode_t ret = 0; struct ocfs2_path *path = NULL; assert(left_path || right_path); if (left_path) { ret = ocfs2_write_path_eb(fs, left_path, subtree_index); if (ret) goto bail; } if (right_path) { ret = ocfs2_write_path_eb(fs, right_path, subtree_index); if (ret) goto bail; } if (subtree_index) { /* subtree_index indicates an extent block. */ path = right_path ? right_path : left_path; ret = ocfs2_write_extent_block(fs, path->p_node[subtree_index].blkno, path->p_node[subtree_index].buf); if (ret) goto bail; } bail: return ret; } /* * Return the index of the extent record which contains cluster #v_cluster. * -1 is returned if it was not found. * * Should work fine on interior and exterior nodes. */ int ocfs2_search_extent_list(struct ocfs2_extent_list *el, uint32_t v_cluster) { int ret = -1; int i; struct ocfs2_extent_rec *rec; uint32_t rec_end, rec_start, clusters; for(i = 0; i < el->l_next_free_rec; i++) { rec = &el->l_recs[i]; rec_start = rec->e_cpos; clusters = ocfs2_rec_clusters(el->l_tree_depth, rec); rec_end = rec_start + clusters; if (v_cluster >= rec_start && v_cluster < rec_end) { ret = i; break; } } return ret; } /* * NOTE: ocfs2_block_extent_contig(), ocfs2_extents_adjacent() and * ocfs2_extent_rec_contig only work properly against leaf nodes! */ static inline int ocfs2_block_extent_contig(ocfs2_filesys *fs, struct ocfs2_extent_rec *ext, uint64_t blkno) { uint64_t blk_end = ext->e_blkno; blk_end += ocfs2_clusters_to_blocks(fs, ext->e_leaf_clusters); return blkno == blk_end; } static inline int ocfs2_extents_adjacent(struct ocfs2_extent_rec *left, struct ocfs2_extent_rec *right) { uint32_t left_range; left_range = left->e_cpos + left->e_leaf_clusters; return (left_range == right->e_cpos); } static enum ocfs2_contig_type ocfs2_extent_rec_contig(ocfs2_filesys *fs, struct ocfs2_extent_rec *ext, struct ocfs2_extent_rec *insert_rec) { uint64_t blkno = insert_rec->e_blkno; /* * Refuse to coalesce extent records with different flag * fields - we don't want to mix unwritten extents with user * data. */ if (ext->e_flags != insert_rec->e_flags) return CONTIG_NONE; if (ocfs2_extents_adjacent(ext, insert_rec) && ocfs2_block_extent_contig(fs, ext, blkno)) return CONTIG_RIGHT; blkno = ext->e_blkno; if (ocfs2_extents_adjacent(insert_rec, ext) && ocfs2_block_extent_contig(fs, insert_rec, blkno)) return CONTIG_LEFT; return CONTIG_NONE; } /* * NOTE: We can have pretty much any combination of contiguousness and * appending. * * The usefulness of APPEND_TAIL is more in that it lets us know that * we'll have to update the path to that leaf. */ enum ocfs2_append_type { APPEND_NONE = 0, APPEND_TAIL, }; enum ocfs2_split_type { SPLIT_NONE = 0, SPLIT_LEFT, SPLIT_RIGHT, }; struct ocfs2_insert_type { enum ocfs2_split_type ins_split; enum ocfs2_append_type ins_appending; enum ocfs2_contig_type ins_contig; int ins_contig_index; int ins_tree_depth; }; struct ocfs2_merge_ctxt { enum ocfs2_contig_type c_contig_type; int c_has_empty_extent; int c_split_covers_rec; }; /* * Helper function for ocfs2_add_branch() and shift_tree_depth(). * * Returns the sum of the rightmost extent rec logical offset and * cluster count. * * ocfs2_add_branch() uses this to determine what logical cluster * value should be populated into the leftmost new branch records. * * shift_tree_depth() uses this to determine the # clusters * value for the new topmost tree record. */ static inline uint32_t ocfs2_sum_rightmost_rec(struct ocfs2_extent_list *el) { uint16_t i = el->l_next_free_rec - 1; return el->l_recs[i].e_cpos + ocfs2_rec_clusters(el->l_tree_depth, &el->l_recs[i]); } /* * Add an entire tree branch to our inode. eb_buf is the extent block * to start at, if we don't want to start the branch at the dinode * structure. * * last_eb_buf is required as we have to update it's next_leaf pointer * for the new last extent block. * * the new branch will be 'empty' in the sense that every block will * contain a single record with e_clusters == 0. */ static int ocfs2_add_branch(ocfs2_filesys *fs, struct ocfs2_extent_tree *et, char *eb_buf, char **last_eb_buf) { errcode_t ret; int new_blocks, i; uint64_t next_blkno, new_last_eb_blk; struct ocfs2_extent_block *eb; struct ocfs2_extent_list *eb_el; struct ocfs2_extent_list *el; uint32_t new_cpos; uint64_t *new_blknos = NULL; char **new_eb_bufs = NULL; char *buf = NULL; assert(*last_eb_buf); if (eb_buf) { eb = (struct ocfs2_extent_block *) eb_buf; el = &eb->h_list; } else el = et->et_root_el; /* we never add a branch to a leaf. */ assert(el->l_tree_depth); new_blocks = el->l_tree_depth; /* allocate the number of new eb blocks we need new_blocks should be * allocated here.*/ ret = ocfs2_malloc0(sizeof(uint64_t) * new_blocks, &new_blknos); if (ret) goto bail; memset(new_blknos, 0, sizeof(uint64_t) * new_blocks); ret = ocfs2_malloc0(sizeof(char *) * new_blocks, &new_eb_bufs); if (ret) goto bail; memset(new_eb_bufs, 0, sizeof(char *) * new_blocks); for (i = 0; i < new_blocks; i++) { ret = ocfs2_malloc_block(fs->fs_io, &buf); if (ret) return ret; new_eb_bufs[i] = buf; ret = ocfs2_new_extent_block(fs, &new_blknos[i]); if (ret) goto bail; ret = ocfs2_read_extent_block(fs, new_blknos[i], buf); if (ret) goto bail; } eb = (struct ocfs2_extent_block *)(*last_eb_buf); new_cpos = ocfs2_sum_rightmost_rec(&eb->h_list); /* Note: new_eb_bufs[new_blocks - 1] is the guy which will be * linked with the rest of the tree. * conversly, new_eb_bufs[0] is the new bottommost leaf. * * when we leave the loop, new_last_eb_blk will point to the * newest leaf, and next_blkno will point to the topmost extent * block. */ next_blkno = new_last_eb_blk = 0; for(i = 0; i < new_blocks; i++) { buf = new_eb_bufs[i]; eb = (struct ocfs2_extent_block *) buf; eb_el = &eb->h_list; eb->h_next_leaf_blk = 0; eb_el->l_tree_depth = i; eb_el->l_next_free_rec = 1; memset(eb_el->l_recs, 0, sizeof(struct ocfs2_extent_rec) * eb_el->l_count); /* * This actually counts as an empty extent as * c_clusters == 0 */ eb_el->l_recs[0].e_cpos = new_cpos; eb_el->l_recs[0].e_blkno = next_blkno; /* * eb_el isn't always an interior node, but even leaf * nodes want a zero'd flags and reserved field so * this gets the whole 32 bits regardless of use. */ eb_el->l_recs[0].e_int_clusters = 0; if (!eb_el->l_tree_depth) new_last_eb_blk = eb->h_blkno; next_blkno = eb->h_blkno; } /* Link the new branch into the rest of the tree (el will * either be on the fe, or the extent block passed in. */ i = el->l_next_free_rec; el->l_recs[i].e_blkno = next_blkno; el->l_recs[i].e_cpos = new_cpos; el->l_recs[i].e_int_clusters = 0; el->l_next_free_rec++; /* fe needs a new last extent block pointer, as does the * next_leaf on the previously last-extent-block. */ ocfs2_et_set_last_eb_blk(et, new_last_eb_blk); /* here all the extent block and the new inode information should be * written back to the disk. */ for(i = 0; i < new_blocks; i++) { buf = new_eb_bufs[i]; ret = ocfs2_write_extent_block(fs, new_blknos[i], buf); if (ret) goto bail; } /* update last_eb_buf's next_leaf pointer for * the new last extent block. */ eb = (struct ocfs2_extent_block *)(*last_eb_buf); eb->h_next_leaf_blk = new_last_eb_blk; ret = ocfs2_write_extent_block(fs, eb->h_blkno, *last_eb_buf); if (ret) goto bail; if (eb_buf) { eb = (struct ocfs2_extent_block *)eb_buf; ret = ocfs2_write_extent_block(fs, eb->h_blkno, eb_buf); if (ret) goto bail; } /* * Some callers want to track the rightmost leaf so pass it * back here. */ memcpy(*last_eb_buf, new_eb_bufs[0], fs->fs_blocksize); /* The inode information isn't updated since we use duplicated extent * block in the insertion and it may fail in other steps. */ ret = 0; bail: if (new_eb_bufs) { for (i = 0; i < new_blocks; i++) if (new_eb_bufs[i]) ocfs2_free(&new_eb_bufs[i]); ocfs2_free(&new_eb_bufs); } if (ret && new_blknos) for (i = 0; i < new_blocks; i++) if (new_blknos[i]) ocfs2_delete_extent_block(fs, new_blknos[i]); if (new_blknos) ocfs2_free(&new_blknos); return ret; } /* * Should only be called when there is no space left in any of the * leaf nodes. What we want to do is find the lowest tree depth * non-leaf extent block with room for new records. There are three * valid results of this search: * * 1) a lowest extent block is found, then we pass it back in * *target_buf and return '0' * * 2) the search fails to find anything, but the dinode has room. We * pass NULL back in *target_buf, but still return '0' * * 3) the search fails to find anything AND the dinode is full, in * which case we return > 0 * * return status < 0 indicates an error. */ static errcode_t ocfs2_find_branch_target(ocfs2_filesys *fs, struct ocfs2_extent_tree *et, char **target_buf) { errcode_t ret = 0; int i; uint64_t blkno; struct ocfs2_extent_block *eb; char *buf = NULL, *lowest_buf = NULL; struct ocfs2_extent_list *el = et->et_root_el; *target_buf = NULL; ret = ocfs2_malloc_block(fs->fs_io, &buf); if (ret) return ret; while(el->l_tree_depth > 1) { if (el->l_next_free_rec == 0) { ret = OCFS2_ET_CORRUPT_EXTENT_BLOCK; goto bail; } i = el->l_next_free_rec - 1; blkno = el->l_recs[i].e_blkno; if (!blkno) { ret = OCFS2_ET_CORRUPT_EXTENT_BLOCK; goto bail; } ret = ocfs2_read_extent_block(fs, blkno, buf); if (ret) goto bail; eb = (struct ocfs2_extent_block *) buf; el = &eb->h_list; if (el->l_next_free_rec < el->l_count) lowest_buf = buf; } el = et->et_root_el; /* If we didn't find one and the fe doesn't have any room, * then return '1' */ if (!lowest_buf && el->l_next_free_rec == el->l_count) ret = 1; *target_buf = lowest_buf; bail: if (buf && !*target_buf) ocfs2_free(&buf); return ret; } /* * This function will discard the rightmost extent record. */ static void ocfs2_shift_records_right(struct ocfs2_extent_list *el) { int next_free = el->l_next_free_rec; int count = el->l_count; unsigned int num_bytes; assert(next_free); /* This will cause us to go off the end of our extent list. */ assert(next_free < count); num_bytes = sizeof(struct ocfs2_extent_rec) * next_free; memmove(&el->l_recs[1], &el->l_recs[0], num_bytes); } static void ocfs2_rotate_leaf(struct ocfs2_extent_list *el, struct ocfs2_extent_rec *insert_rec) { int i, insert_index, next_free, has_empty, num_bytes; uint32_t insert_cpos = insert_rec->e_cpos; struct ocfs2_extent_rec *rec; next_free = el->l_next_free_rec; has_empty = ocfs2_is_empty_extent(&el->l_recs[0]); assert(next_free); /* The tree code before us didn't allow enough room in the leaf. */ if (el->l_next_free_rec == el->l_count && !has_empty) assert(0); /* * The easiest way to approach this is to just remove the * empty extent and temporarily decrement next_free. */ if (has_empty) { /* * If next_free was 1 (only an empty extent), this * loop won't execute, which is fine. We still want * the decrement above to happen. */ for(i = 0; i < (next_free - 1); i++) el->l_recs[i] = el->l_recs[i+1]; next_free--; } /* Figure out what the new record index should be. */ for(i = 0; i < next_free; i++) { rec = &el->l_recs[i]; if (insert_cpos < rec->e_cpos) break; } insert_index = i; assert(insert_index >= 0); assert(insert_index < el->l_count); assert(insert_index <= next_free); /* No need to memmove if we're just adding to the tail. */ if (insert_index != next_free) { assert(next_free < el->l_count); num_bytes = next_free - insert_index; num_bytes *= sizeof(struct ocfs2_extent_rec); memmove(&el->l_recs[insert_index + 1], &el->l_recs[insert_index], num_bytes); } /* * Either we had an empty extent, and need to re-increment or * there was no empty extent on a non full rightmost leaf node, * in which case we still need to increment. */ next_free++; el->l_next_free_rec = next_free; /* Make sure none of the math above just messed up our tree. */ assert(el->l_next_free_rec <= el->l_count); el->l_recs[insert_index] = *insert_rec; } static void ocfs2_remove_empty_extent(struct ocfs2_extent_list *el) { int size, num_recs = el->l_next_free_rec; assert(num_recs); if (ocfs2_is_empty_extent(&el->l_recs[0])) { num_recs--; size = num_recs * sizeof(struct ocfs2_extent_rec); memmove(&el->l_recs[0], &el->l_recs[1], size); memset(&el->l_recs[num_recs], 0, sizeof(struct ocfs2_extent_rec)); el->l_next_free_rec = num_recs; } } /* * Create an empty extent record . * * l_next_free_rec may be updated. * * If an empty extent already exists do nothing. */ static void ocfs2_create_empty_extent(struct ocfs2_extent_list *el) { int next_free = el->l_next_free_rec; assert(el->l_tree_depth == 0); if (next_free == 0) goto set_and_inc; if (ocfs2_is_empty_extent(&el->l_recs[0])) return; ocfs2_shift_records_right(el); set_and_inc: el->l_next_free_rec += 1; memset(&el->l_recs[0], 0, sizeof(struct ocfs2_extent_rec)); } /* * For a rotation which involves two leaf nodes, the "root node" is * the lowest level tree node which contains a path to both leafs. This * resulting set of information can be used to form a complete "subtree" * * This function is passed two full paths from the dinode down to a * pair of adjacent leaves. It's task is to figure out which path * index contains the subtree root - this can be the root index itself * in a worst-case rotation. * * The array index of the subtree root is passed back. */ static int ocfs2_find_subtree_root(struct ocfs2_path *left, struct ocfs2_path *right) { int i = 0; /* Check that the caller passed in two paths from the same tree. */ assert(path_root_blkno(left) == path_root_blkno(right)); do { i++; /* The caller didn't pass two adjacent paths. */ if (i > left->p_tree_depth) assert(0); } while (left->p_node[i].blkno == right->p_node[i].blkno); return i - 1; } typedef errcode_t (path_insert_t)(void *, char *); /* * Traverse a btree path in search of cpos, starting at root_el. * * This code can be called with a cpos larger than the tree, in which * case it will return the rightmost path. */ static errcode_t __ocfs2_find_path(ocfs2_filesys *fs, struct ocfs2_extent_list *root_el, uint32_t cpos, path_insert_t *func, void *data) { int i, ret = 0; uint32_t range; uint64_t blkno; char *buf = NULL; struct ocfs2_extent_block *eb; struct ocfs2_extent_list *el; struct ocfs2_extent_rec *rec; el = root_el; while (el->l_tree_depth) { if (el->l_next_free_rec == 0) { ret = OCFS2_ET_CORRUPT_EXTENT_BLOCK; goto out; } for(i = 0; i < el->l_next_free_rec - 1; i++) { rec = &el->l_recs[i]; /* * In the case that cpos is off the allocation * tree, this should just wind up returning the * rightmost record. */ range = rec->e_cpos + ocfs2_rec_clusters(el->l_tree_depth, rec); if (cpos >= rec->e_cpos && cpos < range) break; } blkno = el->l_recs[i].e_blkno; if (blkno == 0) { ret = OCFS2_ET_CORRUPT_EXTENT_BLOCK; goto out; } ret = ocfs2_malloc_block(fs->fs_io, &buf); if (ret) return ret; ret = ocfs2_read_extent_block(fs, blkno, buf); if (ret) goto out; eb = (struct ocfs2_extent_block *) buf; el = &eb->h_list; if (el->l_next_free_rec > el->l_count) { ret = OCFS2_ET_CORRUPT_EXTENT_BLOCK; goto out; } /* The user's callback must give us the tip for how to * handle the buf we allocated by return values. * * 1) return '0': * the function succeeds,and it will use the buf and * take care of the buffer release. * * 2) return > 0: * the function succeeds, and there is no need for buf, * so we will release it. * * 3) return < 0: * the function fails. */ if (func) { ret = func(data, buf); if (ret == 0) { buf = NULL; continue; } else if (ret < 0) goto out; } ocfs2_free(&buf); buf = NULL; } out: /* Catch any trailing buf that the loop didn't handle. */ if (buf) ocfs2_free(&buf); return ret; } /* * Given an initialized path (that is, it has a valid root extent * list), this function will traverse the btree in search of the path * which would contain cpos. * * The path traveled is recorded in the path structure. * * Note that this will not do any comparisons on leaf node extent * records, so it will work fine in the case that we just added a tree * branch. */ struct find_path_data { int index; struct ocfs2_path *path; }; static errcode_t find_path_ins(void *data, char *eb) { struct find_path_data *fp = data; ocfs2_path_insert_eb(fp->path, fp->index, eb); fp->index++; return 0; } int ocfs2_find_path(ocfs2_filesys *fs, struct ocfs2_path *path, uint32_t cpos) { struct find_path_data data; data.index = 1; data.path = path; return __ocfs2_find_path(fs, path_root_el(path), cpos, find_path_ins, &data); } /* * Find the leaf block in the tree which would contain cpos. No * checking of the actual leaf is done. * * This function doesn't handle non btree extent lists. */ int ocfs2_tree_find_leaf(ocfs2_filesys *fs, struct ocfs2_extent_list *el, uint64_t el_blkno, char *el_blk, uint32_t cpos, char **leaf_buf) { int ret; char *buf = NULL; struct ocfs2_path *path = NULL; assert(el->l_tree_depth > 0); path = ocfs2_new_path(el_blk, el, el_blkno); if (!path) { ret = OCFS2_ET_NO_MEMORY; goto out; } ret = ocfs2_find_path(fs, path, cpos); if (ret) goto out; ret = ocfs2_malloc_block(fs->fs_io, &buf); if (ret) goto out; memcpy(buf, path_leaf_buf(path), fs->fs_blocksize); *leaf_buf = buf; out: ocfs2_free_path(path); return ret; } int ocfs2_find_leaf(ocfs2_filesys *fs, struct ocfs2_dinode *di, uint32_t cpos, char **leaf_buf) { return ocfs2_tree_find_leaf(fs, &di->id2.i_list, di->i_blkno, (char *)di, cpos, leaf_buf); } /* * Adjust the adjacent records (left_rec, right_rec) involved in a rotation. * * Basically, we've moved stuff around at the bottom of the tree and * we need to fix up the extent records above the changes to reflect * the new changes. * * left_rec: the record on the left. * left_child_el: is the child list pointed to by left_rec * right_rec: the record to the right of left_rec * right_child_el: is the child list pointed to by right_rec * * By definition, this only works on interior nodes. */ static void ocfs2_adjust_adjacent_records(struct ocfs2_extent_rec *left_rec, struct ocfs2_extent_list *left_child_el, struct ocfs2_extent_rec *right_rec, struct ocfs2_extent_list *right_child_el) { uint32_t left_clusters, right_end; /* * Interior nodes never have holes. Their cpos is the cpos of * the leftmost record in their child list. Their cluster * count covers the full theoretical range of their child list * - the range between their cpos and the cpos of the record * immediately to their right. */ left_clusters = right_child_el->l_recs[0].e_cpos; if (ocfs2_is_empty_extent(&right_child_el->l_recs[0])) { assert(right_child_el->l_next_free_rec > 1); left_clusters = right_child_el->l_recs[1].e_cpos; } left_clusters -= left_rec->e_cpos; left_rec->e_int_clusters = left_clusters; /* * Calculate the rightmost cluster count boundary before * moving cpos - we will need to adjust clusters after * updating e_cpos to keep the same highest cluster count. */ right_end = right_rec->e_cpos; right_end += right_rec->e_int_clusters; right_rec->e_cpos = left_rec->e_cpos; right_rec->e_cpos += left_clusters; right_end -= right_rec->e_cpos; right_rec->e_int_clusters = right_end; } /* * Adjust the adjacent root node records involved in a * rotation. left_el_blkno is passed in as a key so that we can easily * find it's index in the root list. */ static void ocfs2_adjust_root_records(struct ocfs2_extent_list *root_el, struct ocfs2_extent_list *left_el, struct ocfs2_extent_list *right_el, uint64_t left_el_blkno) { int i; assert(root_el->l_tree_depth > left_el->l_tree_depth); for(i = 0; i < root_el->l_next_free_rec - 1; i++) { if (root_el->l_recs[i].e_blkno == left_el_blkno) break; } /* * The path walking code should have never returned a root and * two paths which are not adjacent. */ assert(i < (root_el->l_next_free_rec - 1)); ocfs2_adjust_adjacent_records(&root_el->l_recs[i], left_el, &root_el->l_recs[i + 1], right_el); } /* * We've changed a leaf block (in right_path) and need to reflect that * change back up the subtree. * * This happens in multiple places: * - When we've moved an extent record from the left path leaf to the right * path leaf to make room for an empty extent in the left path leaf. * - When our insert into the right path leaf is at the leftmost edge * and requires an update of the path immediately to it's left. This * can occur at the end of some types of rotation and appending inserts. */ static void ocfs2_complete_edge_insert(ocfs2_filesys *fs, struct ocfs2_path *left_path, struct ocfs2_path *right_path, int subtree_index) { int i, idx; uint64_t blkno; struct ocfs2_extent_list *el, *left_el, *right_el; struct ocfs2_extent_rec *left_rec, *right_rec; /* * Update the counts and position values within all the * interior nodes to reflect the leaf rotation we just did. * * The root node is handled below the loop. * * We begin the loop with right_el and left_el pointing to the * leaf lists and work our way up. * * NOTE: within this loop, left_el and right_el always refer * to the *child* lists. */ left_el = path_leaf_el(left_path); right_el = path_leaf_el(right_path); for(i = left_path->p_tree_depth - 1; i > subtree_index; i--) { /* * One nice property of knowing that all of these * nodes are below the root is that we only deal with * the leftmost right node record and the rightmost * left node record. */ el = left_path->p_node[i].el; idx = left_el->l_next_free_rec - 1; left_rec = &el->l_recs[idx]; el = right_path->p_node[i].el; right_rec = &el->l_recs[0]; ocfs2_adjust_adjacent_records(left_rec, left_el, right_rec, right_el); /* * Setup our list pointers now so that the current * parents become children in the next iteration. */ left_el = left_path->p_node[i].el; right_el = right_path->p_node[i].el; } /* * At the root node, adjust the two adjacent records which * begin our path to the leaves. */ el = left_path->p_node[subtree_index].el; left_el = left_path->p_node[subtree_index + 1].el; right_el = right_path->p_node[subtree_index + 1].el; blkno = left_path->p_node[subtree_index + 1].blkno; ocfs2_adjust_root_records(el, left_el, right_el, blkno); /* ocfs2_adjust_root_records only update the extent block in the left * path, and actually right_path->p_node[subtree_index].eb indicates the * same extent block, so we must keep them the same content. */ memcpy(right_path->p_node[subtree_index].buf, left_path->p_node[subtree_index].buf, fs->fs_blocksize); } /* Rotate the subtree to right. * * Note: After successful rotation, the extent block will be flashed * to disk accordingly. */ static errcode_t ocfs2_rotate_subtree_right(ocfs2_filesys *fs, struct ocfs2_path *left_path, struct ocfs2_path *right_path, int subtree_index) { errcode_t ret; int i; struct ocfs2_extent_list *right_el, *left_el; struct ocfs2_extent_rec move_rec; left_el = path_leaf_el(left_path); if (left_el->l_next_free_rec != left_el->l_count) return OCFS2_ET_CORRUPT_EXTENT_BLOCK; /* * This extent block may already have an empty record, so we * return early if so. */ if (ocfs2_is_empty_extent(&left_el->l_recs[0])) return 0; assert(left_path->p_node[subtree_index].blkno == right_path->p_node[subtree_index].blkno); right_el = path_leaf_el(right_path); ocfs2_create_empty_extent(right_el); /* Do the copy now. */ i = left_el->l_next_free_rec - 1; move_rec = left_el->l_recs[i]; right_el->l_recs[0] = move_rec; /* * Clear out the record we just copied and shift everything * over, leaving an empty extent in the left leaf. * * We temporarily subtract from next_free_rec so that the * shift will lose the tail record (which is now defunct). */ left_el->l_next_free_rec -= 1; ocfs2_shift_records_right(left_el); memset(&left_el->l_recs[0], 0, sizeof(struct ocfs2_extent_rec)); left_el->l_next_free_rec += 1; ocfs2_complete_edge_insert(fs, left_path, right_path, subtree_index); ret = ocfs2_sync_path_to_disk(fs, left_path, right_path, subtree_index); return ret; } /* * Given a full path, determine what cpos value would return us a path * containing the leaf immediately to the left of the current one. * * Will return zero if the path passed in is already the leftmost path. */ static int ocfs2_find_cpos_for_left_leaf(struct ocfs2_path *path, uint32_t *cpos) { int i, j, ret = 0; uint64_t blkno; struct ocfs2_extent_list *el; assert(path->p_tree_depth > 0); *cpos = 0; blkno = path_leaf_blkno(path); /* Start at the tree node just above the leaf and work our way up. */ i = path->p_tree_depth - 1; while (i >= 0) { el = path->p_node[i].el; /* Find the extent record just before the one in our path. */ for(j = 0; j < el->l_next_free_rec; j++) { if (el->l_recs[j].e_blkno == blkno) { if (j == 0) { if (i == 0) { /* * We've determined that the * path specified is already * the leftmost one - return a * cpos of zero. */ goto out; } /* * The leftmost record points to our * leaf - we need to travel up the * tree one level. */ goto next_node; } *cpos = el->l_recs[j - 1].e_cpos; *cpos = *cpos + ocfs2_rec_clusters( el->l_tree_depth, &el->l_recs[j - 1]); *cpos = *cpos - 1; goto out; } } /* * If we got here, we never found a valid node where * the tree indicated one should be. */ ret = OCFS2_ET_CORRUPT_EXTENT_BLOCK; goto out; next_node: blkno = path->p_node[i].blkno; i--; } out: return ret; } /* * Trap the case where we're inserting into the theoretical range past * the _actual_ left leaf range. Otherwise, we'll rotate a record * whose cpos is less than ours into the right leaf. * * It's only necessary to look at the rightmost record of the left * leaf because the logic that calls us should ensure that the * theoretical ranges in the path components above the leaves are * correct. */ static int ocfs2_rotate_requires_path_adjustment(struct ocfs2_path *left_path, uint32_t insert_cpos) { struct ocfs2_extent_list *left_el; struct ocfs2_extent_rec *rec; int next_free; left_el = path_leaf_el(left_path); next_free = left_el->l_next_free_rec; rec = &left_el->l_recs[next_free - 1]; if (insert_cpos > rec->e_cpos) return 1; return 0; } static int ocfs2_leftmost_rec_contains(struct ocfs2_extent_list *el, uint32_t cpos) { int next_free = el->l_next_free_rec; unsigned int range; struct ocfs2_extent_rec *rec; if (next_free == 0) return 0; rec = &el->l_recs[0]; if (ocfs2_is_empty_extent(rec)) { /* Empty list. */ if (next_free == 1) return 0; rec = &el->l_recs[1]; } range = rec->e_cpos + ocfs2_rec_clusters(el->l_tree_depth, rec); if (cpos >= rec->e_cpos && cpos < range) return 1; return 0; } /* * Rotate all the records in a btree right one record, starting at insert_cpos. * * The path to the rightmost leaf should be passed in. * * The array is assumed to be large enough to hold an entire path (tree depth). * * Upon succesful return from this function: * * - The 'right_path' array will contain a path to the leaf block * whose range contains e_cpos. * - That leaf block will have a single empty extent in list index 0. * - In the case that the rotation requires a post-insert update, * *ret_left_path will contain a valid path which can be passed to * ocfs2_insert_path(). */ static int ocfs2_rotate_tree_right(ocfs2_filesys *fs, enum ocfs2_split_type split, uint32_t insert_cpos, struct ocfs2_path *right_path, struct ocfs2_path **ret_left_path) { int ret, start; uint32_t cpos; struct ocfs2_path *left_path = NULL; *ret_left_path = NULL; left_path = ocfs2_new_path_from_path(right_path); if (!left_path) { ret = OCFS2_ET_NO_MEMORY; goto out; } ret = ocfs2_find_cpos_for_left_leaf(right_path, &cpos); if (ret) goto out; /* * What we want to do here is: * * 1) Start with the rightmost path. * * 2) Determine a path to the leaf block directly to the left * of that leaf. * * 3) Determine the 'subtree root' - the lowest level tree node * which contains a path to both leaves. * * 4) Rotate the subtree. * * 5) Find the next subtree by considering the left path to be * the new right path. * * The check at the top of this while loop also accepts * insert_cpos == cpos because cpos is only a _theoretical_ * value to get us the left path - insert_cpos might very well * be filling that hole. * * Stop at a cpos of '0' because we either started at the * leftmost branch (i.e., a tree with one branch and a * rotation inside of it), or we've gone as far as we can in * rotating subtrees. */ while (cpos && insert_cpos <= cpos) { ret = ocfs2_find_path(fs, left_path, cpos); if (ret) goto out; if (path_leaf_blkno(left_path) == path_leaf_blkno(right_path)) assert(0); if (split == SPLIT_NONE && ocfs2_rotate_requires_path_adjustment(left_path, insert_cpos)) { /* * We've rotated the tree as much as we * should. The rest is up to * ocfs2_insert_path() to complete, after the * record insertion. We indicate this * situation by returning the left path. * * The reason we don't adjust the records here * before the record insert is that an error * later might break the rule where a parent * record e_cpos will reflect the actual * e_cpos of the 1st nonempty record of the * child list. */ *ret_left_path = left_path; goto out_ret_path; } start = ocfs2_find_subtree_root(left_path, right_path); ret = ocfs2_rotate_subtree_right(fs, left_path, right_path, start); if (ret) goto out; if (split != SPLIT_NONE && ocfs2_leftmost_rec_contains(path_leaf_el(right_path), insert_cpos)) { /* * A rotate moves the rightmost left leaf * record over to the leftmost right leaf * slot. If we're doing an extent split * instead of a real insert, then we have to * check that the extent to be split wasn't * just moved over. If it was, then we can * exit here, passing left_path back - * ocfs2_split_extent() is smart enough to * search both leaves. */ *ret_left_path = left_path; goto out_ret_path; } /* * There is no need to re-read the next right path * as we know that it'll be our current left * path. Optimize by copying values instead. */ ocfs2_mv_path(right_path, left_path); ret = ocfs2_find_cpos_for_left_leaf(right_path, &cpos); if (ret) goto out; } out: ocfs2_free_path(left_path); out_ret_path: return ret; } static void ocfs2_update_edge_lengths(struct ocfs2_path *path) { int i, idx; struct ocfs2_extent_rec *rec; struct ocfs2_extent_list *el; struct ocfs2_extent_block *eb; uint32_t range; /* Path should always be rightmost. */ eb = (struct ocfs2_extent_block *)path_leaf_buf(path); assert(eb->h_next_leaf_blk == 0ULL); el = &eb->h_list; assert(el->l_next_free_rec > 0); idx = el->l_next_free_rec - 1; rec = &el->l_recs[idx]; range = rec->e_cpos + ocfs2_rec_clusters(el->l_tree_depth, rec); for (i = 0; i < path->p_tree_depth; i++) { el = path->p_node[i].el; idx = el->l_next_free_rec - 1; rec = &el->l_recs[idx]; rec->e_int_clusters = range; rec->e_int_clusters -= rec->e_cpos; } } static errcode_t ocfs2_unlink_path(ocfs2_filesys *fs, struct ocfs2_path *path, int unlink_start) { int ret, i; struct ocfs2_extent_block *eb; struct ocfs2_extent_list *el; char *buf; for(i = unlink_start; i < path_num_items(path); i++) { buf = path->p_node[i].buf; eb = (struct ocfs2_extent_block *)buf; /* * Not all nodes might have had their final count * decremented by the caller - handle this here. */ el = &eb->h_list; assert(el->l_next_free_rec <= 1); el->l_next_free_rec = 0; memset(&el->l_recs[0], 0, sizeof(struct ocfs2_extent_rec)); ret = ocfs2_delete_extent_block(fs, path->p_node[i].blkno); if (ret) return ret; } return 0; } /* * ocfs2_unlink_subtree will delete extent blocks in the "right_path" * from "subtree_index". */ static errcode_t ocfs2_unlink_subtree(ocfs2_filesys *fs, struct ocfs2_path *left_path, struct ocfs2_path *right_path, int subtree_index) { errcode_t ret; int i; struct ocfs2_extent_list *root_el = left_path->p_node[subtree_index].el; struct ocfs2_extent_block *eb; eb = (struct ocfs2_extent_block *)right_path->p_node[subtree_index + 1].buf; for(i = 1; i < root_el->l_next_free_rec; i++) if (root_el->l_recs[i].e_blkno == eb->h_blkno) break; assert(i < root_el->l_next_free_rec); memset(&root_el->l_recs[i], 0, sizeof(struct ocfs2_extent_rec)); root_el->l_next_free_rec -= 1; eb = (struct ocfs2_extent_block *)path_leaf_buf(left_path); eb->h_next_leaf_blk = 0; ret = ocfs2_unlink_path(fs, right_path, subtree_index + 1); if (ret) return ret; return 0; } static int ocfs2_rotate_subtree_left(ocfs2_filesys *fs, struct ocfs2_extent_tree *et, struct ocfs2_path *left_path, struct ocfs2_path *right_path, int subtree_index, int *deleted) { errcode_t ret; int i, del_right_subtree = 0, right_has_empty = 0; struct ocfs2_extent_list *right_leaf_el, *left_leaf_el; struct ocfs2_extent_block *eb; *deleted = 0; right_leaf_el = path_leaf_el(right_path); left_leaf_el = path_leaf_el(left_path); assert(left_path->p_node[subtree_index].blkno == right_path->p_node[subtree_index].blkno); if (!ocfs2_is_empty_extent(&left_leaf_el->l_recs[0])) return 0; eb = (struct ocfs2_extent_block *)path_leaf_buf(right_path); if (ocfs2_is_empty_extent(&right_leaf_el->l_recs[0])) { /* * It's legal for us to proceed if the right leaf is * the rightmost one and it has an empty extent. There * are two cases to handle - whether the leaf will be * empty after removal or not. If the leaf isn't empty * then just remove the empty extent up front. The * next block will handle empty leaves by flagging * them for unlink. * * Non rightmost leaves will throw EAGAIN and the * caller can manually move the subtree and retry. */ if (eb->h_next_leaf_blk != 0ULL) return EAGAIN; if (right_leaf_el->l_next_free_rec > 1) { ocfs2_remove_empty_extent(right_leaf_el); } else right_has_empty = 1; } if (eb->h_next_leaf_blk == 0ULL && right_leaf_el->l_next_free_rec == 1) { /* * We have to update i_last_eb_blk during the meta * data delete. */ del_right_subtree = 1; } /* * Getting here with an empty extent in the right path implies * that it's the rightmost path and will be deleted. */ assert(!right_has_empty || del_right_subtree); if (!right_has_empty) { /* * Only do this if we're moving a real * record. Otherwise, the action is delayed until * after removal of the right path in which case we * can do a simple shift to remove the empty extent. */ ocfs2_rotate_leaf(left_leaf_el, &right_leaf_el->l_recs[0]); memset(&right_leaf_el->l_recs[0], 0, sizeof(struct ocfs2_extent_rec)); } if (eb->h_next_leaf_blk == 0ULL) { /* * Move recs over to get rid of empty extent, decrease * next_free. This is allowed to remove the last * extent in our leaf (setting l_next_free_rec to * zero) - the delete code below won't care. */ ocfs2_remove_empty_extent(right_leaf_el); } if (del_right_subtree) { ocfs2_unlink_subtree(fs, left_path, right_path, subtree_index); ocfs2_update_edge_lengths(left_path); /* * Now the good extent block information is stored in left_path, so * synchronize the right path with it. */ for (i = 0; i <= subtree_index; i++) memcpy(right_path->p_node[i].buf, left_path->p_node[i].buf, fs->fs_blocksize); eb = (struct ocfs2_extent_block *)path_leaf_buf(left_path); ocfs2_et_set_last_eb_blk(et, eb->h_blkno); /* * Removal of the extent in the left leaf was skipped * above so we could delete the right path * 1st. */ if (right_has_empty) ocfs2_remove_empty_extent(left_leaf_el); *deleted = 1; /* * The extent block in the right path belwo subtree_index * have been deleted, so we don't need to synchronize * them to the disk. */ ret = ocfs2_sync_path_to_disk(fs, left_path, NULL, subtree_index); } else { ocfs2_complete_edge_insert(fs, left_path, right_path, subtree_index); ret = ocfs2_sync_path_to_disk(fs, left_path, right_path, subtree_index); } return ret; } /* * Given a full path, determine what cpos value would return us a path * containing the leaf immediately to the right of the current one. * * Will return zero if the path passed in is already the rightmost path. * * This looks similar, but is subtly different to * ocfs2_find_cpos_for_left_leaf(). */ static int ocfs2_find_cpos_for_right_leaf(ocfs2_filesys *fs, struct ocfs2_path *path, uint32_t *cpos) { int i, j, ret = 0; uint64_t blkno; struct ocfs2_extent_list *el; *cpos = 0; if (path->p_tree_depth == 0) return 0; blkno = path_leaf_blkno(path); /* Start at the tree node just above the leaf and work our way up. */ i = path->p_tree_depth - 1; while (i >= 0) { int next_free; el = path->p_node[i].el; /* * Find the extent record just after the one in our * path. */ next_free = el->l_next_free_rec; for(j = 0; j < el->l_next_free_rec; j++) { if (el->l_recs[j].e_blkno == blkno) { if (j == (next_free - 1)) { if (i == 0) { /* * We've determined that the * path specified is already * the rightmost one - return a * cpos of zero. */ goto out; } /* * The rightmost record points to our * leaf - we need to travel up the * tree one level. */ goto next_node; } *cpos = el->l_recs[j + 1].e_cpos; goto out; } } /* * If we got here, we never found a valid node where * the tree indicated one should be. */ ret = OCFS2_ET_CORRUPT_EXTENT_BLOCK; goto out; next_node: blkno = path->p_node[i].blkno; i--; } out: return ret; } static void ocfs2_rotate_rightmost_leaf_left(ocfs2_filesys *fs, struct ocfs2_extent_list *el) { if (!ocfs2_is_empty_extent(&el->l_recs[0])) return; ocfs2_remove_empty_extent(el); return; } static int __ocfs2_rotate_tree_left(ocfs2_filesys *fs, struct ocfs2_extent_tree *et, struct ocfs2_path *path, struct ocfs2_path **empty_extent_path) { int i, ret, subtree_root, deleted; uint32_t right_cpos; struct ocfs2_path *left_path = NULL; struct ocfs2_path *right_path = NULL; assert(ocfs2_is_empty_extent(&(path_leaf_el(path)->l_recs[0]))); *empty_extent_path = NULL; ret = ocfs2_find_cpos_for_right_leaf(fs, path, &right_cpos); if (ret) goto out; left_path = ocfs2_new_path_from_path(path); if (!left_path) { ret = OCFS2_ET_NO_MEMORY; goto out; } ocfs2_cp_path(fs, left_path, path); right_path = ocfs2_new_path_from_path(path); if (!right_path) { ret = OCFS2_ET_NO_MEMORY; goto out; } while (right_cpos) { ret = ocfs2_find_path(fs, right_path, right_cpos); if (ret) goto out; subtree_root = ocfs2_find_subtree_root(left_path, right_path); ret = ocfs2_rotate_subtree_left(fs, et, left_path, right_path, subtree_root, &deleted); if (ret == EAGAIN) { /* * The rotation has to temporarily stop due to * the right subtree having an empty * extent. Pass it back to the caller for a * fixup. */ *empty_extent_path = right_path; right_path = NULL; goto out; } if (ret) goto out; /* * The subtree rotate might have removed records on * the rightmost edge. If so, then rotation is * complete. */ if (deleted) break; ocfs2_mv_path(left_path, right_path); ret = ocfs2_find_cpos_for_right_leaf(fs, left_path, &right_cpos); if (ret) goto out; } out: ocfs2_free_path(right_path); ocfs2_free_path(left_path); /* * the path's information is changed during the process of rotation, * so re-read them. */ for (i = 1; i <= path->p_tree_depth; i++) { ret = ocfs2_read_extent_block(fs, path->p_node[i].blkno, path->p_node[i].buf); if (ret) break; } return ret; } static int ocfs2_remove_rightmost_path(ocfs2_filesys *fs, struct ocfs2_extent_tree *et, struct ocfs2_path *path) { int ret, subtree_index, i; uint32_t cpos; struct ocfs2_path *left_path = NULL; struct ocfs2_extent_block *eb; struct ocfs2_extent_list *el; ret = ocfs2_find_cpos_for_left_leaf(path, &cpos); if (ret) goto out; if (cpos) { /* * We have a path to the left of this one - it needs * an update too. */ left_path = ocfs2_new_path_from_path(path); if (!left_path) { ret = OCFS2_ET_NO_MEMORY; goto out; } ret = ocfs2_find_path(fs, left_path, cpos); if (ret) goto out; subtree_index = ocfs2_find_subtree_root(left_path, path); ocfs2_unlink_subtree(fs, left_path, path, subtree_index); ocfs2_update_edge_lengths(left_path); /* * Now the good extent block information is stored in left_path, so * synchronize the right path with it. */ for (i = 0; i <= subtree_index; i++) memcpy(path->p_node[i].buf, left_path->p_node[i].buf, fs->fs_blocksize); ret = ocfs2_sync_path_to_disk(fs, left_path, NULL, subtree_index); if (ret) goto out; eb = (struct ocfs2_extent_block *)path_leaf_buf(left_path); ocfs2_et_set_last_eb_blk(et, eb->h_blkno); } else { /* * 'path' is also the leftmost path which * means it must be the only one. This gets * handled differently because we want to * revert the inode back to having extents * in-line. */ ocfs2_unlink_path(fs, path, 1); el = et->et_root_el; el->l_tree_depth = 0; el->l_next_free_rec = 0; memset(&el->l_recs[0], 0, sizeof(struct ocfs2_extent_rec)); ocfs2_et_set_last_eb_blk(et, 0); } out: ocfs2_free_path(left_path); return ret; } /* * Left rotation of btree records. * * In many ways, this is (unsurprisingly) the opposite of right * rotation. We start at some non-rightmost path containing an empty * extent in the leaf block. The code works its way to the rightmost * path by rotating records to the left in every subtree. * * This is used by any code which reduces the number of extent records * in a leaf. After removal, an empty record should be placed in the * leftmost list position. * * This won't handle a length update of the rightmost path records if * the rightmost tree leaf record is removed so the caller is * responsible for detecting and correcting that. */ static int ocfs2_rotate_tree_left(ocfs2_filesys *fs, struct ocfs2_extent_tree *et, struct ocfs2_path *path) { int ret = 0; struct ocfs2_path *tmp_path = NULL, *restart_path = NULL; struct ocfs2_extent_block *eb; struct ocfs2_extent_list *el; el = path_leaf_el(path); if (!ocfs2_is_empty_extent(&el->l_recs[0])) return 0; if (path->p_tree_depth == 0) { rightmost_no_delete: /* * In-inode extents. This is trivially handled, so do * it up front. */ ocfs2_rotate_rightmost_leaf_left(fs, path_leaf_el(path)); /* we have to synchronize the modified extent block to disk. */ if (path->p_tree_depth > 0) { ret = ocfs2_write_extent_block(fs, path_leaf_blkno(path), path_leaf_buf(path)); } goto out; } /* * Handle rightmost branch now. There's several cases: * 1) simple rotation leaving records in there. That's trivial. * 2) rotation requiring a branch delete - there's no more * records left. Two cases of this: * a) There are branches to the left. * b) This is also the leftmost (the only) branch. * * 1) is handled via ocfs2_rotate_rightmost_leaf_left() * 2a) we need the left branch so that we can update it with the unlink * 2b) we need to bring the inode back to inline extents. */ eb = (struct ocfs2_extent_block *)path_leaf_buf(path); el = &eb->h_list; if (eb->h_next_leaf_blk == 0) { /* * This gets a bit tricky if we're going to delete the * rightmost path. Get the other cases out of the way * 1st. */ if (el->l_next_free_rec > 1) goto rightmost_no_delete; if (el->l_next_free_rec == 0) { ret = OCFS2_ET_CORRUPT_EXTENT_BLOCK; goto out; } /* * XXX: The caller can not trust "path" any more after * this as it will have been deleted. What do we do? * * In theory the rotate-for-merge code will never get * here because it'll always ask for a rotate in a * nonempty list. */ ret = ocfs2_remove_rightmost_path(fs, et, path); goto out; } /* * Now we can loop, remembering the path we get from EAGAIN * and restarting from there. */ try_rotate: ret = __ocfs2_rotate_tree_left(fs, et, path, &restart_path); if (ret && ret != EAGAIN) { goto out; } while (ret == EAGAIN) { tmp_path = restart_path; restart_path = NULL; ret = __ocfs2_rotate_tree_left(fs, et, tmp_path, &restart_path); if (ret && ret != EAGAIN) { goto out; } ocfs2_free_path(tmp_path); tmp_path = NULL; if (ret == 0) goto try_rotate; } out: ocfs2_free_path(tmp_path); ocfs2_free_path(restart_path); return ret; } static void ocfs2_cleanup_merge(struct ocfs2_extent_list *el, int index) { struct ocfs2_extent_rec *rec = &el->l_recs[index]; unsigned int size; if (rec->e_leaf_clusters == 0) { /* * We consumed all of the merged-from record. An empty * extent cannot exist anywhere but the 1st array * position, so move things over if the merged-from * record doesn't occupy that position. * * This creates a new empty extent so the caller * should be smart enough to have removed any existing * ones. */ if (index > 0) { assert(!ocfs2_is_empty_extent(&el->l_recs[0])); size = index * sizeof(struct ocfs2_extent_rec); memmove(&el->l_recs[1], &el->l_recs[0], size); } /* * Always memset - the caller doesn't check whether it * created an empty extent, so there could be junk in * the other fields. */ memset(&el->l_recs[0], 0, sizeof(struct ocfs2_extent_rec)); } } /* * Remove split_rec clusters from the record at index and merge them * onto the beginning of the record at index + 1. */ static int ocfs2_merge_rec_right(ocfs2_filesys *fs, struct ocfs2_extent_rec *split_rec, struct ocfs2_extent_list *el, int index) { unsigned int split_clusters = split_rec->e_leaf_clusters; struct ocfs2_extent_rec *left_rec; struct ocfs2_extent_rec *right_rec; assert(index < el->l_next_free_rec); left_rec = &el->l_recs[index]; right_rec = &el->l_recs[index + 1]; left_rec->e_leaf_clusters -= split_clusters; right_rec->e_cpos -= split_clusters; right_rec->e_blkno -= ocfs2_clusters_to_blocks(fs, split_clusters); right_rec->e_leaf_clusters += split_clusters; ocfs2_cleanup_merge(el, index); return 0; } /* * Remove split_rec clusters from the record at index and merge them * onto the tail of the record at index - 1. */ static int ocfs2_merge_rec_left(ocfs2_filesys *fs, struct ocfs2_extent_rec *split_rec, struct ocfs2_extent_list *el, int index) { int has_empty_extent = 0; unsigned int split_clusters = split_rec->e_leaf_clusters; struct ocfs2_extent_rec *left_rec; struct ocfs2_extent_rec *right_rec; assert(index > 0); left_rec = &el->l_recs[index - 1]; right_rec = &el->l_recs[index]; if (ocfs2_is_empty_extent(&el->l_recs[0])) has_empty_extent = 1; if (has_empty_extent && index == 1) { /* * The easy case - we can just plop the record right in. */ *left_rec = *split_rec; has_empty_extent = 0; } else { left_rec->e_leaf_clusters += split_clusters; } right_rec->e_cpos += split_clusters; right_rec->e_blkno += ocfs2_clusters_to_blocks(fs, split_clusters); right_rec->e_leaf_clusters -= split_clusters; ocfs2_cleanup_merge(el, index); return 0; } static int ocfs2_try_to_merge_extent(ocfs2_filesys *fs, struct ocfs2_extent_tree *et, struct ocfs2_path *left_path, int split_index, struct ocfs2_extent_rec *split_rec, struct ocfs2_merge_ctxt *ctxt) { int ret = 0; struct ocfs2_extent_list *el = path_leaf_el(left_path); struct ocfs2_extent_rec *rec = &el->l_recs[split_index]; assert(ctxt->c_contig_type != CONTIG_NONE); if (ctxt->c_split_covers_rec && ctxt->c_has_empty_extent) { /* * The merge code will need to create an empty * extent to take the place of the newly * emptied slot. Remove any pre-existing empty * extents - having more than one in a leaf is * illegal. */ ret = ocfs2_rotate_tree_left(fs, et, left_path); if (ret) goto out; split_index--; rec = &el->l_recs[split_index]; } if (ctxt->c_contig_type == CONTIG_LEFTRIGHT) { /* * Left-right contig implies this. */ assert(ctxt->c_split_covers_rec); assert(split_index != 0); /* * Since the leftright insert always covers the entire * extent, this call will delete the insert record * entirely, resulting in an empty extent record added to * the extent block. * * Since the adding of an empty extent shifts * everything back to the right, there's no need to * update split_index here. */ ret = ocfs2_merge_rec_left(fs, split_rec, el, split_index); if (ret) goto out; /* * We can only get this from logic error above. */ assert(ocfs2_is_empty_extent(&el->l_recs[0])); /* * The left merge left us with an empty extent, remove * it. */ ret = ocfs2_rotate_tree_left(fs, et, left_path); if (ret) goto out; split_index--; rec = &el->l_recs[split_index]; /* * Note that we don't pass split_rec here on purpose - * we've merged it into the left side. */ ret = ocfs2_merge_rec_right(fs, rec, el, split_index); if (ret) goto out; assert(ocfs2_is_empty_extent(&el->l_recs[0])); ret = ocfs2_rotate_tree_left(fs, et, left_path); /* * Error from this last rotate is not critical, so * don't bubble it up. */ ret = 0; } else { /* * Merge a record to the left or right. * * 'contig_type' is relative to the existing record, * so for example, if we're "right contig", it's to * the record on the left (hence the left merge). */ if (ctxt->c_contig_type == CONTIG_RIGHT) { ret = ocfs2_merge_rec_left(fs, split_rec, el, split_index); if (ret) goto out; } else { ret = ocfs2_merge_rec_right(fs, split_rec, el, split_index); if (ret) goto out; } /* we have to synchronize the modified extent block to disk. */ if (left_path->p_tree_depth > 0) { ret = ocfs2_write_extent_block(fs, path_leaf_blkno(left_path), path_leaf_buf(left_path)); if (ret) goto out; } if (ctxt->c_split_covers_rec) { /* * The merge may have left an empty extent in * our leaf. Try to rotate it away. */ ret = ocfs2_rotate_tree_left(fs, et, left_path); ret = 0; } } out: return ret; } static void ocfs2_subtract_from_rec(ocfs2_filesys *fs, enum ocfs2_split_type split, struct ocfs2_extent_rec *rec, struct ocfs2_extent_rec *split_rec) { uint64_t len_blocks; len_blocks = ocfs2_clusters_to_blocks(fs, split_rec->e_leaf_clusters); if (split == SPLIT_LEFT) { /* * Region is on the left edge of the existing * record. */ rec->e_cpos += split_rec->e_leaf_clusters; rec->e_blkno += len_blocks; rec->e_leaf_clusters -= split_rec->e_leaf_clusters; } else { /* * Region is on the right edge of the existing * record. */ rec->e_leaf_clusters -= split_rec->e_leaf_clusters; } } /* * Change the depth of the tree. That means allocating an extent block, * copying all extent records from the dinode into the extent block, * and then pointing the dinode to the new extent_block. */ static errcode_t shift_tree_depth(ocfs2_filesys *fs, struct ocfs2_extent_tree *et, char **new_eb) { errcode_t ret; char *buf = NULL; uint64_t blkno; struct ocfs2_extent_list *el = et->et_root_el; struct ocfs2_extent_block *eb; uint32_t new_clusters; if (el->l_next_free_rec != el->l_count) return OCFS2_ET_INTERNAL_FAILURE; ret = ocfs2_malloc_block(fs->fs_io, &buf); if (ret) return ret; ret = ocfs2_new_extent_block(fs, &blkno); if (ret) goto out; ret = ocfs2_read_extent_block(fs, blkno, buf); if (ret) goto out; eb = (struct ocfs2_extent_block *)buf; eb->h_list.l_tree_depth = el->l_tree_depth; eb->h_list.l_next_free_rec = el->l_next_free_rec; memcpy(eb->h_list.l_recs, el->l_recs, sizeof(struct ocfs2_extent_rec) * el->l_count); new_clusters = ocfs2_sum_rightmost_rec(&eb->h_list); el->l_tree_depth++; memset(el->l_recs, 0, sizeof(struct ocfs2_extent_rec) * el->l_count); el->l_recs[0].e_cpos = 0; el->l_recs[0].e_blkno = blkno; el->l_recs[0].e_int_clusters = new_clusters; el->l_next_free_rec = 1; if (el->l_tree_depth == 1) ocfs2_et_set_last_eb_blk(et, blkno); ret = ocfs2_write_extent_block(fs, blkno, buf); if (!ret) *new_eb = buf; out: if (buf && !*new_eb) ocfs2_free(&buf); return ret; } static enum ocfs2_contig_type ocfs2_figure_merge_contig_type(ocfs2_filesys *fs, struct ocfs2_extent_tree *et, struct ocfs2_extent_list *el, int index, struct ocfs2_extent_rec *split_rec) { struct ocfs2_extent_rec *rec; enum ocfs2_contig_type ret = CONTIG_NONE; /* * We're careful to check for an empty extent record here - * the merge code will know what to do if it sees one. */ if (index > 0) { rec = &el->l_recs[index - 1]; if (index == 1 && ocfs2_is_empty_extent(rec)) { if (split_rec->e_cpos == el->l_recs[index].e_cpos) ret = CONTIG_RIGHT; } else { ret = ocfs2_et_extent_contig(fs, et, rec, split_rec); } } if (index < el->l_next_free_rec - 1) { enum ocfs2_contig_type contig_type; rec = &el->l_recs[index + 1]; contig_type = ocfs2_et_extent_contig(fs, et, rec, split_rec); if (contig_type == CONTIG_LEFT && ret == CONTIG_RIGHT) ret = CONTIG_LEFTRIGHT; else if (ret == CONTIG_NONE) ret = contig_type; } return ret; } static void ocfs2_figure_contig_type(ocfs2_filesys *fs, struct ocfs2_extent_tree *et, struct ocfs2_insert_type *insert, struct ocfs2_extent_list *el, struct ocfs2_extent_rec *insert_rec) { int i; enum ocfs2_contig_type contig_type = CONTIG_NONE; assert(el->l_tree_depth == 0); for(i = 0; i < el->l_next_free_rec; i++) { contig_type = ocfs2_et_extent_contig(fs, et, &el->l_recs[i], insert_rec); if (contig_type != CONTIG_NONE) { insert->ins_contig_index = i; break; } } insert->ins_contig = contig_type; } /* * This should only be called against the righmost leaf extent list. * * ocfs2_figure_appending_type() will figure out whether we'll have to * insert at the tail of the rightmost leaf. * * This should also work against the dinode list for tree's with 0 * depth. If we consider the dinode list to be the rightmost leaf node * then the logic here makes sense. */ static void ocfs2_figure_appending_type(struct ocfs2_insert_type *insert, struct ocfs2_extent_list *el, struct ocfs2_extent_rec *insert_rec) { int i; uint32_t cpos = insert_rec->e_cpos; struct ocfs2_extent_rec *rec; insert->ins_appending = APPEND_NONE; assert(el->l_tree_depth == 0); if (!el->l_next_free_rec) goto set_tail_append; if (ocfs2_is_empty_extent(&el->l_recs[0])) { /* Were all records empty? */ if (el->l_next_free_rec == 1) goto set_tail_append; } i = el->l_next_free_rec - 1; rec = &el->l_recs[i]; if (cpos >= (rec->e_cpos + rec->e_leaf_clusters)) goto set_tail_append; return; set_tail_append: insert->ins_appending = APPEND_TAIL; } /* * Helper function called at the begining of an insert. * * This computes a few things that are commonly used in the process of * inserting into the btree: * - Whether the new extent is contiguous with an existing one. * - The current tree depth. * - Whether the insert is an appending one. * - The total # of free records in the tree. * * All of the information is stored on the ocfs2_insert_type * structure. */ static int ocfs2_figure_insert_type(struct insert_ctxt *ctxt, char **last_eb_buf, int *free_records, struct ocfs2_insert_type *insert) { int ret; struct ocfs2_extent_block *eb; struct ocfs2_extent_list *el; struct ocfs2_extent_rec *insert_rec = &ctxt->rec; ocfs2_filesys *fs = ctxt->fs; struct ocfs2_extent_tree *et = ctxt->et; struct ocfs2_path *path = NULL; char *buf = *last_eb_buf; uint64_t last_eb_blk = ocfs2_et_get_last_eb_blk(et); insert->ins_split = SPLIT_NONE; el = et->et_root_el; insert->ins_tree_depth = el->l_tree_depth; if (el->l_tree_depth) { /* * If we have tree depth, we read in the * rightmost extent block ahead of time as * ocfs2_figure_insert_type() and ocfs2_add_branch() * may want it later. */ assert(buf); ret = ocfs2_read_extent_block(fs, last_eb_blk, buf); if (ret) goto out; eb = (struct ocfs2_extent_block *) buf; el = &eb->h_list; } /* * Unless we have a contiguous insert, we'll need to know if * there is room left in our allocation tree for another * extent record. * * XXX: This test is simplistic, we can search for empty * extent records too. */ *free_records = el->l_count - el->l_next_free_rec; if (!insert->ins_tree_depth) { ocfs2_figure_contig_type(fs, et, insert, el, insert_rec); ocfs2_figure_appending_type(insert, el, insert_rec); return 0; } path = ocfs2_new_path_from_et(et); if (!path) { ret = OCFS2_ET_NO_MEMORY; goto out; } /* * In the case that we're inserting past what the tree * currently accounts for, ocf2_find_path() will return for * us the rightmost tree path. This is accounted for below in * the appending code. */ ret = ocfs2_find_path(fs, path, insert_rec->e_cpos); if (ret) goto out; el = path_leaf_el(path); /* * Now that we have the path, there's two things we want to determine: * 1) Contiguousness (also set contig_index if this is so) * * 2) Are we doing an append? We can trivially break this up * into two types of appends: simple record append, or a * rotate inside the tail leaf. */ ocfs2_figure_contig_type(fs, et, insert, el, insert_rec); /* * The insert code isn't quite ready to deal with all cases of * left contiguousness. Specifically, if it's an insert into * the 1st record in a leaf, it will require the adjustment of * e_clusters on the last record of the path directly to it's * left. For now, just catch that case and fool the layers * above us. This works just fine for tree_depth == 0, which * is why we allow that above. */ if (insert->ins_contig == CONTIG_LEFT && insert->ins_contig_index == 0) insert->ins_contig = CONTIG_NONE; /* * Ok, so we can simply compare against last_eb to figure out * whether the path doesn't exist. This will only happen in * the case that we're doing a tail append, so maybe we can * take advantage of that information somehow. */ if (last_eb_blk == path_leaf_blkno(path)) { /* * Ok, ocfs2_find_path() returned us the rightmost * tree path. This might be an appending insert. There are * two cases: * 1) We're doing a true append at the tail: * -This might even be off the end of the leaf * 2) We're "appending" by rotating in the tail */ ocfs2_figure_appending_type(insert, el, insert_rec); } out: ocfs2_free_path(path); return ret; } /* * Do the final bits of extent record insertion at the target leaf * list. If this leaf is part of an allocation tree, it is assumed * that the tree above has been prepared. */ static void ocfs2_insert_at_leaf(ocfs2_filesys *fs, struct ocfs2_extent_rec *insert_rec, struct ocfs2_extent_list *el, struct ocfs2_insert_type *insert) { int i = insert->ins_contig_index; unsigned int range; struct ocfs2_extent_rec *rec; assert(el->l_tree_depth == 0); if (insert->ins_split != SPLIT_NONE) { i = ocfs2_search_extent_list(el, insert_rec->e_cpos); assert(i != -1); rec = &el->l_recs[i]; ocfs2_subtract_from_rec(fs, insert->ins_split, rec, insert_rec); goto rotate; } /* * Contiguous insert - either left or right. */ if (insert->ins_contig != CONTIG_NONE) { rec = &el->l_recs[i]; if (insert->ins_contig == CONTIG_LEFT) { rec->e_blkno = insert_rec->e_blkno; rec->e_cpos = insert_rec->e_cpos; } rec->e_leaf_clusters += insert_rec->e_leaf_clusters; return; } /* * Handle insert into an empty leaf. */ if (el->l_next_free_rec == 0 || (el->l_next_free_rec == 1 && ocfs2_is_empty_extent(&el->l_recs[0]))) { el->l_recs[0] = *insert_rec; el->l_next_free_rec = 1; return; } /* * Appending insert. */ if (insert->ins_appending == APPEND_TAIL) { i = el->l_next_free_rec - 1; rec = &el->l_recs[i]; range = rec->e_cpos + rec->e_leaf_clusters; assert(insert_rec->e_cpos >= range); i++; el->l_recs[i] = *insert_rec; el->l_next_free_rec += 1; return; } rotate: /* * Ok, we have to rotate. * * At this point, it is safe to assume that inserting into an * empty leaf and appending to a leaf have both been handled * above. * * This leaf needs to have space, either by the empty 1st * extent record, or by virtue of an l_next_rec < l_count. */ ocfs2_rotate_leaf(el, insert_rec); } static int ocfs2_adjust_rightmost_records(ocfs2_filesys *fs, struct ocfs2_path *path, struct ocfs2_extent_rec *insert_rec) { int i, next_free; struct ocfs2_extent_list *el; struct ocfs2_extent_rec *rec; /* * Update everything except the leaf block. */ for (i = 0; i < path->p_tree_depth; i++) { el = path->p_node[i].el; next_free = el->l_next_free_rec; if (next_free == 0) return OCFS2_ET_CORRUPT_EXTENT_BLOCK; rec = &el->l_recs[next_free - 1]; rec->e_int_clusters = insert_rec->e_cpos; rec->e_int_clusters += insert_rec->e_leaf_clusters; rec->e_int_clusters -= rec->e_cpos; } return 0; } static int ocfs2_append_rec_to_path(ocfs2_filesys *fs, struct ocfs2_extent_rec *insert_rec, struct ocfs2_path *right_path, struct ocfs2_path **ret_left_path) { int ret, next_free, i; struct ocfs2_extent_list *el; struct ocfs2_path *left_path = NULL; *ret_left_path = NULL; /* * This shouldn't happen for non-trees. The extent rec cluster * count manipulation below only works for interior nodes. */ assert(right_path->p_tree_depth > 0); /* * If our appending insert is at the leftmost edge of a leaf, * then we might need to update the rightmost records of the * neighboring path. */ el = path_leaf_el(right_path); next_free = el->l_next_free_rec; if (next_free == 0 || (next_free == 1 && ocfs2_is_empty_extent(&el->l_recs[0]))) { uint32_t left_cpos; ret = ocfs2_find_cpos_for_left_leaf(right_path, &left_cpos); if (ret) goto out; /* * No need to worry if the append is already in the * leftmost leaf. */ if (left_cpos) { left_path = ocfs2_new_path_from_path(right_path); if (!left_path) { ret = OCFS2_ET_NO_MEMORY; goto out; } ret = ocfs2_find_path(fs, left_path, left_cpos); if (ret) goto out; } } ret = ocfs2_adjust_rightmost_records(fs, right_path, insert_rec); if (ret) goto out; if (left_path) { /* * Userspace sepcially. * In case we have changed some blocks that is also in * right_path, we have to update them in the left_path. */ i = 0; while (i++ < left_path->p_tree_depth) if (left_path->p_node[i].blkno == right_path->p_node[i].blkno) memcpy(left_path->p_node[i].buf, right_path->p_node[i].buf, fs->fs_blocksize); } *ret_left_path = left_path; ret = 0; out: if (ret) ocfs2_free_path(left_path); return ret; } static void ocfs2_split_record(ocfs2_filesys *fs, struct ocfs2_path *left_path, struct ocfs2_path *right_path, struct ocfs2_extent_rec *split_rec, enum ocfs2_split_type split) { int index; uint32_t cpos = split_rec->e_cpos; struct ocfs2_extent_list *left_el = NULL, *right_el, *insert_el, *el; struct ocfs2_extent_rec *rec, *tmprec; right_el = path_leaf_el(right_path);; if (left_path) left_el = path_leaf_el(left_path); el = right_el; insert_el = right_el; index = ocfs2_search_extent_list(el, cpos); if (index != -1) { if (index == 0 && left_path) { assert(!ocfs2_is_empty_extent(&el->l_recs[0])); /* * This typically means that the record * started in the left path but moved to the * right as a result of rotation. We either * move the existing record to the left, or we * do the later insert there. * * In this case, the left path should always * exist as the rotate code will have passed * it back for a post-insert update. */ if (split == SPLIT_LEFT) { /* * It's a left split. Since we know * that the rotate code gave us an * empty extent in the left path, we * can just do the insert there. */ insert_el = left_el; } else { /* * Right split - we have to move the * existing record over to the left * leaf. The insert will be into the * newly created empty extent in the * right leaf. */ tmprec = &right_el->l_recs[index]; ocfs2_rotate_leaf(left_el, tmprec); el = left_el; memset(tmprec, 0, sizeof(*tmprec)); index = ocfs2_search_extent_list(left_el, cpos); assert(index != -1); } } } else { assert(left_path); assert(ocfs2_is_empty_extent(&left_el->l_recs[0])); /* * Left path is easy - we can just allow the insert to * happen. */ el = left_el; insert_el = left_el; index = ocfs2_search_extent_list(el, cpos); assert(index != -1); } rec = &el->l_recs[index]; ocfs2_subtract_from_rec(fs, split, rec, split_rec); ocfs2_rotate_leaf(insert_el, split_rec); } /* * This function only does inserts on an allocation b-tree. For dinode * lists, ocfs2_insert_at_leaf() is called directly. * * right_path is the path we want to do the actual insert * in. left_path should only be passed in if we need to update that * portion of the tree after an edge insert. */ static int ocfs2_insert_path(ocfs2_filesys *fs, struct ocfs2_path *left_path, struct ocfs2_path *right_path, struct ocfs2_extent_rec *insert_rec, struct ocfs2_insert_type *insert) { int ret, subtree_index; if (insert->ins_split != SPLIT_NONE) { /* * We could call ocfs2_insert_at_leaf() for some types * of splits, but it's easier to just let one seperate * function sort it all out. */ ocfs2_split_record(fs, left_path, right_path, insert_rec, insert->ins_split); } else ocfs2_insert_at_leaf(fs, insert_rec, path_leaf_el(right_path), insert); if (left_path) { /* * The rotate code has indicated that we need to fix * up portions of the tree after the insert. */ subtree_index = ocfs2_find_subtree_root(left_path, right_path); ocfs2_complete_edge_insert(fs, left_path, right_path, subtree_index); } else subtree_index = 0; ret = ocfs2_sync_path_to_disk(fs, left_path, right_path, subtree_index); if (ret) goto out; ret = 0; out: return ret; } static int ocfs2_do_insert_extent(struct insert_ctxt* ctxt, struct ocfs2_insert_type *type) { int ret, rotate = 0; uint32_t cpos; struct ocfs2_path *right_path = NULL; struct ocfs2_path *left_path = NULL; struct ocfs2_extent_rec *insert_rec = &ctxt->rec; ocfs2_filesys *fs = ctxt->fs; struct ocfs2_extent_list *el = ctxt->et->et_root_el; if (el->l_tree_depth == 0) { ocfs2_insert_at_leaf(fs, insert_rec, el, type); goto out_update_clusters; } right_path = ocfs2_new_path_from_et(ctxt->et); if (!right_path) { ret = OCFS2_ET_NO_MEMORY; goto out; } /* * Determine the path to start with. Rotations need the * rightmost path, everything else can go directly to the * target leaf. */ cpos = insert_rec->e_cpos; if (type->ins_appending == APPEND_NONE && type->ins_contig == CONTIG_NONE) { rotate = 1; cpos = UINT_MAX; } ret = ocfs2_find_path(fs, right_path, cpos); if (ret) goto out; /* * Rotations and appends need special treatment - they modify * parts of the tree's above them. * * Both might pass back a path immediate to the left of the * one being inserted to. This will be cause * ocfs2_insert_path() to modify the rightmost records of * left_path to account for an edge insert. * * XXX: When modifying this code, keep in mind that an insert * can wind up skipping both of these two special cases... */ if (rotate) { ret = ocfs2_rotate_tree_right(fs, type->ins_split, insert_rec->e_cpos, right_path, &left_path); if (ret) goto out; } else if (type->ins_appending == APPEND_TAIL && type->ins_contig != CONTIG_LEFT) { ret = ocfs2_append_rec_to_path(fs, insert_rec, right_path, &left_path); if (ret) goto out; } ret = ocfs2_insert_path(fs, left_path, right_path, insert_rec, type); if (ret) goto out; out_update_clusters: if (type->ins_split == SPLIT_NONE) ocfs2_et_update_clusters(ctxt->et, insert_rec->e_leaf_clusters); ret = 0; out: ocfs2_free_path(left_path); ocfs2_free_path(right_path); return ret; } struct duplicate_ctxt { struct ocfs2_extent_tree *et; uint64_t next_leaf_blk; }; static errcode_t duplicate_extent_block(ocfs2_filesys *fs, struct ocfs2_extent_list *old_el, struct ocfs2_extent_list *new_el, struct duplicate_ctxt *ctxt) { int i; errcode_t ret; uint64_t blkno, new_blkno; struct ocfs2_extent_rec *rec = NULL; char *eb_buf = NULL, *new_eb_buf = NULL; struct ocfs2_extent_block *eb = NULL; struct ocfs2_extent_list *child_old_el = NULL, *child_new_el = NULL; assert (old_el->l_tree_depth > 0); /* empty the whole extent list at first. */ *new_el = *old_el; new_el->l_next_free_rec = 0; memset(new_el->l_recs, 0, sizeof(struct ocfs2_extent_rec) * new_el->l_count); if (old_el->l_next_free_rec == 0) { /* XXX: * We have a tree depth > 0 and no extent record in it, * should it be a corrupted block? */ ret = OCFS2_ET_CORRUPT_EXTENT_BLOCK; goto bail; } ret = ocfs2_malloc_block(fs->fs_io, &eb_buf); if (ret) goto bail; ret = ocfs2_malloc_block(fs->fs_io, &new_eb_buf); if (ret) goto bail; /* we iterate the extent list from the last one for recording * the next_leaf_blk for the previous leaf. */ for (i = old_el->l_next_free_rec - 1; i >= 0; i--) { rec = &old_el->l_recs[i]; if (!ocfs2_rec_clusters(old_el->l_tree_depth, rec)) continue; blkno = rec->e_blkno; ret = ocfs2_read_extent_block(fs, blkno, eb_buf); if (ret) goto bail; /* First make the new_buf the same as the old buf. */ memcpy(new_eb_buf, eb_buf, fs->fs_blocksize); eb = (struct ocfs2_extent_block *)eb_buf; child_old_el = &eb->h_list; eb = (struct ocfs2_extent_block *)new_eb_buf; child_new_el = &eb->h_list; if (child_old_el->l_tree_depth > 0) { /* the extent record in our list still has child extent * block, so we have to iterate it. */ ret = duplicate_extent_block(fs, child_old_el, child_new_el, ctxt); if (ret) goto bail; } /* now we allocate a new extent block and save it. */ ret = ocfs2_new_extent_block(fs, &new_blkno); if (ret) goto bail; eb = (struct ocfs2_extent_block *)new_eb_buf; eb->h_blkno = new_blkno; if (child_old_el->l_tree_depth == 0) { /* * This is the leaf blkno, we have to set its * h_next_leaf_blk and then record itself for * future use. */ eb->h_next_leaf_blk = ctxt->next_leaf_blk; ctxt->next_leaf_blk = new_blkno; } ret = ocfs2_write_extent_block(fs, new_blkno, new_eb_buf); if (ret) goto bail; memcpy(&new_el->l_recs[i], rec, sizeof(struct ocfs2_extent_rec)); new_el->l_recs[i].e_blkno = new_blkno; eb = (struct ocfs2_extent_block *)new_eb_buf; /* set the new i_last_eb_blk in the new dinode. */ if (ocfs2_et_get_last_eb_blk(ctxt->et) == blkno) ocfs2_et_set_last_eb_blk(ctxt->et, new_blkno); } new_el->l_next_free_rec = old_el->l_next_free_rec; ret = 0; bail: if (eb_buf) ocfs2_free(&eb_buf); if (new_eb_buf) ocfs2_free(&new_eb_buf); /* Free all the extent block we allocate. */ if (ret) { for (i = 0; i < old_el->l_next_free_rec; i++) { rec = &new_el->l_recs[i]; if (rec->e_blkno) ocfs2_delete_extent_block(fs, rec->e_blkno); } } return ret; } static errcode_t duplicate_extent_block_et(ocfs2_filesys *fs, struct ocfs2_extent_tree *et) { errcode_t ret = 0; struct ocfs2_extent_list *old_el = NULL, *new_el = NULL; char *old_buf, *new_buf; struct duplicate_ctxt ctxt; ret = ocfs2_malloc_block(fs->fs_io, &old_buf); if (ret) return ret; memcpy(old_buf, et->et_root_buf, fs->fs_blocksize); new_buf = et->et_root_buf; new_el = et->et_root_el; old_el = (struct ocfs2_extent_list *) (old_buf + ((char *)new_el - new_buf)); assert(old_el->l_tree_depth > 0); /* empty the whole extent list at first. */ *new_el = *old_el; memset(new_el->l_recs, 0, sizeof(struct ocfs2_extent_rec) * new_el->l_count); new_el->l_next_free_rec = 0; memset(&ctxt, 0, sizeof(ctxt)); ctxt.et = et; ctxt.next_leaf_blk = 0; ret = duplicate_extent_block(fs, old_el, new_el, &ctxt); ocfs2_free(&old_buf); return ret; } static void free_duplicated_extent_block(ocfs2_filesys *fs, struct ocfs2_extent_list *el) { int i; errcode_t ret; char *buf = NULL; struct ocfs2_extent_rec *rec; struct ocfs2_extent_list *child_el; struct ocfs2_extent_block *eb; assert(el->l_tree_depth > 0); ret = ocfs2_malloc_block(fs->fs_io, &buf); if (ret) return; for (i = 0; i < el->l_next_free_rec; i ++) { rec = &el->l_recs[i]; if (!ocfs2_rec_clusters(el->l_tree_depth, rec)) continue; ret = ocfs2_read_extent_block(fs, rec->e_blkno, buf); if (ret) continue; eb = (struct ocfs2_extent_block *)buf; child_el = &eb->h_list; if (child_el->l_tree_depth > 0) free_duplicated_extent_block(fs, child_el); ocfs2_delete_extent_block(fs, rec->e_blkno); } if(buf) ocfs2_free(&buf); } /* * Grow a b-tree so that it has more records. * * We might shift the tree depth in which case existing paths should * be considered invalid. * * Tree depth after the grow is returned via *final_depth. * * *last_eb will be updated by ocfs2_add_branch(). */ static int ocfs2_grow_tree(ocfs2_filesys *fs, struct ocfs2_extent_tree *et, int *final_depth, char **last_eb) { errcode_t ret; char *eb_buf = NULL; int shift; struct ocfs2_extent_list *el = et->et_root_el; int depth = el->l_tree_depth; shift = ocfs2_find_branch_target(fs, et, &eb_buf); if (shift < 0) { ret = shift; goto out; } /* We traveled all the way to the bottom of the allocation tree * and didn't find room for any more extents - we need to add * another tree level */ if (shift) { /* shift_tree_depth will return us a buffer with * the new extent block (so we can pass that to * ocfs2_add_branch). */ ret = shift_tree_depth(fs, et, &eb_buf); if (ret) goto out; depth++; if (depth == 1) { /* * Special case: we have room now if we shifted from * tree_depth 0, so no more work needs to be done. * * We won't be calling add_branch, so pass * back *last_eb as the new leaf. */ assert(*last_eb); memcpy(*last_eb, eb_buf, fs->fs_blocksize); goto out; } } /* call ocfs2_add_branch to add the final part of the tree with * the new data. */ ret = ocfs2_add_branch(fs, et, eb_buf, last_eb); out: if (final_depth) *final_depth = depth; return ret; } errcode_t ocfs2_tree_insert_extent(ocfs2_filesys *fs, struct ocfs2_extent_tree *et, uint32_t cpos, uint64_t c_blkno, uint32_t clusters, uint16_t flag) { errcode_t ret; struct insert_ctxt ctxt; struct ocfs2_insert_type insert = {0, }; char *last_eb = NULL; char *backup_buf = NULL; char *root_buf = et->et_root_buf; int free_records = 0; ctxt.fs = fs; ctxt.et = et; /* In order to orderize the written block sequence and avoid * the corruption for the b-tree, we duplicate the extent block * here and do the insertion in the duplicated ones. * * Note: we only do this in case the b-tree has extent blocks. * And if the duplicate process fails, we should go on the normal * insert process. */ if (et->et_root_el->l_tree_depth) { ret = ocfs2_malloc_block(fs->fs_io, &backup_buf); if (ret) goto bail; memcpy(backup_buf, root_buf, fs->fs_blocksize); /* duplicate the extent block. If it succeeds, di_buf * will point to the new allocated extent blocks, and * the following insertion will happens to the new ones. */ ret = duplicate_extent_block_et(fs, et); if (ret) { memcpy(root_buf, backup_buf, fs->fs_blocksize); ocfs2_free(&backup_buf); backup_buf = NULL; } } memset(&ctxt.rec, 0, sizeof(struct ocfs2_extent_rec)); ctxt.rec.e_cpos = cpos; ctxt.rec.e_blkno = c_blkno; ctxt.rec.e_leaf_clusters = clusters; ctxt.rec.e_flags = flag; ret = ocfs2_malloc_block(fs->fs_io, &last_eb); if (ret) return ret; ret = ocfs2_figure_insert_type(&ctxt, &last_eb, &free_records, &insert); if (ret) goto bail; if (insert.ins_contig == CONTIG_NONE && free_records == 0) { ret = ocfs2_grow_tree(fs, ctxt.et, &insert.ins_tree_depth, &last_eb); if (ret) goto bail; } /* Finally, we can add clusters. This might rotate the tree for us. */ ret = ocfs2_do_insert_extent(&ctxt, &insert); if (ret) goto bail; bail: if (backup_buf) { struct ocfs2_extent_list *free_el; int offset = (char *)et->et_root_el - et->et_root_buf; /* we have duplicated the extent block during the insertion. * so if it succeeds, we should free the old ones, and if fails, * the duplicate ones should be freed. */ if (ret) free_el = (struct ocfs2_extent_list *) (et->et_root_buf + offset); else free_el = (struct ocfs2_extent_list *) (backup_buf + offset); free_duplicated_extent_block(fs, free_el); ocfs2_free(&backup_buf); } if (last_eb) ocfs2_free(&last_eb); /* * Write the root buffer here. * If the caller don't initialize the write function, it should * be responsible for write the root buffer. */ if (!ret && et->et_root_write) ret = et->et_root_write(fs, et->et_root_blkno, root_buf); return ret; } static void ocfs2_make_right_split_rec(ocfs2_filesys *fs, struct ocfs2_extent_rec *split_rec, uint32_t cpos, struct ocfs2_extent_rec *rec) { uint32_t rec_cpos = rec->e_cpos; uint32_t rec_range = rec_cpos + rec->e_leaf_clusters; memset(split_rec, 0, sizeof(struct ocfs2_extent_rec)); split_rec->e_cpos = cpos; split_rec->e_leaf_clusters = rec_range - cpos; split_rec->e_blkno = rec->e_blkno; split_rec->e_blkno += ocfs2_clusters_to_blocks(fs, cpos - rec_cpos); split_rec->e_flags = rec->e_flags; } static int ocfs2_split_and_insert(struct insert_ctxt *ctxt, struct ocfs2_path *path, char **last_eb_buf, int split_index, struct ocfs2_extent_rec *orig_split_rec) { int ret = 0, depth; unsigned int insert_range, rec_range, do_leftright = 0; struct ocfs2_extent_rec tmprec; struct ocfs2_extent_list *rightmost_el; struct ocfs2_extent_rec rec; struct ocfs2_insert_type insert; struct ocfs2_extent_block *eb; leftright: /* * Store a copy of the record on the stack - it might move * around as the tree is manipulated below. */ rec = path_leaf_el(path)->l_recs[split_index]; rightmost_el = ctxt->et->et_root_el; depth = rightmost_el->l_tree_depth; if (depth) { assert(*last_eb_buf); eb = (struct ocfs2_extent_block *) (*last_eb_buf); rightmost_el = &eb->h_list; } if (rightmost_el->l_next_free_rec == rightmost_el->l_count) { ret = ocfs2_grow_tree(ctxt->fs, ctxt->et, &depth, last_eb_buf); if (ret) goto out; } memset(&insert, 0, sizeof(struct ocfs2_insert_type)); insert.ins_appending = APPEND_NONE; insert.ins_contig = CONTIG_NONE; insert.ins_tree_depth = depth; insert_range = ctxt->rec.e_cpos + ctxt->rec.e_leaf_clusters; rec_range = rec.e_cpos + rec.e_leaf_clusters; if (ctxt->rec.e_cpos == rec.e_cpos) { insert.ins_split = SPLIT_LEFT; } else if (insert_range == rec_range) { insert.ins_split = SPLIT_RIGHT; } else { /* * Left/right split. We fake this as a right split * first and then make a second pass as a left split. */ insert.ins_split = SPLIT_RIGHT; ocfs2_make_right_split_rec(ctxt->fs, &tmprec, insert_range, &rec); ctxt->rec = tmprec; assert(!do_leftright); do_leftright = 1; } ret = ocfs2_do_insert_extent(ctxt, &insert); if (ret) goto out; if (do_leftright == 1) { uint32_t cpos; struct ocfs2_extent_list *el; do_leftright++; ctxt->rec = *orig_split_rec; ocfs2_reinit_path(path, 1); cpos = ctxt->rec.e_cpos; ret = ocfs2_find_path(ctxt->fs, path, cpos); if (ret) goto out; el = path_leaf_el(path); split_index = ocfs2_search_extent_list(el, cpos); goto leftright; } out: return ret; } /* * Split part or all of the extent record at split_index in the leaf * pointed to by path. Merge with the contiguous extent record if needed. * * Care is taken to handle contiguousness so as to not grow the tree. * * last_eb_buf should be the rightmost leaf block for any inode with a * btree. Since a split may grow the tree or a merge might shrink it, * the caller cannot trust the contents of that buffer after this call. * * This code is optimized for readability - several passes might be * made over certain portions of the tree. */ static int ocfs2_split_extent(struct insert_ctxt *insert_ctxt, struct ocfs2_path *path, int split_index) { int ret = 0; struct ocfs2_extent_rec split_rec = insert_ctxt->rec; struct ocfs2_extent_list *el = path_leaf_el(path); char *last_eb_buf = NULL; struct ocfs2_extent_rec *rec = &el->l_recs[split_index]; struct ocfs2_merge_ctxt merge_ctxt; ocfs2_filesys *fs = insert_ctxt->fs; if (rec->e_cpos > split_rec.e_cpos || ((rec->e_cpos + rec->e_leaf_clusters) < (split_rec.e_cpos + split_rec.e_leaf_clusters))) { ret = OCFS2_ET_INVALID_ARGUMENT; goto out; } merge_ctxt.c_contig_type = ocfs2_figure_merge_contig_type(fs, insert_ctxt->et, el, split_index, &split_rec); /* * We have to allocate the last_eb_buf no matter the current tree * depth is since we may shift the tree depth from 0 to 1 in * ocfs2_split_and_insert and use last_eb_buf to store. */ ret = ocfs2_malloc_block(fs->fs_io, &last_eb_buf); if (ret) goto out; /* * The core merge / split code wants to know how much room is * left in this inodes allocation tree, so we pass the * rightmost extent list. */ if (path->p_tree_depth) { ret = ocfs2_read_extent_block(fs, ocfs2_et_get_last_eb_blk(insert_ctxt->et), last_eb_buf); if (ret) goto out; } if (rec->e_cpos == split_rec.e_cpos && rec->e_leaf_clusters == split_rec.e_leaf_clusters) merge_ctxt.c_split_covers_rec = 1; else merge_ctxt.c_split_covers_rec = 0; merge_ctxt.c_has_empty_extent = ocfs2_is_empty_extent(&el->l_recs[0]); if (merge_ctxt.c_contig_type == CONTIG_NONE) { if (merge_ctxt.c_split_covers_rec) { el->l_recs[split_index] = split_rec; /* * We only write the leaf block, and leave * the write of the root to the caller. */ if (path->p_tree_depth) ret = ocfs2_write_extent_block(fs, path_leaf_blkno(path), path_leaf_buf(path)); } else ret = ocfs2_split_and_insert(insert_ctxt, path, &last_eb_buf, split_index, &split_rec); } else { ret = ocfs2_try_to_merge_extent(fs, insert_ctxt->et, path, split_index, &split_rec, &merge_ctxt); } out: if (last_eb_buf) ocfs2_free(&last_eb_buf); return ret; } /* * Change the flags of the already-existing extent at cpos for len clusters. * * new_flags: the flags we want to set. * clear_flags: the flags we want to clear. * p_blkno: the new physical offset we want this new extent starts from. * * If the existing extent is larger than the request, initiate a * split. An attempt will be made at merging with adjacent extents. */ int ocfs2_change_extent_flag(ocfs2_filesys *fs, struct ocfs2_extent_tree *et, uint32_t cpos, uint32_t len, uint64_t p_blkno, int new_flags, int clear_flags) { int ret, index; struct ocfs2_path *left_path = NULL; struct ocfs2_extent_list *el; struct insert_ctxt ctxt; struct ocfs2_extent_rec *rec; char *backup_buf = NULL; /* In order to orderize the written block sequence and avoid * the corruption for the inode, we duplicate the extent block * here and do the insertion in the duplicated ones. * * Note: we only do this in case the file has extent blocks. * And if the duplicate process fails, we should go on the normal * insert process. */ if (et->et_root_el->l_tree_depth) { ret = ocfs2_malloc_block(fs->fs_io, &backup_buf); if (ret) goto out; memcpy(backup_buf, et->et_root_buf, fs->fs_blocksize); /* duplicate the extent block. If it succeeds, di_buf * will point to the new allocated extent blocks, and * the following insertion will happens to the new ones. */ ret = duplicate_extent_block_et(fs, et); if (ret) { memcpy(et->et_root_buf, backup_buf, fs->fs_blocksize); ocfs2_free(&backup_buf); backup_buf = NULL; } } left_path = ocfs2_new_path_from_et(et); if (!left_path) { ret = OCFS2_ET_NO_MEMORY; goto out; } ret = ocfs2_find_path(fs, left_path, cpos); if (ret) goto out; el = path_leaf_el(left_path); index = ocfs2_search_extent_list(el, cpos); if (index == -1 || index >= el->l_next_free_rec) { ret = OCFS2_ET_CORRUPT_EXTENT_BLOCK; goto out; } ctxt.fs = fs; ctxt.et = et; ret = OCFS2_ET_IO; rec = &el->l_recs[index]; if (new_flags && (rec->e_flags & new_flags)) goto out; if (clear_flags && !(rec->e_flags & clear_flags)) goto out; memset(&ctxt.rec, 0, sizeof(struct ocfs2_extent_rec)); ctxt.rec.e_cpos = cpos; ctxt.rec.e_leaf_clusters = len; ctxt.rec.e_blkno = p_blkno; ctxt.rec.e_flags = rec->e_flags; if (new_flags) ctxt.rec.e_flags |= new_flags; if (clear_flags) ctxt.rec.e_flags &= ~clear_flags; ret = ocfs2_split_extent(&ctxt, left_path, index); if (ret) goto out; /* * Write the root buffer here. * If the caller don't initialize the write function, it should * be responsible for write the root buffer. */ if (!ret && et->et_root_write) ret = et->et_root_write(fs, et->et_root_blkno, et->et_root_buf); out: if (backup_buf) { struct ocfs2_extent_list *free_el; int offset = (char *)et->et_root_el - et->et_root_buf; /* we have duplicated the extent block during the insertion. * so if it succeeds, we should free the old ones, and if fails, * the duplicate ones should be freed. */ if (ret) free_el = (struct ocfs2_extent_list *) (et->et_root_buf + offset); else free_el = (struct ocfs2_extent_list *) (backup_buf + offset); free_duplicated_extent_block(fs, free_el); ocfs2_free(&backup_buf); } ocfs2_free_path(left_path); return ret; } static int ocfs2_split_tree(ocfs2_filesys *fs, struct ocfs2_extent_tree *et, struct ocfs2_path *path, int index, uint32_t new_range) { errcode_t ret; int depth; char *last_eb_buf = NULL; struct ocfs2_extent_block *eb; struct ocfs2_extent_list *rightmost_el, *el; struct ocfs2_extent_rec *rec; struct ocfs2_insert_type insert; struct insert_ctxt ctxt; ctxt.fs = fs; ctxt.et = et; ret = ocfs2_malloc_block(fs->fs_io, &last_eb_buf); if (ret) return ret; /* * Setup the record to split before we grow the tree. */ el = path_leaf_el(path); rec = &el->l_recs[index]; ocfs2_make_right_split_rec(fs, &ctxt.rec, new_range, rec); depth = path->p_tree_depth; if (depth > 0) { ret = ocfs2_read_extent_block(fs, ocfs2_et_get_last_eb_blk(et), last_eb_buf); if (ret) goto out; eb = (struct ocfs2_extent_block *)last_eb_buf; rightmost_el = &eb->h_list; } else rightmost_el = path_leaf_el(path); if (rightmost_el->l_next_free_rec == rightmost_el->l_count) { ret = ocfs2_grow_tree(fs, et, &depth, &last_eb_buf); if (ret) goto out; } memset(&insert, 0, sizeof(struct ocfs2_insert_type)); insert.ins_appending = APPEND_NONE; insert.ins_contig = CONTIG_NONE; insert.ins_split = SPLIT_RIGHT; insert.ins_tree_depth = depth; ret = ocfs2_do_insert_extent(&ctxt, &insert); out: if (last_eb_buf) ocfs2_free(&last_eb_buf); return ret; } static int ocfs2_truncate_rec(ocfs2_filesys *fs, struct ocfs2_extent_tree *et, struct ocfs2_path *path, int index, uint32_t cpos, uint32_t len) { errcode_t ret; uint32_t left_cpos, rec_range, trunc_range; int is_rightmost_tree_rec = 0; struct ocfs2_path *left_path = NULL; struct ocfs2_extent_list *el = path_leaf_el(path); struct ocfs2_extent_rec *rec; struct ocfs2_extent_block *eb; if (ocfs2_is_empty_extent(&el->l_recs[0]) && index > 0) { ret = ocfs2_rotate_tree_left(fs, et, path); if (ret) goto out; index--; } if (index == el->l_next_free_rec - 1 && path->p_tree_depth) { /* * Check whether this is the rightmost tree record. If * we remove all of this record or part of its right * edge then an update of the record lengths above it * will be required. */ eb = (struct ocfs2_extent_block *)path_leaf_buf(path); if (eb->h_next_leaf_blk == 0) is_rightmost_tree_rec = 1; } rec = &el->l_recs[index]; if (index == 0 && path->p_tree_depth && rec->e_cpos == cpos) { /* * Changing the leftmost offset (via partial or whole * record truncate) of an interior (or rightmost) path * means we have to update the subtree that is formed * by this leaf and the one to it's left. * * There are two cases we can skip: * 1) Path is the leftmost one in our btree. * 2) The leaf is rightmost and will be empty after * we remove the extent record - the rotate code * knows how to update the newly formed edge. */ ret = ocfs2_find_cpos_for_left_leaf(path, &left_cpos); if (ret) goto out; if (left_cpos && el->l_next_free_rec > 1) { left_path = ocfs2_new_path_from_path(path); if (!left_path) { ret = OCFS2_ET_NO_MEMORY; goto out; } ret = ocfs2_find_path(fs, left_path, left_cpos); if (ret) goto out; } } rec_range = rec->e_cpos + ocfs2_rec_clusters(el->l_tree_depth, rec); trunc_range = cpos + len; if (rec->e_cpos == cpos && rec_range == trunc_range) { int next_free; memset(rec, 0, sizeof(*rec)); ocfs2_cleanup_merge(el, index); next_free = el->l_next_free_rec; if (is_rightmost_tree_rec && next_free > 1) { /* * We skip the edge update if this path will * be deleted by the rotate code. */ rec = &el->l_recs[next_free - 1]; ocfs2_adjust_rightmost_records(fs, path, rec); } } else if (rec->e_cpos == cpos) { /* Remove leftmost portion of the record. */ rec->e_cpos += len; rec->e_blkno += ocfs2_clusters_to_blocks(fs, len); rec->e_leaf_clusters -= len; } else if (rec_range == trunc_range) { /* Remove rightmost portion of the record */ rec->e_leaf_clusters -= len; if (is_rightmost_tree_rec) ocfs2_adjust_rightmost_records(fs, path, rec); } else { /* Caller should have trapped this. */ assert(0); } if (left_path) { int subtree_index; subtree_index = ocfs2_find_subtree_root(left_path, path); ocfs2_complete_edge_insert(fs, left_path, path, subtree_index); } ret = ocfs2_rotate_tree_left(fs, et, path); out: ocfs2_free_path(left_path); return ret; } int ocfs2_remove_extent(ocfs2_filesys *fs, struct ocfs2_extent_tree *et, uint32_t cpos, uint32_t len) { int ret, index; uint32_t rec_range, trunc_range; struct ocfs2_extent_rec *rec; struct ocfs2_extent_list *el; struct ocfs2_path *path = NULL; path = ocfs2_new_path_from_et(et); if (!path) { ret = OCFS2_ET_NO_MEMORY; goto out; } ret = ocfs2_find_path(fs, path, cpos); if (ret) goto out; el = path_leaf_el(path); index = ocfs2_search_extent_list(el, cpos); if (index == -1 || index >= el->l_next_free_rec) { ret = OCFS2_ET_CORRUPT_EXTENT_BLOCK; goto out; } /* * We have 3 cases of extent removal: * 1) Range covers the entire extent rec * 2) Range begins or ends on one edge of the extent rec * 3) Range is in the middle of the extent rec (no shared edges) * * For case 1 we remove the extent rec and left rotate to * fill the hole. * * For case 2 we just shrink the existing extent rec, with a * tree update if the shrinking edge is also the edge of an * extent block. * * For case 3 we do a right split to turn the extent rec into * something case 2 can handle. */ rec = &el->l_recs[index]; rec_range = rec->e_cpos + ocfs2_rec_clusters(el->l_tree_depth, rec); trunc_range = cpos + len; assert(cpos >= rec->e_cpos && trunc_range <= rec_range); if (rec->e_cpos == cpos || rec_range == trunc_range) { ret = ocfs2_truncate_rec(fs, et, path, index, cpos, len); if (ret) goto out; } else { ret = ocfs2_split_tree(fs, et, path, index, trunc_range); if (ret) goto out; /* * The split could have manipulated the tree enough to * move the record location, so we have to look for it again. */ ocfs2_reinit_path(path, 1); ret = ocfs2_find_path(fs, path, cpos); if (ret) goto out; el = path_leaf_el(path); index = ocfs2_search_extent_list(el, cpos); if (index == -1 || index >= el->l_next_free_rec) { ret = OCFS2_ET_CORRUPT_EXTENT_BLOCK; goto out; } /* * Double check our values here. If anything is fishy, * it's easier to catch it at the top level. */ rec = &el->l_recs[index]; rec_range = rec->e_cpos + ocfs2_rec_clusters(el->l_tree_depth, rec); if (rec_range != trunc_range) { ret = OCFS2_ET_CORRUPT_EXTENT_BLOCK; goto out; } ret = ocfs2_truncate_rec(fs, et, path, index, cpos, len); if (ret) goto out; } out: ocfs2_free_path(path); return ret; } ocfs2-tools-ocfs2-tools-1.8.6/libocfs2/extent_tree.h000066400000000000000000000132701347147137200222660ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * extent_tree.h * * Copyright (C) 2009 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License version 2 as published by the Free Software Foundation. * * 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. */ /* Useful typedef for passing around writing functions for extent tree root. */ typedef errcode_t (*ocfs2_root_write_func)(ocfs2_filesys *fs, uint64_t blkno, char *root_buf); struct ocfs2_extent_tree { struct ocfs2_extent_tree_operations *et_ops; char *et_root_buf; uint64_t et_root_blkno; ocfs2_root_write_func et_root_write; struct ocfs2_extent_list *et_root_el; void *et_object; uint32_t et_max_leaf_clusters; }; enum ocfs2_contig_type { CONTIG_NONE = 0, CONTIG_LEFT, CONTIG_RIGHT, CONTIG_LEFTRIGHT, }; /* * Operations for a specific extent tree type. * * To implement an on-disk btree (extent tree) type in ocfs2, add * an ocfs2_extent_tree_operations structure and the matching * ocfs2_init__extent_tree() function. That's pretty much it * for the allocation portion of the extent tree. */ struct ocfs2_extent_tree_operations { /* * last_eb_blk is the block number of the right most leaf extent * block. Most on-disk structures containing an extent tree store * this value for fast access. The ->eo_set_last_eb_blk() and * ->eo_get_last_eb_blk() operations access this value. They are * both required. */ void (*eo_set_last_eb_blk)(struct ocfs2_extent_tree *et, uint64_t blkno); uint64_t (*eo_get_last_eb_blk)(struct ocfs2_extent_tree *et); /* * The on-disk structure usually keeps track of how many total * clusters are stored in this extent tree. This function updates * that value. new_clusters is the delta, and must be * added to the total. Required. */ void (*eo_update_clusters)(/*struct inode *inode,*/ struct ocfs2_extent_tree *et, uint32_t new_clusters); uint32_t (*eo_get_clusters)(struct ocfs2_extent_tree *et); /* * If ->eo_insert_check() exists, it is called before rec is * inserted into the extent tree. It is optional. */ /* int (*eo_insert_check)(struct inode *inode, struct ocfs2_extent_tree *et, struct ocfs2_extent_rec *rec); int (*eo_sanity_check)(struct inode *inode, struct ocfs2_extent_tree *et); */ int (*eo_sanity_check)(struct ocfs2_extent_tree *et); /* * -------------------------------------------------------------- * The remaining are internal to ocfs2_extent_tree and don't have * accessor functions */ /* * ->eo_fill_root_el() takes et->et_object and sets et->et_root_el. * It is required. */ void (*eo_fill_root_el)(struct ocfs2_extent_tree *et); /* * ->eo_fill_max_leaf_clusters sets et->et_max_leaf_clusters if * it exists. If it does not, et->et_max_leaf_clusters is set * to 0 (unlimited). Optional. */ void (*eo_fill_max_leaf_clusters)(ocfs2_filesys *fs, struct ocfs2_extent_tree *et); /* * ->eo_extent_contig test whether the 2 ocfs2_extent_rec * are contiguous or not. Optional. Don't need to set it if use * ocfs2_extent_rec as the tree leaf. */ enum ocfs2_contig_type (*eo_extent_contig)(ocfs2_filesys *fs, struct ocfs2_extent_tree *et, struct ocfs2_extent_rec *ext, struct ocfs2_extent_rec *insert_rec); }; void ocfs2_init_dinode_extent_tree(struct ocfs2_extent_tree *et, ocfs2_filesys *fs, char *buf, uint64_t blkno); void ocfs2_init_refcount_extent_tree(struct ocfs2_extent_tree *et, ocfs2_filesys *fs, char *buf, uint64_t blkno); void ocfs2_init_xattr_value_extent_tree(struct ocfs2_extent_tree *et, ocfs2_filesys *fs, char *buf, uint64_t blkno, ocfs2_root_write_func write, struct ocfs2_xattr_value_root *xv); void ocfs2_init_dx_root_extent_tree(struct ocfs2_extent_tree *et, ocfs2_filesys *fs, char *buf, uint64_t blkno); errcode_t ocfs2_tree_insert_extent(ocfs2_filesys *fs, struct ocfs2_extent_tree *et, uint32_t cpos, uint64_t c_blkno, uint32_t clusters, uint16_t flag); int ocfs2_change_extent_flag(ocfs2_filesys *fs, struct ocfs2_extent_tree *et, uint32_t cpos, uint32_t len, uint64_t p_blkno, int new_flags, int clear_flags); int ocfs2_remove_extent(ocfs2_filesys *fs, struct ocfs2_extent_tree *et, uint32_t cpos, uint32_t len); /* * Structures which describe a path through a btree, and functions to * manipulate them. * * The idea here is to be as generic as possible with the tree * manipulation code. */ struct ocfs2_path_item { uint64_t blkno; char *buf; struct ocfs2_extent_list *el; }; #define OCFS2_MAX_PATH_DEPTH 5 struct ocfs2_path { int p_tree_depth; struct ocfs2_path_item p_node[OCFS2_MAX_PATH_DEPTH]; }; #define path_root_blkno(_path) ((_path)->p_node[0].blkno) #define path_root_buf(_path) ((_path)->p_node[0].buf) #define path_root_el(_path) ((_path)->p_node[0].el) #define path_leaf_blkno(_path) ((_path)->p_node[(_path)->p_tree_depth].blkno) #define path_leaf_buf(_path) ((_path)->p_node[(_path)->p_tree_depth].buf) #define path_leaf_el(_path) ((_path)->p_node[(_path)->p_tree_depth].el) #define path_num_items(_path) ((_path)->p_tree_depth + 1) struct ocfs2_path *ocfs2_new_path_from_et(struct ocfs2_extent_tree *et); int ocfs2_find_path(ocfs2_filesys *fs, struct ocfs2_path *path, uint32_t cpos); void ocfs2_free_path(struct ocfs2_path *path); ocfs2-tools-ocfs2-tools-1.8.6/libocfs2/extents.c000066400000000000000000000563601347147137200214340ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * extents.c * * Iterate over the extents in an inode. Part of the OCFS2 userspace * library. * * Copyright (C) 2004 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License, version 2, as published by the Free Software Foundation. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * * Ideas taken from e2fsprogs/lib/ext2fs/block.c * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o. */ #define _XOPEN_SOURCE 600 /* Triggers XOPEN2K in features.h */ #define _LARGEFILE64_SOURCE #include #include #include "ocfs2/byteorder.h" #include "ocfs2/ocfs2.h" static void ocfs2_swap_extent_list_primary(struct ocfs2_extent_list *el) { el->l_tree_depth = bswap_16(el->l_tree_depth); el->l_count = bswap_16(el->l_count); el->l_next_free_rec = bswap_16(el->l_next_free_rec); } static void ocfs2_swap_extent_list_secondary(ocfs2_filesys *fs, void *obj, struct ocfs2_extent_list *el) { uint16_t i; for(i = 0; i < el->l_next_free_rec; i++) { struct ocfs2_extent_rec *rec = &el->l_recs[i]; if (ocfs2_swap_barrier(fs, obj, rec, sizeof(struct ocfs2_extent_rec))) break; rec->e_cpos = bswap_32(rec->e_cpos); if (el->l_tree_depth) rec->e_int_clusters = bswap_32(rec->e_int_clusters); else rec->e_leaf_clusters = bswap_16(rec->e_leaf_clusters); rec->e_blkno = bswap_64(rec->e_blkno); } } void ocfs2_swap_extent_list_from_cpu(ocfs2_filesys *fs, void *obj, struct ocfs2_extent_list *el) { if (cpu_is_little_endian) return; ocfs2_swap_extent_list_secondary(fs, obj, el); ocfs2_swap_extent_list_primary(el); } void ocfs2_swap_extent_list_to_cpu(ocfs2_filesys *fs, void *obj, struct ocfs2_extent_list *el) { if (cpu_is_little_endian) return; ocfs2_swap_extent_list_primary(el); ocfs2_swap_extent_list_secondary(fs, obj, el); } static void ocfs2_swap_extent_block_header(struct ocfs2_extent_block *eb) { eb->h_suballoc_slot = bswap_16(eb->h_suballoc_slot); eb->h_suballoc_bit = bswap_16(eb->h_suballoc_bit); eb->h_fs_generation = bswap_32(eb->h_fs_generation); eb->h_blkno = bswap_64(eb->h_blkno); eb->h_next_leaf_blk = bswap_64(eb->h_next_leaf_blk); eb->h_suballoc_loc = bswap_64(eb->h_suballoc_loc); } void ocfs2_swap_extent_block_from_cpu(ocfs2_filesys *fs, struct ocfs2_extent_block *eb) { if (cpu_is_little_endian) return; ocfs2_swap_extent_block_header(eb); ocfs2_swap_extent_list_from_cpu(fs, eb, &eb->h_list); } void ocfs2_swap_extent_block_to_cpu(ocfs2_filesys *fs, struct ocfs2_extent_block *eb) { if (cpu_is_little_endian) return; ocfs2_swap_extent_block_header(eb); ocfs2_swap_extent_list_to_cpu(fs, eb, &eb->h_list); } errcode_t ocfs2_read_extent_block_nocheck(ocfs2_filesys *fs, uint64_t blkno, char *eb_buf) { errcode_t ret; char *blk; struct ocfs2_extent_block *eb; if ((blkno < OCFS2_SUPER_BLOCK_BLKNO) || (blkno > fs->fs_blocks)) return OCFS2_ET_BAD_BLKNO; ret = ocfs2_malloc_block(fs->fs_io, &blk); if (ret) return ret; ret = ocfs2_read_blocks(fs, blkno, 1, blk); if (ret) goto out; eb = (struct ocfs2_extent_block *)blk; ret = ocfs2_validate_meta_ecc(fs, blk, &eb->h_check); if (ret) goto out; if (memcmp(eb->h_signature, OCFS2_EXTENT_BLOCK_SIGNATURE, strlen(OCFS2_EXTENT_BLOCK_SIGNATURE))) { ret = OCFS2_ET_BAD_EXTENT_BLOCK_MAGIC; goto out; } memcpy(eb_buf, blk, fs->fs_blocksize); eb = (struct ocfs2_extent_block *) eb_buf; ocfs2_swap_extent_block_to_cpu(fs, eb); out: ocfs2_free(&blk); return ret; } errcode_t ocfs2_read_extent_block(ocfs2_filesys *fs, uint64_t blkno, char *eb_buf) { errcode_t ret; struct ocfs2_extent_block *eb = (struct ocfs2_extent_block *)eb_buf; ret = ocfs2_read_extent_block_nocheck(fs, blkno, eb_buf); if (ret == 0 && eb->h_list.l_next_free_rec > eb->h_list.l_count) ret = OCFS2_ET_CORRUPT_EXTENT_BLOCK; return ret; } errcode_t ocfs2_write_extent_block(ocfs2_filesys *fs, uint64_t blkno, char *eb_buf) { errcode_t ret; char *blk; struct ocfs2_extent_block *eb; if (!(fs->fs_flags & OCFS2_FLAG_RW)) return OCFS2_ET_RO_FILESYS; if ((blkno < OCFS2_SUPER_BLOCK_BLKNO) || (blkno > fs->fs_blocks)) return OCFS2_ET_BAD_BLKNO; ret = ocfs2_malloc_block(fs->fs_io, &blk); if (ret) return ret; memcpy(blk, eb_buf, fs->fs_blocksize); eb = (struct ocfs2_extent_block *) blk; ocfs2_swap_extent_block_from_cpu(fs, eb); ocfs2_compute_meta_ecc(fs, blk, &eb->h_check); ret = io_write_block(fs->fs_io, blkno, 1, blk); if (ret) goto out; fs->fs_flags |= OCFS2_FLAG_CHANGED; ret = 0; out: ocfs2_free(&blk); return ret; } struct extent_context { ocfs2_filesys *fs; int (*func)(ocfs2_filesys *fs, struct ocfs2_extent_rec *rec, int tree_depth, uint32_t ccount, uint64_t ref_blkno, int ref_recno, void *priv_data); uint32_t ccount; int flags; errcode_t errcode; char **eb_bufs; void *priv_data; uint64_t last_eb_blkno; uint64_t last_eb_cpos; }; static int extent_iterate_eb(struct ocfs2_extent_rec *eb_rec, int tree_depth, uint64_t ref_blkno, int ref_recno, struct extent_context *ctxt); static int update_leaf_rec(struct extent_context *ctxt, struct ocfs2_extent_rec *before, struct ocfs2_extent_rec *current) { return 0; } static int update_eb_rec(struct extent_context *ctxt, struct ocfs2_extent_rec *before, struct ocfs2_extent_rec *current) { return 0; } static int extent_iterate_el(struct ocfs2_extent_list *el, uint64_t ref_blkno, struct extent_context *ctxt) { struct ocfs2_extent_rec before; int iret = 0; int i; for (i = 0; i < el->l_next_free_rec; i++) { /* XXX we could put some constraints on how the rec * is allowed to change.. */ before = el->l_recs[i]; if (el->l_tree_depth) { iret |= extent_iterate_eb(&el->l_recs[i], el->l_tree_depth, ref_blkno, i, ctxt); if (iret & OCFS2_EXTENT_CHANGED) iret |= update_eb_rec(ctxt, &before, &el->l_recs[i]); if (el->l_recs[i].e_int_clusters && (el->l_recs[i].e_cpos >= ctxt->last_eb_cpos)) { /* * Only set last_eb_blkno if current extent * list point to leaf blocks. */ if (el->l_tree_depth == 1) ctxt->last_eb_blkno = el->l_recs[i].e_blkno; ctxt->last_eb_cpos = el->l_recs[i].e_cpos; } } else { /* * For a sparse file, we may find an empty record * in the left most record. Just skip it. */ if (!i && !el->l_recs[i].e_leaf_clusters) continue; iret |= (*ctxt->func)(ctxt->fs, &el->l_recs[i], el->l_tree_depth, ctxt->ccount, ref_blkno, i, ctxt->priv_data); if (iret & OCFS2_EXTENT_CHANGED) iret |= update_leaf_rec(ctxt, &before, &el->l_recs[i]); ctxt->ccount += ocfs2_rec_clusters(el->l_tree_depth, &el->l_recs[i]); } if (iret & (OCFS2_EXTENT_ABORT | OCFS2_EXTENT_ERROR)) break; } if (iret & OCFS2_EXTENT_CHANGED) { for (i = 0; i < el->l_count; i++) { if (ocfs2_rec_clusters(el->l_tree_depth, &el->l_recs[i])) continue; el->l_next_free_rec = i; break; } } return iret; } static int extent_iterate_eb(struct ocfs2_extent_rec *eb_rec, int ref_tree_depth, uint64_t ref_blkno, int ref_recno, struct extent_context *ctxt) { int iret = 0, changed = 0, flags; int tree_depth = ref_tree_depth - 1; struct ocfs2_extent_block *eb; struct ocfs2_extent_list *el; if (!(ctxt->flags & OCFS2_EXTENT_FLAG_DEPTH_TRAVERSE) && !(ctxt->flags & OCFS2_EXTENT_FLAG_DATA_ONLY)) iret = (*ctxt->func)(ctxt->fs, eb_rec, ref_tree_depth, ctxt->ccount, ref_blkno, ref_recno, ctxt->priv_data); if (!eb_rec->e_blkno || (iret & OCFS2_EXTENT_ABORT)) goto out; if ((eb_rec->e_blkno < OCFS2_SUPER_BLOCK_BLKNO) || (eb_rec->e_blkno > ctxt->fs->fs_blocks)) { ctxt->errcode = OCFS2_ET_BAD_BLKNO; iret |= OCFS2_EXTENT_ERROR; goto out; } ctxt->errcode = ocfs2_read_extent_block(ctxt->fs, eb_rec->e_blkno, ctxt->eb_bufs[tree_depth]); if (ctxt->errcode) { iret |= OCFS2_EXTENT_ERROR; goto out; } eb = (struct ocfs2_extent_block *)ctxt->eb_bufs[tree_depth]; el = &eb->h_list; if ((el->l_tree_depth != tree_depth) || (eb->h_blkno != eb_rec->e_blkno)) { ctxt->errcode = OCFS2_ET_CORRUPT_EXTENT_BLOCK; iret |= OCFS2_EXTENT_ERROR; goto out; } flags = extent_iterate_el(el, eb_rec->e_blkno, ctxt); changed |= flags; if (flags & (OCFS2_EXTENT_ABORT | OCFS2_EXTENT_ERROR)) iret |= flags & (OCFS2_EXTENT_ABORT | OCFS2_EXTENT_ERROR); /* * If the list was changed, we should write the changes to disk. * Note: * For a sparse file, we may have an empty extent block. */ if (changed & OCFS2_EXTENT_CHANGED) { ctxt->errcode = ocfs2_write_extent_block(ctxt->fs, eb_rec->e_blkno, ctxt->eb_bufs[tree_depth]); if (ctxt->errcode) { iret |= OCFS2_EXTENT_ERROR; goto out; } } if ((ctxt->flags & OCFS2_EXTENT_FLAG_DEPTH_TRAVERSE) && !(ctxt->flags & OCFS2_EXTENT_FLAG_DATA_ONLY) && !(iret & (OCFS2_EXTENT_ABORT|OCFS2_EXTENT_ERROR))) iret = (*ctxt->func)(ctxt->fs, eb_rec, ref_tree_depth, ctxt->ccount, ref_blkno, ref_recno, ctxt->priv_data); out: return iret; } errcode_t ocfs2_extent_iterate_xattr(ocfs2_filesys *fs, struct ocfs2_extent_list *el, uint64_t last_eb_blk, int flags, int (*func)(ocfs2_filesys *fs, struct ocfs2_extent_rec *rec, int tree_depth, uint32_t ccount, uint64_t ref_blkno, int ref_recno, void *priv_data), void *priv_data, int *changed) { int i; int iret = 0; errcode_t ret; struct extent_context ctxt; if (el->l_tree_depth) { ret = ocfs2_malloc0(sizeof(char *) * el->l_tree_depth, &ctxt.eb_bufs); if (ret) goto out; ret = ocfs2_malloc0(fs->fs_blocksize * el->l_tree_depth, &ctxt.eb_bufs[0]); if (ret) goto out_eb_bufs; for (i = 1; i < el->l_tree_depth; i++) { ctxt.eb_bufs[i] = ctxt.eb_bufs[0] + i * fs->fs_blocksize; } } else ctxt.eb_bufs = NULL; ctxt.fs = fs; ctxt.func = func; ctxt.priv_data = priv_data; ctxt.flags = flags; ctxt.ccount = 0; ctxt.last_eb_blkno = 0; ctxt.last_eb_cpos = 0; ret = 0; iret |= extent_iterate_el(el, 0, &ctxt); if (iret & OCFS2_EXTENT_ERROR) ret = ctxt.errcode; if (iret & OCFS2_EXTENT_ABORT) goto out_abort; if (last_eb_blk != ctxt.last_eb_blkno) { last_eb_blk = ctxt.last_eb_blkno; iret |= OCFS2_EXTENT_CHANGED; } out_abort: if (!ret && (iret & OCFS2_EXTENT_CHANGED)) *changed = 1; out_eb_bufs: if (ctxt.eb_bufs) { if (ctxt.eb_bufs[0]) ocfs2_free(&ctxt.eb_bufs[0]); ocfs2_free(&ctxt.eb_bufs); } out: return ret; } errcode_t ocfs2_extent_iterate_inode(ocfs2_filesys *fs, struct ocfs2_dinode *inode, int flags, char *block_buf, int (*func)(ocfs2_filesys *fs, struct ocfs2_extent_rec *rec, int tree_depth, uint32_t ccount, uint64_t ref_blkno, int ref_recno, void *priv_data), void *priv_data) { int i; int iret = 0; struct ocfs2_extent_list *el; errcode_t ret; struct extent_context ctxt; ret = OCFS2_ET_INODE_NOT_VALID; if (!(inode->i_flags & OCFS2_VALID_FL)) goto out; ret = OCFS2_ET_INODE_CANNOT_BE_ITERATED; if (inode->i_flags & (OCFS2_SUPER_BLOCK_FL | OCFS2_LOCAL_ALLOC_FL | OCFS2_CHAIN_FL)) goto out; if (inode->i_dyn_features & OCFS2_INLINE_DATA_FL) goto out; el = &inode->id2.i_list; if (el->l_tree_depth) { ret = ocfs2_malloc0(sizeof(char *) * el->l_tree_depth, &ctxt.eb_bufs); if (ret) goto out; if (block_buf) { ctxt.eb_bufs[0] = block_buf; } else { ret = ocfs2_malloc0(fs->fs_blocksize * el->l_tree_depth, &ctxt.eb_bufs[0]); if (ret) goto out_eb_bufs; } for (i = 1; i < el->l_tree_depth; i++) { ctxt.eb_bufs[i] = ctxt.eb_bufs[0] + i * fs->fs_blocksize; } } else ctxt.eb_bufs = NULL; ctxt.fs = fs; ctxt.func = func; ctxt.priv_data = priv_data; ctxt.flags = flags; ctxt.ccount = 0; ctxt.last_eb_blkno = 0; ctxt.last_eb_cpos = 0; ret = 0; iret |= extent_iterate_el(el, 0, &ctxt); if (iret & OCFS2_EXTENT_ERROR) ret = ctxt.errcode; if (iret & OCFS2_EXTENT_ABORT) goto out_abort; /* we can only trust ctxt.last_eb_blkno if we walked the whole tree */ if (inode->i_last_eb_blk != ctxt.last_eb_blkno) { inode->i_last_eb_blk = ctxt.last_eb_blkno; iret |= OCFS2_EXTENT_CHANGED; } out_abort: if (!ret && (iret & OCFS2_EXTENT_CHANGED)) ret = ocfs2_write_inode(fs, inode->i_blkno, (char *)inode); out_eb_bufs: if (ctxt.eb_bufs) { if (!block_buf && ctxt.eb_bufs[0]) ocfs2_free(&ctxt.eb_bufs[0]); ocfs2_free(&ctxt.eb_bufs); } out: return ret; } errcode_t ocfs2_extent_iterate_dx_root(ocfs2_filesys *fs, struct ocfs2_dx_root_block *dx_root, int flags, char *block_buf, int (*func)(ocfs2_filesys *fs, struct ocfs2_extent_rec *rec, int tree_depth, uint32_t ccount, uint64_t ref_blkno, int ref_recno, void *priv_data), void *priv_data) { int i; int iret = 0; struct ocfs2_extent_list *el; errcode_t ret; struct extent_context ctxt; if (dx_root->dr_flags & OCFS2_DX_FLAG_INLINE) return OCFS2_ET_INODE_CANNOT_BE_ITERATED; el = &dx_root->dr_list; if (el->l_tree_depth) { ret = ocfs2_malloc0(sizeof(char *) * el->l_tree_depth, &ctxt.eb_bufs); if (ret) goto out; if (block_buf) { ctxt.eb_bufs[0] = block_buf; } else { ret = ocfs2_malloc0(fs->fs_blocksize * el->l_tree_depth, &ctxt.eb_bufs[0]); if (ret) goto out_eb_bufs; } for (i = 1; i < el->l_tree_depth; i++) { ctxt.eb_bufs[i] = ctxt.eb_bufs[0] + i * fs->fs_blocksize; } } else ctxt.eb_bufs = NULL; ctxt.fs = fs; ctxt.func = func; ctxt.priv_data = priv_data; ctxt.flags = flags; ctxt.ccount = 0; ctxt.last_eb_blkno = 0; ctxt.last_eb_cpos = 0; ret = 0; iret |= extent_iterate_el(el, 0, &ctxt); if (iret & OCFS2_EXTENT_ERROR) ret = ctxt.errcode; if (iret & OCFS2_EXTENT_ABORT) goto out_abort; /* we can only trust ctxt.last_eb_blkno if we walked the whole tree */ if (dx_root->dr_last_eb_blk != ctxt.last_eb_blkno) { dx_root->dr_last_eb_blk = ctxt.last_eb_blkno; iret |= OCFS2_EXTENT_CHANGED; } out_abort: #if 0 /* * This block needs to be fixed up for write support. */ if (!ret && (iret & OCFS2_EXTENT_CHANGED)) ret = ocfs2_write_inode(fs, inode->i_blkno, (char *)inode); #endif out_eb_bufs: if (ctxt.eb_bufs) { if (!block_buf && ctxt.eb_bufs[0]) ocfs2_free(&ctxt.eb_bufs[0]); ocfs2_free(&ctxt.eb_bufs); } out: return ret; } errcode_t ocfs2_extent_iterate(ocfs2_filesys *fs, uint64_t blkno, int flags, char *block_buf, int (*func)(ocfs2_filesys *fs, struct ocfs2_extent_rec *rec, int tree_depth, uint32_t ccount, uint64_t ref_blkno, int ref_recno, void *priv_data), void *priv_data) { char *buf = NULL; struct ocfs2_dinode *inode; errcode_t ret; ret = ocfs2_malloc_block(fs->fs_io, &buf); if (ret) return ret; ret = ocfs2_read_inode(fs, blkno, buf); if (ret) goto out_buf; inode = (struct ocfs2_dinode *)buf; ret = ocfs2_extent_iterate_inode(fs, inode, flags, block_buf, func, priv_data); out_buf: if (buf) ocfs2_free(&buf); return ret; } struct block_context { int (*func)(ocfs2_filesys *fs, uint64_t blkno, uint64_t bcount, uint16_t ext_flags, void *priv_data); int flags; struct ocfs2_dinode *inode; errcode_t errcode; void *priv_data; }; static int block_iterate_func(ocfs2_filesys *fs, struct ocfs2_extent_rec *rec, int tree_depth, uint32_t ccount, uint64_t ref_blkno, int ref_recno, void *priv_data) { struct block_context *ctxt = priv_data; uint64_t blkno, bcount, bend; int iret = 0; bcount = ocfs2_clusters_to_blocks(fs, rec->e_cpos); bend = bcount + ocfs2_clusters_to_blocks(fs, ocfs2_rec_clusters(tree_depth, rec)); for (blkno = rec->e_blkno; bcount < bend; blkno++, bcount++) { if (((bcount * fs->fs_blocksize) >= ctxt->inode->i_size) && !(ctxt->flags & OCFS2_BLOCK_FLAG_APPEND)) break; iret = (*ctxt->func)(fs, blkno, bcount, rec->e_flags, ctxt->priv_data); if (iret & OCFS2_BLOCK_ABORT) break; } return iret; } errcode_t ocfs2_block_iterate_inode(ocfs2_filesys *fs, struct ocfs2_dinode *inode, int flags, int (*func)(ocfs2_filesys *fs, uint64_t blkno, uint64_t bcount, uint16_t ext_flags, void *priv_data), void *priv_data) { errcode_t ret; struct block_context ctxt; ctxt.inode = inode; ctxt.flags = flags; ctxt.func = func; ctxt.errcode = 0; ctxt.priv_data = priv_data; ret = ocfs2_extent_iterate_inode(fs, inode, OCFS2_EXTENT_FLAG_DATA_ONLY, NULL, block_iterate_func, &ctxt); return ret; } errcode_t ocfs2_block_iterate(ocfs2_filesys *fs, uint64_t blkno, int flags, int (*func)(ocfs2_filesys *fs, uint64_t blkno, uint64_t bcount, uint16_t ext_flags, void *priv_data), void *priv_data) { struct ocfs2_dinode *inode; errcode_t ret; char *buf; ret = ocfs2_malloc_block(fs->fs_io, &buf); if (ret) return ret; ret = ocfs2_read_inode(fs, blkno, buf); if (ret) goto out_buf; inode = (struct ocfs2_dinode *)buf; ret = ocfs2_block_iterate_inode(fs, inode, flags, func, priv_data); out_buf: ocfs2_free(&buf); return ret; } #ifdef DEBUG_EXE #include #include static uint64_t read_number(const char *num) { uint64_t val; char *ptr; val = strtoull(num, &ptr, 0); if (!ptr || *ptr) return 0; return val; } static void print_usage(void) { fprintf(stderr, "Usage: extents -i [-e] [-b] \n"); } struct walk_it { struct ocfs2_dinode *di; }; static int walk_extents_func(ocfs2_filesys *fs, struct ocfs2_extent_rec *rec, int tree_depth, uint32_t ccount, uint64_t ref_blkno, int ref_recno, void *priv_data) { struct walk_it *wi = priv_data; int pad_amount = wi->di->id2.i_list.l_tree_depth - tree_depth; int i; if (!ccount && !pad_amount) fprintf(stdout, "EXTENTS:\n"); fprintf(stdout, "0x%08"PRIX64":%02u ", ref_blkno, ref_recno); for (i = 0; i < pad_amount; i++) fprintf(stdout, " "); fprintf(stdout, "(%08"PRIu32", %08"PRIu32", %08"PRIu64") |" " + %08"PRIu32" = %08"PRIu32" / %08"PRIu32"\n", rec->e_cpos, ocfs2_rec_clusters(tree_depth, rec), rec->e_blkno, ccount, ccount + ocfs2_rec_clusters(tree_depth, rec), wi->di->i_clusters); if (!tree_depth && ((ccount + ocfs2_rec_clusters(tree_depth, rec)) == wi->di->i_clusters)) fprintf(stdout, "TOTAL: %u\n", wi->di->i_clusters); return 0; } struct walk_block { struct ocfs2_dinode *di; uint64_t last_block; uint64_t run_first_blkno; uint64_t run_first_bcount; uint64_t run_prev_blkno; }; static int walk_blocks_func(ocfs2_filesys *fs, uint64_t blkno, uint64_t bcount, uint16_t ext_flags, void *priv_data) { struct walk_block *wb = priv_data; /* Very first block */ if (!wb->run_prev_blkno) { wb->run_prev_blkno = blkno; wb->run_first_blkno = blkno; fprintf(stdout, "BLOCKS:\n"); } else if ((wb->run_prev_blkno + 1) != blkno) { if (wb->run_first_bcount) fprintf(stdout, ", "); if ((wb->run_first_bcount + 1) == bcount) { fprintf(stdout, "(%"PRIu64"):%"PRIu64"", wb->run_first_bcount, wb->run_first_blkno); } else { fprintf(stdout, "(%"PRIu64"-%"PRIu64"):%"PRIu64"-%"PRIu64"", wb->run_first_bcount, bcount - 1, wb->run_first_blkno, wb->run_prev_blkno); } wb->run_first_bcount = bcount; wb->run_first_blkno = blkno; } if ((bcount + 1) == wb->last_block) { if (wb->run_first_bcount) fprintf(stdout, ", "); if ((wb->run_prev_blkno + 1) != blkno) { fprintf(stdout, "(%"PRIu64"):%"PRIu64"\n", bcount, blkno); } else { fprintf(stdout, "(%"PRIu64"-%"PRIu64"):%"PRIu64"-%"PRIu64"\n", wb->run_first_bcount, bcount, wb->run_first_blkno, blkno); } fprintf(stdout, "TOTAL: %"PRIu64"\n", bcount + 1); } wb->run_prev_blkno = blkno; return 0; } extern int opterr, optind; extern char *optarg; int main(int argc, char *argv[]) { errcode_t ret; uint64_t blkno; int c; int walk_blocks = 0, walk_extents = 0; char *filename, *buf, *eb_buf = NULL; ocfs2_filesys *fs; struct ocfs2_dinode *di; struct walk_it wi; struct walk_block wb; blkno = OCFS2_SUPER_BLOCK_BLKNO; initialize_ocfs_error_table(); while ((c = getopt(argc, argv, "bei:")) != EOF) { switch (c) { case 'b': walk_blocks = 1; break; case 'e': walk_extents = 1; break; case 'i': blkno = read_number(optarg); if (blkno <= OCFS2_SUPER_BLOCK_BLKNO) { fprintf(stderr, "Invalid inode block: %s\n", optarg); print_usage(); return 1; } break; default: print_usage(); return 1; break; } } if (optind >= argc) { fprintf(stderr, "Missing filename\n"); print_usage(); return 1; } filename = argv[optind]; if (!(walk_blocks + walk_extents)) { fprintf(stderr, "No operation specified\n"); print_usage(); return 1; } ret = ocfs2_open(filename, OCFS2_FLAG_RO, 0, 0, &fs); if (ret) { com_err(argv[0], ret, "while opening file \"%s\"", filename); goto out; } ret = ocfs2_malloc_block(fs->fs_io, &buf); if (ret) { com_err(argv[0], ret, "while allocating inode buffer"); goto out_close; } ret = ocfs2_read_inode(fs, blkno, buf); if (ret) { com_err(argv[0], ret, "while reading inode %"PRIu64, blkno); goto out_free; } di = (struct ocfs2_dinode *)buf; fprintf(stdout, "OCFS2 inode %"PRIu64" on \"%s\" has depth %"PRId16"\n", blkno, filename, di->id2.i_list.l_tree_depth); if (walk_extents) { if (di->id2.i_list.l_tree_depth) { ret = ocfs2_malloc_blocks(fs->fs_io, di->id2.i_list.l_tree_depth, &eb_buf); if (ret) { com_err(argv[0], ret, "while allocating eb buffer"); goto out_free; } } wi.di = di; ret = ocfs2_extent_iterate(fs, blkno, 0, eb_buf, walk_extents_func, &wi); if (ret) { com_err(argv[0], ret, "while walking extents"); goto out_free; } } if (walk_blocks) { wb.di = di; wb.run_first_blkno = wb.run_first_bcount = wb.run_prev_blkno = 0; wb.last_block = (wb.di->i_size + (fs->fs_blocksize - 1)) / fs->fs_blocksize; ret = ocfs2_block_iterate(fs, blkno, 0, walk_blocks_func, &wb); if (ret) { com_err(argv[0], ret, "while walking blocks"); goto out_free; } } out_free: if (eb_buf) ocfs2_free(&eb_buf); ocfs2_free(&buf); out_close: ret = ocfs2_close(fs); if (ret) { com_err(argv[0], ret, "while closing file \"%s\"", filename); } out: return 0; } #endif /* DEBUG_EXE */ ocfs2-tools-ocfs2-tools-1.8.6/libocfs2/feature_string.c000066400000000000000000000620201347147137200227510ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * feature_strings.c * * Routines for analyzing a feature string. * * Copyright (C) 2007 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License, version 2, as published by the Free Software Foundation. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * */ #include "ocfs2/ocfs2.h" struct fs_feature_flags { const char *ff_str; /* this flag is the feature's own flag. */ ocfs2_fs_options ff_own_flags; /* * this flag includes the feature's own flag and * all the other features' flag it depends on. */ ocfs2_fs_options ff_flags; }; /* Printable names for feature flags */ struct feature_name { const char *fn_name; ocfs2_fs_options fn_flag; /* Only the bit for this feature */ }; struct flag_name { const char *fl_name; uint32_t fl_flag; }; struct feature_level_translation { const char *fl_str; enum ocfs2_feature_levels fl_type; }; static struct feature_level_translation ocfs2_feature_levels_table[] = { {"default", OCFS2_FEATURE_LEVEL_DEFAULT}, {"max-compat", OCFS2_FEATURE_LEVEL_MAX_COMPAT}, {"max-features", OCFS2_FEATURE_LEVEL_MAX_FEATURES}, {NULL, OCFS2_FEATURE_LEVEL_DEFAULT}, }; static ocfs2_fs_options feature_level_defaults[] = { {OCFS2_FEATURE_COMPAT_BACKUP_SB | OCFS2_FEATURE_COMPAT_JBD2_SB, OCFS2_FEATURE_INCOMPAT_SPARSE_ALLOC | OCFS2_FEATURE_INCOMPAT_EXTENDED_SLOT_MAP | OCFS2_FEATURE_INCOMPAT_INLINE_DATA | OCFS2_FEATURE_INCOMPAT_XATTR | OCFS2_FEATURE_INCOMPAT_REFCOUNT_TREE | OCFS2_FEATURE_INCOMPAT_INDEXED_DIRS | OCFS2_FEATURE_INCOMPAT_DISCONTIG_BG | OCFS2_FEATURE_INCOMPAT_APPEND_DIO, OCFS2_FEATURE_RO_COMPAT_UNWRITTEN}, /* OCFS2_FEATURE_LEVEL_DEFAULT */ {OCFS2_FEATURE_COMPAT_BACKUP_SB | OCFS2_FEATURE_COMPAT_JBD2_SB, 0, 0}, /* OCFS2_FEATURE_LEVEL_MAX_COMPAT */ {OCFS2_FEATURE_COMPAT_BACKUP_SB | OCFS2_FEATURE_COMPAT_JBD2_SB, OCFS2_FEATURE_INCOMPAT_SPARSE_ALLOC | OCFS2_FEATURE_INCOMPAT_EXTENDED_SLOT_MAP | OCFS2_FEATURE_INCOMPAT_INLINE_DATA | OCFS2_FEATURE_INCOMPAT_META_ECC | OCFS2_FEATURE_INCOMPAT_XATTR | OCFS2_FEATURE_INCOMPAT_REFCOUNT_TREE | OCFS2_FEATURE_INCOMPAT_INDEXED_DIRS | OCFS2_FEATURE_INCOMPAT_DISCONTIG_BG | OCFS2_FEATURE_INCOMPAT_APPEND_DIO, OCFS2_FEATURE_RO_COMPAT_UNWRITTEN | OCFS2_FEATURE_RO_COMPAT_USRQUOTA | OCFS2_FEATURE_RO_COMPAT_GRPQUOTA }, /* OCFS2_FEATURE_LEVEL_MAX_FEATURES */ }; static ocfs2_fs_options mkfstypes_features_defaults[] = { {OCFS2_FEATURE_COMPAT_BACKUP_SB | OCFS2_FEATURE_COMPAT_JBD2_SB, OCFS2_FEATURE_INCOMPAT_SPARSE_ALLOC | OCFS2_FEATURE_INCOMPAT_EXTENDED_SLOT_MAP | OCFS2_FEATURE_INCOMPAT_INLINE_DATA | OCFS2_FEATURE_INCOMPAT_XATTR | OCFS2_FEATURE_INCOMPAT_REFCOUNT_TREE | OCFS2_FEATURE_INCOMPAT_INDEXED_DIRS | OCFS2_FEATURE_INCOMPAT_DISCONTIG_BG | OCFS2_FEATURE_INCOMPAT_APPEND_DIO, OCFS2_FEATURE_RO_COMPAT_UNWRITTEN}, /* OCFS2_MKFSTYPE_DEFAULT */ {OCFS2_FEATURE_COMPAT_BACKUP_SB | OCFS2_FEATURE_COMPAT_JBD2_SB, OCFS2_FEATURE_INCOMPAT_SPARSE_ALLOC | OCFS2_FEATURE_INCOMPAT_EXTENDED_SLOT_MAP | OCFS2_FEATURE_INCOMPAT_INLINE_DATA | OCFS2_FEATURE_INCOMPAT_XATTR | OCFS2_FEATURE_INCOMPAT_REFCOUNT_TREE | OCFS2_FEATURE_INCOMPAT_INDEXED_DIRS | OCFS2_FEATURE_INCOMPAT_DISCONTIG_BG | OCFS2_FEATURE_INCOMPAT_APPEND_DIO, OCFS2_FEATURE_RO_COMPAT_UNWRITTEN}, /* OCFS2_MKFSTYPE_DATAFILES */ {OCFS2_FEATURE_COMPAT_BACKUP_SB | OCFS2_FEATURE_COMPAT_JBD2_SB, OCFS2_FEATURE_INCOMPAT_SPARSE_ALLOC | OCFS2_FEATURE_INCOMPAT_EXTENDED_SLOT_MAP | OCFS2_FEATURE_INCOMPAT_INLINE_DATA | OCFS2_FEATURE_INCOMPAT_XATTR | OCFS2_FEATURE_INCOMPAT_REFCOUNT_TREE | OCFS2_FEATURE_INCOMPAT_INDEXED_DIRS | OCFS2_FEATURE_INCOMPAT_DISCONTIG_BG | OCFS2_FEATURE_INCOMPAT_APPEND_DIO, OCFS2_FEATURE_RO_COMPAT_UNWRITTEN}, /* OCFS2_MKFSTYPE_MAIL */ {OCFS2_FEATURE_COMPAT_BACKUP_SB | OCFS2_FEATURE_COMPAT_JBD2_SB, OCFS2_FEATURE_INCOMPAT_SPARSE_ALLOC | OCFS2_FEATURE_INCOMPAT_EXTENDED_SLOT_MAP | OCFS2_FEATURE_INCOMPAT_INLINE_DATA | OCFS2_FEATURE_INCOMPAT_XATTR | OCFS2_FEATURE_INCOMPAT_REFCOUNT_TREE | OCFS2_FEATURE_INCOMPAT_INDEXED_DIRS | OCFS2_FEATURE_INCOMPAT_DISCONTIG_BG | OCFS2_FEATURE_INCOMPAT_APPEND_DIO, OCFS2_FEATURE_RO_COMPAT_UNWRITTEN}, /* OCFS2_MKFSTYPE_VMSTORE */ }; /* These are the features we support in mkfs/tunefs via --fs-features */ static struct fs_feature_flags ocfs2_supported_features[] = { { "local", {0, OCFS2_FEATURE_INCOMPAT_LOCAL_MOUNT, 0}, {0, OCFS2_FEATURE_INCOMPAT_LOCAL_MOUNT, 0}, }, { "sparse", {0, OCFS2_FEATURE_INCOMPAT_SPARSE_ALLOC, 0}, {0, OCFS2_FEATURE_INCOMPAT_SPARSE_ALLOC, 0}, }, { "backup-super", {OCFS2_FEATURE_COMPAT_BACKUP_SB, 0, 0}, {OCFS2_FEATURE_COMPAT_BACKUP_SB, 0, 0}, }, { "unwritten", {0, 0, OCFS2_FEATURE_RO_COMPAT_UNWRITTEN}, {0, OCFS2_FEATURE_INCOMPAT_SPARSE_ALLOC, OCFS2_FEATURE_RO_COMPAT_UNWRITTEN}, }, { "extended-slotmap", {0, OCFS2_FEATURE_INCOMPAT_EXTENDED_SLOT_MAP, 0}, {0, OCFS2_FEATURE_INCOMPAT_EXTENDED_SLOT_MAP, 0}, }, { "inline-data", {0, OCFS2_FEATURE_INCOMPAT_INLINE_DATA, 0}, {0, OCFS2_FEATURE_INCOMPAT_INLINE_DATA, 0}, }, { "metaecc", {0, OCFS2_FEATURE_INCOMPAT_META_ECC, 0}, {0, OCFS2_FEATURE_INCOMPAT_META_ECC, 0}, }, { "xattr", {0, OCFS2_FEATURE_INCOMPAT_XATTR, 0}, {0, OCFS2_FEATURE_INCOMPAT_XATTR, 0}, }, { "indexed-dirs", {0, OCFS2_FEATURE_INCOMPAT_INDEXED_DIRS, 0}, {0, OCFS2_FEATURE_INCOMPAT_INDEXED_DIRS, 0}, }, { "usrquota", {0, 0, OCFS2_FEATURE_RO_COMPAT_USRQUOTA}, {0, 0, OCFS2_FEATURE_RO_COMPAT_USRQUOTA}, }, { "grpquota", {0, 0, OCFS2_FEATURE_RO_COMPAT_GRPQUOTA}, {0, 0, OCFS2_FEATURE_RO_COMPAT_GRPQUOTA}, }, { "refcount", {0, OCFS2_FEATURE_INCOMPAT_REFCOUNT_TREE, 0}, {0, OCFS2_FEATURE_INCOMPAT_REFCOUNT_TREE, 0}, }, { "discontig-bg", {0, OCFS2_FEATURE_INCOMPAT_DISCONTIG_BG, 0}, {0, OCFS2_FEATURE_INCOMPAT_DISCONTIG_BG, 0}, }, { "clusterinfo", {0, OCFS2_FEATURE_INCOMPAT_CLUSTERINFO, 0}, {0, OCFS2_FEATURE_INCOMPAT_CLUSTERINFO, 0}, }, { "append-dio", {0, OCFS2_FEATURE_INCOMPAT_APPEND_DIO, 0}, {0, OCFS2_FEATURE_INCOMPAT_APPEND_DIO, 0}, }, { NULL, {0, 0, 0}, {0, 0, 0} }, }; /* * These are the printable names of all flags in s_feature_compat, * s_feature_ro_compat, and s_feature_incompat. If libocfs2 supports this * feature, its printable name must be here. * * These MUST be kept in sync with the flags in ocfs2_fs.h. */ static struct feature_name ocfs2_feature_names[] = { { .fn_name = "heartbeat-device", .fn_flag = {0, OCFS2_FEATURE_INCOMPAT_HEARTBEAT_DEV, 0}, }, { .fn_name = "aborted-resize", .fn_flag = {0, OCFS2_FEATURE_INCOMPAT_RESIZE_INPROG, 0}, }, { .fn_name = "local", .fn_flag = {0, OCFS2_FEATURE_INCOMPAT_LOCAL_MOUNT, 0}, }, { .fn_name = "sparse", .fn_flag = {0, OCFS2_FEATURE_INCOMPAT_SPARSE_ALLOC, 0}, }, { .fn_name = "extended-slotmap", .fn_flag = {0, OCFS2_FEATURE_INCOMPAT_EXTENDED_SLOT_MAP, 0}, }, { .fn_name = "aborted-tunefs", .fn_flag = {0, OCFS2_FEATURE_INCOMPAT_TUNEFS_INPROG, 0}, }, { .fn_name = "userspace-stack", .fn_flag = {0, OCFS2_FEATURE_INCOMPAT_USERSPACE_STACK, 0}, }, { .fn_name = "backup-super", .fn_flag = {OCFS2_FEATURE_COMPAT_BACKUP_SB, 0, 0}, }, { .fn_name = "unwritten", .fn_flag = {0, 0, OCFS2_FEATURE_RO_COMPAT_UNWRITTEN}, }, { .fn_name = "inline-data", .fn_flag = {0, OCFS2_FEATURE_INCOMPAT_INLINE_DATA, 0}, }, { .fn_name = "strict-journal-super", .fn_flag = {OCFS2_FEATURE_COMPAT_JBD2_SB, 0, 0}, }, { .fn_name = "metaecc", .fn_flag = {0, OCFS2_FEATURE_INCOMPAT_META_ECC, 0}, }, { .fn_name = "xattr", .fn_flag = {0, OCFS2_FEATURE_INCOMPAT_XATTR, 0}, }, { .fn_name = "indexed-dirs", .fn_flag = {0, OCFS2_FEATURE_INCOMPAT_INDEXED_DIRS, 0}, }, { .fn_name = "usrquota", .fn_flag = {0, 0, OCFS2_FEATURE_RO_COMPAT_USRQUOTA}, }, { .fn_name = "grpquota", .fn_flag = {0, 0, OCFS2_FEATURE_RO_COMPAT_GRPQUOTA}, }, { .fn_name = "refcount", .fn_flag = {0, OCFS2_FEATURE_INCOMPAT_REFCOUNT_TREE, 0}, }, { .fn_name = "discontig-bg", .fn_flag = {0, OCFS2_FEATURE_INCOMPAT_DISCONTIG_BG, 0}, }, { .fn_name = "clusterinfo", .fn_flag = {0, OCFS2_FEATURE_INCOMPAT_CLUSTERINFO, 0}, }, { .fn_name = "append-dio", .fn_flag = {0, OCFS2_FEATURE_INCOMPAT_APPEND_DIO, 0}, }, { .fn_name = NULL, }, }; /* * The printable names of every flag in s_tunefs_flag. If libocfs2 supports * the flag, its name must be here. * * These MUST be kept in sync with the flags in ocfs2_fs.h. */ static struct flag_name ocfs2_tunefs_flag_names[] = { { .fl_name = "remove-slot", .fl_flag = OCFS2_TUNEFS_INPROG_REMOVE_SLOT, }, { .fl_name = "dir-trailer", .fl_flag = OCFS2_TUNEFS_INPROG_DIR_TRAILER, }, { .fl_name = NULL, }, }; /* * The printable names of every flag in e_flags. If libocfs2 supports the * flag, its name must be here. * * These MUST be kept in sync with the flags in ocfs2_fs.h. */ static struct flag_name ocfs2_extent_flag_names[] = { { .fl_name = "Unwritten", .fl_flag = OCFS2_EXT_UNWRITTEN, }, { .fl_name = "Refcounted", .fl_flag = OCFS2_EXT_REFCOUNTED, }, { .fl_name = NULL, }, }; /* * The printable names of every flag in rf_flags. If libocfs2 supports the * flag, its name must be here. * * These MUST be kept in sync with the flags in ocfs2_fs.h. */ static struct flag_name ocfs2_refcount_flag_names[] = { { .fl_name = "Leaf", .fl_flag = OCFS2_REFCOUNT_LEAF_FL, }, { .fl_name = "Tree", .fl_flag = OCFS2_REFCOUNT_TREE_FL, }, { .fl_name = NULL, }, }; static struct flag_name ocfs2_cluster_o2cb_flag_names[] = { { .fl_name = "Globalheartbeat", .fl_flag = OCFS2_CLUSTER_O2CB_GLOBAL_HEARTBEAT, }, { .fl_name = NULL, }, }; static inline void merge_features(ocfs2_fs_options *features, ocfs2_fs_options new_features) { features->opt_compat |= new_features.opt_compat; features->opt_incompat |= new_features.opt_incompat; features->opt_ro_compat |= new_features.opt_ro_compat; } /* Get the feature level according to the value set by "--fs-feature-level". */ errcode_t ocfs2_parse_feature_level(const char *typestr, enum ocfs2_feature_levels *level) { int i; for(i = 0; ocfs2_feature_levels_table[i].fl_str; i++) { if (strcmp(typestr, ocfs2_feature_levels_table[i].fl_str) == 0) { *level = ocfs2_feature_levels_table[i].fl_type; break; } } if (!ocfs2_feature_levels_table[i].fl_str) { return OCFS2_ET_UNSUPP_FEATURE; } return 0; } static int check_feature_flags(ocfs2_fs_options *fs_flags, ocfs2_fs_options *fs_r_flags) { int ret = 1; if (fs_r_flags->opt_compat && fs_flags->opt_compat & fs_r_flags->opt_compat) ret = 0; else if (fs_r_flags->opt_incompat && fs_flags->opt_incompat & fs_r_flags->opt_incompat) ret = 0; else if (fs_r_flags->opt_ro_compat && fs_flags->opt_ro_compat & fs_r_flags->opt_ro_compat) ret = 0; return ret; } static int feature_match(ocfs2_fs_options *a, ocfs2_fs_options *b) { if ((a->opt_compat & b->opt_compat) || (a->opt_incompat & b->opt_incompat) || (a->opt_ro_compat & b->opt_ro_compat)) return 1; return 0; } errcode_t ocfs2_snprint_feature_flags(char *str, size_t size, ocfs2_fs_options *flags) { int i, printed; char *ptr = str; size_t remain = size; errcode_t err = 0; char *sep = " "; ocfs2_fs_options found = {0, 0, 0}; for (i = 0; ocfs2_feature_names[i].fn_name; i++) { if (!feature_match(flags, &ocfs2_feature_names[i].fn_flag)) continue; merge_features(&found, ocfs2_feature_names[i].fn_flag); printed = snprintf(ptr, remain, "%s%s", ptr == str ? "" : sep, ocfs2_feature_names[i].fn_name); if (printed < 0) err = OCFS2_ET_INTERNAL_FAILURE; else if (printed >= remain) err = OCFS2_ET_NO_SPACE; if (err) break; remain -= printed; ptr += printed; } if (!err) { if ((found.opt_compat != flags->opt_compat) || (found.opt_ro_compat != flags->opt_ro_compat) || (found.opt_incompat != flags->opt_incompat)) { printed = snprintf(ptr, remain, "%sunknown", ptr == str ? "" : sep); if (printed < 0) err = OCFS2_ET_INTERNAL_FAILURE; else if (printed >= remain) err = OCFS2_ET_NO_SPACE; } } return err; } static errcode_t ocfs2_snprint_flag_names(struct flag_name *flag_names, char *str, size_t size, uint32_t flags) { int i, printed; char *ptr = str; size_t remain = size; errcode_t err = 0; char *sep = " "; uint16_t found = 0; for (i = 0; flag_names[i].fl_name; i++) { if (!(flags & flag_names[i].fl_flag)) continue; found |= flag_names[i].fl_flag; printed = snprintf(ptr, remain, "%s%s", ptr == str ? "" : sep, flag_names[i].fl_name); if (printed < 0) err = OCFS2_ET_INTERNAL_FAILURE; else if (printed >= remain) err = OCFS2_ET_NO_SPACE; if (err) break; remain -= printed; ptr += printed; } if (!err) { if (found != flags) { printed = snprintf(ptr, remain, "%sunknown", ptr == str ? "" : sep); if (printed < 0) err = OCFS2_ET_INTERNAL_FAILURE; else if (printed >= remain) err = OCFS2_ET_NO_SPACE; } } return err; } errcode_t ocfs2_snprint_tunefs_flags(char *str, size_t size, uint16_t flags) { return ocfs2_snprint_flag_names(ocfs2_tunefs_flag_names, str, size, (uint32_t)flags); } errcode_t ocfs2_snprint_extent_flags(char *str, size_t size, uint8_t flags) { return ocfs2_snprint_flag_names(ocfs2_extent_flag_names, str, size, (uint32_t)flags); } errcode_t ocfs2_snprint_refcount_flags(char *str, size_t size, uint8_t flags) { return ocfs2_snprint_flag_names(ocfs2_refcount_flag_names, str, size, (uint32_t)flags); } errcode_t ocfs2_snprint_cluster_o2cb_flags(char *str, size_t size, uint8_t flags) { return ocfs2_snprint_flag_names(ocfs2_cluster_o2cb_flag_names, str, size, (uint32_t)flags); } /* * If we are asked to clear a feature, we also need to clear any other * features that depend on it. */ static void ocfs2_feature_clear_deps(ocfs2_fs_options *reverse_set) { int i; for(i = 0; ocfs2_supported_features[i].ff_str; i++) { if (feature_match(reverse_set, &ocfs2_supported_features[i].ff_flags)) { merge_features(reverse_set, ocfs2_supported_features[i].ff_own_flags); } } } /* * Check and Merge all the diffent features set by the user. * * index: the feature level. * feature_set: all the features a user set by "--fs-features". * reverse_set: all the features a user want to clear by "--fs-features". */ errcode_t ocfs2_merge_feature_flags_with_level(ocfs2_fs_options *dest, enum ocfs2_mkfs_types fstype, int level, ocfs2_fs_options *feature_set, ocfs2_fs_options *reverse_set) { ocfs2_fs_options level_set; if (level == OCFS2_FEATURE_LEVEL_DEFAULT) level_set = mkfstypes_features_defaults[fstype]; else level_set = feature_level_defaults[level]; /* * Ensure that all dependancies are correct in the reverse set. * A reverse set from ocfs2_parse_feature() will be correct, but * a hand-built one might not be. */ ocfs2_feature_clear_deps(reverse_set); /* * Check whether the user asked for a flag to be set and cleared, * which is illegal. The feature_set and reverse_set are both set * by "--fs-features", so they shouldn't collide with each other, * but a hand-built one might have problems. */ if (!check_feature_flags(feature_set, reverse_set)) return OCFS2_ET_CONFLICTING_FEATURES; /* Now combine all the features the user has set. */ *dest = level_set; merge_features(dest, *feature_set); /* Now clear the reverse set from our destination */ dest->opt_compat &= ~reverse_set->opt_compat; dest->opt_ro_compat &= ~reverse_set->opt_ro_compat; dest->opt_incompat &= ~reverse_set->opt_incompat; return 0; } /* * Parse the feature string. * * For those the user want to clear(with "no" in the beginning), * they are stored in "reverse_flags". * * For those the user want to set, they are stored in "feature_flags". */ errcode_t ocfs2_parse_feature(const char *opts, ocfs2_fs_options *feature_flags, ocfs2_fs_options *reverse_flags) { char *options, *token, *next, *p, *arg; int i, reverse = 0; memset(feature_flags, 0, sizeof(ocfs2_fs_options)); memset(reverse_flags, 0, sizeof(ocfs2_fs_options)); options = strdup(opts); for (token = options; token && *token; token = next) { reverse = 0; p = strchr(token, ','); next = NULL; if (p) { *p = '\0'; next = p + 1; } arg = strstr(token, "no"); if (arg && arg == token) { reverse = 1; token += 2; } for(i = 0; ocfs2_supported_features[i].ff_str; i++) { if (strcmp(token, ocfs2_supported_features[i].ff_str) == 0) { if (!reverse) merge_features(feature_flags, ocfs2_supported_features[i].ff_flags); else merge_features(reverse_flags, ocfs2_supported_features[i].ff_own_flags); break; } } if (!ocfs2_supported_features[i].ff_str) { free(options); return OCFS2_ET_UNSUPP_FEATURE; } } free(options); ocfs2_feature_clear_deps(reverse_flags); /* * Check whether the user asked for a flag to be set and cleared, * which is illegal. The feature_set and reverse_set are both set * by "--fs-features", so they shouldn't collide with each other. */ if (!check_feature_flags(feature_flags, reverse_flags)) return OCFS2_ET_CONFLICTING_FEATURES; return 0; } static int compare_feature_forward(const void *pa, const void *pb) { int ia = *(int *)pa; int ib = *(int *)pb; struct fs_feature_flags *fa = &ocfs2_supported_features[ia]; struct fs_feature_flags *fb = &ocfs2_supported_features[ib]; if (feature_match(&fb->ff_flags, &fa->ff_own_flags)) return -1; if (feature_match(&fa->ff_flags, &fb->ff_own_flags)) return 1; return 0; } static int compare_feature_backward(const void *pa, const void *pb) { return compare_feature_forward(pb, pa); } static void __feature_foreach(int reverse, ocfs2_fs_options *feature_set, int (*func)(ocfs2_fs_options *feature, void *user_data), void *user_data) { int i, index; int num_features = sizeof(ocfs2_supported_features) / sizeof(ocfs2_supported_features[0]); int indices[num_features]; index = 0; for (i = 0; ocfs2_supported_features[i].ff_str; i++) { if (feature_match(feature_set, &ocfs2_supported_features[i].ff_own_flags)) { indices[index] = i; index++; } } qsort(indices, index, sizeof(indices[0]), reverse ? compare_feature_backward : compare_feature_forward); for (i = 0; i < index; i++) { if (func(&ocfs2_supported_features[indices[i]].ff_own_flags, user_data)) break; } } void ocfs2_feature_foreach(ocfs2_fs_options *feature_set, int (*func)(ocfs2_fs_options *feature, void *user_data), void *user_data) { __feature_foreach(0, feature_set, func, user_data); } void ocfs2_feature_reverse_foreach(ocfs2_fs_options *reverse_set, int (*func)(ocfs2_fs_options *feature, void *user_data), void *user_data) { __feature_foreach(1, reverse_set, func, user_data); } #ifdef DEBUG_EXE #include #include #include "ocfs2/ocfs2.h" static void print_features(char *desc, ocfs2_fs_options *feature_set) { int i; fprintf(stdout, "%s:\n", desc); fprintf(stdout, "COMPAT:\t\t"); for(i = 0; ocfs2_supported_features[i].ff_str; i++) if (feature_set->opt_compat & ocfs2_supported_features[i].ff_own_flags.opt_compat) fprintf(stdout, " %s", ocfs2_supported_features[i].ff_str); fprintf(stdout, "\n"); fprintf(stdout, "RO_COMPAT:\t"); for(i = 0; ocfs2_supported_features[i].ff_str; i++) if (feature_set->opt_ro_compat & ocfs2_supported_features[i].ff_own_flags.opt_ro_compat) fprintf(stdout, " %s", ocfs2_supported_features[i].ff_str); fprintf(stdout, "\n"); fprintf(stdout, "INCOMPAT:\t"); for(i = 0; ocfs2_supported_features[i].ff_str; i++) if (feature_set->opt_incompat & ocfs2_supported_features[i].ff_own_flags.opt_incompat) fprintf(stdout, " %s", ocfs2_supported_features[i].ff_str); fprintf(stdout, "\n"); } static void printable_mkfs(ocfs2_fs_options *feature_set) { errcode_t err; char buf[PATH_MAX]; ocfs2_fs_options flags; fprintf(stdout, "Printable version of mkfs features:\n"); memset(&flags, 0, sizeof(flags)); flags.opt_compat = feature_set->opt_compat; err = ocfs2_snprint_feature_flags(buf, PATH_MAX, &flags); if (err) snprintf(buf, PATH_MAX, "An error occurred: %s", error_message(err)); fprintf(stdout, "COMPAT:\t\t%s\n", buf); memset(&flags, 0, sizeof(flags)); flags.opt_ro_compat = feature_set->opt_ro_compat; err = ocfs2_snprint_feature_flags(buf, PATH_MAX, &flags); if (err) snprintf(buf, PATH_MAX, "An error occurred: %s", error_message(err)); fprintf(stdout, "RO_COMPAT:\t%s\n", buf); memset(&flags, 0, sizeof(flags)); flags.opt_incompat = feature_set->opt_incompat; err = ocfs2_snprint_feature_flags(buf, PATH_MAX, &flags); if (err) snprintf(buf, PATH_MAX, "An error occurred: %s", error_message(err)); fprintf(stdout, "INCOMPAT:\t%s\n", buf); fprintf(stdout, "\n"); } static void print_tunefs_flags(void) { errcode_t err; char buf[PATH_MAX]; fprintf(stdout, "Printable s_tunefs_flag:\n"); err = ocfs2_snprint_tunefs_flags(buf, PATH_MAX, OCFS2_TUNEFS_INPROG_REMOVE_SLOT | OCFS2_TUNEFS_INPROG_DIR_TRAILER); if (err) snprintf(buf, PATH_MAX, "An error occurred: %s", error_message(err)); fprintf(stdout, "FLAGS:\t\t%s\n", buf); fprintf(stdout, "\n"); } static void print_extent_flags(void) { errcode_t err; char buf[PATH_MAX]; fprintf(stdout, "Printable e_flags:\n"); err = ocfs2_snprint_extent_flags(buf, PATH_MAX, OCFS2_EXT_UNWRITTEN | OCFS2_EXT_REFCOUNTED); if (err) snprintf(buf, PATH_MAX, "An error occurred: %s", error_message(err)); fprintf(stdout, "FLAGS:\t\t%s\n", buf); fprintf(stdout, "\n"); } static void print_refcount_flags(void) { errcode_t err; char buf[PATH_MAX]; fprintf(stdout, "Printable rf_flags:\n"); err = ocfs2_snprint_refcount_flags(buf, PATH_MAX, OCFS2_REFCOUNT_TREE_FL | OCFS2_REFCOUNT_LEAF_FL); if (err) snprintf(buf, PATH_MAX, "An error occurred: %s", error_message(err)); fprintf(stdout, "FLAGS:\t\t%s\n", buf); fprintf(stdout, "\n"); } static int p_feature(ocfs2_fs_options *feature_set, void *user_data) { int i; for (i = 0; ocfs2_supported_features[i].ff_str; i++) { if (feature_match(feature_set, &ocfs2_supported_features[i].ff_own_flags)) fprintf(stdout, " %s", ocfs2_supported_features[i].ff_str); } return 0; } static void print_order(int reverse, ocfs2_fs_options *feature_set) { fprintf(stdout, "In this order:"); if (reverse) ocfs2_feature_reverse_foreach(feature_set, p_feature, NULL); else ocfs2_feature_foreach(feature_set, p_feature, NULL); fprintf(stdout, "\n\n"); } extern int optind, optopt, opterr; extern char *optarg; int main(int argc, char *argv[]) { int c; enum ocfs2_feature_levels level = OCFS2_FEATURE_LEVEL_DEFAULT; errcode_t err; char *feature_string = NULL; char *level_string = NULL; ocfs2_fs_options set_features, clear_features, mkfs_features; initialize_ocfs_error_table(); opterr = 0; memset(&set_features, 0, sizeof(ocfs2_fs_options)); memset(&clear_features, 0, sizeof(ocfs2_fs_options)); while ((c = getopt(argc, argv, ":l:s:")) != EOF) { switch (c) { case 'l': if (level_string) free(level_string); level_string = strdup(optarg); err = ocfs2_parse_feature_level(optarg, &level); if (err) { com_err(argv[0], err, "while parsing the feature level string"); exit(1); } break; case 's': if (feature_string) free(feature_string); feature_string = strdup(optarg); memset(&set_features, 0, sizeof(ocfs2_fs_options)); memset(&clear_features, 0, sizeof(ocfs2_fs_options)); err = ocfs2_parse_feature(optarg, &set_features, &clear_features); if (err) { com_err(argv[0], err, "while parsing the feature string"); exit(1); } break; default: fprintf(stderr, "%s: Invalid argument: '-%c'\n", argv[0], optopt); exit(1); break; } } memset(&mkfs_features, 0, sizeof(ocfs2_fs_options)); err = ocfs2_merge_feature_flags_with_level(&mkfs_features, OCFS2_MKFSTYPE_DEFAULT, level, &set_features, &clear_features); if (err) { com_err(argv[0], err, "while trying to reconcile default and specified features"); exit(1); } print_features("\nmkfs.ocfs2 would set these features", &mkfs_features); print_order(0, &mkfs_features); printable_mkfs(&mkfs_features); print_features("tunefs.ocfs2 would set these features", &set_features); print_order(0, &set_features); print_features("tunefs.ocfs2 would clear these features", &clear_features); print_order(1, &clear_features); print_tunefs_flags(); print_extent_flags(); print_refcount_flags(); return 0; } #endif /* DEBUG_EXE */ ocfs2-tools-ocfs2-tools-1.8.6/libocfs2/fileio.c000066400000000000000000000442241347147137200212050ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * fileio.c * * I/O to files. Part of the OCFS2 userspace library. * * Copyright (C) 2004 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License, version 2, as published by the Free Software Foundation. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * * Ideas taken from e2fsprogs/lib/ext2fs/fileio.c * Copyright (C) 1997 Theodore Ts'o. */ #define _XOPEN_SOURCE 600 /* Triggers XOPEN2K in features.h */ #define _LARGEFILE64_SOURCE #include #include #include #include "ocfs2/ocfs2.h" #include "refcount.h" struct read_whole_context { char *buf; char *ptr; int size; int offset; errcode_t errcode; }; static int read_whole_func(ocfs2_filesys *fs, uint64_t blkno, uint64_t bcount, uint16_t ext_flags, void *priv_data) { struct read_whole_context *ctx = priv_data; if (ext_flags & OCFS2_EXT_UNWRITTEN) { memset(ctx->ptr, 0, fs->fs_blocksize); ctx->errcode = 0; } else ctx->errcode = ocfs2_read_blocks(fs, blkno, 1, ctx->ptr); if (ctx->errcode) return OCFS2_BLOCK_ABORT; ctx->ptr += fs->fs_blocksize; ctx->offset += fs->fs_blocksize; return 0; } static errcode_t ocfs2_inline_data_read(struct ocfs2_dinode *di, void *buf, uint32_t count, uint64_t offset, uint32_t *got) { struct ocfs2_inline_data *id; uint8_t *p; if (!(di->i_dyn_features & OCFS2_INLINE_DATA_FL)) return OCFS2_ET_INVALID_ARGUMENT; id = &(di->id2.i_data); *got = 0; if (offset > id->id_count) return 0; p = (__u8 *) &(id->id_data); p += offset; *got = ocfs2_min((uint64_t)(di->i_size - offset), (uint64_t)count); memcpy(buf, p, *got); return 0; } errcode_t ocfs2_read_whole_file(ocfs2_filesys *fs, uint64_t blkno, char **buf, int *len) { struct read_whole_context ctx; errcode_t retval; char *inode_buf; struct ocfs2_dinode *di; /* So the caller can see nothing was read */ *len = 0; *buf = NULL; retval = ocfs2_malloc_block(fs->fs_io, &inode_buf); if (retval) return retval; retval = ocfs2_read_inode(fs, blkno, inode_buf); if (retval) goto out_free; di = (struct ocfs2_dinode *)inode_buf; /* Arbitrary limit for our malloc */ retval = OCFS2_ET_INVALID_ARGUMENT; if (di->i_size > INT_MAX) goto out_free; retval = ocfs2_malloc_blocks(fs->fs_io, ocfs2_blocks_in_bytes(fs, di->i_size), buf); if (retval) goto out_free; if (di->i_dyn_features & OCFS2_INLINE_DATA_FL) return ocfs2_inline_data_read(di, *buf, di->i_size, 0, (uint32_t *)len); ctx.buf = *buf; ctx.ptr = *buf; ctx.size = di->i_size; ctx.offset = 0; ctx.errcode = 0; retval = ocfs2_block_iterate(fs, blkno, 0, read_whole_func, &ctx); *len = ctx.size; if (ctx.offset < ctx.size) *len = ctx.offset; out_free: ocfs2_free(&inode_buf); if (!(*len)) { ocfs2_free(buf); *buf = NULL; } if (retval) return retval; return ctx.errcode; } errcode_t ocfs2_file_read(ocfs2_cached_inode *ci, void *buf, uint32_t count, uint64_t offset, uint32_t *got) { ocfs2_filesys *fs = ci->ci_fs; errcode_t ret = 0; char *ptr = (char *) buf; uint32_t wanted_blocks; uint64_t contig_blocks; uint64_t v_blkno; uint64_t p_blkno; uint32_t tmp; uint64_t num_blocks; uint16_t extent_flags; if (ci->ci_inode->i_dyn_features & OCFS2_INLINE_DATA_FL) return ocfs2_inline_data_read(ci->ci_inode, buf, count, offset, got); /* o_direct requires aligned io */ tmp = fs->fs_blocksize - 1; if ((count & tmp) || (offset & (uint64_t)tmp) || ((unsigned long)ptr & tmp)) return OCFS2_ET_INVALID_ARGUMENT; wanted_blocks = count >> OCFS2_RAW_SB(fs->fs_super)->s_blocksize_bits; v_blkno = offset >> OCFS2_RAW_SB(fs->fs_super)->s_blocksize_bits; *got = 0; num_blocks = (ci->ci_inode->i_size + fs->fs_blocksize - 1) >> OCFS2_RAW_SB(fs->fs_super)->s_blocksize_bits; if (v_blkno >= num_blocks) return 0; if (v_blkno + wanted_blocks > num_blocks) wanted_blocks = (uint32_t) (num_blocks - v_blkno); while(wanted_blocks) { ret = ocfs2_extent_map_get_blocks(ci, v_blkno, 1, &p_blkno, &contig_blocks, &extent_flags); if (ret) return ret; if (contig_blocks > wanted_blocks) contig_blocks = wanted_blocks; if (!p_blkno || extent_flags & OCFS2_EXT_UNWRITTEN) { /* * we meet with a hole or an unwritten extent, * so just empty the content. */ memset(ptr, 0, contig_blocks * fs->fs_blocksize); } else { ret = ocfs2_read_blocks(fs, p_blkno, contig_blocks, ptr); if (ret) return ret; } *got += (contig_blocks << OCFS2_RAW_SB(fs->fs_super)->s_blocksize_bits); wanted_blocks -= contig_blocks; if (wanted_blocks) { ptr += (contig_blocks << OCFS2_RAW_SB(fs->fs_super)->s_blocksize_bits); v_blkno += (uint64_t)contig_blocks; } else { if (*got + offset > ci->ci_inode->i_size) *got = (uint32_t) (ci->ci_inode->i_size - offset); /* break */ } } return ret; } /* * Emtpy the blocks on the disk. */ static errcode_t empty_blocks(ocfs2_filesys *fs, uint64_t start_blk, uint64_t num_blocks) { errcode_t ret; char *buf = NULL; ret = ocfs2_malloc_block(fs->fs_io, &buf); if (ret) goto bail; memset(buf, 0, fs->fs_blocksize); while (num_blocks) { ret = io_write_block(fs->fs_io, start_blk, 1, buf); if (ret) goto bail; num_blocks--; start_blk++; } bail: if (buf) ocfs2_free(&buf); return ret; } static errcode_t ocfs2_inline_data_write(struct ocfs2_dinode *di, void *buf, uint32_t count, uint64_t offset) { struct ocfs2_inline_data *id; uint8_t *p; if (!(di->i_dyn_features & OCFS2_INLINE_DATA_FL)) return OCFS2_ET_INVALID_ARGUMENT; id = &(di->id2.i_data); if (offset + count > id->id_count) return OCFS2_ET_NO_SPACE; p = (__u8 *) &(id->id_data); p += offset; memcpy(p, buf, count); return 0; } static errcode_t ocfs2_file_block_write(ocfs2_cached_inode *ci, void *buf, uint32_t count, uint64_t offset, uint32_t *wrote) { ocfs2_filesys *fs = ci->ci_fs; errcode_t ret = 0; char *ptr = (char *) buf; uint32_t wanted_blocks; uint64_t contig_blocks; uint64_t v_blkno; uint64_t p_blkno, p_start, p_end; uint64_t begin_blocks = 0, end_blocks = 0; uint32_t tmp; uint64_t num_blocks; int bs_bits = OCFS2_RAW_SB(fs->fs_super)->s_blocksize_bits; uint32_t n_clusters, cluster_begin, cluster_end; uint64_t bpc = fs->fs_clustersize/fs->fs_blocksize; int insert = 0; uint16_t extent_flags = 0; /* o_direct requires aligned io */ tmp = fs->fs_blocksize - 1; if ((count & tmp) || (offset & (uint64_t)tmp) || ((unsigned long)ptr & tmp)) return OCFS2_ET_INVALID_ARGUMENT; wanted_blocks = count >> bs_bits; v_blkno = offset >> bs_bits; *wrote = 0; num_blocks = (ci->ci_inode->i_size + fs->fs_blocksize - 1) >> bs_bits; if (v_blkno >= num_blocks) return 0; if (v_blkno + wanted_blocks > num_blocks) wanted_blocks = (uint32_t) (num_blocks - v_blkno); if (ocfs2_refcount_tree(OCFS2_RAW_SB(fs->fs_super)) && (ci->ci_inode->i_dyn_features & OCFS2_HAS_REFCOUNT_FL)) { cluster_begin = ocfs2_blocks_to_clusters(fs, v_blkno); cluster_end = ocfs2_blocks_to_clusters(fs, v_blkno + wanted_blocks - 1); n_clusters = cluster_end - cluster_begin + 1; ret = ocfs2_refcount_cow(ci, cluster_begin, n_clusters, UINT_MAX); if (ret) return ret; } while(wanted_blocks) { ret = ocfs2_extent_map_get_blocks(ci, v_blkno, 1, &p_blkno, &contig_blocks, &extent_flags); if (ret) return ret; if (contig_blocks > wanted_blocks) contig_blocks = wanted_blocks; begin_blocks = 0; end_blocks = 0; p_end = 0; if (!p_blkno) { /* * We meet with a hole here, so we allocate clusters * and empty the both ends in case. * * We will postpone the extent insertion after we * successfully write the extent block, so that and * problems happens in block writing would not affect * the file. */ cluster_begin = ocfs2_blocks_to_clusters(fs, v_blkno); cluster_end = ocfs2_blocks_to_clusters(fs, v_blkno + contig_blocks -1); n_clusters = cluster_end - cluster_begin + 1; ret = ocfs2_new_clusters(fs, 1, n_clusters, &p_start, &n_clusters); if (ret || n_clusters == 0) return ret; begin_blocks = v_blkno & (bpc - 1); p_blkno = p_start + begin_blocks; contig_blocks = n_clusters * bpc - begin_blocks; if (contig_blocks > wanted_blocks) { end_blocks = contig_blocks - wanted_blocks; contig_blocks = wanted_blocks; p_end = p_blkno + wanted_blocks; } insert = 1; } else if (extent_flags & OCFS2_EXT_UNWRITTEN) { begin_blocks = v_blkno & (bpc - 1); p_start = p_blkno - begin_blocks; p_end = p_blkno + wanted_blocks; end_blocks = (p_end & (bpc - 1)) ? bpc - (p_end & (bpc - 1 )) : 0; } if (begin_blocks) { /* * The user don't write the first blocks, * so we have to empty them. */ ret = empty_blocks(fs, p_start, begin_blocks); if (ret) return ret; } if (end_blocks) { /* * we don't need to write that many blocks, * so empty the blocks at the bottom. */ ret = empty_blocks(fs, p_end, end_blocks); if (ret) return ret; } ret = io_write_block(fs->fs_io, p_blkno, contig_blocks, ptr); if (ret) return ret; if (insert) { ret = ocfs2_cached_inode_insert_extent(ci, ocfs2_blocks_to_clusters(fs,v_blkno), p_start, n_clusters, 0); if (ret) { /* * XXX: We don't wan't to overwrite the error * from insert_extent(). But we probably need * to BE LOUDLY UPSET. */ ocfs2_free_clusters(fs, n_clusters, p_start); return ret; } /* save up what we have done. */ ret = ocfs2_write_cached_inode(fs, ci); if (ret) return ret; ret = ocfs2_extent_map_get_blocks(ci, v_blkno, 1, &p_blkno, NULL, NULL); /* now we shouldn't find a hole. */ if (!p_blkno || p_blkno != p_start + begin_blocks) ret = OCFS2_ET_INTERNAL_FAILURE; if (ret) return ret; insert = 0; } else if (extent_flags & OCFS2_EXT_UNWRITTEN) { cluster_begin = ocfs2_blocks_to_clusters(fs, v_blkno); cluster_end = ocfs2_blocks_to_clusters(fs, v_blkno + contig_blocks -1); n_clusters = cluster_end - cluster_begin + 1; ret = ocfs2_mark_extent_written(fs, ci->ci_inode, cluster_begin, n_clusters, p_blkno & ~(bpc - 1)); if (ret) return ret; /* * We don't cache in the library right now, so any * work done in mark_extent_written won't be reflected * in our now stale copy. So refresh it. */ ret = ocfs2_refresh_cached_inode(fs, ci); if (ret) return ret; } *wrote += (contig_blocks << bs_bits); wanted_blocks -= contig_blocks; if (wanted_blocks) { ptr += (contig_blocks << bs_bits); v_blkno += (uint64_t)contig_blocks; } else { if (*wrote + offset > ci->ci_inode->i_size) *wrote = (uint32_t) (ci->ci_inode->i_size - offset); /* break */ } } return ret; } static inline int ocfs2_size_fits_inline_data(struct ocfs2_dinode *di, uint64_t new_size) { if (new_size <= di->id2.i_data.id_count) return 1; return 0; } static void ocfs2_expand_last_dirent(char *start, uint16_t old_size, uint16_t new_size) { struct ocfs2_dir_entry *de; struct ocfs2_dir_entry *prev_de; char *de_buf, *limit; uint16_t bytes = new_size - old_size; limit = start + old_size; de_buf = start; de = (struct ocfs2_dir_entry *)de_buf; do { prev_de = de; de_buf += de->rec_len; de = (struct ocfs2_dir_entry *)de_buf; } while (de_buf < limit); prev_de->rec_len += bytes; } errcode_t ocfs2_convert_inline_data_to_extents(ocfs2_cached_inode *ci) { errcode_t ret; uint32_t bytes, n_clusters; uint64_t p_start; char *inline_data = NULL; struct ocfs2_dinode *di = ci->ci_inode; ocfs2_filesys *fs = ci->ci_fs; uint64_t bpc = fs->fs_clustersize/fs->fs_blocksize; unsigned int new_size; if (di->i_size) { ret = ocfs2_malloc_block(fs->fs_io, &inline_data); if (ret) goto out; ret = ocfs2_inline_data_read(di, inline_data, fs->fs_blocksize, 0, &bytes); if (ret) goto out; } ocfs2_dinode_new_extent_list(fs, di); di->i_dyn_features &= ~OCFS2_INLINE_DATA_FL; ret = ocfs2_new_clusters(fs, 1, 1, &p_start, &n_clusters); if (ret || n_clusters == 0) goto out; ret = empty_blocks(fs, p_start, bpc); if (ret) goto out; if (di->i_size) { if (S_ISDIR(di->i_mode)) { if (ocfs2_supports_dir_trailer(fs)) new_size = ocfs2_dir_trailer_blk_off(fs); else new_size = fs->fs_blocksize; ocfs2_expand_last_dirent(inline_data, di->i_size, new_size); if (ocfs2_supports_dir_trailer(fs)) ocfs2_init_dir_trailer(fs, di, p_start, inline_data); di->i_size = fs->fs_blocksize; ret = ocfs2_write_dir_block(fs, di, p_start, inline_data); } else ret = io_write_block(fs->fs_io, p_start, 1, inline_data); if (ret) goto out; } ret = ocfs2_cached_inode_insert_extent(ci, 0, p_start, n_clusters, 0); if (ret) goto out; ret = ocfs2_write_cached_inode(fs, ci); out: if (inline_data) ocfs2_free(&inline_data); return ret; } static errcode_t ocfs2_try_to_write_inline_data(ocfs2_cached_inode *ci, void *buf, uint32_t count, uint64_t offset) { int ret; uint64_t end = offset + count; ocfs2_filesys *fs = ci->ci_fs; struct ocfs2_dinode *di = ci->ci_inode; /* Handle inodes which already have inline data 1st. */ if (di->i_dyn_features & OCFS2_INLINE_DATA_FL) { if (ocfs2_size_fits_inline_data(ci->ci_inode, end)) goto do_inline_write; /* * The write won't fit - we have to give this inode an * inline extent list now. */ ret = ocfs2_convert_inline_data_to_extents(ci); if (!ret) ret = OCFS2_ET_CANNOT_INLINE_DATA; goto out; } if (di->i_clusters > 0 || end > ocfs2_max_inline_data_with_xattr(fs->fs_blocksize, di)) return OCFS2_ET_CANNOT_INLINE_DATA; ocfs2_set_inode_data_inline(fs, ci->ci_inode); ci->ci_inode->i_dyn_features |= OCFS2_INLINE_DATA_FL; do_inline_write: ret = ocfs2_inline_data_write(di, buf, count, offset); if (ret) goto out; ret = ocfs2_write_cached_inode(fs, ci); out: return ret; } errcode_t ocfs2_file_write(ocfs2_cached_inode *ci, void *buf, uint32_t count, uint64_t offset, uint32_t *wrote) { errcode_t ret; ocfs2_filesys *fs = ci->ci_fs; if (ocfs2_support_inline_data(OCFS2_RAW_SB(fs->fs_super))) { ret = ocfs2_try_to_write_inline_data(ci, buf, count, offset); if (!ret || ret != OCFS2_ET_CANNOT_INLINE_DATA) goto out; } ret = ocfs2_file_block_write(ci, buf, count, offset, wrote); out: return ret; } /* * FIXME: port the reset of e2fsprogs/lib/ext2fs/fileio.c */ #ifdef DEBUG_EXE #include #include #include #include static uint64_t read_number(const char *num) { uint64_t val; char *ptr; val = strtoull(num, &ptr, 0); if (!ptr || *ptr) return 0; return val; } static void print_usage(void) { fprintf(stderr, "Usage: debug_fileio [-i ] \n"); } static void dump_filebuf(const char *buf, int len) { int rc, offset; offset = 0; while (offset < len) { rc = write(STDOUT_FILENO, buf + offset, len - offset); if (rc < 0) { fprintf(stderr, "Write error: %s\n", strerror(errno)); return; } else if (rc) { offset += rc; } else { fprintf(stderr, "Wha? Unexpected EOF\n"); return; } } return; } extern int opterr, optind; extern char *optarg; int main(int argc, char *argv[]) { errcode_t ret; uint64_t blkno, result_blkno; int c, len; char *filename, *lookup_path, *buf; char *filebuf; char *p; char lookup_name[256]; ocfs2_filesys *fs; blkno = 0; initialize_ocfs_error_table(); while ((c = getopt(argc, argv, "i:")) != EOF) { switch (c) { case 'i': blkno = read_number(optarg); if (blkno <= OCFS2_SUPER_BLOCK_BLKNO) { fprintf(stderr, "Invalid inode block: %s\n", optarg); print_usage(); return 1; } break; default: print_usage(); return 1; break; } } if (optind >= argc) { fprintf(stderr, "Missing filename\n"); print_usage(); return 1; } filename = argv[optind]; optind++; if (optind >= argc) { fprintf(stdout, "Missing path to lookup\n"); print_usage(); return 1; } lookup_path = argv[optind]; ret = ocfs2_open(filename, OCFS2_FLAG_RO, 0, 0, &fs); if (ret) { com_err(argv[0], ret, "while opening file \"%s\"", filename); goto out; } ret = ocfs2_malloc_block(fs->fs_io, &buf); if (ret) { com_err(argv[0], ret, "while allocating inode buffer"); goto out_close; } if (!blkno) blkno = OCFS2_RAW_SB(fs->fs_super)->s_root_blkno; for (p = lookup_path; *p == '/'; p++); lookup_path = p; for (p = lookup_path; ; p++) { if (*p && *p != '/') continue; memcpy(lookup_name, lookup_path, p - lookup_path); lookup_name[p - lookup_path] = '\0'; ret = ocfs2_lookup(fs, blkno, lookup_name, strlen(lookup_name), NULL, &result_blkno); if (ret) { com_err(argv[0], ret, "while looking up \"%s\" in inode %"PRIu64 " on \"%s\"\n", lookup_name, blkno, filename); goto out_free; } blkno = result_blkno; for (; *p == '/'; p++); lookup_path = p; if (!*p) break; } if (ocfs2_check_directory(fs, blkno) != OCFS2_ET_NO_DIRECTORY) { com_err(argv[0], ret, "\"%s\" is not a file", filename); goto out_free; } ret = ocfs2_read_whole_file(fs, blkno, &filebuf, &len); if (ret) { com_err(argv[0], ret, "while reading file \"%s\" -- read %d bytes", filename, len); goto out_free_filebuf; } if (!len) fprintf(stderr, "boo!\n"); dump_filebuf(filebuf, len); out_free_filebuf: if (len) ocfs2_free(&filebuf); out_free: ocfs2_free(&buf); out_close: ret = ocfs2_close(fs); if (ret) { com_err(argv[0], ret, "while closing file \"%s\"", filename); } out: return 0; } #endif /* DEBUG_EXE */ ocfs2-tools-ocfs2-tools-1.8.6/libocfs2/freefs.c000066400000000000000000000026031347147137200212030ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * freefs.c * * Free an OCFS2 filesystem. Part of the OCFS2 userspace library. * * Copyright (C) 2004 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License, version 2, as published by the Free Software Foundation. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * * Ideas taken from e2fsprogs/lib/ext2fs/freefs.c * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o. */ #define _XOPEN_SOURCE 600 /* Triggers XOPEN2K in features.h */ #define _LARGEFILE64_SOURCE #include #include "ocfs2/ocfs2.h" void ocfs2_freefs(ocfs2_filesys *fs) { if (!fs) abort(); if (fs->fs_orig_super) ocfs2_free(&fs->fs_orig_super); if (fs->fs_super) ocfs2_free(&fs->fs_super); if (fs->fs_devname) ocfs2_free(&fs->fs_devname); if (fs->fs_io) io_close(fs->fs_io); ocfs2_free(&fs); } ocfs2-tools-ocfs2-tools-1.8.6/libocfs2/getsectsize.c000066400000000000000000000033731347147137200222670ustar00rootroot00000000000000/* * getsectsize.c --- get the sector size of a device. * * Copyright (C) 1995, 1995 Theodore Ts'o. * Copyright (C) 2003 VMware, Inc. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Public * License. * %End-Header% */ /* Modified for OCFS2 by Manish Singh */ #define HAVE_UNISTD_H 1 #define HAVE_ERRNO_H 1 #define HAVE_LINUX_FD_H 1 #define HAVE_OPEN64 1 #define _LARGEFILE_SOURCE #define _LARGEFILE64_SOURCE #include #if HAVE_UNISTD_H #include #endif #if HAVE_ERRNO_H #include #endif #include #ifdef HAVE_LINUX_FD_H #include #include #endif #if defined(__linux__) && defined(_IO) && !defined(BLKSSZGET) #define BLKSSZGET _IO(0x12,104)/* get block device sector size */ #endif #include "ocfs2/ocfs2.h" /* * Returns the number of blocks in a partition */ errcode_t ocfs2_get_device_sectsize(const char *file, int *sectsize) { int fd; int ret; #ifdef HAVE_OPEN64 fd = open64(file, O_RDONLY); #else fd = open(file, O_RDONLY); #endif if (fd < 0) { if (errno == ENOENT) return OCFS2_ET_NAMED_DEVICE_NOT_FOUND; else return OCFS2_ET_IO; } ret = OCFS2_ET_CANNOT_DETERMINE_SECTOR_SIZE; #ifdef BLKSSZGET if (ioctl(fd, BLKSSZGET, sectsize) >= 0) ret = 0; #endif close(fd); return ret; } #ifdef DEBUG_EXE int main(int argc, char **argv) { int sectsize; int retval; if (argc < 2) { fprintf(stderr, "Usage: %s device\n", argv[0]); exit(1); } retval = ocfs2_get_device_sectsize(argv[1], §size); if (retval) { com_err(argv[0], retval, "while calling ocfs2_get_device_sectsize"); exit(1); } printf("Device %s has a hardware sector size of %d.\n", argv[1], sectsize); exit(0); } #endif ocfs2-tools-ocfs2-tools-1.8.6/libocfs2/getsize.c000066400000000000000000000152251347147137200214070ustar00rootroot00000000000000/* * getsize.c --- get the size of a partition. * * Copyright (C) 1995, 1995 Theodore Ts'o. * Copyright (C) 2003 VMware, Inc. * * Windows version of ext2fs_get_device_size by Chris Li, VMware. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Public * License. * %End-Header% */ /* Modified for OCFS2 by Manish Singh */ #define HAVE_UNISTD_H 1 #define HAVE_ERRNO_H 1 #define HAVE_LINUX_FD_H 1 #define HAVE_OPEN64 1 #define HAVE_SYS_IOCTL_H 1 #define HAVE_SYS_STAT_H 1 #define HAVE_FSTAT64 1 #define _LARGEFILE_SOURCE #define _LARGEFILE64_SOURCE #include #if HAVE_UNISTD_H #include #endif #if HAVE_ERRNO_H #include #endif #include #ifdef HAVE_SYS_IOCTL_H #include #endif #ifdef HAVE_LINUX_FD_H #include #endif #ifdef HAVE_SYS_DISKLABEL_H #include #endif #ifdef HAVE_SYS_DISK_H #ifdef HAVE_SYS_QUEUE_H #include /* for LIST_HEAD */ #endif #include #endif #ifdef __linux__ #include #endif #if HAVE_SYS_STAT_H #include #endif #if defined(__linux__) && defined(_IO) && !defined(BLKGETSIZE) #define BLKGETSIZE _IO(0x12,96) /* return device size */ #endif #if defined(__linux__) && defined(_IOR) && !defined(BLKGETSIZE64) #define BLKGETSIZE64 _IOR(0x12,114,size_t) /* return device size in bytes (u64 *arg) */ #endif #ifdef APPLE_DARWIN #define BLKGETSIZE DKIOCGETBLOCKCOUNT32 #endif /* APPLE_DARWIN */ #include "ocfs2/ocfs2.h" #if defined(__CYGWIN__) || defined (WIN32) #include "windows.h" #include "winioctl.h" #if (_WIN32_WINNT >= 0x0500) #define HAVE_GET_FILE_SIZE_EX 1 #endif errcode_t ocfs2_get_device_size(const char *file, int blocksize, uint64_t *retblocks) { HANDLE dev; PARTITION_INFORMATION pi; DISK_GEOMETRY gi; DWORD retbytes; #ifdef HAVE_GET_FILE_SIZE_EX LARGE_INTEGER filesize; #else DWORD filesize; #endif /* HAVE_GET_FILE_SIZE_EX */ dev = CreateFile(file, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE , NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (dev == INVALID_HANDLE_VALUE) return EBADF; if (DeviceIoControl(dev, IOCTL_DISK_GET_PARTITION_INFO, &pi, sizeof(PARTITION_INFORMATION), &pi, sizeof(PARTITION_INFORMATION), &retbytes, NULL)) { *retblocks = pi.PartitionLength.QuadPart / blocksize; } else if (DeviceIoControl(dev, IOCTL_DISK_GET_DRIVE_GEOMETRY, &gi, sizeof(DISK_GEOMETRY), &gi, sizeof(DISK_GEOMETRY), &retbytes, NULL)) { *retblocks = gi.BytesPerSector * gi.SectorsPerTrack * gi.TracksPerCylinder * gi.Cylinders.QuadPart / blocksize; #ifdef HAVE_GET_FILE_SIZE_EX } else if (GetFileSizeEx(dev, &filesize)) { *retblocks = filesize.QuadPart / blocksize; } #else } else { filesize = GetFileSize(dev, NULL); if (INVALID_FILE_SIZE != filesize) { *retblocks = filesize / blocksize; } } #endif /* HAVE_GET_FILE_SIZE_EX */ CloseHandle(dev); return 0; } #else static int valid_offset (int fd, off64_t offset) { char ch; if (lseek64 (fd, offset, 0) < 0) return 0; if (read (fd, &ch, 1) < 1) return 0; return 1; } /* * Returns the number of blocks in a partition */ errcode_t ocfs2_get_device_size(const char *file, int blocksize, uint64_t *retblocks) { int fd, rc = 0; int valid_blkgetsize64 = 1; #ifdef __linux__ struct utsname ut; #endif unsigned long long size64; unsigned long size; off64_t high, low; #ifdef FDGETPRM struct floppy_struct this_floppy; #endif #ifdef HAVE_SYS_DISKLABEL_H int part; struct disklabel lab; struct partition *pp; char ch; #endif /* HAVE_SYS_DISKLABEL_H */ #ifdef HAVE_OPEN64 fd = open64(file, O_RDONLY); #else fd = open(file, O_RDONLY); #endif if (fd < 0) return errno; #ifdef DKIOCGETBLOCKCOUNT /* For Apple Darwin */ if (ioctl(fd, DKIOCGETBLOCKCOUNT, &size64) >= 0) { if ((sizeof(*retblocks) < sizeof(unsigned long long)) && ((size64 / (blocksize / 512)) > 0xFFFFFFFF)) return EFBIG; *retblocks = size64 / (blocksize / 512); goto out; } #endif #ifdef BLKGETSIZE64 #ifdef __linux__ if ((uname(&ut) == 0) && ((ut.release[0] == '2') && (ut.release[1] == '.') && (ut.release[2] < '6') && (ut.release[3] == '.'))) valid_blkgetsize64 = 0; #endif if (valid_blkgetsize64 && ioctl(fd, BLKGETSIZE64, &size64) >= 0) { if ((sizeof(*retblocks) < sizeof(unsigned long long)) && ((size64 / blocksize) > 0xFFFFFFFF)) { rc = EFBIG; goto out; } *retblocks = size64 / blocksize; goto out; } #endif #ifdef BLKGETSIZE if (ioctl(fd, BLKGETSIZE, &size) >= 0) { *retblocks = size / (blocksize / 512); goto out; } #endif #ifdef FDGETPRM if (ioctl(fd, FDGETPRM, &this_floppy) >= 0) { *retblocks = this_floppy.size / (blocksize / 512); goto out; } #endif #ifdef HAVE_SYS_DISKLABEL_H #if defined(DIOCGMEDIASIZE) { off_t ms; u_int bs; if (ioctl(fd, DIOCGMEDIASIZE, &ms) >= 0) { *retblocks = ms / blocksize; goto out; } } #elif defined(DIOCGDINFO) /* old disklabel interface */ part = strlen(file) - 1; if (part >= 0) { ch = file[part]; if (isdigit(ch)) part = 0; else if (ch >= 'a' && ch <= 'h') part = ch - 'a'; else part = -1; } if (part >= 0 && (ioctl(fd, DIOCGDINFO, (char *)&lab) >= 0)) { pp = &lab.d_partitions[part]; if (pp->p_size) { *retblocks = pp->p_size / (blocksize / 512); goto out; } } #endif /* defined(DIOCG*) */ #endif /* HAVE_SYS_DISKLABEL_H */ { #ifdef HAVE_FSTAT64 struct stat64 st; if (fstat64(fd, &st) == 0) #else struct stat st; if (fstat(fd, &st) == 0) #endif if (S_ISREG(st.st_mode)) { *retblocks = st.st_size / blocksize; goto out; } } /* * OK, we couldn't figure it out by using a specialized ioctl, * which is generally the best way. So do binary search to * find the size of the partition. */ low = 0; for (high = 1024; valid_offset (fd, high); high *= 2) low = high; while (low < high - 1) { const off64_t mid = (low + high) / 2; if (valid_offset (fd, mid)) low = mid; else high = mid; } valid_offset (fd, 0); size64 = low + 1; if ((sizeof(*retblocks) < sizeof(unsigned long long)) && ((size64 / blocksize) > 0xFFFFFFFF)) return EFBIG; *retblocks = size64 / blocksize; out: close(fd); return rc; } #endif /* WIN32 */ #ifdef DEBUG_EXE #include int main(int argc, char **argv) { uint64_t blocks; int retval; if (argc < 2) { fprintf(stderr, "Usage: %s device\n", argv[0]); exit(1); } retval = ocfs2_get_device_size(argv[1], 1024, &blocks); if (retval) { com_err(argv[0], retval, "while calling ocfs2_get_device_size"); exit(1); } printf("Device %s has %"PRIu64" 1k blocks.\n", argv[1], blocks); exit(0); } #endif ocfs2-tools-ocfs2-tools-1.8.6/libocfs2/heartbeat.c000066400000000000000000000060741347147137200216760ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * heartbeat.c * * Interface the OCFS2 userspace library to the userspace heartbeat * functionality * * Copyright (C) 2005 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License, version 2, as published by the Free Software Foundation. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * * Authors: Mark Fasheh, Zach Brown */ #include #include #include "ocfs2/byteorder.h" #include "ocfs2/ocfs2.h" void ocfs2_swap_disk_heartbeat_block(struct o2hb_disk_heartbeat_block *hb) { if (cpu_is_little_endian) return; hb->hb_seq = bswap_64(hb->hb_seq); hb->hb_cksum = bswap_32(hb->hb_cksum); hb->hb_generation = bswap_64(hb->hb_generation); hb->hb_dead_ms = bswap_32(hb->hb_dead_ms); } errcode_t ocfs2_fill_heartbeat_desc(ocfs2_filesys *fs, struct o2cb_region_desc *desc) { errcode_t ret; char *filename; char *buf = NULL; uint64_t blkno, blocks, start_block; uint32_t block_bits, cluster_bits; int sectsize, sectsize_bits; struct ocfs2_dinode *di; struct ocfs2_extent_rec *rec; ret = ocfs2_get_device_sectsize(fs->fs_devname, §size); if (ret) { if (ret == OCFS2_ET_CANNOT_DETERMINE_SECTOR_SIZE) sectsize = OCFS2_MIN_BLOCKSIZE; else goto leave; } sectsize_bits = ffs(sectsize) - 1; filename = ocfs2_system_inodes[HEARTBEAT_SYSTEM_INODE].si_name; ret = ocfs2_lookup(fs, fs->fs_sysdir_blkno, filename, strlen(filename), NULL, &blkno); if (ret) goto leave; ret = ocfs2_malloc_block(fs->fs_io, &buf); if (ret) goto leave; ret = ocfs2_read_inode(fs, blkno, buf); if (ret) goto leave; di = (struct ocfs2_dinode *)buf; if (di->id2.i_list.l_tree_depth || di->id2.i_list.l_next_free_rec != 1) { ret = OCFS2_ET_BAD_HEARTBEAT_FILE; goto leave; } rec = &(di->id2.i_list.l_recs[0]); block_bits = OCFS2_RAW_SB(fs->fs_super)->s_blocksize_bits; cluster_bits = OCFS2_RAW_SB(fs->fs_super)->s_clustersize_bits; if (block_bits < sectsize_bits) { ret = OCFS2_ET_BLOCK_SIZE_TOO_SMALL_FOR_HARDWARE; goto leave; } blocks = ocfs2_rec_clusters(0, rec) << cluster_bits; blocks >>= block_bits; if (blocks < O2NM_MAX_NODES) { ret = OCFS2_ET_BAD_HEARTBEAT_FILE; goto leave; } else blocks = O2NM_MAX_NODES; start_block = rec->e_blkno << block_bits; start_block >>= sectsize_bits; desc->r_name = fs->uuid_str; desc->r_device_name = fs->fs_devname; desc->r_block_bytes = sectsize; desc->r_start_block = start_block; desc->r_blocks = blocks; leave: if (buf) ocfs2_free(&buf); return ret; } ocfs2-tools-ocfs2-tools-1.8.6/libocfs2/image.c000066400000000000000000000163731347147137200210240ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * image.c * * supporting structures/functions to handle ocfs2 image file * * Copyright (C) 2008 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License, version 2, as published by the Free Software Foundation. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. */ #define _XOPEN_SOURCE 600 /* Triggers magic in features.h */ #define _LARGEFILE64_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include "ocfs2/ocfs2.h" #include "ocfs2/byteorder.h" #include "ocfs2/image.h" void ocfs2_image_swap_header(struct ocfs2_image_hdr *hdr) { int i; if (cpu_is_little_endian) return; for (i = 0; i < hdr->hdr_superblkcnt; i++) hdr->hdr_superblocks[i] = bswap_64(hdr->hdr_superblocks[i]); hdr->hdr_magic = bswap_32(hdr->hdr_magic); hdr->hdr_version = bswap_32(hdr->hdr_version); hdr->hdr_timestamp = bswap_32(hdr->hdr_timestamp); hdr->hdr_fsblkcnt = bswap_64(hdr->hdr_fsblkcnt); hdr->hdr_fsblksz = bswap_64(hdr->hdr_fsblksz); hdr->hdr_imgblkcnt = bswap_64(hdr->hdr_imgblkcnt); hdr->hdr_bmpblksz = bswap_64(hdr->hdr_bmpblksz); hdr->hdr_superblkcnt = bswap_64(hdr->hdr_superblkcnt); } void ocfs2_image_free_bitmap(ocfs2_filesys *ofs) { struct ocfs2_image_state *ost = ofs->ost; int i; if (!ost->ost_bmparr) return; for (i=0; iost_bmpblks; i++) if (ost->ost_bmparr[i].arr_self) ocfs2_free(&ost->ost_bmparr[i].arr_self); if (ost->ost_bmparr) ocfs2_free(&ost->ost_bmparr); return; } /* * allocate ocfs2_image_bitmap_arr and ocfs2 image bitmap blocks. o2image bitmap * block is of size OCFS2_IMAGE_BITMAP_BLOCKSIZE and ocfs2_image_bitmap_arr * tracks the bitmap blocks */ errcode_t ocfs2_image_alloc_bitmap(ocfs2_filesys *ofs) { uint64_t blks, allocsize, leftsize; struct ocfs2_image_state *ost = ofs->ost; int indx, i, n; errcode_t ret; char *buf; ost->ost_bmpblks = ((ost->ost_fsblkcnt - 1) / (OCFS2_IMAGE_BITS_IN_BLOCK)) + 1; ost->ost_bmpblksz = OCFS2_IMAGE_BITMAP_BLOCKSIZE; blks = ost->ost_bmpblks; /* allocate memory for an array to track bitmap blocks */ ret = ocfs2_malloc0((blks * sizeof(ocfs2_image_bitmap_arr)), &ost->ost_bmparr); if (ret) return ret; allocsize = blks * OCFS2_IMAGE_BITMAP_BLOCKSIZE; leftsize = allocsize; indx = 0; /* allocate bitmap blocks and assign blocks to above array */ while (leftsize) { ret = ocfs2_malloc_blocks(ofs->fs_io, allocsize/io_get_blksize(ofs->fs_io), &buf); if (ret && (ret != -ENOMEM)) goto out; if (ret == -ENOMEM) { if (allocsize == OCFS2_IMAGE_BITMAP_BLOCKSIZE) goto out; allocsize >>= 1; if (allocsize % OCFS2_IMAGE_BITMAP_BLOCKSIZE) { allocsize /= OCFS2_IMAGE_BITMAP_BLOCKSIZE; allocsize *= OCFS2_IMAGE_BITMAP_BLOCKSIZE; } continue; } n = allocsize / OCFS2_IMAGE_BITMAP_BLOCKSIZE; for (i = 0; i < n; i++) { ost->ost_bmparr[indx].arr_set_bit_cnt = 0; ost->ost_bmparr[indx].arr_map = ((char *)buf + (i * OCFS2_IMAGE_BITMAP_BLOCKSIZE)); /* remember buf address to free it later */ if (!i) ost->ost_bmparr[indx].arr_self = buf; indx++; } leftsize -= allocsize; if (leftsize <= allocsize) allocsize = leftsize; } out: /* If allocation failed free and return error */ if (leftsize) { for (i = 0; i < indx; i++) if (ost->ost_bmparr[i].arr_self) ocfs2_free(&ost->ost_bmparr[i].arr_self); ocfs2_free(&ost->ost_bmparr); } return ret; } /* * This routine loads bitmap blocks from an o2image image file into memory. * This process happens during file open. bitmap blocks reside towards * the end of the imagefile. */ errcode_t ocfs2_image_load_bitmap(ocfs2_filesys *ofs) { struct ocfs2_image_state *ost; struct ocfs2_image_hdr *hdr; uint64_t blk_off, bits_set; int i, j, fd; ssize_t count; errcode_t ret; char *blk = NULL; ret = ocfs2_malloc0(sizeof(struct ocfs2_image_state), &ofs->ost); if (ret) return ret; ost = ofs->ost; ret = ocfs2_malloc_block(ofs->fs_io, &blk); if (ret) goto out; /* read ocfs2 image header */ ret = io_read_block(ofs->fs_io, 0, 1, blk); if (ret) goto out; hdr = (struct ocfs2_image_hdr *)blk; ocfs2_image_swap_header(hdr); ret = OCFS2_ET_BAD_MAGIC; if (hdr->hdr_magic != OCFS2_IMAGE_MAGIC) goto out; if (memcmp(hdr->hdr_magic_desc, OCFS2_IMAGE_DESC, sizeof(OCFS2_IMAGE_DESC))) goto out; ret = OCFS2_ET_OCFS_REV; if (hdr->hdr_version > OCFS2_IMAGE_VERSION) goto out; ost->ost_fsblkcnt = hdr->hdr_fsblkcnt; ost->ost_fsblksz = hdr->hdr_fsblksz; ost->ost_imgblkcnt = hdr->hdr_imgblkcnt; ost->ost_bmpblksz = hdr->hdr_bmpblksz; ret = ocfs2_image_alloc_bitmap(ofs); if (ret) goto out; /* load bitmap blocks ocfs2 image state */ bits_set = 0; fd = io_get_fd(ofs->fs_io); blk_off = (ost->ost_imgblkcnt + 1) * ost->ost_fsblksz; for (i = 0; i < ost->ost_bmpblks; i++) { ost->ost_bmparr[i].arr_set_bit_cnt = bits_set; /* * we don't use io_read_block as ocfs2 image bitmap block size * could be different from filesystem block size */ count = pread64(fd, ost->ost_bmparr[i].arr_map, ost->ost_bmpblksz, blk_off); if (count < 0) { ret = OCFS2_ET_IO; goto out; } /* add bits set in this bitmap */ for (j = 0; j < (ost->ost_bmpblksz * 8); j++) if (ocfs2_test_bit(j, ost->ost_bmparr[i].arr_map)) bits_set++; blk_off += ost->ost_bmpblksz; } out: if (blk) ocfs2_free(&blk); if (ret) { ocfs2_image_free_bitmap(ofs); ocfs2_free(&ofs->ost); } return ret; } void ocfs2_image_mark_bitmap(ocfs2_filesys *ofs, uint64_t blkno) { struct ocfs2_image_state *ost = ofs->ost; int bitmap_blk; int bit; bit = blkno % OCFS2_IMAGE_BITS_IN_BLOCK; bitmap_blk = blkno / OCFS2_IMAGE_BITS_IN_BLOCK; ocfs2_set_bit(bit, ost->ost_bmparr[bitmap_blk].arr_map); } int ocfs2_image_test_bit(ocfs2_filesys *ofs, uint64_t blkno) { struct ocfs2_image_state *ost = ofs->ost; int bitmap_blk; int bit; bit = blkno % OCFS2_IMAGE_BITS_IN_BLOCK; bitmap_blk = blkno / OCFS2_IMAGE_BITS_IN_BLOCK; if (ocfs2_test_bit(bit, ost->ost_bmparr[bitmap_blk].arr_map)) return 1; else return 0; } uint64_t ocfs2_image_get_blockno(ocfs2_filesys *ofs, uint64_t blkno) { struct ocfs2_image_state *ost = ofs->ost; uint64_t ret_blk; int bitmap_blk; int i, bit; bit = blkno % OCFS2_IMAGE_BITS_IN_BLOCK; bitmap_blk = blkno / OCFS2_IMAGE_BITS_IN_BLOCK; if (ocfs2_test_bit(bit, ost->ost_bmparr[bitmap_blk].arr_map)) { ret_blk = ost->ost_bmparr[bitmap_blk].arr_set_bit_cnt + 1; /* add bits set in this block before the block no */ for (i = 0; i < bit; i++) if (ocfs2_test_bit(i, ost->ost_bmparr[bitmap_blk].arr_map)) ret_blk++; } else ret_blk = -1; return ret_blk; } ocfs2-tools-ocfs2-tools-1.8.6/libocfs2/inode.c000066400000000000000000000320261347147137200210310ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * inode.c * * Inode operations for the OCFS2 userspace library. * * Copyright (C) 2004 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License, version 2, as published by the Free Software Foundation. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * * Ideas taken from e2fsprogs/lib/ext2fs/inode.c * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o. */ #define _XOPEN_SOURCE 600 /* Triggers XOPEN2K in features.h */ #define _LARGEFILE64_SOURCE #include #include #include "ocfs2/byteorder.h" #include "ocfs2/ocfs2.h" errcode_t ocfs2_check_directory(ocfs2_filesys *fs, uint64_t dir) { struct ocfs2_dinode *inode; char *buf; errcode_t ret; if ((dir < OCFS2_SUPER_BLOCK_BLKNO) || (dir > fs->fs_blocks)) return OCFS2_ET_BAD_BLKNO; ret = ocfs2_malloc_block(fs->fs_io, &buf); if (ret) return ret; ret = ocfs2_read_inode(fs, dir, buf); if (ret) goto out_buf; inode = (struct ocfs2_dinode *)buf; if (!S_ISDIR(inode->i_mode)) ret = OCFS2_ET_NO_DIRECTORY; out_buf: ocfs2_free(&buf); return ret; } static void ocfs2_swap_inode_third(ocfs2_filesys *fs, struct ocfs2_dinode *di) { if (di->i_flags & OCFS2_CHAIN_FL) { struct ocfs2_chain_list *cl = &di->id2.i_chain; uint16_t i; for (i = 0; i < cl->cl_next_free_rec; i++) { struct ocfs2_chain_rec *rec = &cl->cl_recs[i]; if (ocfs2_swap_barrier(fs, di, rec, sizeof(struct ocfs2_chain_rec))) break; rec->c_free = bswap_32(rec->c_free); rec->c_total = bswap_32(rec->c_total); rec->c_blkno = bswap_64(rec->c_blkno); } } else if (di->i_flags & OCFS2_DEALLOC_FL) { struct ocfs2_truncate_log *tl = &di->id2.i_dealloc; uint16_t i; for(i = 0; i < tl->tl_count; i++) { struct ocfs2_truncate_rec *rec = &tl->tl_recs[i]; if (ocfs2_swap_barrier(fs, di, rec, sizeof(struct ocfs2_truncate_rec))) break; rec->t_start = bswap_32(rec->t_start); rec->t_clusters = bswap_32(rec->t_clusters); } } } static void ocfs2_swap_inode_second(struct ocfs2_dinode *di) { if (S_ISCHR(di->i_mode) || S_ISBLK(di->i_mode)) di->id1.dev1.i_rdev = bswap_64(di->id1.dev1.i_rdev); else if (di->i_flags & OCFS2_BITMAP_FL) { di->id1.bitmap1.i_used = bswap_32(di->id1.bitmap1.i_used); di->id1.bitmap1.i_total = bswap_32(di->id1.bitmap1.i_total); } else if (di->i_flags & OCFS2_JOURNAL_FL) { di->id1.journal1.ij_flags = bswap_32(di->id1.journal1.ij_flags); di->id1.journal1.ij_recovery_generation = bswap_32(di->id1.journal1.ij_recovery_generation); } /* we need to be careful to swap the union member that is in use. * first the ones that are explicitly marked with flags.. */ if (di->i_flags & OCFS2_SUPER_BLOCK_FL) { struct ocfs2_super_block *sb = &di->id2.i_super; sb->s_major_rev_level = bswap_16(sb->s_major_rev_level); sb->s_minor_rev_level = bswap_16(sb->s_minor_rev_level); sb->s_mnt_count = bswap_16(sb->s_mnt_count); sb->s_max_mnt_count = bswap_16(sb->s_max_mnt_count); sb->s_state = bswap_16(sb->s_state); sb->s_errors = bswap_16(sb->s_errors); sb->s_checkinterval = bswap_32(sb->s_checkinterval); sb->s_lastcheck = bswap_64(sb->s_lastcheck); sb->s_creator_os = bswap_32(sb->s_creator_os); sb->s_feature_compat = bswap_32(sb->s_feature_compat); sb->s_feature_ro_compat = bswap_32(sb->s_feature_ro_compat); sb->s_feature_incompat = bswap_32(sb->s_feature_incompat); sb->s_root_blkno = bswap_64(sb->s_root_blkno); sb->s_system_dir_blkno = bswap_64(sb->s_system_dir_blkno); sb->s_blocksize_bits = bswap_32(sb->s_blocksize_bits); sb->s_clustersize_bits = bswap_32(sb->s_clustersize_bits); sb->s_max_slots = bswap_16(sb->s_max_slots); sb->s_tunefs_flag = bswap_16(sb->s_tunefs_flag); sb->s_uuid_hash = bswap_32(sb->s_uuid_hash); sb->s_first_cluster_group = bswap_64(sb->s_first_cluster_group); sb->s_xattr_inline_size = bswap_16(sb->s_xattr_inline_size); sb->s_dx_seed[0] = bswap_32(sb->s_dx_seed[0]); sb->s_dx_seed[1] = bswap_32(sb->s_dx_seed[1]); sb->s_dx_seed[2] = bswap_32(sb->s_dx_seed[2]); } else if (di->i_flags & OCFS2_LOCAL_ALLOC_FL) { struct ocfs2_local_alloc *la = &di->id2.i_lab; la->la_bm_off = bswap_32(la->la_bm_off); la->la_size = bswap_16(la->la_size); } else if (di->i_flags & OCFS2_CHAIN_FL) { struct ocfs2_chain_list *cl = &di->id2.i_chain; cl->cl_cpg = bswap_16(cl->cl_cpg); cl->cl_bpc = bswap_16(cl->cl_bpc); cl->cl_count = bswap_16(cl->cl_count); cl->cl_next_free_rec = bswap_16(cl->cl_next_free_rec); } else if (di->i_flags & OCFS2_DEALLOC_FL) { struct ocfs2_truncate_log *tl = &di->id2.i_dealloc; tl->tl_count = bswap_16(tl->tl_count); tl->tl_used = bswap_16(tl->tl_used); } else if (di->i_dyn_features & OCFS2_INLINE_DATA_FL) { struct ocfs2_inline_data *id = &di->id2.i_data; id->id_count = bswap_16(id->id_count); } else if (di->i_dyn_features & OCFS2_INDEXED_DIR_FL) { di->i_dx_root = bswap_64(di->i_dx_root); } } static void ocfs2_swap_inode_first(struct ocfs2_dinode *di) { di->i_generation = bswap_32(di->i_generation); di->i_suballoc_slot = bswap_16(di->i_suballoc_slot); di->i_suballoc_bit = bswap_16(di->i_suballoc_bit); di->i_xattr_inline_size = bswap_16(di->i_xattr_inline_size); di->i_clusters = bswap_32(di->i_clusters); di->i_uid = bswap_32(di->i_uid); di->i_gid = bswap_32(di->i_gid); di->i_size = bswap_64(di->i_size); di->i_mode = bswap_16(di->i_mode); di->i_links_count = bswap_16(di->i_links_count); di->i_flags = bswap_32(di->i_flags); di->i_atime = bswap_64(di->i_atime); di->i_ctime = bswap_64(di->i_ctime); di->i_mtime = bswap_64(di->i_mtime); di->i_dtime = bswap_64(di->i_dtime); di->i_blkno = bswap_64(di->i_blkno); di->i_last_eb_blk = bswap_64(di->i_last_eb_blk); di->i_fs_generation = bswap_32(di->i_fs_generation); di->i_atime_nsec = bswap_32(di->i_atime_nsec); di->i_ctime_nsec = bswap_32(di->i_ctime_nsec); di->i_mtime_nsec = bswap_32(di->i_mtime_nsec); di->i_attr = bswap_32(di->i_attr); di->i_orphaned_slot = bswap_16(di->i_orphaned_slot); di->i_dyn_features = bswap_16(di->i_dyn_features); di->i_xattr_loc = bswap_64(di->i_xattr_loc); di->i_refcount_loc = bswap_64(di->i_refcount_loc); di->i_suballoc_loc = bswap_64(di->i_suballoc_loc); } static int has_extents(struct ocfs2_dinode *di) { /* inodes flagged with other stuff in id2 */ if (di->i_flags & (OCFS2_SUPER_BLOCK_FL | OCFS2_LOCAL_ALLOC_FL | OCFS2_CHAIN_FL | OCFS2_DEALLOC_FL)) return 0; /* i_flags doesn't indicate when id2 is a fast symlink */ if (S_ISLNK(di->i_mode) && di->i_size && di->i_clusters == 0) return 0; if (di->i_dyn_features & OCFS2_INLINE_DATA_FL) return 0; return 1; } static inline void ocfs2_swap_inline_dir(ocfs2_filesys *fs, struct ocfs2_dinode *di, int to_cpu) { void *de_buf = di->id2.i_data.id_data; uint64_t bytes = di->id2.i_data.id_count; int max_inline = ocfs2_max_inline_data_with_xattr(fs->fs_blocksize, di); /* Just in case i_xattr_inline_size is garbage */ if (max_inline < 0) max_inline = 0; if (bytes > max_inline) bytes = max_inline; /* if inode is empty do not swap */ if (di->i_size == 0) return; if (to_cpu) ocfs2_swap_dir_entries_to_cpu(de_buf, bytes); else ocfs2_swap_dir_entries_from_cpu(de_buf, bytes); } void ocfs2_swap_inode_from_cpu(ocfs2_filesys *fs, struct ocfs2_dinode *di) { if (cpu_is_little_endian) return; if (di->i_dyn_features & OCFS2_INLINE_XATTR_FL) { struct ocfs2_xattr_header *xh = (struct ocfs2_xattr_header *) ((void *)di + fs->fs_blocksize - di->i_xattr_inline_size); ocfs2_swap_xattrs_from_cpu(fs, di, xh); } if (has_extents(di)) ocfs2_swap_extent_list_from_cpu(fs, di, &di->id2.i_list); if (di->i_dyn_features & OCFS2_INLINE_DATA_FL && S_ISDIR(di->i_mode)) ocfs2_swap_inline_dir(fs, di, 0); ocfs2_swap_inode_third(fs, di); ocfs2_swap_inode_second(di); ocfs2_swap_inode_first(di); } void ocfs2_swap_inode_to_cpu(ocfs2_filesys *fs, struct ocfs2_dinode *di) { if (cpu_is_little_endian) return; ocfs2_swap_inode_first(di); ocfs2_swap_inode_second(di); ocfs2_swap_inode_third(fs, di); if (di->i_dyn_features & OCFS2_INLINE_DATA_FL && S_ISDIR(di->i_mode)) ocfs2_swap_inline_dir(fs, di, 1); if (has_extents(di)) ocfs2_swap_extent_list_to_cpu(fs, di, &di->id2.i_list); if (di->i_dyn_features & OCFS2_INLINE_XATTR_FL) { struct ocfs2_xattr_header *xh = (struct ocfs2_xattr_header *) ((void *)di + fs->fs_blocksize - di->i_xattr_inline_size); ocfs2_swap_xattrs_to_cpu(fs, di, xh); } } errcode_t ocfs2_read_inode(ocfs2_filesys *fs, uint64_t blkno, char *inode_buf) { errcode_t ret; char *blk; struct ocfs2_dinode *di; if ((blkno < OCFS2_SUPER_BLOCK_BLKNO) || (blkno > fs->fs_blocks)) return OCFS2_ET_BAD_BLKNO; ret = ocfs2_malloc_block(fs->fs_io, &blk); if (ret) return ret; ret = ocfs2_read_blocks(fs, blkno, 1, blk); if (ret) goto out; di = (struct ocfs2_dinode *)blk; ret = OCFS2_ET_BAD_INODE_MAGIC; if (memcmp(di->i_signature, OCFS2_INODE_SIGNATURE, strlen(OCFS2_INODE_SIGNATURE))) goto out; memcpy(inode_buf, blk, fs->fs_blocksize); ret = ocfs2_validate_meta_ecc(fs, blk, &di->i_check); if (ret) goto out; di = (struct ocfs2_dinode *) inode_buf; ocfs2_swap_inode_to_cpu(fs, di); ret = 0; out: ocfs2_free(&blk); return ret; } errcode_t ocfs2_write_inode(ocfs2_filesys *fs, uint64_t blkno, char *inode_buf) { errcode_t ret; char *blk; struct ocfs2_dinode *di; if (!(fs->fs_flags & OCFS2_FLAG_RW)) return OCFS2_ET_RO_FILESYS; if ((blkno < OCFS2_SUPER_BLOCK_BLKNO) || (blkno > fs->fs_blocks)) return OCFS2_ET_BAD_BLKNO; ret = ocfs2_malloc_block(fs->fs_io, &blk); if (ret) return ret; memcpy(blk, inode_buf, fs->fs_blocksize); di = (struct ocfs2_dinode *)blk; ocfs2_swap_inode_from_cpu(fs, di); ocfs2_compute_meta_ecc(fs, blk, &di->i_check); ret = io_write_block(fs->fs_io, blkno, 1, blk); if (ret) goto out; fs->fs_flags |= OCFS2_FLAG_CHANGED; ret = 0; out: ocfs2_free(&blk); return ret; } /** * Same as ocfs2_write_inode, but without ocfs2_compute_meta_ecc function so * that the inode meta CRC and ECC value wouldn't be overwritten after corrupted * in fswreck. */ errcode_t ocfs2_write_inode_without_meta_ecc(ocfs2_filesys *fs, uint64_t blkno, char *inode_buf) { errcode_t ret; char *blk; struct ocfs2_dinode *di; if (!(fs->fs_flags & OCFS2_FLAG_RW)) return OCFS2_ET_RO_FILESYS; if ((blkno < OCFS2_SUPER_BLOCK_BLKNO) || (blkno > fs->fs_blocks)) return OCFS2_ET_BAD_BLKNO; ret = ocfs2_malloc_block(fs->fs_io, &blk); if (ret) return ret; memcpy(blk, inode_buf, fs->fs_blocksize); di = (struct ocfs2_dinode *)blk; ocfs2_swap_inode_from_cpu(fs, di); ret = io_write_block(fs->fs_io, blkno, 1, blk); if (ret) goto out; fs->fs_flags |= OCFS2_FLAG_CHANGED; ret = 0; out: ocfs2_free(&blk); return ret; } #ifdef DEBUG_EXE #include static uint64_t read_number(const char *num) { uint64_t val; char *ptr; val = strtoull(num, &ptr, 0); if (!ptr || *ptr) return 0; return val; } static void print_usage(void) { fprintf(stderr, "Usage: inode \n"); } extern int opterr, optind; extern char *optarg; int main(int argc, char *argv[]) { errcode_t ret; uint64_t blkno; char *filename, *buf; ocfs2_filesys *fs; struct ocfs2_dinode *di; blkno = OCFS2_SUPER_BLOCK_BLKNO; initialize_ocfs_error_table(); if (argc < 2) { fprintf(stderr, "Missing filename\n"); print_usage(); return 1; } filename = argv[1]; if (argc > 2) { blkno = read_number(argv[2]); if (blkno < OCFS2_SUPER_BLOCK_BLKNO) { fprintf(stderr, "Invalid blockno: %"PRIu64"\n", blkno); print_usage(); return 1; } } ret = ocfs2_open(filename, OCFS2_FLAG_RO, 0, 0, &fs); if (ret) { com_err(argv[0], ret, "while opening file \"%s\"", filename); goto out; } ret = ocfs2_malloc_block(fs->fs_io, &buf); if (ret) { com_err(argv[0], ret, "while allocating inode buffer"); goto out_close; } ret = ocfs2_read_inode(fs, blkno, buf); if (ret) { com_err(argv[0], ret, "while reading inode %"PRIu64, blkno); goto out_free; } di = (struct ocfs2_dinode *)buf; fprintf(stdout, "OCFS2 inode %"PRIu64" on \"%s\"\n", blkno, filename); out_free: ocfs2_free(&buf); out_close: ret = ocfs2_close(fs); if (ret) { com_err(argv[0], ret, "while closing file \"%s\"", filename); } out: return 0; } #endif /* DEBUG_EXE */ ocfs2-tools-ocfs2-tools-1.8.6/libocfs2/inode_scan.c000066400000000000000000000264211347147137200220370ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * inode_scan.c * * Scan all inodes in an OCFS2 filesystem. For the OCFS2 userspace * library. * * Copyright (C) 2004, 2011 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License, version 2, as published by the Free Software Foundation. * * 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. * * Ideas taken from e2fsprogs/lib/ext2fs/inode_scan.c * Copyright (C) 1993, 1994, 1995, 1996, 1997 Theodore Ts'o. */ #define _XOPEN_SOURCE 600 /* Triggers XOPEN2K in features.h */ #define _LARGEFILE64_SOURCE #include #include #include #include "ocfs2/ocfs2.h" #include "extent_map.h" struct _ocfs2_inode_scan { ocfs2_filesys *fs; int num_inode_alloc; int next_inode_file; ocfs2_cached_inode *cur_inode_alloc; ocfs2_cached_inode **inode_alloc; struct ocfs2_chain_rec *cur_rec; int next_rec; struct ocfs2_group_desc *cur_desc; unsigned int count; uint64_t cur_blkno; char *group_buffer; char *cur_block; int buffer_blocks; int blocks_in_buffer; unsigned int blocks_left; uint64_t b_offset; /* bit offset in the group bitmap. */ uint16_t cur_discontig_rec; /* Only valid in discontig group. */ }; /* * This function is called by fill_group_buffer when an alloc group has * been completely read. It must not be called from the last group. * ocfs2_get_next_inode() should have detected that condition. */ static errcode_t get_next_group(ocfs2_inode_scan *scan) { errcode_t ret; if (!scan->cur_desc) { if (scan->b_offset) abort(); ret = ocfs2_malloc_block(scan->fs->fs_io, &scan->cur_desc); if (ret) return ret; } if (scan->b_offset) scan->cur_blkno = scan->cur_desc->bg_next_group; /* * scan->cur_blkno better be nonzero, either set by * get_next_chain() or valid from bg_next_group */ if (!scan->cur_blkno) abort(); ret = ocfs2_read_group_desc(scan->fs, scan->cur_blkno, (char *)scan->cur_desc); if (ret) return (ret); if (scan->cur_desc->bg_blkno != scan->cur_blkno) return OCFS2_ET_CORRUPT_GROUP_DESC; /* Skip past group descriptor block */ scan->cur_blkno++; scan->count++; scan->blocks_left--; scan->b_offset = 1; scan->cur_discontig_rec = 0; return 0; } /* * This function is called by fill_group_buffer when an alloc chain * has been completely read. It must not be called when the current * inode alloc file has been read in its entirety. This condition * should have been detected by ocfs2_get_next_inode(). */ static errcode_t get_next_chain(ocfs2_inode_scan *scan) { struct ocfs2_dinode *di = scan->cur_inode_alloc->ci_inode; if (scan->next_rec == di->id2.i_chain.cl_next_free_rec) { if (!scan->next_rec) { /* * The only way we can get here with next_rec * == cl_next_free_rec == 0 is if * bitmap1.i_total was non-zero. But if i_total * was non-zero and we have no chains, we're * corrupt. */ return OCFS2_ET_CORRUPT_CHAIN; } else abort(); } scan->cur_rec = &di->id2.i_chain.cl_recs[scan->next_rec]; scan->next_rec++; scan->count = 0; scan->b_offset = 0; scan->cur_blkno = scan->cur_rec->c_blkno; return 0; } /* * Get the number of blocks we will read next. * In case of discontiguous group, we will set scan->cur_blkno in case * we need to read next extent record. */ static int get_next_read_blocks(ocfs2_inode_scan *scan) { int num_blocks, rec_end; struct ocfs2_extent_rec *rec; if (!scan->cur_desc) abort(); if (!scan->cur_desc->bg_list.l_next_free_rec) { /* Contiguous group. Just set num_blocks. */ num_blocks = scan->cur_desc->bg_bits - scan->b_offset; goto out; } /* We shouldn't arrived here. */ if (scan->cur_discontig_rec == scan->cur_desc->bg_list.l_next_free_rec) abort(); rec = &scan->cur_desc->bg_list.l_recs[scan->cur_discontig_rec]; rec_end = ocfs2_clusters_to_blocks(scan->fs, rec->e_cpos + rec->e_leaf_clusters); if (rec_end < scan->b_offset) abort(); else if (rec_end > scan->b_offset) { /* OK, we have more blocks to read in this rec. */ num_blocks = rec_end - scan->b_offset; } else { /* We have to read the next rec now. */ scan->cur_discontig_rec++; rec = &scan->cur_desc->bg_list.l_recs[scan->cur_discontig_rec]; scan->cur_blkno = rec->e_blkno; num_blocks = ocfs2_clusters_to_blocks(scan->fs, rec->e_leaf_clusters); } out: if (num_blocks > scan->buffer_blocks) num_blocks = scan->buffer_blocks; return num_blocks; } /* * This function is called by ocfs2_get_next_inode when it needs * to read in more clusters from the current inode alloc file. It * must not be called when the current inode alloc file has been read * in its entirety. This condition is detected by * ocfs2_get_next_inode(). */ static errcode_t fill_group_buffer(ocfs2_inode_scan *scan) { errcode_t ret; int num_blocks; if (scan->cur_rec && (scan->count > scan->cur_rec->c_total)) abort(); if (scan->cur_rec && (scan->b_offset > scan->cur_desc->bg_bits)) abort(); if (!scan->cur_rec || (scan->count == scan->cur_rec->c_total)) { ret = get_next_chain(scan); if (ret) return ret; } if (!scan->b_offset || (scan->b_offset == scan->cur_desc->bg_bits)) { ret = get_next_group(scan); if (ret) return ret; } num_blocks = get_next_read_blocks(scan); ret = ocfs2_read_blocks(scan->fs, scan->cur_blkno, num_blocks, scan->group_buffer); if (ret) return ret; scan->b_offset += num_blocks; scan->blocks_in_buffer = num_blocks; scan->cur_block = scan->group_buffer; return 0; } /* This function sets the starting points for a given cached inode */ static int get_next_inode_alloc(ocfs2_inode_scan *scan) { ocfs2_cached_inode *cinode = scan->cur_inode_alloc; if (cinode && scan->blocks_left) abort(); do { if (scan->next_inode_file == scan->num_inode_alloc) return 1; /* Out of files */ scan->cur_inode_alloc = scan->inode_alloc[scan->next_inode_file]; cinode = scan->cur_inode_alloc; scan->next_inode_file++; } while (!cinode->ci_inode->id1.bitmap1.i_total); scan->next_rec = 0; scan->count = 0; scan->cur_blkno = 0; scan->cur_rec = NULL; scan->blocks_left = cinode->ci_inode->id1.bitmap1.i_total; return 0; } uint64_t ocfs2_get_max_inode_count(ocfs2_inode_scan *scan) { struct ocfs2_dinode *di = NULL; uint64_t count = 0; int i; if (!scan || !scan->num_inode_alloc) return 0; for (i = 0; i < scan->num_inode_alloc; i++) { if (scan->inode_alloc[i]) di = scan->inode_alloc[i]->ci_inode; if (!di) continue; count += ocfs2_clusters_to_blocks(scan->fs, di->i_clusters); di = NULL; } return count; } errcode_t ocfs2_get_next_inode(ocfs2_inode_scan *scan, uint64_t *blkno, char *inode) { errcode_t ret; if (!scan->blocks_left) { if (scan->blocks_in_buffer) abort(); if (get_next_inode_alloc(scan)) { *blkno = 0; return 0; } } if (!scan->blocks_in_buffer) { ret = fill_group_buffer(scan); if (ret) return ret; } /* the caller swap after verifying the inode's signature */ memcpy(inode, scan->cur_block, scan->fs->fs_blocksize); scan->cur_block += scan->fs->fs_blocksize; scan->blocks_in_buffer--; scan->blocks_left--; *blkno = scan->cur_blkno; scan->cur_blkno++; scan->count++; return 0; } errcode_t ocfs2_open_inode_scan(ocfs2_filesys *fs, ocfs2_inode_scan **ret_scan) { ocfs2_inode_scan *scan; uint64_t blkno; errcode_t ret; int i, slot_num; ret = ocfs2_malloc0(sizeof(struct _ocfs2_inode_scan), &scan); if (ret) return ret; scan->fs = fs; /* One inode alloc per slot, one global inode alloc */ scan->num_inode_alloc = OCFS2_RAW_SB(fs->fs_super)->s_max_slots + 1; ret = ocfs2_malloc0(sizeof(ocfs2_cached_inode *) * scan->num_inode_alloc, &scan->inode_alloc); if (ret) goto out_scan; /* * Ideally the buffer size should be one cpg. But finding that value * is not worth the effort. Instead we default to 4MB, which is a * typical value in most ocfs2 file systems. */ #define OPEN_SCAN_BUFFER_SIZE (4 * 1024 * 1024) scan->buffer_blocks = OPEN_SCAN_BUFFER_SIZE / fs->fs_blocksize; ret = ocfs2_malloc_blocks(fs->fs_io, scan->buffer_blocks, &scan->group_buffer); if (ret) goto out_inode_files; ret = ocfs2_lookup_system_inode(fs, GLOBAL_INODE_ALLOC_SYSTEM_INODE, 0, &blkno); if (ret) goto out_cleanup; ret = ocfs2_read_cached_inode(fs, blkno, &scan->inode_alloc[0]); if (ret) goto out_cleanup; for (i = 1; i < scan->num_inode_alloc; i++) { slot_num = i - 1; ret = ocfs2_lookup_system_inode(fs, INODE_ALLOC_SYSTEM_INODE, slot_num, &blkno); if (ret) goto out_cleanup; ret = ocfs2_read_cached_inode(fs, blkno, &scan->inode_alloc[i]); if (ret) goto out_cleanup; } /* * FIXME: Should this pre-read all the group descriptors like * the old code read all the extent maps? */ *ret_scan = scan; return 0; out_inode_files: ocfs2_free(&scan->inode_alloc); out_scan: ocfs2_free(&scan); out_cleanup: if (scan) ocfs2_close_inode_scan(scan); return ret; } void ocfs2_close_inode_scan(ocfs2_inode_scan *scan) { int i; if (!scan) return; for (i = 0; i < scan->num_inode_alloc; i++) { if (scan->inode_alloc[i]) { ocfs2_free_cached_inode(scan->fs, scan->inode_alloc[i]); } } ocfs2_free(&scan->group_buffer); ocfs2_free(&scan->cur_desc); ocfs2_free(&scan->inode_alloc); ocfs2_free(&scan); return; } #ifdef DEBUG_EXE #include #include static void print_usage(void) { fprintf(stderr, "Usage: debug_inode_scan \n"); } extern int opterr, optind; extern char *optarg; int main(int argc, char *argv[]) { errcode_t ret; int done; uint64_t blkno; char *filename, *buf; ocfs2_filesys *fs; struct ocfs2_dinode *di; ocfs2_inode_scan *scan; initialize_ocfs_error_table(); if (argc < 2) { fprintf(stderr, "Missing filename\n"); print_usage(); return 1; } filename = argv[1]; ret = ocfs2_open(filename, OCFS2_FLAG_RO|OCFS2_FLAG_BUFFERED, 0, 0, &fs); if (ret) { com_err(argv[0], ret, "while opening file \"%s\"", filename); goto out; } ret = ocfs2_malloc_block(fs->fs_io, &buf); if (ret) { com_err(argv[0], ret, "while allocating inode buffer"); goto out_close; } di = (struct ocfs2_dinode *)buf; ret = ocfs2_open_inode_scan(fs, &scan); if (ret) { com_err(argv[0], ret, "while opening inode scan"); goto out_free; } done = 0; while (!done) { ret = ocfs2_get_next_inode(scan, &blkno, buf); if (ret) { com_err(argv[0], ret, "while getting next inode"); goto out_close_scan; } if (blkno) { if (memcmp(di->i_signature, OCFS2_INODE_SIGNATURE, strlen(OCFS2_INODE_SIGNATURE))) continue; if (!(di->i_flags & OCFS2_VALID_FL)) continue; fprintf(stdout, "%snode %"PRIu64" with size %"PRIu64"\n", (di->i_flags & OCFS2_SYSTEM_FL) ? "System i" : "I", blkno, di->i_size); } else done = 1; } out_close_scan: ocfs2_close_inode_scan(scan); out_free: ocfs2_free(&buf); out_close: ret = ocfs2_close(fs); if (ret) { com_err(argv[0], ret, "while closing file \"%s\"", filename); } out: return 0; } #endif /* DEBUG_EXE */ ocfs2-tools-ocfs2-tools-1.8.6/libocfs2/ismounted.c000066400000000000000000000224421347147137200217430ustar00rootroot00000000000000/* * ismounted.c --- Check to see if the filesystem was mounted * * Copyright (C) 1995,1996,1997,1998,1999,2000 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Public * License. * %End-Header% */ /* Modified for OCFS2 by Manish Singh */ #define HAVE_UNISTD_H 1 #define HAVE_ERRNO_H 1 #define HAVE_LINUX_FD_H 1 #define HAVE_MNTENT_H 1 #include #if HAVE_UNISTD_H #include #endif #if HAVE_ERRNO_H #include #endif #include #ifdef HAVE_LINUX_FD_H #include #endif #ifdef HAVE_MNTENT_H #include #endif #ifdef HAVE_GETMNTINFO #include #include #include #endif /* HAVE_GETMNTINFO */ #include #include #include "ocfs2/ocfs2.h" #ifdef HAVE_MNTENT_H /* * Helper function which checks a file in /etc/mtab format to see if a * filesystem is mounted. Returns an error if the file doesn't exist * or can't be opened. */ static errcode_t check_mntent_file(const char *mtab_file, const char *file, int *mount_flags, char *mtpt, int mtlen) { struct mntent *mnt; struct stat st_buf; errcode_t retval = 0; dev_t file_dev=0, file_rdev=0; ino_t file_ino=0; FILE *f; int fd; *mount_flags = 0; if ((f = setmntent (mtab_file, "r")) == NULL) return errno; if (stat(file, &st_buf) == 0) { if (S_ISBLK(st_buf.st_mode)) { #ifndef __GNU__ /* The GNU hurd is broken with respect to stat devices */ file_rdev = st_buf.st_rdev; #endif /* __GNU__ */ } else { file_dev = st_buf.st_dev; file_ino = st_buf.st_ino; } } while ((mnt = getmntent (f)) != NULL) { if (strcmp(file, mnt->mnt_fsname) == 0) break; if (stat(mnt->mnt_fsname, &st_buf) == 0) { if (S_ISBLK(st_buf.st_mode)) { #ifndef __GNU__ if (file_rdev && (file_rdev == st_buf.st_rdev)) break; #endif /* __GNU__ */ } else { if (file_dev && ((file_dev == st_buf.st_dev) && (file_ino == st_buf.st_ino))) break; } } } if (mnt == 0) { #ifndef __GNU__ /* The GNU hurd is broken with respect to stat devices */ /* * Do an extra check to see if this is the root device. We * can't trust /etc/mtab, and /proc/mounts will only list * /dev/root for the root filesystem. Argh. Instead we * check if the given device has the same major/minor number * as the device that the root directory is on. */ if (file_rdev && stat("/", &st_buf) == 0) { if (st_buf.st_dev == file_rdev) { *mount_flags = OCFS2_MF_MOUNTED; if (mtpt) strncpy(mtpt, "/", mtlen); goto is_root; } } #endif /* __GNU__ */ goto errout; } #ifndef __GNU__ /* The GNU hurd is deficient; what else is new? */ /* Validate the entry in case /etc/mtab is out of date */ /* * We need to be paranoid, because some broken distributions * (read: Slackware) don't initialize /etc/mtab before checking * all of the non-root filesystems on the disk. */ if (stat(mnt->mnt_dir, &st_buf) < 0) { retval = errno; if (retval == ENOENT) { #ifdef DEBUG printf("Bogus entry in %s! (%s does not exist)\n", mtab_file, mnt->mnt_dir); #endif /* DEBUG */ retval = 0; } goto errout; } if (file_rdev && (st_buf.st_dev != file_rdev)) { #ifdef DEBUG printf("Bogus entry in %s! (%s not mounted on %s)\n", mtab_file, file, mnt->mnt_dir); #endif /* DEBUG */ goto errout; } #endif /* __GNU__ */ *mount_flags = OCFS2_MF_MOUNTED; #ifdef MNTOPT_RO /* Check to see if the ro option is set */ if (hasmntopt(mnt, MNTOPT_RO)) *mount_flags |= OCFS2_MF_READONLY; #endif if (mtpt) strncpy(mtpt, mnt->mnt_dir, mtlen); /* * Check to see if we're referring to the root filesystem. * If so, do a manual check to see if we can open /etc/mtab * read/write, since if the root is mounted read/only, the * contents of /etc/mtab may not be accurate. */ if (!strcmp(mnt->mnt_dir, "/")) { is_root: #define TEST_FILE "/.ismount-test-file" *mount_flags |= OCFS2_MF_ISROOT; fd = open(TEST_FILE, O_RDWR|O_CREAT, 0600); if (fd < 0) { if (errno == EROFS) *mount_flags |= OCFS2_MF_READONLY; } else close(fd); (void) unlink(TEST_FILE); } retval = 0; errout: endmntent (f); return retval; } static errcode_t check_mntent(const char *file, int *mount_flags, char *mtpt, int mtlen) { errcode_t retval; #ifdef DEBUG retval = check_mntent_file("/tmp/mtab", file, mount_flags, mtpt, mtlen); if (retval == 0) return 0; #endif /* DEBUG */ #ifdef __linux__ retval = check_mntent_file("/proc/mounts", file, mount_flags, mtpt, mtlen); if (retval == 0 && (*mount_flags != 0)) return 0; #endif /* __linux__ */ #if defined(MOUNTED) || defined(_PATH_MOUNTED) #ifndef MOUNTED #define MOUNTED _PATH_MOUNTED #endif /* MOUNTED */ retval = check_mntent_file(MOUNTED, file, mount_flags, mtpt, mtlen); return retval; #else *mount_flags = 0; return 0; #endif /* defined(MOUNTED) || defined(_PATH_MOUNTED) */ } #else #if defined(HAVE_GETMNTINFO) static errcode_t check_getmntinfo(const char *file, int *mount_flags, char *mtpt, int mtlen) { struct statfs *mp; int len, n; const char *s1; char *s2; n = getmntinfo(&mp, MNT_NOWAIT); if (n == 0) return errno; len = sizeof(_PATH_DEV) - 1; s1 = file; if (strncmp(_PATH_DEV, s1, len) == 0) s1 += len; *mount_flags = 0; while (--n >= 0) { s2 = mp->f_mntfromname; if (strncmp(_PATH_DEV, s2, len) == 0) { s2 += len - 1; *s2 = 'r'; } if (strcmp(s1, s2) == 0 || strcmp(s1, &s2[1]) == 0) { *mount_flags = OCFS2_MF_MOUNTED; break; } ++mp; } if (mtpt) strncpy(mtpt, mp->f_mntonname, mtlen); return 0; } #endif /* HAVE_GETMNTINFO */ #endif /* HAVE_MNTENT_H */ /* * Check to see if we're dealing with the swap device. */ static int is_swap_device(const char *file) { FILE *f; char buf[1024], *cp; dev_t file_dev; struct stat st_buf; int ret = 0; file_dev = 0; #ifndef __GNU__ /* The GNU hurd is broken with respect to stat devices */ if ((stat(file, &st_buf) == 0) && S_ISBLK(st_buf.st_mode)) file_dev = st_buf.st_rdev; #endif /* __GNU__ */ if (!(f = fopen("/proc/swaps", "r"))) return 0; /* Skip the first line */ if (!fgets(buf, sizeof(buf), f)) return 0; while (!feof(f)) { if (!fgets(buf, sizeof(buf), f)) break; if ((cp = strchr(buf, ' ')) != NULL) *cp = 0; if ((cp = strchr(buf, '\t')) != NULL) *cp = 0; if (strcmp(buf, file) == 0) { ret++; break; } #ifndef __GNU__ if (file_dev && (stat(buf, &st_buf) == 0) && S_ISBLK(st_buf.st_mode) && file_dev == st_buf.st_rdev) { ret++; break; } #endif /* __GNU__ */ } fclose(f); return ret; } /* * ocfs2_check_mount_point() fills determines if the device is * mounted or otherwise busy, and fills in mount_flags with one or * more of the following flags: OCFS2_MF_MOUNTED, OCFS2_MF_ISROOT, * OCFS2_MF_READONLY, OCFS2_MF_SWAP, and OCFS2_MF_BUSY. If mtpt is * non-NULL, the directory where the device is mounted is copied to * where mtpt is pointing, up to mtlen characters. */ #ifdef __TURBOC__ #pragma argsused #endif errcode_t ocfs2_check_mount_point(const char *device, int *mount_flags, char *mtpt, int mtlen) { struct stat st_buf; errcode_t retval = 0; int fd; if (is_swap_device(device)) { *mount_flags = OCFS2_MF_MOUNTED | OCFS2_MF_SWAP; strncpy(mtpt, "", mtlen); } else { #ifdef HAVE_MNTENT_H retval = check_mntent(device, mount_flags, mtpt, mtlen); #else #ifdef HAVE_GETMNTINFO retval = check_getmntinfo(device, mount_flags, mtpt, mtlen); #else #ifdef __GNUC__ #warning "Can't use getmntent or getmntinfo to check for mounted filesystems!" #endif *mount_flags = 0; #endif /* HAVE_GETMNTINFO */ #endif /* HAVE_MNTENT_H */ } if (retval) return retval; #ifdef __linux__ /* This only works on Linux 2.6+ systems */ if ((stat(device, &st_buf) != 0) || !S_ISBLK(st_buf.st_mode)) return 0; fd = open(device, O_RDONLY | O_EXCL); if (fd < 0) { if (errno == EBUSY) *mount_flags |= OCFS2_MF_BUSY; } else close(fd); return 0; #endif } /* * ocfs2_check_if_mounted() sets the mount_flags OCFS2_MF_MOUNTED, * OCFS2_MF_READONLY, and OCFS2_MF_ROOT * */ errcode_t ocfs2_check_if_mounted(const char *file, int *mount_flags) { return ocfs2_check_mount_point(file, mount_flags, NULL, 0); } #ifdef DEBUG_EXE int main(int argc, char **argv) { int retval, mount_flags; char mntpt[80]; if (argc < 2) { fprintf(stderr, "Usage: %s device\n", argv[0]); exit(1); } mntpt[0] = 0; retval = ocfs2_check_mount_point(argv[1], &mount_flags, mntpt, sizeof(mntpt)); if (retval) { com_err(argv[0], retval, "while calling ocfs2_check_if_mounted"); exit(1); } printf("Device %s reports flags %02x\n", argv[1], mount_flags); if (mount_flags & OCFS2_MF_BUSY) printf("\t%s is apparently in use.\n", argv[1]); if (mount_flags & OCFS2_MF_MOUNTED) printf("\t%s is mounted.\n", argv[1]); if (mount_flags & OCFS2_MF_SWAP) printf("\t%s is a swap device.\n", argv[1]); if (mount_flags & OCFS2_MF_READONLY) printf("\t%s is read-only.\n", argv[1]); if (mount_flags & OCFS2_MF_ISROOT) printf("\t%s is the root filesystem.\n", argv[1]); if (mntpt[0]) printf("\t%s is mounted on %s.\n", argv[1], mntpt); exit(0); } #endif /* DEBUG_EXE */ ocfs2-tools-ocfs2-tools-1.8.6/libocfs2/kernel-rbtree.c000066400000000000000000000216321347147137200224750ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * kernel-rbtree.c * * This is imported from the Linux kernel to give us a tested and * portable tree library. */ /* Red Black Trees (C) 1999 Andrea Arcangeli (C) 2002 David Woodhouse 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA linux/lib/rbtree.c */ #include "ocfs2/kernel-rbtree.h" static void __rb_rotate_left(struct rb_node *node, struct rb_root *root) { struct rb_node *right = node->rb_right; if ((node->rb_right = right->rb_left)) right->rb_left->rb_parent = node; right->rb_left = node; if ((right->rb_parent = node->rb_parent)) { if (node == node->rb_parent->rb_left) node->rb_parent->rb_left = right; else node->rb_parent->rb_right = right; } else root->rb_node = right; node->rb_parent = right; } static void __rb_rotate_right(struct rb_node *node, struct rb_root *root) { struct rb_node *left = node->rb_left; if ((node->rb_left = left->rb_right)) left->rb_right->rb_parent = node; left->rb_right = node; if ((left->rb_parent = node->rb_parent)) { if (node == node->rb_parent->rb_right) node->rb_parent->rb_right = left; else node->rb_parent->rb_left = left; } else root->rb_node = left; node->rb_parent = left; } void rb_insert_color(struct rb_node *node, struct rb_root *root) { struct rb_node *parent, *gparent; while ((parent = node->rb_parent) && parent->rb_color == RB_RED) { gparent = parent->rb_parent; if (!gparent) break; if (parent == gparent->rb_left) { { register struct rb_node *uncle = gparent->rb_right; if (uncle && uncle->rb_color == RB_RED) { uncle->rb_color = RB_BLACK; parent->rb_color = RB_BLACK; gparent->rb_color = RB_RED; node = gparent; continue; } } if (parent->rb_right == node) { register struct rb_node *tmp; __rb_rotate_left(parent, root); tmp = parent; parent = node; node = tmp; } parent->rb_color = RB_BLACK; gparent->rb_color = RB_RED; __rb_rotate_right(gparent, root); } else { { register struct rb_node *uncle = gparent->rb_left; if (uncle && uncle->rb_color == RB_RED) { uncle->rb_color = RB_BLACK; parent->rb_color = RB_BLACK; gparent->rb_color = RB_RED; node = gparent; continue; } } if (parent->rb_left == node) { register struct rb_node *tmp; __rb_rotate_right(parent, root); tmp = parent; parent = node; node = tmp; } parent->rb_color = RB_BLACK; gparent->rb_color = RB_RED; __rb_rotate_left(gparent, root); } } root->rb_node->rb_color = RB_BLACK; } static void __rb_erase_color(struct rb_node *node, struct rb_node *parent, struct rb_root *root) { struct rb_node *other; while ((!node || node->rb_color == RB_BLACK) && node != root->rb_node) { if (parent->rb_left == node) { other = parent->rb_right; if (other->rb_color == RB_RED) { other->rb_color = RB_BLACK; parent->rb_color = RB_RED; __rb_rotate_left(parent, root); other = parent->rb_right; } if ((!other->rb_left || other->rb_left->rb_color == RB_BLACK) && (!other->rb_right || other->rb_right->rb_color == RB_BLACK)) { other->rb_color = RB_RED; node = parent; parent = node->rb_parent; } else { if (!other->rb_right || other->rb_right->rb_color == RB_BLACK) { register struct rb_node *o_left; if ((o_left = other->rb_left)) o_left->rb_color = RB_BLACK; other->rb_color = RB_RED; __rb_rotate_right(other, root); other = parent->rb_right; } other->rb_color = parent->rb_color; parent->rb_color = RB_BLACK; if (other->rb_right) other->rb_right->rb_color = RB_BLACK; __rb_rotate_left(parent, root); node = root->rb_node; break; } } else { other = parent->rb_left; if (other->rb_color == RB_RED) { other->rb_color = RB_BLACK; parent->rb_color = RB_RED; __rb_rotate_right(parent, root); other = parent->rb_left; } if ((!other->rb_left || other->rb_left->rb_color == RB_BLACK) && (!other->rb_right || other->rb_right->rb_color == RB_BLACK)) { other->rb_color = RB_RED; node = parent; parent = node->rb_parent; } else { if (!other->rb_left || other->rb_left->rb_color == RB_BLACK) { register struct rb_node *o_right; if ((o_right = other->rb_right)) o_right->rb_color = RB_BLACK; other->rb_color = RB_RED; __rb_rotate_left(other, root); other = parent->rb_left; } other->rb_color = parent->rb_color; parent->rb_color = RB_BLACK; if (other->rb_left) other->rb_left->rb_color = RB_BLACK; __rb_rotate_right(parent, root); node = root->rb_node; break; } } } if (node) node->rb_color = RB_BLACK; } void rb_erase(struct rb_node *node, struct rb_root *root) { struct rb_node *child, *parent; int color; if (!node->rb_left) child = node->rb_right; else if (!node->rb_right) child = node->rb_left; else { struct rb_node *old = node, *left; node = node->rb_right; while ((left = node->rb_left) != NULL) node = left; child = node->rb_right; parent = node->rb_parent; color = node->rb_color; if (child) child->rb_parent = parent; if (parent) { if (parent->rb_left == node) parent->rb_left = child; else parent->rb_right = child; } else root->rb_node = child; if (node->rb_parent == old) parent = node; node->rb_parent = old->rb_parent; node->rb_color = old->rb_color; node->rb_right = old->rb_right; node->rb_left = old->rb_left; if (old->rb_parent) { if (old->rb_parent->rb_left == old) old->rb_parent->rb_left = node; else old->rb_parent->rb_right = node; } else root->rb_node = node; old->rb_left->rb_parent = node; if (old->rb_right) old->rb_right->rb_parent = node; goto color; } parent = node->rb_parent; color = node->rb_color; if (child) child->rb_parent = parent; if (parent) { if (parent->rb_left == node) parent->rb_left = child; else parent->rb_right = child; } else root->rb_node = child; color: if (color == RB_BLACK) __rb_erase_color(child, parent, root); } /* * This function returns the first node (in sort order) of the tree. */ struct rb_node *rb_first(struct rb_root *root) { struct rb_node *n; n = root->rb_node; if (!n) return NULL; while (n->rb_left) n = n->rb_left; return n; } struct rb_node *rb_last(struct rb_root *root) { struct rb_node *n; n = root->rb_node; if (!n) return NULL; while (n->rb_right) n = n->rb_right; return n; } struct rb_node *rb_next(struct rb_node *node) { /* If we have a right-hand child, go down and then left as far as we can. */ if (node->rb_right) { node = node->rb_right; while (node->rb_left) node=node->rb_left; return node; } /* No right-hand children. Everything down and left is smaller than us, so any 'next' node must be in the general direction of our parent. Go up the tree; any time the ancestor is a right-hand child of its parent, keep going up. First time it's a left-hand child of its parent, said parent is our 'next' node. */ while (node->rb_parent && node == node->rb_parent->rb_right) node = node->rb_parent; return node->rb_parent; } struct rb_node *rb_prev(struct rb_node *node) { /* If we have a left-hand child, go down and then right as far as we can. */ if (node->rb_left) { node = node->rb_left; while (node->rb_right) node=node->rb_right; return node; } /* No left-hand children. Go up till we find an ancestor which is a right-hand child of its parent */ while (node->rb_parent && node == node->rb_parent->rb_left) node = node->rb_parent; return node->rb_parent; } void rb_replace_node(struct rb_node *victim, struct rb_node *new, struct rb_root *root) { struct rb_node *parent = victim->rb_parent; /* Set the surrounding nodes to point to the replacement */ if (parent) { if (victim == parent->rb_left) parent->rb_left = new; else parent->rb_right = new; } else { root->rb_node = new; } if (victim->rb_left) victim->rb_left->rb_parent = new; if (victim->rb_right) victim->rb_right->rb_parent = new; /* Copy the pointers/colour from the victim to the replacement */ *new = *victim; } ocfs2-tools-ocfs2-tools-1.8.6/libocfs2/link.c000066400000000000000000000116621347147137200206730ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * link.c * * Create links in OCFS2 directories. For the OCFS2 userspace library. * * Copyright (C) 2004 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License, version 2, as published by the Free Software Foundation. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * * This code is a port of e2fsprogs/lib/ext2fs/link.c * Copyright (C) 1993, 1994 Theodore Ts'o. */ #define _XOPEN_SOURCE 600 /* Triggers magic in features.h */ #define _LARGEFILE64_SOURCE #include #include "ocfs2/ocfs2.h" struct link_struct { const char *name; int namelen; uint64_t inode; int flags; int done; int blockend; /* What to consider the end of the block. This handles the directory trailer if it exists */ int blkno; struct ocfs2_dinode *sb; }; static int link_proc(struct ocfs2_dir_entry *dirent, uint64_t blocknr, int offset, int blocksize, char *buf, void *priv_data) { struct link_struct *ls = (struct link_struct *) priv_data; struct ocfs2_dir_entry *next; int rec_len, min_rec_len; int ret = 0; rec_len = OCFS2_DIR_REC_LEN(ls->namelen); /* * See if the following directory entry (if any) is unused; * if so, absorb it into this one. */ next = (struct ocfs2_dir_entry *) (buf + offset + dirent->rec_len); if ((offset + dirent->rec_len < ls->blockend - 8) && (next->inode == 0) && (offset + dirent->rec_len + next->rec_len <= ls->blockend)) { dirent->rec_len += next->rec_len; ret = OCFS2_DIRENT_CHANGED; } /* * If the directory entry is used, see if we can split the * directory entry to make room for the new name. If so, * truncate it and return. */ if (dirent->inode) { min_rec_len = OCFS2_DIR_REC_LEN(dirent->name_len & 0xFF); if (dirent->rec_len < (min_rec_len + rec_len)) return ret; rec_len = dirent->rec_len - min_rec_len; dirent->rec_len = min_rec_len; next = (struct ocfs2_dir_entry *) (buf + offset + dirent->rec_len); next->inode = 0; next->name_len = 0; next->rec_len = rec_len; return OCFS2_DIRENT_CHANGED; } /* * If we get this far, then the directory entry is not used. * See if we can fit the request entry in. If so, do it. */ if (dirent->rec_len < rec_len) return ret; dirent->inode = ls->inode; dirent->name_len = ls->namelen; strncpy(dirent->name, ls->name, ls->namelen); dirent->file_type = ls->flags; ls->blkno = blocknr; ls->done++; return OCFS2_DIRENT_ABORT|OCFS2_DIRENT_CHANGED; } /* * Note: the low 3 bits of the flags field are used as the directory * entry filetype. */ errcode_t ocfs2_link(ocfs2_filesys *fs, uint64_t dir, const char *name, uint64_t ino, int flags) { errcode_t retval; struct link_struct ls; char *buf; struct ocfs2_dinode *di; if (!(fs->fs_flags & OCFS2_FLAG_RW)) return OCFS2_ET_RO_FILESYS; if ((ino < OCFS2_SUPER_BLOCK_BLKNO) || (ino > fs->fs_blocks)) return OCFS2_ET_INVALID_ARGUMENT; retval = ocfs2_malloc_block(fs->fs_io, &buf); if (retval) return retval; retval = ocfs2_read_inode(fs, dir, buf); if (retval) goto out_free; di = (struct ocfs2_dinode *)buf; ls.name = name; ls.namelen = name ? strlen(name) : 0; ls.inode = ino; ls.flags = flags; ls.done = 0; ls.sb = fs->fs_super; if (ocfs2_dir_has_trailer(fs, di)) ls.blockend = ocfs2_dir_trailer_blk_off(fs); else ls.blockend = fs->fs_blocksize; retval = ocfs2_dir_iterate(fs, dir, OCFS2_DIRENT_FLAG_INCLUDE_EMPTY, NULL, link_proc, &ls); if (retval) goto out_free; if (!ls.done) { retval = ocfs2_expand_dir(fs, dir); if (retval) goto out_free; /* Gotta refresh */ retval = ocfs2_read_inode(fs, dir, buf); if (retval) goto out_free; if (ocfs2_dir_has_trailer(fs, di)) ls.blockend = ocfs2_dir_trailer_blk_off(fs); else ls.blockend = fs->fs_blocksize; retval = ocfs2_dir_iterate(fs, dir, OCFS2_DIRENT_FLAG_INCLUDE_EMPTY, NULL, link_proc, &ls); if (!retval && !ls.done) retval = OCFS2_ET_INTERNAL_FAILURE; } if (ls.done) { if (ocfs2_supports_indexed_dirs(OCFS2_RAW_SB(fs->fs_super)) && (di->i_dyn_features & OCFS2_INDEXED_DIR_FL)) retval = ocfs2_dx_dir_insert_entry(fs, dir, ls.name, ls.inode, ls.blkno); } out_free: ocfs2_free(&buf); return retval; } ocfs2-tools-ocfs2-tools-1.8.6/libocfs2/lockid.c000066400000000000000000000072451347147137200212050ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * lockid.c * * Encode and decode lockres name * * Copyright (C) 2004, 2008 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License, version 2, as published by the Free Software Foundation. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. */ #include "ocfs2/byteorder.h" #include "ocfs2/ocfs2.h" #include #include enum ocfs2_lock_type ocfs2_get_lock_type(char c) { switch (c) { case 'M': return OCFS2_LOCK_TYPE_META; case 'D': return OCFS2_LOCK_TYPE_DATA; case 'S': return OCFS2_LOCK_TYPE_SUPER; case 'R': return OCFS2_LOCK_TYPE_RENAME; case 'W': return OCFS2_LOCK_TYPE_RW; case 'N': return OCFS2_LOCK_TYPE_DENTRY; case 'O': return OCFS2_LOCK_TYPE_OPEN; case 'F': return OCFS2_LOCK_TYPE_FLOCK; default: return OCFS2_NUM_LOCK_TYPES; } } /* * This function encodes the lockname just like the filesystem. Meaning * the dentry lock gets encoded in its binary form. */ errcode_t ocfs2_encode_lockres(enum ocfs2_lock_type type, uint64_t blkno, uint32_t generation, uint64_t parent, char *lockres) { uint64_t b; if (type >= OCFS2_NUM_LOCK_TYPES) return OCFS2_ET_INVALID_LOCKRES; if (type != OCFS2_LOCK_TYPE_DENTRY) { snprintf(lockres, OCFS2_LOCK_ID_MAX_LEN, "%c%s%016"PRIx64"%08x", ocfs2_lock_type_char(type), OCFS2_LOCK_ID_PAD, blkno, generation); } else { snprintf(lockres, OCFS2_DENTRY_LOCK_INO_START, "%c%016llx", ocfs2_lock_type_char(OCFS2_LOCK_TYPE_DENTRY), (long long)parent); b = bswap_64(blkno); memcpy(&lockres[OCFS2_DENTRY_LOCK_INO_START], &b, sizeof(b)); } return 0; } errcode_t ocfs2_decode_lockres(char *lockres, enum ocfs2_lock_type *type, uint64_t *blkno, uint32_t *generation, uint64_t *parent) { int i = 0; enum ocfs2_lock_type t; char *l = lockres; uint64_t b = 0; uint64_t p = 0; uint32_t g = 0; if ((t = ocfs2_get_lock_type(*l)) >= OCFS2_NUM_LOCK_TYPES) return OCFS2_ET_INVALID_LOCKRES; if (t != OCFS2_LOCK_TYPE_DENTRY) { i = sscanf(l + 1, OCFS2_LOCK_ID_PAD"%016llx%08x", (unsigned long long *)&b, &g); if (i != 2) return OCFS2_ET_INVALID_LOCKRES; } else { i = sscanf(l + 1, "%016llx", (unsigned long long *)&p); if (i != 1) return OCFS2_ET_INVALID_LOCKRES; b = strtoull(&l[OCFS2_DENTRY_LOCK_INO_START], NULL, 16); } if (type) *type = t; if (blkno) *blkno = b; if (generation) *generation = g; if (parent) *parent = p; return 0; } /* * This function is useful when printing the dentry lock. It converts the * dentry lockname into a string using the same scheme as used in dlmglue. */ errcode_t ocfs2_printable_lockres(char *lockres, char *name, int len) { uint64_t b; memset(name, 0, len); if (ocfs2_get_lock_type(*lockres) >= OCFS2_NUM_LOCK_TYPES) return OCFS2_ET_INVALID_LOCKRES; if (ocfs2_get_lock_type(*lockres) == OCFS2_LOCK_TYPE_DENTRY) { memcpy((uint64_t *)&b, (char *)&lockres[OCFS2_DENTRY_LOCK_INO_START], sizeof(uint64_t)); snprintf(name, len, "%.*s%08x", OCFS2_DENTRY_LOCK_INO_START - 1, lockres, (unsigned int)bswap_64(b)); } else snprintf(name, len, "%s", lockres); return 0; } ocfs2-tools-ocfs2-tools-1.8.6/libocfs2/lookup.c000066400000000000000000000135701347147137200212470ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * lookup.c * * Directory lookup routines for the OCFS2 userspace library. * * Copyright (C) 2004 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License, version 2, as published by the Free Software Foundation. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * * This code is a port of e2fsprogs/lib/ext2fs/lookup.c * Copyright (C) 1993, 1994, 1994, 1995 Theodore Ts'o. */ #define _XOPEN_SOURCE 600 /* Triggers magic in features.h */ #define _LARGEFILE64_SOURCE #include #include #include #include "ocfs2/ocfs2.h" struct lookup_struct { const char *name; int len; uint64_t *inode; int found; }; #ifdef __TURBOC__ #pragma argsused #endif static int lookup_proc(struct ocfs2_dir_entry *dirent, uint64_t blocknr, int offset, int blocksize, char *buf, void *priv_data) { struct lookup_struct *ls = (struct lookup_struct *) priv_data; if (ls->len != (dirent->name_len & 0xFF)) return 0; if (strncmp(ls->name, dirent->name, (dirent->name_len & 0xFF))) return 0; *ls->inode = dirent->inode; ls->found++; return OCFS2_DIRENT_ABORT; } static errcode_t ocfs2_find_entry_dx(ocfs2_filesys *fs, struct ocfs2_dinode *di, char *buf, struct lookup_struct *ls) { char *dx_root_buf = NULL; struct ocfs2_dx_root_block *dx_root; struct ocfs2_dir_lookup_result lookup; errcode_t ret; memset(&lookup, 0, sizeof(struct ocfs2_dir_lookup_result)); ret = ocfs2_malloc_block(fs->fs_io, &dx_root_buf); if (ret) goto out; ret = ocfs2_read_dx_root(fs, di->i_dx_root, dx_root_buf); if (ret) goto out; dx_root = (struct ocfs2_dx_root_block *)dx_root_buf; ocfs2_dx_dir_name_hash(fs, ls->name, ls->len, &lookup.dl_hinfo); ret = ocfs2_dx_dir_search(fs, ls->name, ls->len, dx_root, &lookup); if (ret) goto out; *ls->inode = lookup.dl_entry->inode; ls->found++; ret = 0; out: release_lookup_res(&lookup); if (dx_root_buf) ocfs2_free(&dx_root_buf); return ret; } errcode_t ocfs2_lookup(ocfs2_filesys *fs, uint64_t dir, const char *name, int namelen, char *buf, uint64_t *inode) { errcode_t ret; struct lookup_struct ls; char *di_buf = NULL; struct ocfs2_dinode *di; ls.name = name; ls.len = namelen; ls.inode = inode; ls.found = 0; ret = ocfs2_malloc_block(fs->fs_io, &di_buf); if (ret) goto out; ret = ocfs2_read_inode(fs, dir, di_buf); if (ret) goto out; di = (struct ocfs2_dinode *)di_buf; if (ocfs2_supports_indexed_dirs(OCFS2_RAW_SB(fs->fs_super)) && ocfs2_dir_indexed(di)) { ret = ocfs2_find_entry_dx(fs, di, buf, &ls); } else { ret = ocfs2_dir_iterate(fs, dir, 0, buf, lookup_proc, &ls); } if (ret) goto out; ret = (ls.found) ? 0 : OCFS2_ET_FILE_NOT_FOUND; out: if(di_buf) ocfs2_free(&di_buf); return ret; } #ifdef DEBUG_EXE #include #include static uint64_t read_number(const char *num) { uint64_t val; char *ptr; val = strtoull(num, &ptr, 0); if (!ptr || *ptr) return 0; return val; } static void print_usage(void) { fprintf(stderr, "Usage: lookup [-i ] \n"); } extern int opterr, optind; extern char *optarg; int main(int argc, char *argv[]) { errcode_t ret; uint64_t blkno, result_blkno; int c, indent; char *filename, *lookup_path, *buf; char *p; char lookup_name[256]; ocfs2_filesys *fs; blkno = 0; initialize_ocfs_error_table(); while ((c = getopt(argc, argv, "i:")) != EOF) { switch (c) { case 'i': blkno = read_number(optarg); if (blkno <= OCFS2_SUPER_BLOCK_BLKNO) { fprintf(stderr, "Invalid inode block: %s\n", optarg); print_usage(); return 1; } break; default: print_usage(); return 1; break; } } if (optind >= argc) { fprintf(stderr, "Missing filename\n"); print_usage(); return 1; } filename = argv[optind]; optind++; if (optind >= argc) { fprintf(stdout, "Missing path to lookup\n"); print_usage(); return 1; } lookup_path = argv[optind]; ret = ocfs2_open(filename, OCFS2_FLAG_RO, 0, 0, &fs); if (ret) { com_err(argv[0], ret, "while opening file \"%s\"", filename); goto out; } ret = ocfs2_malloc_block(fs->fs_io, &buf); if (ret) { com_err(argv[0], ret, "while allocating inode buffer"); goto out_close; } if (!blkno) blkno = OCFS2_RAW_SB(fs->fs_super)->s_root_blkno; for (p = lookup_path; *p == '/'; p++); lookup_path = p; fprintf(stdout, "/ (%"PRIu64")\n", blkno); indent = 0; for (p = lookup_path; ; p++) { if (*p && *p != '/') continue; memcpy(lookup_name, lookup_path, p - lookup_path); lookup_name[p - lookup_path] = '\0'; ret = ocfs2_lookup(fs, blkno, lookup_name, strlen(lookup_name), NULL, &result_blkno); if (ret) { com_err(argv[0], ret, "while looking up \"%s\" in inode %"PRIu64" on" " \"%s\"\n", lookup_name, blkno, filename); goto out_free; } indent += 4; for (c = 0; c < indent; c++) fprintf(stdout, " "); fprintf(stdout, "%s (%"PRIu64")\n", lookup_name, result_blkno); blkno = result_blkno; for (; *p == '/'; p++); lookup_path = p; if (!*p) break; } out_free: ocfs2_free(&buf); out_close: ret = ocfs2_close(fs); if (ret) { com_err(argv[0], ret, "while closing file \"%s\"", filename); } out: return 0; } #endif /* DEBUG_EXE */ ocfs2-tools-ocfs2-tools-1.8.6/libocfs2/memory.c000066400000000000000000000056341347147137200212500ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * memory.c * * Memory routines for the OCFS2 userspace library. * * Copyright (C) 2004 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License, version 2, as published by the Free Software Foundation. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * * Portions of this code from e2fsprogs/lib/ext2fs/ext2fs.h * Copyright (C) 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, * 2002 by Theodore Ts'o. */ #define _XOPEN_SOURCE 600 /* Triggers XOPEN2K in features.h */ #define _LARGEFILE64_SOURCE #include #include #include #include "ocfs2/ocfs2.h" errcode_t ocfs2_malloc(unsigned long size, void *ptr) { void **pp = (void **)ptr; *pp = malloc(size); if (!*pp) return OCFS2_ET_NO_MEMORY; return 0; } errcode_t ocfs2_malloc0(unsigned long size, void *ptr) { errcode_t ret; void **pp = (void **)ptr; ret = ocfs2_malloc(size, ptr); if (ret) return ret; memset(*pp, 0, size); return 0; } errcode_t ocfs2_free(void *ptr) { void **pp = (void **)ptr; free(*pp); *pp = NULL; return 0; } errcode_t ocfs2_realloc(unsigned long size, void *ptr) { void *p; void **pp = (void **)ptr; p = realloc(*pp, size); if (!p) return OCFS2_ET_NO_MEMORY; *pp = p; return 0; } errcode_t ocfs2_realloc0(unsigned long size, void *ptr, unsigned long old_size) { errcode_t ret; char *p; void **pp = (void **)ptr; ret = ocfs2_realloc(size, ptr); if (ret) return ret; if (size > old_size) { p = (char *)(*pp); memset(p + old_size, 0, size - old_size); } return 0; } errcode_t ocfs2_malloc_blocks(io_channel *channel, int num_blocks, void *ptr) { errcode_t ret; int blksize; size_t bytes; void **pp = (void **)ptr; void *tmp; blksize = io_get_blksize(channel); if (((unsigned long long)num_blocks * blksize) > SIZE_MAX) return OCFS2_ET_NO_MEMORY; bytes = (unsigned long long)num_blocks * blksize; /* * Older glibcs abort when they can't memalign() something. * Ugh! Check with malloc() first. */ tmp = malloc(bytes); if (!tmp) return OCFS2_ET_NO_MEMORY; free(tmp); ret = posix_memalign(pp, blksize, bytes); if (!ret) return 0; if (errno == ENOMEM) return OCFS2_ET_NO_MEMORY; /* blksize better be valid */ abort(); } errcode_t ocfs2_malloc_block(io_channel *channel, void *ptr) { return ocfs2_malloc_blocks(channel, 1, ptr); } ocfs2-tools-ocfs2-tools-1.8.6/libocfs2/mkjournal.c000066400000000000000000000306071347147137200217400ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * mkjournal.c * * Journal creation for the OCFS2 userspace library. * * Copyright (C) 2004 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License, version 2, as published by the Free Software Foundation. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * * Portions of the code from e2fsprogs/lib/ext2fs/mkjournal.c * Copyright (C) 2000 Theodore Ts'o. */ #define _XOPEN_SOURCE 600 /* Triggers XOPEN2K in features.h */ #define _LARGEFILE64_SOURCE #include #include #include "ocfs2/byteorder.h" #include "ocfs2/ocfs2.h" size_t ocfs2_journal_tag_bytes(journal_superblock_t *jsb) { if (JBD2_HAS_INCOMPAT_FEATURE(jsb, JBD2_FEATURE_INCOMPAT_64BIT)) return JBD2_TAG_SIZE64; else return JBD2_TAG_SIZE32; } uint64_t ocfs2_journal_tag_block(journal_block_tag_t *tag, size_t tag_bytes) { uint64_t blockno = be32_to_cpu(tag->t_blocknr); if (tag_bytes > JBD2_TAG_SIZE32) blockno |= (uint64_t)be32_to_cpu(tag->t_blocknr_high) << 32; return blockno; } /* Returns true if we support these journal features */ static int ocfs2_journal_check_available_features(journal_superblock_t *jsb, ocfs2_fs_options *features) { /* ocfs2 has never supported a superblock other than _V2 */ if (jsb->s_header.h_blocktype != JBD2_SUPERBLOCK_V2) return 0; if ((features->opt_compat & JBD2_KNOWN_COMPAT_FEATURES) != features->opt_compat) return 0; /* * ocfs2 will never use jbd2 checksums. If a jbd2 checksum fails, * the transaction is not replayed. This means that some parts of * the transaction may have checkpointed while other parts have not. * This inconsistent state requires fsck to repair. ocfs2, being * clustered, can't tolerate that situation. Instead, ocfs2 * prefers to replay the journal. This allows the cluster to * remain running. Using the METAECC feature allows ocfs2 to * detect and isolate any corrupted blocks. */ if (features->opt_compat & JBD2_FEATURE_COMPAT_CHECKSUM) return 0; if ((features->opt_ro_compat & JBD2_KNOWN_ROCOMPAT_FEATURES) != features->opt_ro_compat) return 0; if ((features->opt_incompat & JBD2_KNOWN_INCOMPAT_FEATURES) != features->opt_incompat) return 0; return 1; } errcode_t ocfs2_journal_set_features(journal_superblock_t *jsb, ocfs2_fs_options *features) { if (!ocfs2_journal_check_available_features(jsb, features)) return OCFS2_ET_UNSUPP_FEATURE; jsb->s_feature_compat |= features->opt_compat; jsb->s_feature_ro_compat |= features->opt_ro_compat; jsb->s_feature_incompat |= features->opt_incompat; return 0; } errcode_t ocfs2_journal_clear_features(journal_superblock_t *jsb, ocfs2_fs_options *features) { if (!ocfs2_journal_check_available_features(jsb, features)) return OCFS2_ET_UNSUPP_FEATURE; jsb->s_feature_compat &= ~(features->opt_compat); jsb->s_feature_ro_compat &= ~(features->opt_ro_compat); jsb->s_feature_incompat &= ~(features->opt_incompat); return 0; } void ocfs2_swap_journal_superblock(journal_superblock_t *jsb) { if (cpu_is_big_endian) return; jsb->s_header.h_magic = bswap_32(jsb->s_header.h_magic); jsb->s_header.h_blocktype = bswap_32(jsb->s_header.h_blocktype); jsb->s_header.h_sequence = bswap_32(jsb->s_header.h_sequence); jsb->s_blocksize = bswap_32(jsb->s_blocksize); jsb->s_maxlen = bswap_32(jsb->s_maxlen); jsb->s_first = bswap_32(jsb->s_first); jsb->s_sequence = bswap_32(jsb->s_sequence); jsb->s_start = bswap_32(jsb->s_start); jsb->s_errno = bswap_32(jsb->s_errno); jsb->s_feature_compat = bswap_32(jsb->s_feature_compat); jsb->s_feature_incompat = bswap_32(jsb->s_feature_incompat); jsb->s_feature_ro_compat = bswap_32(jsb->s_feature_ro_compat); jsb->s_nr_users = bswap_32(jsb->s_nr_users); jsb->s_dynsuper = bswap_32(jsb->s_dynsuper); jsb->s_max_transaction = bswap_32(jsb->s_max_transaction); jsb->s_max_trans_data = bswap_32(jsb->s_max_trans_data); } errcode_t ocfs2_init_journal_superblock(ocfs2_filesys *fs, char *buf, int buflen, uint32_t jrnl_size_in_blks) { journal_superblock_t *jsb = (journal_superblock_t *)buf; if (buflen < fs->fs_blocksize) return OCFS2_ET_INTERNAL_FAILURE; if (jrnl_size_in_blks < 1024) return OCFS2_ET_JOURNAL_TOO_SMALL; memset(buf, 0, buflen); jsb->s_header.h_magic = JBD2_MAGIC_NUMBER; jsb->s_header.h_blocktype = JBD2_SUPERBLOCK_V2; jsb->s_blocksize = fs->fs_blocksize; jsb->s_maxlen = jrnl_size_in_blks; if (fs->fs_blocksize == 512) jsb->s_first = 2; else jsb->s_first = 1; jsb->s_start = 1; jsb->s_sequence = 1; jsb->s_errno = 0; jsb->s_nr_users = 1; memcpy(jsb->s_uuid, OCFS2_RAW_SB(fs->fs_super)->s_uuid, sizeof(jsb->s_uuid)); return 0; } /* * This function automatically sets up the journal superblock and * returns it as an allocated block. */ static errcode_t ocfs2_create_journal_superblock(ocfs2_filesys *fs, uint32_t size, ocfs2_fs_options *features, char **ret_jsb) { errcode_t retval; char *buf = NULL; journal_superblock_t *jsb; *ret_jsb = NULL; if ((retval = ocfs2_malloc_block(fs->fs_io, &buf))) goto bail; retval = ocfs2_init_journal_superblock(fs, buf, fs->fs_blocksize, size); if (retval) goto bail; jsb = (journal_superblock_t *)buf; retval = ocfs2_journal_set_features(jsb, features); if (retval) goto bail; #if 0 /* Someday */ /* * If we're creating an external journal device, we need to * adjust these fields. */ if (fs->super->s_feature_incompat & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV) { jsb->s_nr_users = 0; if (fs->blocksize == 1024) jsb->s_first = 3; else jsb->s_first = 2; } #endif *ret_jsb = buf; bail: if (retval && buf) ocfs2_free(&buf); return retval; } errcode_t ocfs2_read_journal_superblock(ocfs2_filesys *fs, uint64_t blkno, char *jsb_buf) { errcode_t ret; char *blk; journal_superblock_t *disk, *jsb; if ((blkno < OCFS2_SUPER_BLOCK_BLKNO) || (blkno > fs->fs_blocks)) return OCFS2_ET_BAD_BLKNO; ret = ocfs2_malloc_block(fs->fs_io, &blk); if (ret) return ret; ret = ocfs2_read_blocks(fs, blkno, 1, blk); if (ret) goto out; disk = (journal_superblock_t *)blk; jsb = (journal_superblock_t *)jsb_buf; if (disk->s_header.h_magic != htonl(JBD2_MAGIC_NUMBER)) { ret = OCFS2_ET_BAD_JOURNAL_SUPERBLOCK_MAGIC; goto out; } memcpy(jsb_buf, blk, fs->fs_blocksize); ocfs2_swap_journal_superblock(jsb); if (JBD2_HAS_INCOMPAT_FEATURE(jsb, ~JBD2_KNOWN_INCOMPAT_FEATURES)) { ret = OCFS2_ET_UNSUPP_FEATURE; goto out; } if (JBD2_HAS_RO_COMPAT_FEATURE(jsb, ~JBD2_KNOWN_ROCOMPAT_FEATURES)) { ret = OCFS2_ET_RO_UNSUPP_FEATURE; goto out; } ret = 0; out: ocfs2_free(&blk); return ret; } errcode_t ocfs2_write_journal_superblock(ocfs2_filesys *fs, uint64_t blkno, char *jsb_buf) { errcode_t ret; char *blk; journal_superblock_t *disk; if (!(fs->fs_flags & OCFS2_FLAG_RW)) return OCFS2_ET_RO_FILESYS; if ((blkno < OCFS2_SUPER_BLOCK_BLKNO) || (blkno > fs->fs_blocks)) return OCFS2_ET_BAD_BLKNO; ret = ocfs2_malloc_block(fs->fs_io, &blk); if (ret) return ret; disk = (journal_superblock_t *)blk; memcpy(blk, jsb_buf, fs->fs_blocksize); ocfs2_swap_journal_superblock(disk); ret = io_write_block(fs->fs_io, blkno, 1, blk); if (ret) goto out; fs->fs_flags |= OCFS2_FLAG_CHANGED; ret = 0; out: ocfs2_free(&blk); return ret; } static errcode_t ocfs2_format_journal(ocfs2_filesys *fs, ocfs2_cached_inode *ci, ocfs2_fs_options *features) { errcode_t ret = 0; char *buf = NULL, *jsb_buf = NULL; int bs_bits = OCFS2_RAW_SB(fs->fs_super)->s_blocksize_bits; uint64_t offset = 0; uint32_t wrote, count, jrnl_blocks; #define BUFLEN 1048576 ret = ocfs2_malloc_blocks(fs->fs_io, (BUFLEN >> bs_bits), &buf); if (ret) goto out; memset(buf, 0, BUFLEN); io_set_nocache(fs->fs_io, true); count = (uint32_t) ci->ci_inode->i_size; while (count) { ret = ocfs2_file_write(ci, buf, ocfs2_min((uint32_t) BUFLEN, count), offset, &wrote); if (ret) break; offset += wrote; count -= wrote; } io_set_nocache(fs->fs_io, false); if (ret) goto out; jrnl_blocks = ocfs2_clusters_to_blocks(fs, ci->ci_inode->i_clusters); ret = ocfs2_create_journal_superblock(fs, jrnl_blocks, features, &jsb_buf); if (ret) goto out; /* re-use offset here for 1st journal block. */ ret = ocfs2_extent_map_get_blocks(ci, 0, 1, &offset, NULL, NULL); if (ret) goto out; ret = ocfs2_write_journal_superblock(fs, offset, jsb_buf); out: if (buf) ocfs2_free(&buf); if (jsb_buf) ocfs2_free(&jsb_buf); return ret; } errcode_t ocfs2_make_journal(ocfs2_filesys *fs, uint64_t blkno, uint32_t clusters, ocfs2_fs_options *features) { errcode_t ret = 0; ocfs2_cached_inode *ci = NULL; struct ocfs2_dinode *di; if ((clusters << OCFS2_RAW_SB(fs->fs_super)->s_clustersize_bits) < OCFS2_MIN_JOURNAL_SIZE) { ret = OCFS2_ET_JOURNAL_TOO_SMALL; goto out; } ret = ocfs2_read_cached_inode(fs, blkno, &ci); if (ret) goto out; /* verify it is a journal file */ if (!(ci->ci_inode->i_flags & OCFS2_VALID_FL) || !(ci->ci_inode->i_flags & OCFS2_SYSTEM_FL) || !(ci->ci_inode->i_flags & OCFS2_JOURNAL_FL)) { ret = OCFS2_ET_INTERNAL_FAILURE; goto out; } di = ci->ci_inode; if (clusters > di->i_clusters) { ret = ocfs2_extend_allocation(fs, blkno, (clusters - di->i_clusters)); if (ret) goto out; /* We don't cache in the library right now, so any * work done in extend_allocation won't be reflected * in our now stale copy. */ ocfs2_free_cached_inode(fs, ci); ret = ocfs2_read_cached_inode(fs, blkno, &ci); if (ret) { ci = NULL; goto out; } di = ci->ci_inode; di->i_size = di->i_clusters << OCFS2_RAW_SB(fs->fs_super)->s_clustersize_bits; di->i_mtime = time(NULL); ret = ocfs2_write_inode(fs, blkno, (char *)di); if (ret) goto out; } else if (clusters < di->i_clusters) { uint64_t new_size = clusters << OCFS2_RAW_SB(fs->fs_super)->s_clustersize_bits; ret = ocfs2_truncate(fs, blkno, new_size); if (ret) goto out; ocfs2_free_cached_inode(fs, ci); ret = ocfs2_read_cached_inode(fs, blkno, &ci); if (ret) { ci = NULL; goto out; } } ret = ocfs2_format_journal(fs, ci, features); out: if (ci) ocfs2_free_cached_inode(fs, ci); return ret; } #ifdef DEBUG_EXE #if 0 static uint64_t read_number(const char *num) { uint64_t val; char *ptr; val = strtoull(num, &ptr, 0); if (!ptr || *ptr) return 0; return val; } static void print_usage(void) { fprintf(stderr, "Usage: mkjournal \n"); } extern int opterr, optind; extern char *optarg; int main(int argc, char *argv[]) { errcode_t ret; uint64_t blkno; char *filename, *buf; ocfs2_filesys *fs; ocfs2_dinode *di; blkno = OCFS2_SUPER_BLOCK_BLKNO; initialize_ocfs_error_table(); if (argc < 2) { fprintf(stderr, "Missing filename\n"); print_usage(); return 1; } filename = argv[1]; if (argc > 2) { blkno = read_number(argv[2]); if (blkno < OCFS2_SUPER_BLOCK_BLKNO) { fprintf(stderr, "Invalid blockno: %s\n", blkno); print_usage(); return 1; } } ret = ocfs2_open(filename, OCFS2_FLAG_RO, 0, 0, &fs); if (ret) { com_err(argv[0], ret, "while opening file \"%s\"", filename); goto out; } ret = ocfs2_malloc_block(fs->fs_io, &buf); if (ret) { com_err(argv[0], ret, "while allocating inode buffer"); goto out_close; } di = (ocfs2_dinode *)buf; ret = ocfs2_read_inode(fs, blkno, di); if (ret) { com_err(argv[0], ret, "while reading inode %llu", blkno); goto out_free; } fprintf(stdout, "OCFS2 inode %llu on \"%s\"\n", blkno, filename); out_free: ocfs2_free(&buf); out_close: ret = ocfs2_close(fs); if (ret) { com_err(argv[0], ret, "while closing file \"%s\"", filename); } out: return 0; } #endif #include int main(int argc, char *argv[]) { fprintf(stdout, "Does nothing for now\n"); return 0; } #endif /* DEBUG_EXE */ ocfs2-tools-ocfs2-tools-1.8.6/libocfs2/namei.c000066400000000000000000000132131347147137200210210ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * namei.c * * ocfs2 directory lookup operations * * Copyright (C) 2004 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License, version 2, as published by the Free Software Foundation. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * * This code is a port of e2fsprogs/lib/ext2fs/namei.c * Copyright (C) 1993, 1994, 1994, 1995 Theodore Ts'o. */ #define _XOPEN_SOURCE 600 /* Triggers magic in features.h */ #define _LARGEFILE64_SOURCE #include #include #if HAVE_UNISTD_H #include #endif #include "ocfs2/ocfs2.h" static errcode_t open_namei(ocfs2_filesys *fs, uint64_t root, uint64_t base, const char *pathname, size_t pathlen, int follow, int link_count, char *buf, uint64_t *res_inode); /* * follow_link() * */ static errcode_t follow_link(ocfs2_filesys *fs, uint64_t root, uint64_t dir, uint64_t inode, int link_count, char *buf, uint64_t *res_inode) { char *pathname; char *buffer = NULL; errcode_t ret; struct ocfs2_dinode *di = NULL; struct ocfs2_extent_list *el; uint64_t blkno; #ifdef NAMEI_DEBUG printf("follow_link: root=%lu, dir=%lu, inode=%lu, lc=%d\n", root, dir, inode, link_count); #endif ret = ocfs2_malloc_block(fs->fs_io, &di); if (ret) goto bail; ret = ocfs2_read_inode(fs, inode, (char *)di); if (ret) goto bail; if (!S_ISLNK(di->i_mode)) { *res_inode = inode; ret = 0; goto bail; } if (link_count++ > 5) { ret = OCFS2_ET_SYMLINK_LOOP; goto bail; } el = &(di->id2.i_list); if (!di->i_clusters || !el->l_next_free_rec) { ret = OCFS2_ET_INTERNAL_FAILURE; goto bail; } blkno = el->l_recs[0].e_blkno; ret = ocfs2_malloc_block(fs->fs_io, &buffer); if (ret) goto bail; ret = ocfs2_read_blocks(fs, blkno, 1, buffer); if (ret) goto bail; pathname = buffer; ret = open_namei(fs, root, dir, pathname, di->i_size, 1, link_count, buf, res_inode); bail: if (buffer) ocfs2_free(&buffer); if (di) ocfs2_free(&di); return ret; } /* * dir_namei() * * This routine interprets a pathname in the context of the current * directory and the root directory, and returns the inode of the * containing directory, and a pointer to the filename of the file * (pointing into the pathname) and the length of the filename. */ static errcode_t dir_namei(ocfs2_filesys *fs, uint64_t root, uint64_t dir, const char *pathname, int pathlen, int link_count, char *buf, const char **name, int *namelen, uint64_t *res_inode) { char c; const char *thisname; int len; uint64_t inode; errcode_t ret; if ((c = *pathname) == '/') { dir = root; pathname++; pathlen--; } while (1) { thisname = pathname; for (len=0; --pathlen >= 0;len++) { c = *(pathname++); if (c == '/') break; } if (pathlen < 0) break; ret = ocfs2_lookup (fs, dir, thisname, len, buf, &inode); if (ret) return ret; ret = follow_link (fs, root, dir, inode, link_count, buf, &dir); if (ret) return ret; } *name = thisname; *namelen = len; *res_inode = dir; return 0; } /* * open_namei() * */ static errcode_t open_namei(ocfs2_filesys *fs, uint64_t root, uint64_t base, const char *pathname, size_t pathlen, int follow, int link_count, char *buf, uint64_t *res_inode) { const char *basename; int namelen; uint64_t dir, inode; errcode_t ret; #ifdef NAMEI_DEBUG printf("open_namei: root=%lu, dir=%lu, path=%*s, lc=%d\n", root, base, pathlen, pathname, link_count); #endif ret = dir_namei(fs, root, base, pathname, pathlen, link_count, buf, &basename, &namelen, &dir); if (ret) return ret; if (!namelen) { /* special case: '/usr/' etc */ *res_inode=dir; return 0; } ret = ocfs2_lookup (fs, dir, basename, namelen, buf, &inode); if (ret) return ret; if (follow) { ret = follow_link(fs, root, dir, inode, link_count, buf, &inode); if (ret) return ret; } #ifdef NAMEI_DEBUG printf("open_namei: (link_count=%d) returns %lu\n", link_count, inode); #endif *res_inode = inode; return 0; } /* * ocfs2_namei() * */ errcode_t ocfs2_namei(ocfs2_filesys *fs, uint64_t root, uint64_t cwd, const char *name, uint64_t *inode) { char *buf; errcode_t ret; ret = ocfs2_malloc_block(fs->fs_io, &buf); if (ret) return ret; ret = open_namei(fs, root, cwd, name, strlen(name), 0, 0, buf, inode); ocfs2_free(&buf); return ret; } /* * ocfs2_namei_follow() * */ errcode_t ocfs2_namei_follow(ocfs2_filesys *fs, uint64_t root, uint64_t cwd, const char *name, uint64_t *inode) { char *buf; errcode_t ret; ret = ocfs2_malloc_block(fs->fs_io, &buf); if (ret) return ret; ret = open_namei(fs, root, cwd, name, strlen(name), 1, 0, buf, inode); ocfs2_free(&buf); return ret; } /* * ocfs2_follow_link() * */ errcode_t ocfs2_follow_link(ocfs2_filesys *fs, uint64_t root, uint64_t cwd, uint64_t inode, uint64_t *res_inode) { char *buf; errcode_t ret; ret = ocfs2_malloc_block(fs->fs_io, &buf); if (ret) return ret; ret = follow_link(fs, root, cwd, inode, 0, buf, res_inode); ocfs2_free(&buf); return ret; } ocfs2-tools-ocfs2-tools-1.8.6/libocfs2/ocfs2.7.in000066400000000000000000001726661347147137200213170ustar00rootroot00000000000000.TH "OCFS2" "7" "January 2012" "Version @VERSION@" "OCFS2 Manual Pages" .SH "NAME" OCFS2 \- A Shared-Disk Cluster File System for Linux .SH "INTRODUCTION" .PP \fBOCFS2\fR is a \fBfile system\fR. It allows users to store and retrieve data. The data is stored in files that are organized in a hierarchical directory tree. It is a \fBPOSIX compliant\fR file system that supports the standard interfaces and the behavioral semantics as spelled out by that specification. It is also a \fBshared disk cluster\fR file system, one that allows multiple nodes to access the same disk at the same time. This is where the fun begins as allowing a file system to be accessible on multiple nodes opens a can of worms. What if the nodes are of different architectures? What if a node dies while writing to the file system? What data consistency can one expect if processes on two nodes are reading and writing concurrently? What if one node removes a file while it is still being used on another node? Unlike most shared file systems where the answer is fuzzy, the answer in OCFS2 is very well defined. It behaves on all nodes exactly like a \fBlocal\fR file system. If a file is removed, the directory entry is removed but the inode is kept as long as it is in use across the cluster. When the last user closes the descriptor, the inode is marked for deletion. The data consistency model follows the same principle. It works as if the two processes that are running on two different nodes are running on the same node. A read on a node gets the last write irrespective of the IO mode used. The modes can be \fIbuffered\fR, \fIdirect\fR, \fIasynchronous\fR, \fIsplice\fR or \fImemory mapped\fR IOs. It is fully \fBcache coherent\fR. Take for example the REFLINK feature that allows a user to create multiple write-able snapshots of a file. This feature, like all others, is fully cluster-aware. A file being written to on multiple nodes can be safely reflinked on another. The snapshot created is a point-in-time image of the file that includes both the file data and all its attributes (including extended attributes). It is a \fBjournaling\fR file system. When a node dies, a surviving node transparently replays the journal of the dead node. This ensures that the file system metadata is always consistent. It also defaults to ordered data journaling to ensure the file data is flushed to disk before the journal commit, to remove the small possibility of stale data appearing in files after a crash. It is \fBarchitecture\fR and \fBendian neutral\fR. It allows concurrent mounts on nodes with different processors like x86, x86_64, IA64 and PPC64. It handles little and big endian, 32-bit and 64-bit architectures. It is \fBfeature rich\fR. It supports \fIindexed directories\fR, \fImetadata checksums\fR, \fIextended attributes\fR, \fIPOSIX ACLs\fR, \fIquotas\fR, \fIREFLINKs\fR, \fIsparse files\fR, \fIunwritten extents\fR and \fIinline-data\fR. It is \fBfully integrated\fR with the mainline Linux kernel. The file system was merged into Linux kernel 2.6.16 in early 2006. It is \fBquickly installed\fR. It is available with almost all Linux distributions. The file system is \fBon-disk compatible\fR across all of them. It is \fBmodular\fR. The file system can be configured to operate with other cluster stacks like \fIPacemaker\fR and \fICMAN\fR along with its own stack, \fIO2CB\fR. It is \fBeasily configured\fR. The O2CB cluster stack configuration involves editing two files, one for cluster layout and the other for cluster timeouts. It is \fBvery efficient\fR. The file system consumes very little resources. It is used to store virtual machine images in limited memory environments like Xen and KVM. In summary, OCFS2 is an efficient, easily configured, modular, quickly installed, fully integrated and compatible, feature-rich, architecture and endian neutral, cache coherent, ordered data journaling, POSIX-compliant, shared disk cluster file system. .SH "OVERVIEW" .PP OCFS2 is a general-purpose shared-disk cluster file system for Linux capable of providing both high performance and high availability. As it provides local file system semantics, it can be used with almost all applications. Cluster-aware applications can make use of cache-coherent parallel I/Os from multiple nodes to scale out applications easily. Other applications can make use of the clustering facilities to fail-over running application in the event of a node failure. The notable features of the file system are: .TP \fBTunable Block size\fR The file system supports block sizes of 512, 1K, 2K and 4K bytes. 4KB is almost always recommended. This feature is available in all releases of the file system. .TP \fBTunable Cluster size\fR A cluster size is also referred to as an allocation unit. The file system supports cluster sizes of 4K, 8K, 16K, 32K, 64K, 128K, 256K, 512K and 1M bytes. For most use cases, 4KB is recommended. However, a larger value is recommended for volumes hosting mostly very large files like database files, virtual machine images, etc. A large cluster size allows the file system to store large files more efficiently. This feature is available in all releases of the file system. .TP \fBEndian and Architecture neutral\fR The file system can be mounted concurrently on nodes having different architectures. Like 32-bit, 64-bit, little-endian (x86, x86_64, ia64) and big-endian (ppc64, s390x). This feature is available in all releases of the file system. .TP \fBBuffered, Direct, Asynchronous, Splice and Memory Mapped I/O modes\fR The file system supports all modes of I/O for maximum flexibility and performance. It also supports cluster-wide \fBshared writeable mmap(2)\fR. The support for bufferred, direct and asynchronous I/O is available in all releases. The support for splice I/O was added in Linux kernel \fB2.6.20\fR and for shared writeable map(2) in \fB2.6.23\fR. .TP \fBMultiple Cluster Stacks\fR The file system includes a flexible framework to allow it to function with userspace cluster stacks like Pacemaker (\fBpcmk\fR) and CMAN (\fBcman\fR), its own in-kernel cluster stack \fBo2cb\fR and \fIno\fR cluster stack. The support for \fBo2cb\fR cluster stack is available in all releases. The support for \fIno\fR cluster stack, or \fBlocal\fR mount, was added in Linux kernel \fB2.6.20\fR. The support for userspace cluster stack was added in Linux kernel \fB2.6.26\fR. .TP \fBJournaling\fR The file system supports both \fBordered\fR (default) and \fBwriteback\fR data journaling modes to provide file system consistency in the event of power failure or system crash. It uses \fBJBD2\fR in Linux kernel \fB2.6.28\fR and later. It used \fBJBD\fR in earlier kernels. .TP \fBExtent-based Allocations\fR The file system allocates and tracks space in ranges of clusters. This is unlike block based file systems that have to track each and every block. This feature allows the file system to be very efficient when dealing with both large volumes and large files. This feature is available in all releases of the file system. .TP \fBSparse files\fR Sparse files are files with holes. With this feature, the file system delays allocating space until a write is issued to a cluster. This feature was added in Linux kernel \fB2.6.22\fR and requires enabling on-disk feature \fBsparse\fR. .TP \fBUnwritten Extents\fR An unwritten extent is also referred to as user pre-allocation. It allows an application to request a range of clusters to be allocated, but not initialized, within a file. Pre-allocation allows the file system to optimize the data layout with fewer, larger extents. It also provides a performance boost, delaying initialization until the user writes to the clusters. This feature was added in Linux kernel \fB2.6.23\fR and requires enabling on-disk feature \fBunwritten\fR. .TP \fBHole Punching\fR Hole punching allows an application to remove arbitrary allocated regions within a file. Creating holes, essentially. This is more efficient than zeroing the same extents. This feature is especially useful in virtualized environments as it allows a block discard in a guest file system to be converted to a hole punch in the host file system thus allowing users to reduce disk space usage. This feature was added in Linux kernel \fB2.6.23\fR and requires enabling on-disk features \fBsparse\fR and \fBunwritten\fR. .TP \fBInline-data\fR Inline data is also referred to as data-in-inode as it allows storing small files and directories in the inode block. This not only saves space but also has a positive impact on cold-cache directory and file operations. The data is transparently moved out to an extent when it no longer fits inside the inode block. This feature was added in Linux kernel \fB2.6.24\fR and requires enabling on-disk feature \fBinline-data\fR. .TP \fBREFLINK\fR REFLINK is also referred to as fast copy. It allows users to atomically (and instantly) copy regular files. In other words, create multiple writeable snapshots of regular files. It is called REFLINK because it looks and feels more like a (hard) \fBlink(2)\fR than a traditional snapshot. Like a link, it is a regular user operation, subject to the security attributes of the inode being reflinked and not to the super user privileges typically required to create a snapshot. Like a link, it operates within a file system. But unlike a link, it links the inodes at the data extent level allowing each reflinked inode to grow independently as and when written to. Up to four billion inodes can share a data extent. This feature was added in Linux kernel \fB2.6.32\fR and requires enabling on-disk feature \fBrefcount\fR. .TP \fBAllocation Reservation\fR File contiguity plays an important role in file system performance. When a file is fragmented on disk, reading and writing to the file involves many seeks, leading to lower throughput. Contiguous files, on the other hand, minimize seeks, allowing the disks to perform IO at the maximum rate. With allocation reservation, the file system reserves a window in the bitmap for all extending files allowing each to grow as contiguously as possible. As this extra space is not actually allocated, it is available for use by other files if the need arises. This feature was added in Linux kernel \fB2.6.35\fR and can be tuned using the mount option \fBresv_level\fR. .TP \fBIndexed Directories\fR An indexed directory allows users to perform quick lookups of a file in very large directories. It also results in faster creates and unlinks and thus provides better overall performance. This feature was added in Linux kernel \fB2.6.30\fR and requires enabling on-disk feature \fBindexed-dirs\fR. .TP \fBFile Attributes\fR This refers to EXT2-style file attributes, such as immutable, modified using \fBchattr(1)\fR and queried using \fBlsattr(1)\fR. This feature was added in Linux kernel \fB2.6.19\fR. .TP \fBExtended Attributes\fR An extended attribute refers to a name:value pair than can be associated with file system objects like regular files, directories, symbolic links, etc. \fIOCFS2\fR allows associating an \fIunlimited\fR number of attributes per object. The attribute names can be up to 255 bytes in length, terminated by the first NUL character. While it is not required, printable names (ASCII) are recommended. The attribute values can be up to 64 KB of arbitrary binary data. These attributes can be modified and listed using standard Linux utilities \fBsetfattr(1)\fR and \fBgetfattr(1)\fR. This feature was added in Linux kernel \fB2.6.29\fR and requires enabling on-disk feature \fBxattr\fR. .TP \fBMetadata Checksums\fR This feature allows the file system to detect silent corruptions in all metadata blocks like inodes and directories. This feature was added in Linux kernel \fB2.6.29\fR and requires enabling on-disk feature \fBmetaecc\fR. .TP \fBPOSIX ACLs and Security Attributes\fR POSIX ACLs allows assigning fine-grained discretionary access rights for files and directories. This security scheme is a lot more flexible than the traditional file access permissions that imposes a strict user-group-other model. Security attributes allow the file system to support other security regimes like SELinux, SMACK, AppArmor, etc. Both these security extensions were added in Linux kernel \fB2.6.29\fR and requires enabling on-disk feature \fBxattr\fR. .TP \fBUser and Group Quotas\fR This feature allows setting up usage quotas on user and group basis by using the standard utilities like \fBquota(1)\fR, \fBsetquota(8)\fR, \fBquotacheck(8)\fR, and \fBquotaon(8)\fR. This feature was added in Linux kernel \fB2.6.29\fR and requires enabling on-disk features \fBusrquota\fR and \fBgrpquota\fR. .TP \fBUnix File Locking\fR The Unix operating system has historically provided two system calls to lock files. \fBflock(2)\fR or BSD locking and \fBfcntl(2)\fR or POSIX locking. \fIOCFS2\fR extends both file locks to the cluster. File locks taken on one node interact with those taken on other nodes. The support for clustered \fBflock(2)\fR was added in Linux kernel \fB2.6.26\fR. All \fBflock(2)\fR options are supported, including the kernels ability to cancel a lock request when an appropriate kill signal is received by the user. This feature is supported with all cluster-stacks including \fBo2cb\fR. The support for clustered \fBfcntl(2)\fR was added in Linux kernel \fB2.6.28\fR. But because it requires group communication to make the locks coherent, it is only supported with userspace cluster stacks, \fBpcmk\fR and \fBcman\fR and \fInot\fR with the default cluster stack \fBo2cb\fR. .TP \fBComprehensive Tools Support\fR The file system has a comprehensive EXT3-style toolset that tries to use similar parameters for ease-of-use. It includes mkfs.ocfs2(8) (format), tunefs.ocfs2(8) (tune), fsck.ocfs2(8) (check), debugfs.ocfs2(8) (debug), etc. .TP \fBOnline Resize\fR The file system can be dynamically grown using \fBtunefs.ocfs2(8)\fR. This feature was added in Linux kernel \fB2.6.25\fR. .SH "RECENT CHANGES" .PP The O2CB cluster stack has a \fBglobal heartbeat\fR mode. It allows users to specify heartbeat regions that are consistent across all nodes. The cluster stack also allows online addition and removal of both nodes and heartbeat regions. \fBo2cb(8)\fR is the new cluster configuration utility. It is an easy to use utility that allows users to create the cluster configuration on a node that is not part of the cluster. It replaces the older utility \fBo2cb_ctl(8)\fR which has being deprecated. \fBocfs2console(8)\fR has been obsoleted. \fBo2info(8)\fR is a new utility that can be used to provide file system information. It allows non-privileged users to see the enabled file system features, block and cluster sizes, extended file stat, free space fragmentation, etc. \fBo2hbmonitor(8)\fR is a \fBo2hb\fR heartbeat monitor. It is an extremely light weight utility that logs messages to the system logger once the heartbeat delay exceeds the warn threshold. This utility is useful in identifying volumes encountering I/O delays. \fBdebugfs.ocfs2(8)\fR has some new commands. \fInet_stats\fR shows the \fBo2net\fR message times between various nodes. This is useful in identifying nodes are that slowing down the cluster operations. \fIstat_sysdir\fR allows the user to dump the entire system directory that can be used to debug issues. \fIgrpextents\fR dumps the complete free space fragmentation in the cluster group allocator. \fBmkfs.ocfs2(8)\fR now enables \fIxattr\fB, \fIindexed-dirs\fR, \fIdiscontig-bg\fR, \fIrefcount\fR, \fIextended-slotmap\fR and \fIclusterinfo\fR feature flags by default, in addition to the older defaults, \fIsparse\fR, \fIunwritten\fR and \fIinline-data\fR. \fBmount.ocfs2(8)\fR allows users to specify the level of cache coherency between nodes. By default the file system operates in full coherency mode that also serializes the direct I/Os. While this mode is technically correct, it limits the I/O thruput in a clustered database. This mount option allows the user to limit the cache coherency to only the buffered I/Os to allow multiple nodes to do concurrent direct writes to the same file. This feature works with Linux kernel \fB2.6.37\fR and later. .SH "COMPATIBILITY" .PP The OCFS2 development teams goes to great lengths to maintain compatibility. It attempts to maintain both on-disk and network protocol compatibility across all releases of the file system. It does so even while adding new features that entail on-disk format and network protocol changes. To do this successfully, it follows a few rules: .in +4n \fB1\fR. The on-disk format changes are managed by a set of feature flags that can be turned on and off. The file system in kernel detects these features during mount and continues only if it understands all the features. Users encountering this have the option of either disabling that feature or upgrading the file system to a newer release. \fB2\fR. The latest release of ocfs2-tools is compatible with all versions of the file system. All utilities detect the features enabled on disk and continue only if it understands all the features. Users encountering this have to upgrade the tools to a newer release. \fB3\fR. The network protocol version is negotiated by the nodes to ensure all nodes understand the active protocol version. .in .TP \fBFEATURE FLAGS\fR The feature flags are split into three categories, namely, \fBCompat\fR, \fBIncompat\fR and \fBRO Compat\fR. \fBCompat\fR, or compatible, is a feature that the file system does not need to fully understand to safely read/write to the volume. An example of this is the backup-super feature that added the capability to backup the super block in multiple locations in the file system. As the backup super blocks are typically not read nor written to by the file system, an older file system can safely mount a volume with this feature enabled. \fBIncompat\fR, or incompatible, is a feature that the file system needs to fully understand to read/write to the volume. Most features fall under this category. \fBRO Compat\fR, or read-only compatible, is a feature that the file system needs to fully understand to write to the volume. Older software can safely read a volume with this feature enabled. An example of this would be user and group quotas. As quotas are manipulated only when the file system is written to, older software can safely mount such volumes in read-only mode. The list of feature flags, the version of the kernel it was added in, the earliest version of the tools that understands it, etc., is as follows: .TS CENTER ALLBOX; LB LB LB LB LB LI C C C C. Feature Flags Kernel Version Tools Version Category Hex Value backup-super All ocfs2-tools 1.2 Compat 1 strict-journal-super All All Compat 2 local Linux 2.6.20 ocfs2-tools 1.2 Incompat 8 sparse Linux 2.6.22 ocfs2-tools 1.4 Incompat 10 inline-data Linux 2.6.24 ocfs2-tools 1.4 Incompat 40 extended-slotmap Linux 2.6.27 ocfs2-tools 1.6 Incompat 100 xattr Linux 2.6.29 ocfs2-tools 1.6 Incompat 200 indexed-dirs Linux 2.6.30 ocfs2-tools 1.6 Incompat 400 metaecc Linux 2.6.29 ocfs2-tools 1.6 Incompat 800 refcount Linux 2.6.32 ocfs2-tools 1.6 Incompat 1000 discontig-bg Linux 2.6.35 ocfs2-tools 1.6 Incompat 2000 clusterinfo Linux 2.6.37 ocfs2-tools 1.8 Incompat 4000 unwritten Linux 2.6.23 ocfs2-tools 1.4 RO Compat 1 grpquota Linux 2.6.29 ocfs2-tools 1.6 RO Compat 2 usrquota Linux 2.6.29 ocfs2-tools 1.6 RO Compat 4 .TE .BR To query the features enabled on a volume, do: .nf .ps 8 .ft 6 $ o2info --fs-features /dev/sdf1 backup-super strict-journal-super sparse extended-slotmap inline-data xattr indexed-dirs refcount discontig-bg clusterinfo unwritten .ft .ps .fi .TP \fBENABLING AND DISABLING FEATURES\fR The format utility, \fBmkfs.ocfs2(8)\fR, allows a user to enable and disable specific features using the fs-features option. The features are provided as a comma separated list. The enabled features are listed as is. The disabled features are prefixed with \fBno\fR. The example below shows the file system being formatted with sparse disabled and inline-data enabled. .nf .ft 6 # mkfs.ocfs2 --fs-features=nosparse,inline-data /dev/sda1 .ft .fi After formatting, the users can toggle features using the tune utility, \fBtunefs.ocfs2(8)\fR. This is an \fIoffline\fR operation. The volume needs to be umounted across the cluster. The example below shows the sparse feature being enabled and inline-data disabled. .nf .ft 6 # tunefs.ocfs2 --fs-features=sparse,noinline-data /dev/sda1 .ft .fi Care should be taken before enabling and disabling features. Users planning to use a volume with an older version of the file system will be better of not enabling newer features as turning disabling may not succeed. An example would be disabling the sparse feature; this requires filling every hole. The operation can only succeed if the file system has enough free space. .TP \fBDETECTING FEATURE INCOMPATIBILITY\fR Say one tries to mount a volume with an incompatible feature. What happens then? How does one detect the problem? How does one know the name of that incompatible feature? To begin with, one should look for error messages in \fBdmesg(8)\fR. Mount failures that are due to an incompatible feature will always result in an error message like the following: .nf .ps 9 .ft 6 ERROR: couldn't mount because of unsupported optional features (200). .ft .ps .fi Here the file system is unable to mount the volume due to an unsupported optional feature. That means that that feature is an \fBIncompat\fR feature. By referring to the table above, one can then deduce that the user failed to mount a volume with the \fBxattr\fR feature enabled. (The value in the error message is in hexadecimal.) Another example of an error message due to incompatibility is as follows: .nf .ps 9 .ft 6 ERROR: couldn't mount RDWR because of unsupported optional features (1). .ft .ps .fi Here the file system is unable to mount the volume in the RW mode. That means that that feature is a \fBRO Compat\fR feature. Another look at the table and it becomes apparent that the volume had the \fBunwritten\fR feature enabled. In both cases, the user has the option of disabling the feature. In the second case, the user has the choice of mounting the volume in the RO mode. .SH "GETTING STARTED" .PP The OCFS2 software is split into two components, namely, kernel and tools. The kernel component includes the core file system and the cluster stack, and is packaged along with the kernel. The tools component is packaged as \fBocfs2-tools\fR and needs to be specifically installed. It provides utilities to format, tune, mount, debug and check the file system. To install \fBocfs2-tools\fR, refer to the package handling utility in in your distributions. The next step is selecting a cluster stack. The options include: .in +4n \fBA\fR. No cluster stack, or \fBlocal mount\fR. \fBB\fR. In-kernel \fBo2cb\fR cluster stack with \fBlocal\fR or \fBglobal\fR heartbeat. \fBC\fR. Userspace cluster stacks \fBpcmk\fR or \fBcman\fR. .in The file system allows changing cluster stacks easily using \fBtunefs.ocfs2(8)\fR. To list the cluster stacks stamped on the OCFS2 volumes, do: .nf .ps 9 .ft 6 # mounted.ocfs2 -d Device Stack Cluster F UUID Label /dev/sdb1 o2cb webcluster G DCDA2845177F4D59A0F2DCD8DE507CC3 hbvol1 /dev/sdc1 None 23878C320CF3478095D1318CB5C99EED localmount /dev/sdd1 o2cb webcluster G 8AB016CD59FC4327A2CDAB69F08518E3 webvol /dev/sdg1 o2cb webcluster G 77D95EF51C0149D2823674FCC162CF8B logsvol /dev/sdh1 o2cb webcluster G BBA1DBD0F73F449384CE75197D9B7098 scratch .ft .ps .fi .TP \fBNON-CLUSTERED OR LOCAL MOUNT\fR To format a \fIOCFS2\fR volume as a non-clustered (\fBlocal\fR) volume, do: .nf .ft 6 # mkfs.ocfs2 -L "mylabel" --fs-features=local /dev/sda1 .ft .fi To convert an existing clustered volume to a non-clustered volume, do: .nf .ft 6 # tunefs.ocfs2 --fs-features=local /dev/sda1 .ft .fi Non-clustered volumes do not interact with the cluster stack. One can have both clustered and non-clustered volumes mounted at the same time. While formatting a non-clustered volume, users should consider the possibility of later converting that volume to a clustered one. If there is a possibility of that, then the user should add enough node-slots using the -N option. Adding node-slots during format creates journals with large extents. If created later, then the journals will be fragmented which is not good for performance. .TP \fBCLUSTERED MOUNT WITH O2CB CLUSTER STACK\fR Only one of the two heartbeat mode can be active at any one time. Changing heartbeat modes is an offline operation. Both heartbeat modes require /etc/ocfs2/cluster.conf and /etc/sysconfig/o2cb to be populated as described in \fBocfs2.cluster.conf(5)\fR and \fBo2cb.sysconfig(5)\fR respectively. The only difference in set up between the two modes is that \fBglobal\fR requires heartbeat devices to be configured whereas \fBlocal\fR does not. Refer \fBo2cb(7)\fR for more information. .RS .TP \fBLOCAL HEARTBEAT\fR This is the default heartbeat mode. The user needs to populate the configuration files as described in \fBocfs2.cluster.conf(5)\fR and \fBo2cb.sysconfig(5)\fR. In this mode, the cluster stack heartbeats on all mounted volumes. Thus, one does not have to specify heartbeat devices in cluster.conf. Once configured, the \fBo2cb\fR cluster stack can be onlined and offlined as follows: .nf .ft 6 # service o2cb online Setting cluster stack "o2cb": OK Registering O2CB cluster "webcluster": OK Setting O2CB cluster timeouts : OK # service o2cb offline Clean userdlm domains: OK Stopping O2CB cluster webcluster: OK Unregistering O2CB cluster "webcluster": OK .ft .fi .TP \fBGLOBAL HEARTBEAT\fR The configuration is similar to \fBlocal\fR heartbeat. The one additional step in this mode is that it requires heartbeat devices to be also configured. These heartbeat devices are OCFS2 formatted volumes with global heartbeat enabled on disk. These volumes can later be mounted and used as clustered file systems. The steps to format a volume with global heartbeat enabled is listed in \fBo2cb(7)\fR. Also listed there is listing all volumes with the cluster stack stamped on disk. In this mode, the heartbeat is started when the cluster is onlined and stopped when the cluster is offlined. .nf .ft 6 # service o2cb online Setting cluster stack "o2cb": OK Registering O2CB cluster "webcluster": OK Setting O2CB cluster timeouts : OK Starting global heartbeat for cluster "webcluster": OK # service o2cb offline Clean userdlm domains: OK Stopping global heartbeat on cluster "webcluster": OK Stopping O2CB cluster webcluster: OK Unregistering O2CB cluster "webcluster": OK # service o2cb status Driver for "configfs": Loaded Filesystem "configfs": Mounted Stack glue driver: Loaded Stack plugin "o2cb": Loaded Driver for "ocfs2_dlmfs": Loaded Filesystem "ocfs2_dlmfs": Mounted Checking O2CB cluster "webcluster": Online Heartbeat dead threshold: 31 Network idle timeout: 30000 Network keepalive delay: 2000 Network reconnect delay: 2000 Heartbeat mode: Global Checking O2CB heartbeat: Active 77D95EF51C0149D2823674FCC162CF8B /dev/sdg1 Nodes in O2CB cluster: 92 96 .ft .fi .RE .TP \fBCLUSTERED MOUNT WITH USERSPACE CLUSTER STACK\fR Configure and online the userspace stack \fBpcmk\fR or \fBcman\fR before using \fBtunefs.ocfs2(8)\fR to update the cluster stack on disk. .nf .ft 6 # tunefs.ocfs2 --update-cluster-stack /dev/sdd1 Updating on-disk cluster information to match the running cluster. DANGER: YOU MUST BE ABSOLUTELY SURE THAT NO OTHER NODE IS USING THIS FILESYSTEM BEFORE MODIFYING ITS CLUSTER CONFIGURATION. Update the on-disk cluster information? y .ft .fi Refer to the cluster stack documentation for information on starting and stopping the cluster stack. .SH "FILE SYSTEM UTILITIES" .PP This sections lists the utilities that are used to manage the \fIOCFS2\fR file systems. This includes tools to format, tune, check, mount, debug the file system. Each utility has a man page that lists its capabilities in detail. .TP \fBmkfs.ocfs2(8)\fR This is the file system \fIformat\fR utility. All volumes have to be formatted prior to its use. As this utility overwrites the volume, use it with care. Double check to ensure the volume is not in use on any node in the cluster. As a precaution, the utility will abort if the volume is locally mounted. It also detects use across the cluster if used by OCFS2. But these checks are not comprehensive and can be overridden. So use it with care. While it is not always required, the cluster should be online. .TP \fBtunefs.ocfs2(8)\fR This is the file system \fItune\fR utility. It allows users to change certain on-disk parameters like label, uuid, number of node-slots, volume size and the size of the journals. It also allows turning on and off the file system features as listed above. This utility requires the cluster to be online. .TP \fBfsck.ocfs2(8)\fR This is the file system \fIcheck\fR utility. It detects and fixes on-disk errors. All the check codes and their fixes are listed in \fBfsck.ocfs2.checks(8)\fR. This utility requires the cluster to be online to ensure the volume is not in use on another node and to prevent the volume from being mounted for the duration of the check. .TP \fBmount.ocfs2(8)\fR This is the file system \fImount\fR utility. It is invoked indirectly by the \fBmount(8)\fR utility. This utility detects the cluster status and aborts if the cluster is offline or does not match the cluster stamped on disk. .TP \fBo2cluster(8)\fR This is the file system \fIcluster stack update\fR utility. It allows the users to update the on-disk cluster stack to the one provided. This utility only updates the disk if the utility is reasonably assured that the file system is not in use on any node. .TP \fBo2info(1)\fR This is the file system \fIinformation\fR utility. It provides information like the features enabled on disk, block size, cluster size, free space fragmentation, etc. It can be used by both privileged and non-privileged users. Users having read permission on the device can provide the path to the device. Other users can provide the path to a file on a mounted file system. .TP \fBdebugfs.ocfs2(8)\fR This is the file system \fIdebug\fR utility. It allows users to examine all file system structures including walking directory structures, displaying inodes, backing up files, etc., without mounting the file system. This utility requires the user to have read permission on the device. .TP \fBo2image(8)\fR This is the file system \fIimage\fR utility. It allows users to copy the file system metadata skeleton, including the inodes, directories, bitmaps, etc. As it excludes data, it shrinks the size of the file system tremendously. The image file created can be used in debugging on-disk corruptions. .TP \fBmounted.ocfs2(8)\fR This is the file system \fIdetect\fR utility. It detects all \fIOCFS2\fR volumes in the system and lists its label, uuid and cluster stack. .SH "O2CB CLUSTER STACK UTILITIES" .PP This sections lists the utilities that are used to manage \fIO2CB\fR cluster stack. Each utility has a man page that lists its capabilities in detail. .TP \fBo2cb(8)\fR This is the cluster \fIconfiguration\fR utility. It allows users to update the cluster configuration by adding and removing nodes and heartbeat regions. This utility is used by the \fIo2cb\fR init script to online and offline the cluster. This is a \fBnew\fR utility and replaces \fBo2cb_ctl(8)\fR which has been deprecated. .TP \fBocfs2_hb_ctl(8)\fR This is the cluster heartbeat utility. It allows users to start and stop \fBlocal\fR heartbeat. This utility is invoked by \fBmount.ocfs2(8)\fR and should not be invoked directly by the user. .TP \fBo2hbmonitor(8)\fR This is the disk heartbeat monitor. It tracks the elapsed time since the last heartbeat and logs warnings once that time exceeds the warn threshold. .SH "FILE SYSTEM NOTES" .PP This section includes some useful notes that may prove helpful to the user. .TP \fBBALANCED CLUSTER\fR A cluster is a computer. This is a fact and not a slogan. What this means is that an errant node in the cluster can affect the behavior of other nodes. If one node is slow, the cluster operations will slow down on all nodes. To prevent that, it is best to have a balanced cluster. This is a cluster that has equally powered and loaded nodes. The standard recommendation for such clusters is to have identical hardware and software across all the nodes. However, that is not a hard and fast rule. After all, we have taken the effort to ensure that OCFS2 works in a mixed architecture environment. If one uses OCFS2 in a mixed architecture environment, try to ensure that the nodes are equally powered and loaded. The use of a load balancer can assist with the latter. Power refers to the number of processors, speed, amount of memory, I/O throughput, network bandwidth, etc. In reality, having equally powered heterogeneous nodes is not always practical. In that case, make the lower node numbers more powerful than the higher node numbers. The O2CB cluster stack favors lower node numbers in all of its tiebreaking logic. This is not to suggest you should add a single core node in a cluster of quad cores. No amount of node number juggling will help you there. .TP \fBFILE DELETION\fR In Linux, rm(1) removes the directory entry. It does not necessarily delete the corresponding inode. But by removing the directory entry, it gives the illusion that the inode has been deleted. This puzzles users when they do not see a corresponding up-tick in the reported free space. The reason is that inode deletion has a few more hurdles to cross. First is the hard link count, that indicates the number of directory entries pointing to that inode. As long as an inode has one or more directory entries pointing to it, it cannot be deleted. The file system has to wait for the removal of all those directory entries. In other words, wait for that count to drop to zero. The second hurdle is the POSIX semantics allowing files to be unlinked even while they are in-use. In OCFS2, that translates to in-use across the cluster. The file system has to wait for all processes across the cluster to stop using the inode. Once these conditions are met, the inode is deleted and the freed space is visible after the next sync. Now the amount of space freed depends on the allocation. Only space that is actually allocated to that inode is freed. The example below shows a sparsely allocated file of size 51TB of which only 2.4GB is actually allocated. .nf .ft 6 $ ls -lsh largefile 2.4G -rw-r--r-- 1 mark mark 51T Sep 29 15:04 largefile .ft .fi Furthermore, for reflinked files, only private extents are freed. Shared extents are freed when the last inode accessing it, is deleted. The example below shows a 4GB file that shares 3GB with other reflinked files. Deleting it will increase the free space by 1GB. However, if it is the only remaining file accessing the shared extents, the full 4G will be freed. (More information on the shared-du(1) utility is provided below.) .nf .ft 6 $ shared-du -m -c --shared-size reflinkedfile 4000 (3000) reflinkedfile .ft .fi The deletion itself is a multi-step process. Once the hard link count falls to zero, the inode is moved to the orphan_dir system directory where it remains until the last process, across the cluster, stops using the inode. Then the file system frees the extents and adds the freed space count to the truncate_log system file where it remains until the next sync. The freed space is made visible to the user only after that sync. .TP \fBDIRECTORY LISTING\fR ls(1) may be a simple command, but it is not cheap. What is expensive is not the part where it reads the directory listing, but the second part where it reads all the inodes, also referred as an inode stat(2). If the inodes are not in cache, this can entail disk I/O. Now, while a cold cache inode stat(2) is expensive in all file systems, it is especially so in a clustered file system as it needs to take a cluster lock on each inode. A hot cache stat(2), on the other hand, has shown to perform on OCFS2 like it does on EXT3. In other words, the second ls(1) will be quicker than the first. However, it is not guaranteed. Say you have a million files in a file system and not enough kernel memory to cache all the inodes. In that case, each ls(1) will involve some cold cache stat(2)s. .TP \fBALLOCATION RESERVATION\fR Allocation reservation allows multiple concurrently extending files to grow as contiguously as possible. One way to demonstrate its functioning is to run a script that extends multiple files in a circular order. The script below does that by writing one hundred 4KB chunks to four files, one after another. .nf .ft 6 $ for i in $(seq 0 99); > do > for j in $(seq 4); > do > dd if=/dev/zero of=file$j bs=4K count=1 seek=$i; > done; > done; .ft .fi When run on a system running Linux kernel 2.6.34 or earlier, we end up with files with 100 extents each. That is full fragmentation. As the files are being extended one after another, the on-disk allocations are fully interleaved. .nf .ft 6 $ filefrag file1 file2 file3 file4 file1: 100 extents found file2: 100 extents found file3: 100 extents found file4: 100 extents found .ft .fi When run on a system running Linux kernel 2.6.35 or later, we see files with 7 extents each. That is a lot fewer than before. Fewer extents mean more on-disk contiguity and that always leads to better overall performance. .nf .ft 6 $ filefrag file1 file2 file3 file4 file1: 7 extents found file2: 7 extents found file3: 7 extents found file4: 7 extents found .ft .fi .TP \fBREFLINK OPERATION\fR This feature allows a user to create a writeable snapshot of a regular file. In this operation, the file system creates a new inode with the same extent pointers as the original inode. Multiple inodes are thus able to share data extents. This adds a twist in file system administration because none of the existing file system utilities in Linux expect this behavior. du(1), a utility to used to compute file space usage, simply adds the blocks allocated to each inode. As it does not know about shared extents, it over estimates the space used. Say, we have a 5GB file in a volume having 42GB free. .nf .ft 6 $ ls -l total 5120000 -rw-r--r-- 1 jeff jeff 5242880000 Sep 24 17:15 myfile $ du -m myfile* 5000 myfile $ df -h . Filesystem Size Used Avail Use% Mounted on /dev/sdd1 50G 8.2G 42G 17% /ocfs2 .ft .fi If we were to reflink it 4 times, we would expect the directory listing to report five 5GB files, but the df(1) to report no loss of available space. du(1), on the other hand, would report the disk usage to climb to 25GB. .nf .ft 6 $ reflink myfile myfile-ref1 $ reflink myfile myfile-ref2 $ reflink myfile myfile-ref3 $ reflink myfile myfile-ref4 $ ls -l total 25600000 -rw-r--r-- 1 jeff jeff 5242880000 Sep 24 17:15 myfile -rw-r--r-- 1 jeff jeff 5242880000 Sep 24 17:16 myfile-ref1 -rw-r--r-- 1 jeff jeff 5242880000 Sep 24 17:16 myfile-ref2 -rw-r--r-- 1 jeff jeff 5242880000 Sep 24 17:16 myfile-ref3 -rw-r--r-- 1 jeff jeff 5242880000 Sep 24 17:16 myfile-ref4 $ df -h . Filesystem Size Used Avail Use% Mounted on /dev/sdd1 50G 8.2G 42G 17% /ocfs2 $ du -m myfile* 5000 myfile 5000 myfile-ref1 5000 myfile-ref2 5000 myfile-ref3 5000 myfile-ref4 25000 total .ft .fi Enter \fBshared-du(1)\fR, a shared extent-aware du. This utility reports the shared extents per file in parenthesis and the overall footprint. As expected, it lists the overall footprint at 5GB. One can view the details of the extents using \fBshared-filefrag(1)\fR. Both these utilities are available at http://oss.oracle.com/~smushran/reflink-tools/. We are currently in the process of pushing the changes to the upstream maintainers of these utilities. .nf .ft 6 $ shared-du -m -c --shared-size myfile* 5000 (5000) myfile 5000 (5000) myfile-ref1 5000 (5000) myfile-ref2 5000 (5000) myfile-ref3 5000 (5000) myfile-ref4 25000 total 5000 footprint # shared-filefrag -v myfile Filesystem type is: 7461636f File size of myfile is 5242880000 (1280000 blocks, blocksize 4096) ext logical physical expected length flags 0 0 2247937 8448 1 8448 2257921 2256384 30720 2 39168 2290177 2288640 30720 3 69888 2322433 2320896 30720 4 100608 2354689 2353152 30720 7 192768 2451457 2449920 30720 . . . 37 1073408 2032129 2030592 30720 shared 38 1104128 2064385 2062848 30720 shared 39 1134848 2096641 2095104 30720 shared 40 1165568 2128897 2127360 30720 shared 41 1196288 2161153 2159616 30720 shared 42 1227008 2193409 2191872 30720 shared 43 1257728 2225665 2224128 22272 shared,eof myfile: 44 extents found .ft .fi .TP \fBDATA COHERENCY\fR One of the challenges in a shared file system is data coherency when multiple nodes are writing to the same set of files. NFS, for example, provides close-to-open data coherency that results in the data being flushed to the server when the file is closed on the client. This leaves open a wide window for stale data being read on another node. A simple test to check the data coherency of a shared file system involves concurrently appending the same file. Like running "uname -a >>/dir/file" using a parallel distributed shell like dsh or pconsole. If coherent, the file will contain the results from all nodes. .nf .ft 6 .ps 8 # dsh -R ssh -w node32,node33,node34,node35 "uname -a >> /ocfs2/test" # cat /ocfs2/test Linux node32 2.6.32-10 #1 SMP Fri Sep 17 17:51:41 EDT 2010 x86_64 x86_64 x86_64 GNU/Linux Linux node35 2.6.32-10 #1 SMP Fri Sep 17 17:51:41 EDT 2010 x86_64 x86_64 x86_64 GNU/Linux Linux node33 2.6.32-10 #1 SMP Fri Sep 17 17:51:41 EDT 2010 x86_64 x86_64 x86_64 GNU/Linux Linux node34 2.6.32-10 #1 SMP Fri Sep 17 17:51:41 EDT 2010 x86_64 x86_64 x86_64 GNU/Linux .ps .ft .fi OCFS2 is a \fBfully cache coherent\fR cluster file system. .TP \fBDISCONTIGUOUS BLOCK GROUP\fR Most file systems pre-allocate space for inodes during format. OCFS2 dynamically allocates this space when required. However, this dynamic allocation has been problematic when the free space is very fragmented, because the file system required the inode and extent allocators to grow in contiguous fixed-size chunks. The discontiguous block group feature takes care of this problem by allowing the allocators to grow in smaller, variable-sized chunks. This feature was added in Linux kernel \fB2.6.35\fR and requires enabling on-disk feature \fBdiscontig-bg\fR. .TP \fBBACKUP SUPER BLOCKS\fR A file system super block stores critical information that is hard to recreate. In OCFS2, it stores the block size, cluster size, and the locations of the root and system directories, among other things. As this block is close to the start of the disk, it is very susceptible to being overwritten by an errant write. Say, dd if=file of=/dev/sda1. Backup super blocks are copies of the super block. These blocks are dispersed in the volume to minimize the chances of being overwritten. On the small chance that the original gets corrupted, the backups are available to scan and fix the corruption. \fBmkfs.ocfs2(8)\fR enables this feature by default. Users can disable this by specifying \fB--fs-features=nobackup-super\fR during format. \fBo2info(1)\fR can be used to view whether the feature has been enabled on a device. .nf .ps 8 .ft 6 # o2info --fs-features /dev/sdb1 backup-super strict-journal-super sparse extended-slotmap inline-data xattr indexed-dirs refcount discontig-bg clusterinfo unwritten .ft .ps .fi In OCFS2, the super block is on the third block. The backups are located at the \fB1G, 4G, 16G, 64G, 256G and 1T\fB byte offsets. The actual number of backup blocks depends on the size of the device. The super block is not backed up on devices smaller than 1GB. \fBfsck.ocfs2(8)\fR refers to these six offsets by numbers, 1 to 6. Users can specify any backup with the -r option to recover the volume. The example below uses the second backup. If successful, \fBfsck.ocfs2(8)\fR overwrites the corrupted super block with the backup. .nf .ps 8 .ft 6 # fsck.ocfs2 -f -r 2 /dev/sdb1 fsck.ocfs2 1.8.0 [RECOVER_BACKUP_SUPERBLOCK] Recover superblock information from backup block#1048576? y Checking OCFS2 filesystem in /dev/sdb1: Label: webhome UUID: B3E021A2A12B4D0EB08E9E986CDC7947 Number of blocks: 13107196 Block size: 4096 Number of clusters: 13107196 Cluster size: 4096 Number of slots: 8 /dev/sdb1 was run with -f, check forced. Pass 0a: Checking cluster allocation chains Pass 0b: Checking inode allocation chains Pass 0c: Checking extent block allocation chains Pass 1: Checking inodes and blocks. Pass 2: Checking directory entries. Pass 3: Checking directory connectivity. Pass 4a: checking for orphaned inodes Pass 4b: Checking inodes link counts. All passes succeeded. .ft .ps .fi .TP \fBSYNTHETIC FILE SYSTEMS\fR The OCFS2 development effort included two synthetic file systems, configfs and dlmfs. It also makes use of a third, debugfs. .RS .TP \fBconfigfs\fR configfs has since been accepted as a generic kernel component and is also used by netconsole and fs/dlm. OCFS2 tools use it to communicate the list of nodes in the cluster, details of the heartbeat device, cluster timeouts, and so on to the in-kernel node manager. The o2cb init script mounts this file system at /sys/kernel/config. .TP \fBdlmfs\fR dlmfs exposes the in-kernel o2dlm to the user-space. While it was developed primarily for OCFS2 tools, it has seen usage by others looking to add a cluster locking dimension in their applications. Users interested in doing the same should look at the libo2dlm library provided by ocfs2-tools. The o2cb init script mounts this file system at /dlm. .TP \fBdebugfs\fR OCFS2 uses debugfs to expose its in-kernel information to user space. For example, listing the file system cluster locks, dlm locks, dlm state, o2net state, etc. Users can access the information by mounting the file system at /sys/kernel/debug. To automount, add the following to /etc/fstab: debugfs /sys/kernel/debug debugfs defaults 0 0 .RE .TP \fBDISTRIBUTED LOCK MANAGER\fR One of the key technologies in a cluster is the lock manager, which maintains the locking state of all resources across the cluster. An easy implementation of a lock manager involves designating one node to handle everything. In this model, if a node wanted to acquire a lock, it would send the request to the lock manager. However, this model has a weakness: lock manager’s death causes the cluster to seize up. A better model is one where all nodes manage a subset of the lock resources. Each node maintains enough information for all the lock resources it is interested in. On event of a node death, the remaining nodes pool in the information to reconstruct the lock state maintained by the dead node. In this scheme, the locking overhead is distributed amongst all the nodes. Hence, the term distributed lock manager. O2DLM is a distributed lock manager. It is based on the specification titled "Programming Locking Application" written by Kristin Thomas and is available at the following link. http://opendlm.sourceforge.net/cvsmirror/opendlm/docs/dlmbook_final.pdf .TP \fBDLM DEBUGGING\fR O2DLM has a rich debugging infrastructure that allows it to show the state of the lock manager, all the lock resources, among other things. The figure below shows the dlm state of a nine-node cluster that has just lost three nodes: 12, 32, and 35. It can be ascertained that node 7, the recovery master, is currently recovering node 12 and has received the lock states of the dead node from all other live nodes. .nf .ps 9 .ft 6 # cat /sys/kernel/debug/o2dlm/45F81E3B6F2B48CCAAD1AE7945AB2001/dlm_state Domain: 45F81E3B6F2B48CCAAD1AE7945AB2001 Key: 0x10748e61 Thread Pid: 24542 Node: 7 State: JOINED Number of Joins: 1 Joining Node: 255 Domain Map: 7 31 33 34 40 50 Live Map: 7 31 33 34 40 50 Lock Resources: 48850 (439879) MLEs: 0 (1428625) Blocking: 0 (1066000) Mastery: 0 (362625) Migration: 0 (0) Lists: Dirty=Empty Purge=Empty PendingASTs=Empty PendingBASTs=Empty Purge Count: 0 Refs: 1 Dead Node: 12 Recovery Pid: 24543 Master: 7 State: ACTIVE Recovery Map: 12 32 35 Recovery Node State: 7 - DONE 31 - DONE 33 - DONE 34 - DONE 40 - DONE 50 - DONE .ft .ps .fi The figure below shows the state of a dlm lock resource that is mastered (owned) by node 25, with 6 locks in the granted queue and node 26 holding the EX (writelock) lock on that resource. .nf .ps 9 .ft 6 # debugfs.ocfs2 -R "dlm_locks M000000000000000022d63c00000000" /dev/sda1 Lockres: M000000000000000022d63c00000000 Owner: 25 State: 0x0 Last Used: 0 ASTs Reserved: 0 Inflight: 0 Migration Pending: No Refs: 8 Locks: 6 On Lists: None Reference Map: 26 27 28 94 95 Lock-Queue Node Level Conv Cookie Refs AST BAST Pending-Action Granted 94 NL -1 94:3169409 2 No No None Granted 28 NL -1 28:3213591 2 No No None Granted 27 NL -1 27:3216832 2 No No None Granted 95 NL -1 95:3178429 2 No No None Granted 25 NL -1 25:3513994 2 No No None Granted 26 EX -1 26:3512906 2 No No None .ft .ps .fi The figure below shows a lock from the file system perspective. Specifically, it shows a lock that is in the process of being upconverted from a NL to EX. Locks in this state are are referred to in the file system as busy locks and can be listed using the debugfs.ocfs2 command, "fs_locks -B". .nf .ps 9 .ft 6 # debugfs.ocfs2 -R "fs_locks -B" /dev/sda1 Lockres: M000000000000000000000b9aba12ec Mode: No Lock Flags: Initialized Attached Busy RO Holders: 0 EX Holders: 0 Pending Action: Convert Pending Unlock Action: None Requested Mode: Exclusive Blocking Mode: No Lock PR > Gets: 0 Fails: 0 Waits Total: 0us Max: 0us Avg: 0ns EX > Gets: 1 Fails: 0 Waits Total: 544us Max: 544us Avg: 544185ns Disk Refreshes: 1 .ft .ps .fi With this debugging infrastructure in place, users can debug hang issues as follows: .in +4n * Dump the busy fs locks for all the OCFS2 volumes on the node with hanging processes. If no locks are found, then the problem is not related to O2DLM. * Dump the corresponding dlm lock for all the busy fs locks. Note down the owner (master) of all the locks. * Dump the dlm locks on the master node for each lock. .in At this stage, one should note that the hanging node is waiting to get an AST from the master. The master, on the other hand, cannot send the AST until the current holder has down converted that lock, which it will do upon receiving a Blocking AST. However, a node can only down convert if all the lock holders have stopped using that lock. After dumping the dlm lock on the master node, identify the current lock holder and dump both the dlm and fs locks on that node. The trick here is to see whether the Blocking AST message has been relayed to file system. If not, the problem is in the dlm layer. If it has, then the most common reason would be a lock holder, the count for which is maintained in the fs lock. At this stage, printing the list of process helps. .nf .ft 6 $ ps -e -o pid,stat,comm,wchan=WIDE-WCHAN-COLUMN .ft .fi Make a note of all D state processes. At least one of them is responsible for the hang on the first node. The challenge then is to figure out why those processes are hanging. Failing that, at least get enough information (like alt-sysrq t output) for the kernel developers to review. What to do next depends on where the process is hanging. If it is waiting for the I/O to complete, the problem could be anywhere in the I/O subsystem, from the block device layer through the drivers to the disk array. If the hang concerns a user lock (flock(2)), the problem could be in the user’s application. A possible solution could be to kill the holder. If the hang is due to tight or fragmented memory, free up some memory by killing non-essential processes. The thing to note is that the symptom for the problem was on one node but the cause is on another. The issue can only be resolved on the node holding the lock. Sometimes, the best solution will be to reset that node. Once killed, the O2DLM recovery process will clear all locks owned by the dead node and let the cluster continue to operate. As harsh as that sounds, at times it is the only solution. The good news is that, by following the trail, you now have enough information to file a bug and get the real issue resolved. .TP \fBNFS EXPORTING\fR OCFS2 volumes can be exported as NFS volumes. This support is limited to NFS version 3, which translates to Linux kernel version 2.4 or later. If the version of the Linux kernel on the system exporting the volume is older than \fB2.6.30\fR, then the NFS clients must mount the volumes using the \fInordirplus\fR mount option. This disables the READDIRPLUS RPC call to workaround a bug in NFSD, detailed in the following link: .nf .ps 9 .ft 6 http://oss.oracle.com/pipermail/ocfs2-announce/2008-June/000025.html .ft .ps .fi Users running NFS version 2 can export the volume after having disabled subtree checking (mount option no_subtree_check). Be warned, disabling the check has security implications (documented in the exports(5) man page) that users must evaluate on their own. .TP \fBFILE SYSTEM LIMITS\fR OCFS2 has no intrinsic limit on the total number of files and directories in the file system. In general, it is only limited by the size of the device. But there is one limit imposed by the current filesystem. It can address at most four billion clusters. A file system with 1MB cluster size can go up to 4PB, while a file system with a 4KB cluster size can address up to 16TB. .TP \fBSYSTEM OBJECTS\fR The OCFS2 file system stores its internal meta-data, including bitmaps, journals, etc., as system files. These are grouped in a system directory. These files and directories are not accessible via the file system interface but can be viewed using the \fBdebugfs.ocfs2(8)\fR tool. To list the system directory (referred to as double-slash), do: .nf .ps 8 .ft 6 # debugfs.ocfs2 -R "ls -l //" /dev/sde1 66 drwxr-xr-x 10 0 0 3896 19-Jul-2011 13:36 . 66 drwxr-xr-x 10 0 0 3896 19-Jul-2011 13:36 .. 67 -rw-r--r-- 1 0 0 0 19-Jul-2011 13:36 bad_blocks 68 -rw-r--r-- 1 0 0 1179648 19-Jul-2011 13:36 global_inode_alloc 69 -rw-r--r-- 1 0 0 4096 19-Jul-2011 14:35 slot_map 70 -rw-r--r-- 1 0 0 1048576 19-Jul-2011 13:36 heartbeat 71 -rw-r--r-- 1 0 0 53686960128 19-Jul-2011 13:36 global_bitmap 72 drwxr-xr-x 2 0 0 3896 25-Jul-2011 15:05 orphan_dir:0000 73 drwxr-xr-x 2 0 0 3896 19-Jul-2011 13:36 orphan_dir:0001 74 -rw-r--r-- 1 0 0 8388608 19-Jul-2011 13:36 extent_alloc:0000 75 -rw-r--r-- 1 0 0 8388608 19-Jul-2011 13:36 extent_alloc:0001 76 -rw-r--r-- 1 0 0 121634816 19-Jul-2011 13:36 inode_alloc:0000 77 -rw-r--r-- 1 0 0 0 19-Jul-2011 13:36 inode_alloc:0001 77 -rw-r--r-- 1 0 0 268435456 19-Jul-2011 13:36 journal:0000 79 -rw-r--r-- 1 0 0 268435456 19-Jul-2011 13:37 journal:0001 80 -rw-r--r-- 1 0 0 0 19-Jul-2011 13:36 local_alloc:0000 81 -rw-r--r-- 1 0 0 0 19-Jul-2011 13:36 local_alloc:0001 82 -rw-r--r-- 1 0 0 0 19-Jul-2011 13:36 truncate_log:0000 83 -rw-r--r-- 1 0 0 0 19-Jul-2011 13:36 truncate_log:0001 .ft 1 .ps .fi The file names that end with numbers are slot specific and are referred to as node-local system files. The set of node-local files used by a node can be determined from the slot map. To list the slot map, do: .nf .ft 6 # debugfs.ocfs2 -R "slotmap" /dev/sde1 Slot# Node# 0 32 1 35 2 40 3 31 4 34 5 33 .ft 1 .fi For more information, refer to the OCFS2 support guides available in the Documentation section at http://oss.oracle.com/projects/ocfs2. .TP \fBHEARTBEAT, QUORUM, AND FENCING\fR Heartbeat is an essential component in any cluster. It is charged with accurately designating nodes as dead or alive. A mistake here could lead to a cluster hang or a corruption. \fIo2hb\fR is the disk heartbeat component of \fBo2cb\fR. It periodically updates a timestamp on disk, indicating to others that this node is alive. It also reads all the timestamps to identify other live nodes. Other cluster components, like \fIo2dlm\fR and \fIo2net\fR, use the \fIo2hb\fR service to get node up and down events. The quorum is the group of nodes in a cluster that is allowed to operate on the shared storage. When there is a failure in the cluster, nodes may be split into groups that can communicate in their groups and with the shared storage but not between groups. \fIo2quo\fR determines which group is allowed to continue and initiates fencing of the other group(s). Fencing is the act of forcefully removing a node from a cluster. A node with OCFS2 mounted will fence itself when it realizes that it does not have quorum in a degraded cluster. It does this so that other nodes won’t be stuck trying to access its resources. \fBo2cb\fR uses a machine reset to fence. This is the quickest route for the node to rejoin the cluster. .TP \fBPROCESSES\fR .RS .TP \fB[o2net]\fR One per node. It is a work-queue thread started when the cluster is brought on-line and stopped when it is off-lined. It handles network communication for all mounts. It gets the list of active nodes from O2HB and sets up a TCP/IP communication channel with each live node. It sends regular keep-alive packets to detect any interruption on the channels. .TP \fB[user_dlm]\fR One per node. It is a work-queue thread started when dlmfs is loaded and stopped when it is unloaded (dlmfs is a synthetic file system that allows user space processes to access the in-kernel dlm). .TP \fB[ocfs2_wq]\fR One per node. It is a work-queue thread started when the OCFS2 module is loaded and stopped when it is unloaded. It is assigned background file system tasks that may take cluster locks like flushing the truncate log, orphan directory recovery and local alloc recovery. For example, orphan directory recovery runs in the background so that it does not affect recovery time. .TP \fB[o2hb-14C29A7392]\fR One per heartbeat device. It is a kernel thread started when the heartbeat region is populated in configfs and stopped when it is removed. It writes every two seconds to a block in the heartbeat region, indicating that this node is alive. It also reads the region to maintain a map of live nodes. It notifies subscribers like o2net and o2dlm of any changes in the live node map. .TP \fB[ocfs2dc]\fR One per mount. It is a kernel thread started when a volume is mounted and stopped when it is unmounted. It downgrades locks in response to blocking ASTs (BASTs) requested by other nodes. .TP \fB[jbd2/sdf1-97]\fR One per mount. It is part of JBD2, which OCFS2 uses for journaling. .TP \fB[ocfs2cmt]\fR One per mount. It is a kernel thread started when a volume is mounted and stopped when it is unmounted. It works with kjournald2. .TP \fB[ocfs2rec]\fR It is started whenever a node has to be recovered. This thread performs file system recovery by replaying the journal of the dead node. It is scheduled to run after dlm recovery has completed. .TP \fB[dlm_thread]\fR One per dlm domain. It is a kernel thread started when a dlm domain is created and stopped when it is destroyed. This thread sends ASTs and blocking ASTs in response to lock level convert requests. It also frees unused lock resources. .TP \fB[dlm_reco_thread]\fR One per dlm domain. It is a kernel thread that handles dlm recovery when another node dies. If this node is the dlm recovery master, it re-masters every lock resource owned by the dead node. .TP \fB[dlm_wq]\fR One per dlm domain. It is a work-queue thread that o2dlm uses to queue blocking tasks. .RE .TP \fBFUTURE WORK\fR File system development is a never ending cycle. Faster and larger disks, faster and more number of processors, larger caches, etc. keep changing the sweet spot for performance forcing developers to rethink long held beliefs. Add to that new use cases, which forces developers to be innovative in providing solutions that melds seamlessly with existing semantics. We are currently looking to add features like transparent compression, transparent encryption, delayed allocation, multi-device support, etc. as well as work on improving performance on newer generation machines. If you are interested in contributing, email the development team at ocfs2-devel@oss.oracle.com. .SH "ACKNOWLEDGEMENTS" .PP The principal developers of the OCFS2 file system, its tools and the O2CB cluster stack, are \fIJoel Becker\fR, \fIZach Brown\fR, \fIMark Fasheh\fR, \fIJan Kara\fR, \fIKurt Hackel\fR, \fITao Ma\fR, \fISunil Mushran\fR, \fITiger Yang\fR and \fITristan Ye\fR. Other developers who have contributed to the file system via bug fixes, testing, etc. are \fIWim Coekaerts\fR, \fISrinivas Eeda\fR, \fIColy Li\fR, \fIJeff Mahoney\fR, \fIMarcos Matsunaga\fR, \fIGoldwyn Rodrigues\fR, \fIManish Singh\fR and \fIWengang Wang\fR. The members of the Linux Cluster community including \fIAndrew Beekhof\fR, \fILars Marowsky-Bree\fR, \fIFabio Massimo Di Nitto\fR and \fIDavid Teigland\fR. The members of the Linux File system community including \fIChristoph Hellwig\fR and \fIChris Mason\fR. The corporations that have contributed resources for this project including \fIOracle\fR, \fISUSE Labs\fR, \fIEMC\fR, \fIEmulex\fR, \fIHP\fR, \fIIBM\fR, \fIIntel\fR and \fINetwork Appliance\fR. .SH "SEE ALSO" .BR debugfs.ocfs2(8) .BR fsck.ocfs2(8) .BR fsck.ocfs2.checks(8) .BR mkfs.ocfs2(8) .BR mount.ocfs2(8) .BR mounted.ocfs2(8) .BR o2cluster(8) .BR o2image(8) .BR o2info(1) .BR o2cb(7) .BR o2cb(8) .BR o2cb.sysconfig(5) .BR o2hbmonitor(8) .BR ocfs2.cluster.conf(5) .BR tunefs.ocfs2(8) .SH "AUTHOR" Oracle Corporation .SH "COPYRIGHT" Copyright \(co 2004, 2012 Oracle. All rights reserved. ocfs2-tools-ocfs2-tools-1.8.6/libocfs2/ocfs2_err.et000066400000000000000000000111211347147137200217760ustar00rootroot00000000000000# # ocfs2_err.et # # Error codes for the OCFS2 userspace library. # # Copyright (C) 2004, 2007 Oracle. All rights reserved. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public # License, version 2, as published by the Free Software Foundation. # # 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., 51 Franklin St, Fifth Floor, # Boston, MA 02110-1301 USA. # error_table ocfs ec OCFS2_ET_NAMED_DEVICE_NOT_FOUND, "Device name specified was not found" ec OCFS2_ET_BAD_DEVICE_NAME, "Illegal or malformed device name" ec OCFS2_ET_IO, "I/O error on channel" ec OCFS2_ET_SHORT_READ, "Attempt to read block from filesystem resulted in short read" ec OCFS2_ET_SHORT_WRITE, "Attempt to write block to the filesystem resulted in short write" ec OCFS2_ET_NO_MEMORY, "Memory allocation failed" ec OCFS2_ET_INVALID_ARGUMENT, "Invalid argument passed to OCFS2 library" ec OCFS2_ET_OCFS_REV, "Device contains an OCFS volume" ec OCFS2_ET_BAD_MAGIC, "Bad magic number in superblock" ec OCFS2_ET_UNEXPECTED_BLOCK_SIZE, "Filesystem has unexpected block size" ec OCFS2_ET_CORRUPT_SUPERBLOCK, "The OCFS2 superblock is corrupt" ec OCFS2_ET_UNSUPP_FEATURE, "Unsupported feature(s) found" ec OCFS2_ET_RO_UNSUPP_FEATURE, "Unsupported read-only feature(s) found" ec OCFS2_ET_CONFLICTING_FEATURES, "Conflicting features specified" ec OCFS2_ET_BAD_INODE_MAGIC, "Bad magic number in inode" ec OCFS2_ET_BAD_BLKNO, "Invalid block number" ec OCFS2_ET_RO_FILESYS, "Attempt to write to filesystem opened read-only" ec OCFS2_ET_JOURNAL_TOO_SMALL, "Journal must be at least 1024 blocks" ec OCFS2_ET_BAD_JOURNAL_SUPERBLOCK_MAGIC, "Bad magic number in journal superblock" ec OCFS2_ET_BAD_JOURNAL_REVOKE, "Invalid revoke record in journal" ec OCFS2_ET_BAD_JOURNAL_TAG, "Invalid block tag in journal" ec OCFS2_ET_INODE_NOT_VALID, "Inode is not valid (active)" ec OCFS2_ET_INODE_CANNOT_BE_ITERATED, "Inode type does not contain extents" ec OCFS2_ET_BAD_EXTENT_BLOCK_MAGIC, "Bad magic number in extent block" ec OCFS2_ET_CORRUPT_EXTENT_BLOCK, "Extent block is corrupt" ec OCFS2_ET_DIR_CORRUPTED, "OCFS2 directory corrupted" ec OCFS2_ET_NO_DIRECTORY, "OCFS2 inode is not a directory" ec OCFS2_ET_FILE_NOT_FOUND, "Directory entry not found" ec OCFS2_ET_DIR_NO_SPACE, "No free space in the directory" ec OCFS2_ET_INVALID_BIT, "Bit does not exist in bitmap range" ec OCFS2_ET_INTERNAL_FAILURE, "Internal logic failure" ec OCFS2_ET_BAD_GROUP_DESC_MAGIC, "Bad magic number in group descriptor" ec OCFS2_ET_CORRUPT_GROUP_DESC, "Group descriptor is corrupt" ec OCFS2_ET_CORRUPT_CHAIN, "Chain allocator is corrupt" ec OCFS2_ET_INVALID_EXTENT_LOOKUP, "Invalid range passed to extent map lookup" ec OCFS2_ET_EXTENT_NOT_FOUND, "No mapping exists for the given extent range" ec OCFS2_ET_DUPLICATE_BLOCK, "Duplicate block discovered" ec OCFS2_ET_BIT_NOT_FOUND, "Unable to find available bit" ec OCFS2_ET_FREEING_UNALLOCATED_REGION, "Attempting to free unallocated region" ec OCFS2_ET_EXPAND_DIR_ERR, "Unable to expand directory" ec OCFS2_ET_NO_SPACE, "No space available" ec OCFS2_ET_ITERATION_COMPLETE, "Iteration complete" ec OCFS2_ET_SYMLINK_LOOP, "Too many symbolic links encountered" ec OCFS2_ET_BAD_HEARTBEAT_FILE, "Invalid heartbeat file" ec OCFS2_ET_UNKNOWN_FILESYSTEM, "Unknown filesystem" ec OCFS2_ET_BLOCK_SIZE_TOO_SMALL_FOR_HARDWARE, "The block size is smaller than the sector size on this device" ec OCFS2_ET_INVALID_LOCKRES, "The lock name is invalid" ec OCFS2_ET_NO_IONICE, "Unable to find ionice(1)" ec OCFS2_ET_NO_BACKUP_SUPER, "Backup superblock not found" ec OCFS2_ET_TOO_MANY_SLOTS, "Too many slots for slot map" ec OCFS2_ET_CANNOT_INLINE_DATA, "Unable to write the data inline" ec OCFS2_ET_BAD_DIR_BLOCK_MAGIC, "Bad magic number in directory block" ec OCFS2_ET_BAD_XATTR_BLOCK_MAGIC, "Bad magic number in xattr block" ec OCFS2_ET_UNKNOWN_FEATURE, "Unknown feature" ec OCFS2_ET_CORRUPT_QUOTA_FILE, "Quota file is corrupted" ec OCFS2_ET_CANNOT_DETERMINE_SECTOR_SIZE, "Unable to determine the sector size" ec OCFS2_ET_DX_BALANCE_EMPTY_LEAF, "Trying to rebalance empty leaf for indexed dir" ec OCFS2_ET_NONEMTY_QUOTA_HASH, "Freeing non-empty quota hash" ec OCFS2_ET_BAD_CRC32, "Bad CRC32" end ocfs2-tools-ocfs2-tools-1.8.6/libocfs2/openfs.c000066400000000000000000000327001347147137200212240ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * openfs.c * * Open an OCFS2 filesystem. Part of the OCFS2 userspace library. * * Copyright (C) 2004 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License, version 2, as published by the Free Software Foundation. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * * Ideas taken from e2fsprogs/lib/ext2fs/openfs.c * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o. */ #define _XOPEN_SOURCE 600 /* Triggers XOPEN2K in features.h */ #define _LARGEFILE64_SOURCE #include #include #include #include /* I hate glibc and gcc */ #ifndef ULLONG_MAX # define ULLONG_MAX 18446744073709551615ULL #endif #include "ocfs2/byteorder.h" #include "ocfs2/ocfs2.h" #include "ocfs2-kernel/ocfs1_fs_compat.h" #include "ocfs2/image.h" /* * if the file is an o2image file, this routine maps the actual blockno to * relative block number in image file and then calls the underlying IO * function. At this point this function returns EIO if image file has any * holes */ static errcode_t __ocfs2_read_blocks(ocfs2_filesys *fs, int64_t blkno, int count, char *data, bool nocache) { int i; errcode_t err; if (fs->fs_flags & OCFS2_FLAG_IMAGE_FILE) { /* * o2image copies all meta blocks. If a caller asks for * N contiguous metadata blocks, all N should be in the * image file. However we check for any holes and * return -EIO if any. */ for (i = 0; i < count; i++) if (!ocfs2_image_test_bit(fs, blkno+i)) return OCFS2_ET_IO; /* translate the block number */ blkno = ocfs2_image_get_blockno(fs, blkno); } if (nocache) err = io_read_block_nocache(fs->fs_io, blkno, count, data); else err = io_read_block(fs->fs_io, blkno, count, data); return err; } errcode_t ocfs2_read_blocks_nocache(ocfs2_filesys *fs, int64_t blkno, int count, char *data) { return __ocfs2_read_blocks(fs, blkno, count, data, true); } errcode_t ocfs2_read_blocks(ocfs2_filesys *fs, int64_t blkno, int count, char *data) { return __ocfs2_read_blocks(fs, blkno, count, data, false); } static errcode_t ocfs2_validate_ocfs1_header(ocfs2_filesys *fs) { errcode_t ret; char *blk; struct ocfs1_vol_disk_hdr *hdr; ret = ocfs2_malloc_block(fs->fs_io, &blk); if (ret) return ret; ret = ocfs2_read_blocks(fs, 0, 1, blk); if (ret) goto out; hdr = (struct ocfs1_vol_disk_hdr *)blk; ret = OCFS2_ET_OCFS_REV; if (le32_to_cpu(hdr->major_version) == OCFS1_MAJOR_VERSION) goto out; if (!memcmp(hdr->signature, OCFS1_VOLUME_SIGNATURE, strlen(OCFS1_VOLUME_SIGNATURE))) goto out; ret = 0; out: ocfs2_free(&blk); return ret; } errcode_t ocfs2_read_super(ocfs2_filesys *fs, uint64_t superblock, char *sb) { errcode_t ret; char *blk, *swapblk; struct ocfs2_dinode *di, *orig_super; int orig_blocksize; int blocksize = io_get_blksize(fs->fs_io); ret = ocfs2_malloc_block(fs->fs_io, &blk); if (ret) return ret; ret = ocfs2_read_blocks(fs, superblock, 1, blk); if (ret) goto out_blk; di = (struct ocfs2_dinode *)blk; ret = OCFS2_ET_BAD_MAGIC; if (memcmp(di->i_signature, OCFS2_SUPER_BLOCK_SIGNATURE, strlen(OCFS2_SUPER_BLOCK_SIGNATURE))) goto out_blk; /* * We want to use the latest superblock to validate. We need * a local-endian copy in fs->fs_super, and the unswapped copy to * check in blk. ocfs2_validate_meta_ecc() uses fs->fs_super and * fs->fs_blocksize. */ ret = ocfs2_malloc_block(fs->fs_io, &swapblk); if (ret) goto out_blk; memcpy(swapblk, blk, blocksize); orig_super = fs->fs_super; orig_blocksize = fs->fs_blocksize; fs->fs_super = (struct ocfs2_dinode *)swapblk; fs->fs_blocksize = blocksize; ocfs2_swap_inode_to_cpu(fs, fs->fs_super); ret = ocfs2_validate_meta_ecc(fs, blk, &di->i_check); fs->fs_super = orig_super; fs->fs_blocksize = orig_blocksize; ocfs2_free(&swapblk); if (ret) goto out_blk; ocfs2_swap_inode_to_cpu(fs, di); if (!sb) fs->fs_super = di; else { memcpy(sb, blk, fs->fs_blocksize); ocfs2_free(&blk); } return 0; out_blk: ocfs2_free(&blk); return ret; } errcode_t ocfs2_write_primary_super(ocfs2_filesys *fs) { errcode_t ret; char *blk; struct ocfs2_dinode *di; if (!(fs->fs_flags & OCFS2_FLAG_RW)) return OCFS2_ET_RO_FILESYS; blk = (char *)fs->fs_super; di = (struct ocfs2_dinode *)blk; ret = OCFS2_ET_BAD_MAGIC; if (memcmp(di->i_signature, OCFS2_SUPER_BLOCK_SIGNATURE, strlen(OCFS2_SUPER_BLOCK_SIGNATURE))) goto out_blk; ret = ocfs2_write_inode(fs, OCFS2_SUPER_BLOCK_BLKNO, blk); if (ret) goto out_blk; return 0; out_blk: return ret; } errcode_t ocfs2_write_super(ocfs2_filesys *fs) { errcode_t ret; ret = ocfs2_write_primary_super(fs); if (!ret) ret = ocfs2_refresh_backup_supers(fs); return ret; } errcode_t ocfs2_write_backup_super(ocfs2_filesys *fs, uint64_t blkno) { errcode_t ret; char *buf = NULL; struct ocfs2_dinode *di; if (!(fs->fs_flags & OCFS2_FLAG_RW)) return OCFS2_ET_RO_FILESYS; ret = ocfs2_malloc_block(fs->fs_io, &buf); if (ret) goto out_blk; memcpy(buf, (char *)fs->fs_super, fs->fs_blocksize); di = (struct ocfs2_dinode *)buf; ret = OCFS2_ET_BAD_MAGIC; if (memcmp(di->i_signature, OCFS2_SUPER_BLOCK_SIGNATURE, strlen(OCFS2_SUPER_BLOCK_SIGNATURE))) goto out_blk; di->i_blkno = blkno; OCFS2_SET_COMPAT_FEATURE(OCFS2_RAW_SB(di), OCFS2_FEATURE_COMPAT_BACKUP_SB); ret = ocfs2_write_inode(fs, blkno, buf); if (ret) goto out_blk; ret = 0; out_blk: if (buf) ocfs2_free(&buf); return ret; } int ocfs2_mount_local(ocfs2_filesys *fs) { return OCFS2_RAW_SB(fs->fs_super)->s_feature_incompat & OCFS2_FEATURE_INCOMPAT_LOCAL_MOUNT; } int ocfs2_is_hard_readonly(ocfs2_filesys *fs) { return fs->fs_flags & OCFS2_FLAG_HARD_RO; } errcode_t ocfs2_open(const char *name, int flags, unsigned int superblock, unsigned int block_size, ocfs2_filesys **ret_fs) { ocfs2_filesys *fs; errcode_t ret; int i, len; char *ptr; unsigned char *raw_uuid; ret = ocfs2_malloc0(sizeof(ocfs2_filesys), &fs); if (ret) return ret; fs->fs_flags = flags; fs->fs_umask = 022; ret = io_open(name, (flags & (OCFS2_FLAG_RO | OCFS2_FLAG_RW | OCFS2_FLAG_BUFFERED)), &fs->fs_io); if (ret) goto out; ret = ocfs2_malloc(strlen(name)+1, &fs->fs_devname); if (ret) goto out; strcpy(fs->fs_devname, name); /* * If OCFS2_FLAG_IMAGE_FILE is specified, it needs to be handled * differently */ if (flags & OCFS2_FLAG_IMAGE_FILE) { ret = ocfs2_image_load_bitmap(fs); if (ret) goto out; if (!superblock) superblock = fs->ost->ost_superblocks[0]; if (!block_size) block_size = fs->ost->ost_fsblksz; } /* image file is not a device */ if (!(flags & OCFS2_FLAG_IMAGE_FILE)) { if (io_is_device_readonly(fs->fs_io)) fs->fs_flags |= OCFS2_FLAG_HARD_RO; } /* * If OCFS2_FLAG_NO_REV_CHECK is specified, fsck (or someone * like it) is asking to ignore the OCFS vol_header at * block 0. */ if (!(flags & OCFS2_FLAG_NO_REV_CHECK)) { ret = ocfs2_validate_ocfs1_header(fs); if (ret) goto out; } if (superblock) { ret = OCFS2_ET_INVALID_ARGUMENT; if (!block_size) goto out; io_set_blksize(fs->fs_io, block_size); ret = ocfs2_read_super(fs, (uint64_t)superblock, NULL); } else { superblock = OCFS2_SUPER_BLOCK_BLKNO; if (block_size) { io_set_blksize(fs->fs_io, block_size); ret = ocfs2_read_super(fs, (uint64_t)superblock, NULL); } else { for (block_size = io_get_blksize(fs->fs_io); block_size <= OCFS2_MAX_BLOCKSIZE; block_size <<= 1) { io_set_blksize(fs->fs_io, block_size); ret = ocfs2_read_super(fs, (uint64_t)superblock, NULL); if ((ret == OCFS2_ET_BAD_MAGIC) || (ret == OCFS2_ET_IO)) continue; break; } } } if (ret) goto out; fs->fs_blocksize = block_size; if (superblock == OCFS2_SUPER_BLOCK_BLKNO) { ret = ocfs2_malloc_block(fs->fs_io, &fs->fs_orig_super); if (ret) goto out; memcpy((char *)fs->fs_orig_super, (char *)fs->fs_super, fs->fs_blocksize); } #if 0 ret = OCFS2_ET_REV_TOO_HIGH; if (fs->fs_super->id2.i_super.s_major_rev_level > OCFS2_LIB_CURRENT_REV) goto out; #endif if (flags & OCFS2_FLAG_STRICT_COMPAT_CHECK) { ret = OCFS2_ET_UNSUPP_FEATURE; if (OCFS2_RAW_SB(fs->fs_super)->s_feature_compat & ~OCFS2_LIB_FEATURE_COMPAT_SUPP) goto out; /* We need to check s_tunefs_flag also to make sure * fsck.ocfs2 won't try to clean up an aborted tunefs * that it doesn't know. */ if (OCFS2_HAS_INCOMPAT_FEATURE(OCFS2_RAW_SB(fs->fs_super), OCFS2_FEATURE_INCOMPAT_TUNEFS_INPROG) && (OCFS2_RAW_SB(fs->fs_super)->s_tunefs_flag & ~OCFS2_LIB_ABORTED_TUNEFS_SUPP)) goto out; } ret = OCFS2_ET_UNSUPP_FEATURE; if (OCFS2_RAW_SB(fs->fs_super)->s_feature_incompat & ~OCFS2_LIB_FEATURE_INCOMPAT_SUPP) goto out; ret = OCFS2_ET_RO_UNSUPP_FEATURE; if ((flags & OCFS2_FLAG_RW) && (OCFS2_RAW_SB(fs->fs_super)->s_feature_ro_compat & ~OCFS2_LIB_FEATURE_RO_COMPAT_SUPP)) goto out; ret = OCFS2_ET_UNSUPP_FEATURE; if (!(flags & OCFS2_FLAG_HEARTBEAT_DEV_OK) && (OCFS2_RAW_SB(fs->fs_super)->s_feature_incompat & OCFS2_FEATURE_INCOMPAT_HEARTBEAT_DEV)) goto out; ret = OCFS2_ET_CORRUPT_SUPERBLOCK; if (!OCFS2_RAW_SB(fs->fs_super)->s_blocksize_bits) goto out; if (fs->fs_super->i_blkno != superblock) goto out; if ((OCFS2_RAW_SB(fs->fs_super)->s_clustersize_bits < 12) || (OCFS2_RAW_SB(fs->fs_super)->s_clustersize_bits > 20)) goto out; if (!OCFS2_RAW_SB(fs->fs_super)->s_root_blkno || !OCFS2_RAW_SB(fs->fs_super)->s_system_dir_blkno) goto out; if (OCFS2_RAW_SB(fs->fs_super)->s_max_slots > OCFS2_MAX_SLOTS) goto out; ret = ocfs2_malloc0(OCFS2_RAW_SB(fs->fs_super)->s_max_slots * sizeof(ocfs2_cached_inode *), &fs->fs_inode_allocs); if (ret) goto out; ret = ocfs2_malloc0(OCFS2_RAW_SB(fs->fs_super)->s_max_slots * sizeof(ocfs2_cached_inode *), &fs->fs_eb_allocs); if (ret) goto out; ret = OCFS2_ET_UNEXPECTED_BLOCK_SIZE; if (block_size != (1U << OCFS2_RAW_SB(fs->fs_super)->s_blocksize_bits)) goto out; fs->fs_clustersize = 1 << OCFS2_RAW_SB(fs->fs_super)->s_clustersize_bits; /* FIXME: Read the system dir */ fs->fs_root_blkno = OCFS2_RAW_SB(fs->fs_super)->s_root_blkno; fs->fs_sysdir_blkno = OCFS2_RAW_SB(fs->fs_super)->s_system_dir_blkno; fs->fs_clusters = fs->fs_super->i_clusters; fs->fs_blocks = ocfs2_clusters_to_blocks(fs, fs->fs_clusters); fs->fs_first_cg_blkno = OCFS2_RAW_SB(fs->fs_super)->s_first_cluster_group; raw_uuid = OCFS2_RAW_SB(fs->fs_super)->s_uuid; for (i = 0, ptr = fs->uuid_str; i < OCFS2_VOL_UUID_LEN; i++) { /* print with null */ len = snprintf(ptr, 3, "%02X", raw_uuid[i]); if (len != 2) { ret = OCFS2_ET_INTERNAL_FAILURE; goto out; } /* then only advace past the last char */ ptr += 2; } *ret_fs = fs; return 0; out: if (fs->fs_inode_allocs) ocfs2_free(&fs->fs_inode_allocs); ocfs2_freefs(fs); return ret; } #ifdef DEBUG_EXE #include #include #include static int64_t read_number(const char *num) { int64_t val; char *ptr; val = strtoll(num, &ptr, 0); if (!ptr || *ptr) return 0; return val; } static void print_usage(void) { fprintf(stderr, "Usage: openfs [-s ] [-B ]\n" " \n"); } extern int opterr, optind; extern char *optarg; int main(int argc, char *argv[]) { errcode_t ret; int c; int64_t blkno, blksize; char *filename; ocfs2_filesys *fs; /* These mean "autodetect" */ blksize = 0; blkno = 0; initialize_ocfs_error_table(); while((c = getopt(argc, argv, "s:B:")) != EOF) { switch (c) { case 's': blkno = read_number(optarg); if (blkno < OCFS2_SUPER_BLOCK_BLKNO) { fprintf(stderr, "Invalid blkno: %s\n", optarg); print_usage(); return 1; } break; case 'B': blksize = read_number(optarg); if (blksize < OCFS2_MIN_BLOCKSIZE) { fprintf(stderr, "Invalid blksize: %s\n", optarg); print_usage(); return 1; } break; default: print_usage(); return 1; break; } } if (blksize % OCFS2_MIN_BLOCKSIZE) { fprintf(stderr, "Invalid blocksize: %"PRId64"\n", blksize); print_usage(); return 1; } if (optind >= argc) { fprintf(stderr, "Missing filename\n"); print_usage(); return 1; } filename = argv[optind]; ret = ocfs2_open(filename, OCFS2_FLAG_RO, blkno, blksize, &fs); if (ret) { com_err(argv[0], ret, "while opening file \"%s\"", filename); goto out; } fprintf(stdout, "OCFS2 filesystem on \"%s\":\n", filename); fprintf(stdout, "\tblocksize = %d\n" "\tclustersize = %d\n" "\tclusters = %u\n" "\tblocks = %"PRIu64"\n" "\troot_blkno = %"PRIu64"\n" "\tsystem_dir_blkno = %"PRIu64"\n", fs->fs_blocksize, fs->fs_clustersize, fs->fs_clusters, fs->fs_blocks, fs->fs_root_blkno, fs->fs_sysdir_blkno); ret = ocfs2_close(fs); if (ret) { com_err(argv[0], ret, "while closing file \"%s\"", filename); } out: return 0; } #endif /* DEBUG_EXE */ ocfs2-tools-ocfs2-tools-1.8.6/libocfs2/quota.c000066400000000000000000001032671347147137200210720ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * quota.c * * Quota operations for the OCFS2 userspace library. * * Copyright (C) 2008 Novell. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License, version 2, as published by the Free Software Foundation. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. */ #include #include "ocfs2/byteorder.h" #include "ocfs2/ocfs2.h" void ocfs2_swap_quota_header(struct ocfs2_disk_dqheader *header) { if (cpu_is_little_endian) return; header->dqh_magic = bswap_32(header->dqh_magic); header->dqh_version = bswap_32(header->dqh_version); } void ocfs2_swap_quota_local_info(struct ocfs2_local_disk_dqinfo *info) { if (cpu_is_little_endian) return; info->dqi_flags = bswap_32(info->dqi_flags); info->dqi_chunks = bswap_32(info->dqi_chunks); info->dqi_blocks = bswap_32(info->dqi_blocks); } void ocfs2_swap_quota_chunk_header(struct ocfs2_local_disk_chunk *chunk) { if (cpu_is_little_endian) return; chunk->dqc_free = bswap_32(chunk->dqc_free); } void ocfs2_swap_quota_global_info(struct ocfs2_global_disk_dqinfo *info) { if (cpu_is_little_endian) return; info->dqi_bgrace = bswap_32(info->dqi_bgrace); info->dqi_igrace = bswap_32(info->dqi_igrace); info->dqi_syncms = bswap_32(info->dqi_syncms); info->dqi_blocks = bswap_32(info->dqi_blocks); info->dqi_free_blk = bswap_32(info->dqi_free_blk); info->dqi_free_entry = bswap_32(info->dqi_free_entry); } void ocfs2_swap_quota_global_dqblk(struct ocfs2_global_disk_dqblk *dqblk) { if (cpu_is_little_endian) return; dqblk->dqb_id = bswap_32(dqblk->dqb_id); dqblk->dqb_use_count = bswap_32(dqblk->dqb_use_count); dqblk->dqb_ihardlimit = bswap_64(dqblk->dqb_ihardlimit); dqblk->dqb_isoftlimit = bswap_64(dqblk->dqb_isoftlimit); dqblk->dqb_curinodes = bswap_64(dqblk->dqb_curinodes); dqblk->dqb_bhardlimit = bswap_64(dqblk->dqb_bhardlimit); dqblk->dqb_bsoftlimit = bswap_64(dqblk->dqb_bsoftlimit); dqblk->dqb_curspace = bswap_64(dqblk->dqb_curspace); dqblk->dqb_btime = bswap_64(dqblk->dqb_btime); dqblk->dqb_itime = bswap_64(dqblk->dqb_itime); } void ocfs2_swap_quota_leaf_block_header(struct qt_disk_dqdbheader *bheader) { if (cpu_is_little_endian) return; bheader->dqdh_next_free = bswap_32(bheader->dqdh_next_free); bheader->dqdh_prev_free = bswap_32(bheader->dqdh_prev_free); bheader->dqdh_entries = bswap_16(bheader->dqdh_entries); } /* Should be power of two */ #define DEFAULT_QUOTA_HASH_SIZE 8192 /* Maxinum number of hash buckets - use at most 16 MB on a 64-bit arch */ #define MAX_QUOTA_HASH_SIZE (1<<21) errcode_t ocfs2_new_quota_hash(ocfs2_quota_hash **hashp) { ocfs2_quota_hash *hash; errcode_t err; err = ocfs2_malloc(sizeof(ocfs2_quota_hash), &hash); if (err) return err; hash->alloc_entries = DEFAULT_QUOTA_HASH_SIZE; hash->used_entries = 0; err = ocfs2_malloc0(sizeof(ocfs2_quota_hash *) * DEFAULT_QUOTA_HASH_SIZE, &hash->hash); if (err) { ocfs2_free(&hash); return err; } *hashp = hash; return 0; } errcode_t ocfs2_free_quota_hash(ocfs2_quota_hash *hash) { errcode_t err = 0, ret; if (hash->used_entries) return OCFS2_ET_NONEMTY_QUOTA_HASH; ret = ocfs2_free(&hash->hash); if (!err && ret) err = ret; ret = ocfs2_free(&hash); if (!err && ret) err = ret; return err; } static int quota_hash(ocfs2_quota_hash *hash, qid_t id) { return (((unsigned long)id) * 5) & (hash->alloc_entries - 1); } static void quota_add_hash_chain(ocfs2_quota_hash *hash, ocfs2_cached_dquot *dquot) { int hash_val = quota_hash(hash, dquot->d_ddquot.dqb_id); dquot->d_next = hash->hash[hash_val]; if (dquot->d_next) dquot->d_next->d_pprev = &dquot->d_next; hash->hash[hash_val] = dquot; dquot->d_pprev = hash->hash + hash_val; } errcode_t ocfs2_insert_quota_hash(ocfs2_quota_hash *hash, ocfs2_cached_dquot *dquot) { errcode_t err; if (hash->used_entries > hash->alloc_entries && hash->alloc_entries * 2 < MAX_QUOTA_HASH_SIZE) { ocfs2_cached_dquot **new_hash, **old_hash; ocfs2_cached_dquot *h_dquot, *h_next; int i; int old_entries; err = ocfs2_malloc0(sizeof(ocfs2_quota_hash *) * hash->alloc_entries * 2, &new_hash); if (err) return err; old_entries = hash->alloc_entries; old_hash = hash->hash; hash->alloc_entries *= 2; hash->hash = new_hash; /* Rehash */ for (i = 0; i < old_entries; i++) { for (h_dquot = old_hash[i]; h_dquot; h_dquot = h_next) { h_next = h_dquot->d_next; quota_add_hash_chain(hash, h_dquot); } } err = ocfs2_free(&old_hash); if (err) return err; } quota_add_hash_chain(hash, dquot); hash->used_entries++; return 0; } errcode_t ocfs2_remove_quota_hash(ocfs2_quota_hash *hash, ocfs2_cached_dquot *dquot) { *(dquot->d_pprev) = dquot->d_next; if (dquot->d_next) dquot->d_next->d_pprev = dquot->d_pprev; hash->used_entries--; return 0; } errcode_t ocfs2_find_quota_hash(ocfs2_quota_hash *hash, qid_t id, ocfs2_cached_dquot **dquotp) { int hash_val = quota_hash(hash, id); ocfs2_cached_dquot *dquot; for (dquot = hash->hash[hash_val]; dquot; dquot = dquot->d_next) { if (dquot->d_ddquot.dqb_id == id) { *dquotp = dquot; return 0; } } *dquotp = NULL; return 0; } errcode_t ocfs2_find_create_quota_hash(ocfs2_quota_hash *hash, qid_t id, ocfs2_cached_dquot **dquotp) { errcode_t err; err = ocfs2_find_quota_hash(hash, id, dquotp); if (err) return err; if (*dquotp) return 0; err = ocfs2_malloc0(sizeof(ocfs2_cached_dquot), dquotp); if (err) return err; (*dquotp)->d_ddquot.dqb_id = id; err = ocfs2_insert_quota_hash(hash, *dquotp); if (err) { ocfs2_free(dquotp); return err; } return 0; } errcode_t ocfs2_find_read_quota_hash(ocfs2_filesys *fs, ocfs2_quota_hash *hash, int type, qid_t id, ocfs2_cached_dquot **dquotp) { errcode_t err; err = ocfs2_find_quota_hash(hash, id, dquotp); if (err) return err; if (*dquotp) return 0; err = ocfs2_read_dquot(fs, type, id, dquotp); if (err) return err; err = ocfs2_insert_quota_hash(hash, *dquotp); if (err) { ocfs2_free(dquotp); return err; } return 0; } errcode_t ocfs2_compute_quota_usage(ocfs2_filesys *fs, ocfs2_quota_hash *usr_hash, ocfs2_quota_hash *grp_hash) { errcode_t err = 0; ocfs2_inode_scan *scan; uint64_t blkno; char *buf; int close_scan = 0; struct ocfs2_dinode *di; ocfs2_cached_dquot *dquot; err = ocfs2_malloc_block(fs->fs_io, &buf); if (err) return err; di = (struct ocfs2_dinode *)buf; err = ocfs2_open_inode_scan(fs, &scan); if (err) goto out; close_scan = 1; while (1) { err = ocfs2_get_next_inode(scan, &blkno, buf); if (err || !blkno) break; /* * Check whether the inode looks reasonable and interesting * for quota */ if (memcmp(di->i_signature, OCFS2_INODE_SIGNATURE, strlen(OCFS2_INODE_SIGNATURE))) continue; ocfs2_swap_inode_to_cpu(fs, di); if (di->i_fs_generation != fs->fs_super->i_fs_generation) continue; if (!(di->i_flags & OCFS2_VALID_FL)) continue; if (di->i_flags & OCFS2_SYSTEM_FL && blkno != OCFS2_RAW_SB(fs->fs_super)->s_root_blkno) continue; if (usr_hash) { err = ocfs2_find_create_quota_hash(usr_hash, di->i_uid, &dquot); if (err) break; dquot->d_ddquot.dqb_curspace += ocfs2_clusters_to_bytes(fs, di->i_clusters); dquot->d_ddquot.dqb_curinodes++; } if (grp_hash) { err = ocfs2_find_create_quota_hash(grp_hash, di->i_gid, &dquot); if (err) break; dquot->d_ddquot.dqb_curspace += ocfs2_clusters_to_bytes(fs, di->i_clusters); dquot->d_ddquot.dqb_curinodes++; } } out: if (close_scan) ocfs2_close_inode_scan(scan); ocfs2_free(&buf); return err; } errcode_t ocfs2_init_quota_change(ocfs2_filesys *fs, ocfs2_quota_hash **usrhash, ocfs2_quota_hash **grphash) { errcode_t err; *usrhash = NULL; *grphash = NULL; if (OCFS2_HAS_RO_COMPAT_FEATURE(OCFS2_RAW_SB(fs->fs_super), OCFS2_FEATURE_RO_COMPAT_USRQUOTA)) { err = ocfs2_new_quota_hash(usrhash); if (err) return err; } if (OCFS2_HAS_RO_COMPAT_FEATURE(OCFS2_RAW_SB(fs->fs_super), OCFS2_FEATURE_RO_COMPAT_GRPQUOTA)) { err = ocfs2_new_quota_hash(grphash); if (err) { if (*usrhash) ocfs2_free_quota_hash(*usrhash); return err; } } return 0; } errcode_t ocfs2_finish_quota_change(ocfs2_filesys *fs, ocfs2_quota_hash *usrhash, ocfs2_quota_hash *grphash) { errcode_t ret = 0, err; if (usrhash) { err = ocfs2_write_release_dquots(fs, USRQUOTA, usrhash); if (!ret) ret = err; err = ocfs2_free_quota_hash(usrhash); if (!ret) ret = err; } if (grphash) { err = ocfs2_write_release_dquots(fs, GRPQUOTA, grphash); if (!ret) ret = err; err = ocfs2_free_quota_hash(grphash); if (!ret) ret = err; } return ret; } errcode_t ocfs2_apply_quota_change(ocfs2_filesys *fs, ocfs2_quota_hash *usrhash, ocfs2_quota_hash *grphash, uid_t uid, gid_t gid, int64_t space_change, int64_t inode_change) { ocfs2_cached_dquot *dquot; errcode_t err; if (usrhash) { err = ocfs2_find_read_quota_hash(fs, usrhash, USRQUOTA, uid, &dquot); if (err) return err; dquot->d_ddquot.dqb_curspace += space_change; dquot->d_ddquot.dqb_curinodes += inode_change; } if (grphash) { err = ocfs2_find_read_quota_hash(fs, grphash, GRPQUOTA, gid, &dquot); if (err) return err; dquot->d_ddquot.dqb_curspace += space_change; dquot->d_ddquot.dqb_curinodes += inode_change; } return 0; } errcode_t ocfs2_iterate_quota_hash(ocfs2_quota_hash *hash, errcode_t (*f)(ocfs2_cached_dquot *, void *), void *data) { errcode_t err = 0; int i; ocfs2_cached_dquot *dquot, *next; for (i = 0; i < hash->alloc_entries; i++) for (dquot = hash->hash[i]; dquot; dquot = next) { next = dquot->d_next; err = f(dquot, data); if (err) goto out; } out: return err; } struct write_rel_ctx { ocfs2_filesys *fs; ocfs2_quota_hash *hash; int type; }; static errcode_t write_release_quota_hash(ocfs2_cached_dquot *dquot, void *p) { struct write_rel_ctx *ctx = p; errcode_t err; if (!dquot->d_ddquot.dqb_isoftlimit || dquot->d_ddquot.dqb_curinodes < dquot->d_ddquot.dqb_isoftlimit) dquot->d_ddquot.dqb_itime = 0; if (!dquot->d_ddquot.dqb_bsoftlimit || dquot->d_ddquot.dqb_curspace < dquot->d_ddquot.dqb_bsoftlimit) dquot->d_ddquot.dqb_btime = 0; err = ocfs2_write_dquot(ctx->fs, ctx->type, dquot); if (err) return err; err = ocfs2_remove_quota_hash(ctx->hash, dquot); if (err) return err; return ocfs2_free(&dquot); } errcode_t ocfs2_write_release_dquots(ocfs2_filesys *fs, int type, ocfs2_quota_hash *hash) { struct write_rel_ctx ctx; ctx.fs = fs; ctx.hash = hash; ctx.type = type; return ocfs2_iterate_quota_hash(hash, write_release_quota_hash, &ctx); } static void mark_quotafile_info_dirty(ocfs2_filesys *fs, int type) { fs->qinfo[type].flags |= OCFS2_QF_INFO_DIRTY; fs->fs_flags |= OCFS2_FLAG_DIRTY; } static void ocfs2_checksum_quota_block(ocfs2_filesys *fs, char *buf) { struct ocfs2_disk_dqtrailer *dqt = ocfs2_block_dqtrailer(fs->fs_blocksize, buf); ocfs2_compute_meta_ecc(fs, buf, &dqt->dq_check); } #define OCFS2_LOCAL_QF_INIT_BLOCKS 2 errcode_t ocfs2_init_local_quota_file(ocfs2_filesys *fs, int type, uint64_t blkno) { ocfs2_cached_inode *ci = NULL; struct ocfs2_dinode *di; struct ocfs2_disk_dqheader *header; struct ocfs2_local_disk_dqinfo *info; unsigned int magics[] = OCFS2_LOCAL_QMAGICS; int versions[] = OCFS2_LOCAL_QVERSIONS; char *buf = NULL; unsigned int written; int bytes = ocfs2_blocks_to_bytes(fs, OCFS2_LOCAL_QF_INIT_BLOCKS); errcode_t err; err = ocfs2_read_cached_inode(fs, blkno, &ci); if (err) goto out; if (!(ci->ci_inode->i_flags & OCFS2_VALID_FL) || !(ci->ci_inode->i_flags & OCFS2_SYSTEM_FL) || !(ci->ci_inode->i_flags & OCFS2_QUOTA_FL)) { err = OCFS2_ET_INTERNAL_FAILURE; goto out; } di = ci->ci_inode; /* We need at least two blocks */ err = ocfs2_cached_inode_extend_allocation(ci, ocfs2_clusters_in_blocks(fs, OCFS2_LOCAL_QF_INIT_BLOCKS)); if (err) goto out; di->i_size = bytes; di->i_mtime = time(NULL); err = ocfs2_write_inode(fs, blkno, (char *)di); if (err) goto out; err = ocfs2_malloc_blocks(fs->fs_io, OCFS2_LOCAL_QF_INIT_BLOCKS, &buf); if (err) goto out; memset(buf, 0, bytes); header = (struct ocfs2_disk_dqheader *)buf; header->dqh_magic = magics[type]; header->dqh_version = versions[type]; ocfs2_swap_quota_header(header); info = (struct ocfs2_local_disk_dqinfo *)(buf + OCFS2_LOCAL_INFO_OFF); info->dqi_chunks = 1; info->dqi_blocks = OCFS2_LOCAL_QF_INIT_BLOCKS; info->dqi_flags = OLQF_CLEAN; ocfs2_swap_quota_local_info(info); /* There are no free chunks because there are no blocks allocated for * them yet. So chunk header is all-zero and needs no initialization */ ocfs2_checksum_quota_block(fs, buf); ocfs2_checksum_quota_block(fs, buf + fs->fs_blocksize); err = ocfs2_file_write(ci, buf, bytes, 0, &written); if (!err && written != bytes) { err = OCFS2_ET_INTERNAL_FAILURE; goto out; } out: if (ci) ocfs2_free_cached_inode(fs, ci); if (buf) ocfs2_free(&buf); return err; } errcode_t ocfs2_init_local_quota_files(ocfs2_filesys *fs, int type) { int num_slots = OCFS2_RAW_SB(fs->fs_super)->s_max_slots; char fname[OCFS2_MAX_FILENAME_LEN]; errcode_t ret; uint64_t blkno; int local_type = (type == USRQUOTA) ? LOCAL_USER_QUOTA_SYSTEM_INODE : LOCAL_GROUP_QUOTA_SYSTEM_INODE; int i; for (i = 0; i < num_slots; i++) { ocfs2_sprintf_system_inode_name(fname, sizeof(fname), local_type, i); ret = ocfs2_lookup(fs, fs->fs_sysdir_blkno, fname, strlen(fname), NULL, &blkno); if (ret) return ret; /* This is here mainly for fsck... */ ret = ocfs2_truncate(fs, blkno, 0); if (ret) return ret; ret = ocfs2_init_local_quota_file(fs, type, blkno); if (ret) return ret; } return 0; } /* Return depth of quota tree in global file */ int ocfs2_qtree_depth(int blocksize) { unsigned int epb = (blocksize - OCFS2_QBLK_RESERVED_SPACE) >> 2; unsigned long long entries = epb; int i; for (i = 1; entries < (1ULL << 32); i++) entries *= epb; return i; } /* Returns index of next block in the tree of dquots */ static int ocfs2_qtree_index(int blocksize, qid_t id, int depth) { unsigned int epb = (blocksize - OCFS2_QBLK_RESERVED_SPACE) >> 2; depth = ocfs2_qtree_depth(blocksize) - depth - 1; while (depth--) id /= epb; return id % epb; } /* Is given leaf entry unused? */ int ocfs2_qtree_entry_unused(struct ocfs2_global_disk_dqblk *ddquot) { static struct ocfs2_global_disk_dqblk empty; return !memcmp(&empty, ddquot, sizeof(empty)); } errcode_t ocfs2_init_fs_quota_info(ocfs2_filesys *fs, int type) { int global_type = (type == USRQUOTA) ? USER_QUOTA_SYSTEM_INODE : GROUP_QUOTA_SYSTEM_INODE; uint64_t blkno; char fname[OCFS2_MAX_FILENAME_LEN]; errcode_t ret; if (fs->qinfo[type].qi_inode) return 0; ocfs2_sprintf_system_inode_name(fname, sizeof(fname), global_type, 0); ret = ocfs2_lookup(fs, fs->fs_sysdir_blkno, fname, strlen(fname), NULL, &blkno); if (ret) return ret; ret = ocfs2_read_cached_inode(fs, blkno, &(fs->qinfo[type].qi_inode)); if (ret) return ret; return 0; } /* Read given block */ static errcode_t read_blk(ocfs2_filesys *fs, int type, unsigned int blk, char *buf) { errcode_t err; uint32_t got; struct ocfs2_disk_dqtrailer *dqt = ocfs2_block_dqtrailer(fs->fs_blocksize, buf); err = ocfs2_file_read(fs->qinfo[type].qi_inode, buf, fs->fs_blocksize, blk * fs->fs_blocksize, &got); if (err) return err; if (got != fs->fs_blocksize) return OCFS2_ET_SHORT_READ; return ocfs2_validate_meta_ecc(fs, buf, &dqt->dq_check); } /* Write given block */ static errcode_t write_blk(ocfs2_filesys *fs, int type, unsigned int blk, char *buf) { errcode_t err; uint32_t written; ocfs2_checksum_quota_block(fs, buf); err = ocfs2_file_write(fs->qinfo[type].qi_inode, buf, fs->fs_blocksize, blk * fs->fs_blocksize, &written); if (err) return err; if (written != fs->fs_blocksize) return OCFS2_ET_SHORT_WRITE; return 0; } errcode_t ocfs2_read_global_quota_info(ocfs2_filesys *fs, int type) { char *buf; errcode_t ret; struct ocfs2_global_disk_dqinfo *info; if (fs->qinfo[type].flags & OCFS2_QF_INFO_LOADED) return 0; ret = ocfs2_malloc_block(fs->fs_io, &buf); if (ret) return ret; ret = read_blk(fs, type, 0, buf); if (ret) return ret; info = (struct ocfs2_global_disk_dqinfo *)(buf + OCFS2_GLOBAL_INFO_OFF); ocfs2_swap_quota_global_info(info); memcpy(&(fs->qinfo[type].qi_info), info, sizeof(struct ocfs2_global_disk_dqinfo)); fs->qinfo[type].flags |= OCFS2_QF_INFO_LOADED; return 0; } errcode_t ocfs2_write_global_quota_info(ocfs2_filesys *fs, int type) { errcode_t ret; char *buf; struct ocfs2_disk_dqheader *header; struct ocfs2_global_disk_dqinfo *info; unsigned int magics[] = OCFS2_GLOBAL_QMAGICS; int versions[] = OCFS2_GLOBAL_QVERSIONS; ret = ocfs2_malloc_block(fs->fs_io, &buf); if (ret) return ret; header = (struct ocfs2_disk_dqheader *)buf; header->dqh_magic = magics[type]; header->dqh_version = versions[type]; ocfs2_swap_quota_header(header); info = (struct ocfs2_global_disk_dqinfo *)(buf + OCFS2_GLOBAL_INFO_OFF); memcpy(info, &(fs->qinfo[type].qi_info), sizeof(struct ocfs2_global_disk_dqinfo)); ocfs2_swap_quota_global_info(info); ret = write_blk(fs, type, 0, buf); if (ret) goto bail; bail: ocfs2_free(&buf); return ret; } errcode_t ocfs2_load_fs_quota_info(ocfs2_filesys *fs) { errcode_t err; if (OCFS2_HAS_RO_COMPAT_FEATURE(OCFS2_RAW_SB(fs->fs_super), OCFS2_FEATURE_RO_COMPAT_USRQUOTA)) { err = ocfs2_init_fs_quota_info(fs, USRQUOTA); if (err) return err; err = ocfs2_read_global_quota_info(fs, USRQUOTA); if (err) return err; } if (OCFS2_HAS_RO_COMPAT_FEATURE(OCFS2_RAW_SB(fs->fs_super), OCFS2_FEATURE_RO_COMPAT_GRPQUOTA)) { err = ocfs2_init_fs_quota_info(fs, GRPQUOTA); if (err) return err; err = ocfs2_read_global_quota_info(fs, GRPQUOTA); if (err) return err; } return 0; } #define OCFS2_GLOBAL_QF_INIT_BLOCKS 2 errcode_t ocfs2_init_global_quota_file(ocfs2_filesys *fs, int type) { ocfs2_cached_inode *ci = fs->qinfo[type].qi_inode; struct ocfs2_dinode *di; char *buf = NULL; struct ocfs2_disk_dqheader *header; struct ocfs2_global_disk_dqinfo *info; unsigned int magics[] = OCFS2_GLOBAL_QMAGICS; int versions[] = OCFS2_GLOBAL_QVERSIONS; errcode_t err; int i; int bytes = ocfs2_blocks_to_bytes(fs, OCFS2_GLOBAL_QF_INIT_BLOCKS); if (!(ci->ci_inode->i_flags & OCFS2_VALID_FL) || !(ci->ci_inode->i_flags & OCFS2_SYSTEM_FL) || !(ci->ci_inode->i_flags & OCFS2_QUOTA_FL)) { err = OCFS2_ET_INTERNAL_FAILURE; goto out; } err = ocfs2_cached_inode_extend_allocation(ci, ocfs2_clusters_in_blocks(fs, OCFS2_GLOBAL_QF_INIT_BLOCKS)); if (err) goto out; /* Mark info dirty so that quota inode gets written */ mark_quotafile_info_dirty(fs, type); di = ci->ci_inode; di->i_size = bytes; di->i_mtime = time(NULL); err = ocfs2_malloc_blocks(fs->fs_io, OCFS2_GLOBAL_QF_INIT_BLOCKS, &buf); if (err) goto out; memset(buf, 0, bytes); header = (struct ocfs2_disk_dqheader *)buf; header->dqh_magic = magics[type]; header->dqh_version = versions[type]; ocfs2_swap_quota_header(header); fs->qinfo[type].qi_info.dqi_blocks = OCFS2_GLOBAL_QF_INIT_BLOCKS; fs->qinfo[type].qi_info.dqi_free_blk = 0; fs->qinfo[type].qi_info.dqi_free_entry = 0; info = (struct ocfs2_global_disk_dqinfo *)(buf + OCFS2_GLOBAL_INFO_OFF); info->dqi_bgrace = fs->qinfo[type].qi_info.dqi_bgrace; info->dqi_igrace = fs->qinfo[type].qi_info.dqi_igrace; info->dqi_syncms = fs->qinfo[type].qi_info.dqi_syncms; info->dqi_blocks = OCFS2_GLOBAL_QF_INIT_BLOCKS; info->dqi_free_blk = 0; info->dqi_free_entry = 0; ocfs2_swap_quota_global_info(info); /* * Write the buffer here so that all the headers are properly written. * Normally we don't write tree root block. */ for (i = 0; i < OCFS2_GLOBAL_QF_INIT_BLOCKS; i++) { err = write_blk(fs, type, i, buf + (i * fs->fs_blocksize)); if (err) goto out; } out: if (buf) ocfs2_free(&buf); return err; } /* Is given dquot empty? */ static int ocfs2_global_entry_unused(struct ocfs2_global_disk_dqblk *ddqblk) { static struct ocfs2_global_disk_dqblk empty; return !memcmp(&empty, ddqblk, sizeof(empty)); } /* Get free block in file (either from free list or create new one) */ static errcode_t ocfs2_get_free_dqblk(ocfs2_filesys *fs, int type, unsigned int *blk) { errcode_t err; char *buf; struct qt_disk_dqdbheader *dh; struct ocfs2_global_disk_dqinfo *info = &(fs->qinfo[type].qi_info); ocfs2_cached_inode *ci = fs->qinfo[type].qi_inode; err = ocfs2_malloc_block(fs->fs_io, &buf); if (err) return err; dh = (struct qt_disk_dqdbheader *)buf; if (info->dqi_free_blk) { *blk = info->dqi_free_blk; err = read_blk(fs, type, *blk, buf); if (err) goto bail; info->dqi_free_blk = le32_to_cpu(dh->dqdh_next_free); } else { if (info->dqi_blocks == ocfs2_clusters_to_blocks(fs, ci->ci_inode->i_clusters)) { err = ocfs2_cached_inode_extend_allocation(ci, 1); if (err) goto bail; } *blk = info->dqi_blocks++; ci->ci_inode->i_size = ocfs2_blocks_to_bytes(fs, info->dqi_blocks); } mark_quotafile_info_dirty(fs, type); bail: ocfs2_free(&buf); return err; } /* Put given block to free list */ static errcode_t ocfs2_put_free_dqblk(ocfs2_filesys *fs, int type, char *buf, unsigned int blk) { errcode_t err; struct qt_disk_dqdbheader *dh = (struct qt_disk_dqdbheader *)buf; struct ocfs2_global_disk_dqinfo *info = &(fs->qinfo[type].qi_info); dh->dqdh_next_free = info->dqi_free_blk; dh->dqdh_prev_free = 0; dh->dqdh_entries = 0; ocfs2_swap_quota_leaf_block_header(dh); err = write_blk(fs, type, blk, buf); ocfs2_swap_quota_leaf_block_header(dh); if (err) return err; info->dqi_free_blk = blk; mark_quotafile_info_dirty(fs, type); return 0; } /* Remove given block from the list of blocks with free entries */ static errcode_t ocfs2_remove_free_dqentry(ocfs2_filesys *fs, int type, char *buf, unsigned int blk) { errcode_t err; char *tmpbuf; struct qt_disk_dqdbheader *dh, *tdh; unsigned int nextblk, prevblk; err = ocfs2_malloc_block(fs->fs_io, &tmpbuf); if (err) return err; dh = (struct qt_disk_dqdbheader *)buf; tdh = (struct qt_disk_dqdbheader *)tmpbuf; nextblk = dh->dqdh_next_free; prevblk = dh->dqdh_prev_free; if (nextblk) { err = read_blk(fs, type, nextblk, tmpbuf); if (err) goto bail; ocfs2_swap_quota_leaf_block_header(tdh); tdh->dqdh_prev_free = prevblk; ocfs2_swap_quota_leaf_block_header(tdh); err = write_blk(fs, type, nextblk, tmpbuf); if (err) goto bail; } if (prevblk) { /* Failure here is bad since we potentially corrupt free list. * OTOH something must be really wrong when read/write fails */ err = read_blk(fs, type, prevblk, tmpbuf); if (err) goto bail; ocfs2_swap_quota_leaf_block_header(tdh); tdh->dqdh_next_free = nextblk; ocfs2_swap_quota_leaf_block_header(tdh); err = write_blk(fs, type, prevblk, tmpbuf); if (err) goto bail; } else { fs->qinfo[type].qi_info.dqi_free_entry = nextblk; mark_quotafile_info_dirty(fs, type); } dh->dqdh_next_free = dh->dqdh_prev_free = 0; ocfs2_swap_quota_leaf_block_header(dh); /* No matter whether write succeeds block is out of list */ write_blk(fs, type, blk, buf); ocfs2_swap_quota_leaf_block_header(dh); bail: ocfs2_free(&tmpbuf); return err; } /* Insert given block to the beginning of list with free entries */ static errcode_t ocfs2_insert_free_dqentry(ocfs2_filesys *fs, int type, char *buf, unsigned int blk) { errcode_t err; char *tmpbuf; struct qt_disk_dqdbheader *tdh, *dh = (struct qt_disk_dqdbheader *)buf; struct ocfs2_global_disk_dqinfo *info = &(fs->qinfo[type].qi_info); err = ocfs2_malloc_block(fs->fs_io, &tmpbuf); if (err) return err; dh->dqdh_next_free = info->dqi_free_entry; dh->dqdh_prev_free = 0; ocfs2_swap_quota_leaf_block_header(dh); err = write_blk(fs, type, blk, buf); ocfs2_swap_quota_leaf_block_header(dh); if (err) goto bail; if (info->dqi_free_entry) { tdh = (struct qt_disk_dqdbheader *)tmpbuf; err = read_blk(fs, type, info->dqi_free_entry, tmpbuf); if (err) goto bail; ocfs2_swap_quota_leaf_block_header(tdh); tdh->dqdh_prev_free = blk; ocfs2_swap_quota_leaf_block_header(tdh); err = write_blk(fs, type, info->dqi_free_entry, tmpbuf); if (err) goto bail; } info->dqi_free_entry = blk; mark_quotafile_info_dirty(fs, type); bail: ocfs2_free(&tmpbuf); return err; } /* Find space for dquot */ static errcode_t ocfs2_find_free_dqentry(ocfs2_filesys *fs, int type, unsigned int *treeblk, loff_t *off) { errcode_t err; unsigned int blk, i; struct ocfs2_global_disk_dqblk *ddquot; struct qt_disk_dqdbheader *dh; struct ocfs2_global_disk_dqinfo *info = &(fs->qinfo[type].qi_info); char *buf; err = ocfs2_malloc_block(fs->fs_io, &buf); if (err) return err; dh = (struct qt_disk_dqdbheader *)buf; ddquot = (struct ocfs2_global_disk_dqblk *)(buf + sizeof(struct qt_disk_dqdbheader)); if (info->dqi_free_entry) { blk = info->dqi_free_entry; err = read_blk(fs, type, blk, buf); if (err) goto bail; ocfs2_swap_quota_leaf_block_header(dh); } else { err = ocfs2_get_free_dqblk(fs, type, &blk); if (err) goto bail; memset(buf, 0, fs->fs_blocksize); info->dqi_free_entry = blk; mark_quotafile_info_dirty(fs, type); } /* Block will be full? */ if (dh->dqdh_entries + 1 >= ocfs2_global_dqstr_in_blk(fs->fs_blocksize)) { err = ocfs2_remove_free_dqentry(fs, type, buf, blk); if (err) goto bail; } dh->dqdh_entries++; /* Find free structure in block */ for (i = 0; i < ocfs2_global_dqstr_in_blk(fs->fs_blocksize) && !ocfs2_global_entry_unused(ddquot + i); i++); if (i == ocfs2_global_dqstr_in_blk(fs->fs_blocksize)) { err = OCFS2_ET_CORRUPT_QUOTA_FILE; goto bail; } ocfs2_swap_quota_leaf_block_header(dh); err = write_blk(fs, type, blk, buf); if (err) goto bail; *off = (blk * fs->fs_blocksize) + sizeof(struct qt_disk_dqdbheader) + i * sizeof(struct ocfs2_global_disk_dqblk); *treeblk = blk; bail: ocfs2_free(&buf); return err; } /* Insert reference to structure into the trie */ static errcode_t ocfs2_do_insert_tree(ocfs2_filesys *fs, int type, qid_t id, unsigned int *treeblk, int depth, loff_t *off) { char *buf; int newson = 0, newact = 0; u_int32_t *ref; unsigned int newblk; errcode_t err; err = ocfs2_malloc_block(fs->fs_io, &buf); if (err) return err; if (!*treeblk) { err = ocfs2_get_free_dqblk(fs, type, &newblk); if (err) goto bail; *treeblk = newblk; memset(buf, 0, fs->fs_blocksize); newact = 1; } else { err = read_blk(fs, type, *treeblk, buf); if (err) goto bail; } ref = (u_int32_t *) buf; newblk = le32_to_cpu(ref[ ocfs2_qtree_index(fs->fs_blocksize, id, depth)]); if (!newblk) newson = 1; if (depth == ocfs2_qtree_depth(fs->fs_blocksize) - 1) { if (newblk) { err = OCFS2_ET_CORRUPT_QUOTA_FILE; goto bail; } err = ocfs2_find_free_dqentry(fs, type, &newblk, off); } else err = ocfs2_do_insert_tree(fs, type, id, &newblk, depth + 1, off); if (newson && !err) { ref[ocfs2_qtree_index(fs->fs_blocksize, id, depth)] = cpu_to_le32(newblk); err = write_blk(fs, type, *treeblk, buf); } else if (newact && err) ocfs2_put_free_dqblk(fs, type, buf, *treeblk); bail: ocfs2_free(&buf); return err; } /* Wrapper for inserting quota structure into tree */ static errcode_t ocfs2_insert_qtree(ocfs2_filesys *fs, int type, qid_t id, loff_t *off) { unsigned int tmp = QT_TREEOFF; return ocfs2_do_insert_tree(fs, type, id, &tmp, 0, off); } /* Write dquot to file */ errcode_t ocfs2_write_dquot(ocfs2_filesys *fs, int type, ocfs2_cached_dquot *dquot) { errcode_t err; char *buf; struct ocfs2_global_disk_dqblk *ddquot; err = ocfs2_malloc_block(fs->fs_io, &buf); if (err) return err; if (!dquot->d_off) { err = ocfs2_insert_qtree(fs, type, dquot->d_ddquot.dqb_id, &dquot->d_off); if (err) goto bail; } err = read_blk(fs, type, dquot->d_off / fs->fs_blocksize, buf); if (err) goto bail; ddquot = (struct ocfs2_global_disk_dqblk *)(buf + (dquot->d_off % fs->fs_blocksize)); memcpy(ddquot, &dquot->d_ddquot, sizeof(struct ocfs2_global_disk_dqblk)); ddquot->dqb_pad1 = 0; ddquot->dqb_pad2 = 0; ocfs2_swap_quota_global_dqblk(ddquot); err = write_blk(fs, type, dquot->d_off / fs->fs_blocksize, buf); bail: ocfs2_free(&buf); return err; } /* Remove dquot entry from its data block */ static errcode_t ocfs2_remove_leaf_dqentry(ocfs2_filesys *fs, int type, ocfs2_cached_dquot *dquot, unsigned int blk) { errcode_t err; char *buf; struct qt_disk_dqdbheader *dh; if (blk != dquot->d_off / fs->fs_blocksize) return OCFS2_ET_CORRUPT_QUOTA_FILE; err = ocfs2_malloc_block(fs->fs_io, &buf); if (err) return err; err = read_blk(fs, type, blk, buf); if (err) goto bail; dh = (struct qt_disk_dqdbheader *)buf; ocfs2_swap_quota_leaf_block_header(dh); dh->dqdh_entries--; if (!dh->dqdh_entries) { /* Block got free? */ err = ocfs2_remove_free_dqentry(fs, type, buf, blk); if (err) goto bail; err = ocfs2_put_free_dqblk(fs, type, buf, blk); if (err) goto bail; } else { memset(buf + (dquot->d_off & (fs->fs_blocksize - 1)), 0, sizeof(struct ocfs2_global_disk_dqblk)); /* First free entry? */ if (dh->dqdh_entries == ocfs2_global_dqstr_in_blk(fs->fs_blocksize) - 1) { /* This will also write data block */ err = ocfs2_insert_free_dqentry(fs, type, buf, blk); } else err = write_blk(fs, type, blk, buf); } dquot->d_off = 0; bail: ocfs2_free(&buf); return err; } /* Remove reference to dquot from tree */ static errcode_t ocfs2_remove_tree_dqentry(ocfs2_filesys *fs, int type, ocfs2_cached_dquot *dquot, unsigned int *blk, int depth) { errcode_t err; char *buf; unsigned int newblk; u_int32_t *ref; err = ocfs2_malloc_block(fs->fs_io, &buf); if (err) return err; err = read_blk(fs, type, *blk, buf); if (err) goto bail; ref = (u_int32_t *)buf; newblk = le32_to_cpu(ref[ocfs2_qtree_index(fs->fs_blocksize, dquot->d_ddquot.dqb_id, depth)]); if (depth == ocfs2_qtree_depth(fs->fs_blocksize) - 1) { err = ocfs2_remove_leaf_dqentry(fs, type, dquot, newblk); newblk = 0; } else err = ocfs2_remove_tree_dqentry(fs, type, dquot, &newblk, depth + 1); if (err) goto bail; if (!newblk) { int i; ref[ocfs2_qtree_index(fs->fs_blocksize, dquot->d_ddquot.dqb_id, depth)] = cpu_to_le32(0); /* Block got empty? */ for (i = 0; i < fs->fs_blocksize - OCFS2_QBLK_RESERVED_SPACE && !buf[i]; i++); /* Don't put the root block into the free block list */ if (i == fs->fs_blocksize - OCFS2_QBLK_RESERVED_SPACE && *blk != QT_TREEOFF) { err = ocfs2_put_free_dqblk(fs, type, buf, *blk); if (err) goto bail; *blk = 0; } else err = write_blk(fs, type, *blk, buf); } bail: ocfs2_free(&buf); return err; } /* Delete dquot from tree */ errcode_t ocfs2_delete_dquot(ocfs2_filesys *fs, int type, ocfs2_cached_dquot *dquot) { unsigned int tmp = QT_TREEOFF; if (!dquot->d_off) /* Even not allocated? */ return 0; return ocfs2_remove_tree_dqentry(fs, type, dquot, &tmp, 0); } /* Find entry in block */ static errcode_t ocfs2_find_block_dqentry(ocfs2_filesys *fs, int type, ocfs2_cached_dquot *dquot, unsigned int blk) { char *buf; errcode_t err; int i; struct ocfs2_global_disk_dqblk *ddquot; err = ocfs2_malloc_block(fs->fs_io, &buf); if (err) return err; err = read_blk(fs, type, blk, buf); if (err) goto bail; ddquot = (struct ocfs2_global_disk_dqblk *)(buf + sizeof(struct qt_disk_dqdbheader)); for (i = 0; i < ocfs2_global_dqstr_in_blk(fs->fs_blocksize); i++, ddquot++) { if (le32_to_cpu(ddquot->dqb_id) == dquot->d_ddquot.dqb_id) { if (dquot->d_ddquot.dqb_id == 0 && ocfs2_qtree_entry_unused(ddquot)) continue; break; } } if (i == ocfs2_global_dqstr_in_blk(fs->fs_blocksize)) { err = OCFS2_ET_CORRUPT_QUOTA_FILE; goto bail; } dquot->d_off = blk * fs->fs_blocksize + ((char *)ddquot - buf); memcpy(&dquot->d_ddquot, ddquot, sizeof(struct ocfs2_global_disk_dqblk)); ocfs2_swap_quota_global_dqblk(&dquot->d_ddquot); bail: ocfs2_free(&buf); return err; } /* Find entry for given id in the tree */ static errcode_t ocfs2_find_tree_dqentry(ocfs2_filesys *fs, int type, ocfs2_cached_dquot *dquot, unsigned int blk, int depth) { errcode_t err; char *buf; u_int32_t *ref; err = ocfs2_malloc_block(fs->fs_io, &buf); if (err) return err; err = read_blk(fs, type, blk, buf); if (err) goto bail; ref = (u_int32_t *)buf; blk = le32_to_cpu(ref[ocfs2_qtree_index(fs->fs_blocksize, dquot->d_ddquot.dqb_id, depth)]); if (!blk) /* No reference? */ goto bail; if (depth < ocfs2_qtree_depth(fs->fs_blocksize) - 1) err = ocfs2_find_tree_dqentry(fs, type, dquot, blk, depth + 1); else err = ocfs2_find_block_dqentry(fs, type, dquot, blk); bail: ocfs2_free(&buf); return err; } /* * Read dquot from disk */ errcode_t ocfs2_read_dquot(ocfs2_filesys *fs, int type, qid_t id, ocfs2_cached_dquot **ret_dquot) { errcode_t err; ocfs2_cached_dquot *dquot; err = ocfs2_malloc0(sizeof(ocfs2_cached_dquot), &dquot); if (err) return err; dquot->d_ddquot.dqb_id = id; err = ocfs2_find_tree_dqentry(fs, type, dquot, QT_TREEOFF, 0); if (err) goto bail; *ret_dquot = dquot; return 0; bail: ocfs2_free(&dquot); return err; } ocfs2-tools-ocfs2-tools-1.8.6/libocfs2/refcount.c000066400000000000000000001650171347147137200215670ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * refcount.c * * Functions for the refcount tree structure. Part of the OCFS2 userspace * library. * * Copyright (C) 2009 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License version 2 as published by the Free Software Foundation. * * 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. */ #define _XOPEN_SOURCE 600 /* Triggers XOPEN2K in features.h */ #define _LARGEFILE64_SOURCE #include #include #include #include #include #include "ocfs2/byteorder.h" #include "ocfs2/ocfs2.h" #include "extent_tree.h" #include "refcount.h" struct ocfs2_cow_context { ocfs2_filesys *fs; uint32_t cow_start; uint32_t cow_len; struct ocfs2_extent_tree data_et; char *ref_root_buf; uint64_t ref_root_blkno; void *cow_object; struct ocfs2_post_refcount *post_refcount; int (*get_clusters)(struct ocfs2_cow_context *context, uint32_t v_cluster, uint32_t *p_cluster, uint32_t *num_clusters, uint16_t *extent_flags); }; static void ocfs2_swap_refcount_list_primary(struct ocfs2_refcount_list *rl) { rl->rl_count = bswap_16(rl->rl_count); rl->rl_used = bswap_16(rl->rl_used); } static void ocfs2_swap_refcount_list_secondary(ocfs2_filesys *fs, void *obj, struct ocfs2_refcount_list *rl) { int i; for (i = 0; i < rl->rl_count; i++) { struct ocfs2_refcount_rec *rec = &rl->rl_recs[i]; if (ocfs2_swap_barrier(fs, obj, rec, sizeof(struct ocfs2_refcount_rec))) break; rec->r_cpos = bswap_64(rec->r_cpos); rec->r_clusters = bswap_32(rec->r_clusters); rec->r_refcount = bswap_32(rec->r_refcount); } } void ocfs2_swap_refcount_list_from_cpu(ocfs2_filesys *fs, void *obj, struct ocfs2_refcount_list *rl) { if (cpu_is_little_endian) return; ocfs2_swap_refcount_list_secondary(fs, obj, rl); ocfs2_swap_refcount_list_primary(rl); } void ocfs2_swap_refcount_list_to_cpu(ocfs2_filesys *fs, void *obj, struct ocfs2_refcount_list *rl) { if (cpu_is_little_endian) return; ocfs2_swap_refcount_list_primary(rl); ocfs2_swap_refcount_list_secondary(fs, obj, rl); } static void ocfs2_swap_refcount_block_header(struct ocfs2_refcount_block *rb) { rb->rf_suballoc_slot = bswap_16(rb->rf_suballoc_slot); rb->rf_suballoc_bit = bswap_16(rb->rf_suballoc_bit); rb->rf_fs_generation = bswap_32(rb->rf_fs_generation); rb->rf_blkno = bswap_64(rb->rf_blkno); rb->rf_parent = bswap_64(rb->rf_parent); rb->rf_last_eb_blk = bswap_64(rb->rf_last_eb_blk); rb->rf_count = bswap_32(rb->rf_count); rb->rf_flags = bswap_32(rb->rf_flags); rb->rf_clusters = bswap_32(rb->rf_clusters); rb->rf_cpos = bswap_32(rb->rf_cpos); rb->rf_suballoc_loc = bswap_64(rb->rf_suballoc_loc); } void ocfs2_swap_refcount_block_from_cpu(ocfs2_filesys *fs, struct ocfs2_refcount_block *rb) { if (cpu_is_little_endian) return; if (rb->rf_flags & OCFS2_REFCOUNT_TREE_FL) ocfs2_swap_extent_list_from_cpu(fs, rb, &rb->rf_list); else ocfs2_swap_refcount_list_from_cpu(fs, rb, &rb->rf_records); ocfs2_swap_refcount_block_header(rb); } void ocfs2_swap_refcount_block_to_cpu(ocfs2_filesys *fs, struct ocfs2_refcount_block *rb) { if (cpu_is_little_endian) return; ocfs2_swap_refcount_block_header(rb); if (rb->rf_flags & OCFS2_REFCOUNT_TREE_FL) ocfs2_swap_extent_list_to_cpu(fs, rb, &rb->rf_list); else ocfs2_swap_refcount_list_to_cpu(fs, rb, &rb->rf_records); } errcode_t ocfs2_read_refcount_block_nocheck(ocfs2_filesys *fs, uint64_t blkno, char *rb_buf) { errcode_t ret; char *blk; struct ocfs2_refcount_block *rb; if ((blkno < OCFS2_SUPER_BLOCK_BLKNO) || (blkno > fs->fs_blocks)) return OCFS2_ET_BAD_BLKNO; ret = ocfs2_malloc_block(fs->fs_io, &blk); if (ret) return ret; ret = ocfs2_read_blocks(fs, blkno, 1, blk); if (ret) goto out; rb = (struct ocfs2_refcount_block *)blk; ret = ocfs2_validate_meta_ecc(fs, blk, &rb->rf_check); if (ret) goto out; if (memcmp(rb->rf_signature, OCFS2_REFCOUNT_BLOCK_SIGNATURE, strlen(OCFS2_REFCOUNT_BLOCK_SIGNATURE))) { ret = OCFS2_ET_BAD_EXTENT_BLOCK_MAGIC; goto out; } memcpy(rb_buf, blk, fs->fs_blocksize); rb = (struct ocfs2_refcount_block *) rb_buf; ocfs2_swap_refcount_block_to_cpu(fs, rb); out: ocfs2_free(&blk); return ret; } errcode_t ocfs2_read_refcount_block(ocfs2_filesys *fs, uint64_t blkno, char *rb_buf) { errcode_t ret; struct ocfs2_refcount_block *rb = (struct ocfs2_refcount_block *)rb_buf; ret = ocfs2_read_refcount_block_nocheck(fs, blkno, rb_buf); /* * Return corruption error here if the user may have a chance * to walk off the end. * XXX: We trust the rb->rf_flags here. */ if (ret == 0 && (((rb->rf_flags & OCFS2_REFCOUNT_TREE_FL) && rb->rf_list.l_next_free_rec > rb->rf_list.l_count) || (!(rb->rf_flags & OCFS2_REFCOUNT_TREE_FL) && rb->rf_records.rl_used > rb->rf_records.rl_count))) ret = OCFS2_ET_CORRUPT_EXTENT_BLOCK; return ret; } errcode_t ocfs2_write_refcount_block(ocfs2_filesys *fs, uint64_t blkno, char *rb_buf) { errcode_t ret; char *blk; struct ocfs2_refcount_block *rb; if (!(fs->fs_flags & OCFS2_FLAG_RW)) return OCFS2_ET_RO_FILESYS; if ((blkno < OCFS2_SUPER_BLOCK_BLKNO) || (blkno > fs->fs_blocks)) return OCFS2_ET_BAD_BLKNO; ret = ocfs2_malloc_block(fs->fs_io, &blk); if (ret) return ret; memcpy(blk, rb_buf, fs->fs_blocksize); rb = (struct ocfs2_refcount_block *)blk; ocfs2_swap_refcount_block_from_cpu(fs, rb); ocfs2_compute_meta_ecc(fs, blk, &rb->rf_check); ret = io_write_block(fs->fs_io, blkno, 1, blk); if (ret) goto out; fs->fs_flags |= OCFS2_FLAG_CHANGED; ret = 0; out: ocfs2_free(&blk); return ret; } static void ocfs2_find_refcount_rec_in_rl(char *ref_leaf_buf, uint64_t cpos, unsigned int len, struct ocfs2_refcount_rec *ret_rec, int *index) { int i = 0; struct ocfs2_refcount_block *rb = (struct ocfs2_refcount_block *)ref_leaf_buf; struct ocfs2_refcount_rec *rec = NULL; for (; i < rb->rf_records.rl_used; i++) { rec = &rb->rf_records.rl_recs[i]; if (rec->r_cpos + rec->r_clusters <= cpos) continue; else if (rec->r_cpos > cpos) break; /* ok, cpos fail in this rec. Just return. */ if (ret_rec) *ret_rec = *rec; goto out; } if (ret_rec) { /* We meet with a hole here, so fake the rec. */ ret_rec->r_cpos = cpos; ret_rec->r_refcount = 0; if (i < rb->rf_records.rl_used && rec->r_cpos < cpos + len) ret_rec->r_clusters = rec->r_cpos - cpos; else ret_rec->r_clusters = len; } out: *index = i; } /* * Given a cpos and len, try to find the refcount record which contains cpos. * 1. If cpos can be found in one refcount record, return the record. * 2. If cpos can't be found, return a fake record which start from cpos * and end at a small value between cpos+len and start of the next record. * This fake record has r_refcount = 0. */ int ocfs2_get_refcount_rec(ocfs2_filesys *fs, char *ref_root_buf, uint64_t cpos, unsigned int len, struct ocfs2_refcount_rec *ret_rec, int *index, char *ret_buf) { int ret = 0, i, found; uint32_t low_cpos; struct ocfs2_extent_list *el; struct ocfs2_extent_rec *tmp, *rec = NULL; struct ocfs2_extent_block *eb; char *eb_buf = NULL, *ref_leaf_buf = NULL; struct ocfs2_refcount_block *rb = (struct ocfs2_refcount_block *)ref_root_buf; if (!(rb->rf_flags & OCFS2_REFCOUNT_TREE_FL)) { ocfs2_find_refcount_rec_in_rl(ref_root_buf, cpos, len, ret_rec, index); memcpy(ret_buf, ref_root_buf, fs->fs_blocksize); return 0; } el = &rb->rf_list; low_cpos = cpos & OCFS2_32BIT_POS_MASK; if (el->l_tree_depth) { ret = ocfs2_tree_find_leaf(fs, el, rb->rf_blkno, (char *)rb, low_cpos, &eb_buf); if (ret) goto out; eb = (struct ocfs2_extent_block *)eb_buf; el = &eb->h_list; if (el->l_tree_depth) { ret = OCFS2_ET_CORRUPT_EXTENT_BLOCK; goto out; } } found = 0; for (i = el->l_next_free_rec - 1; i >= 0; i--) { rec = &el->l_recs[i]; if (rec->e_cpos <= low_cpos) { found = 1; break; } } /* adjust len when we have ocfs2_extent_rec after it. */ if (found && i < el->l_next_free_rec - 1) { tmp = &el->l_recs[i+1]; if (tmp->e_cpos < cpos + len) len = tmp->e_cpos - cpos; } ret = ocfs2_malloc_block(fs->fs_io, &ref_leaf_buf); if (ret) goto out; ret = ocfs2_read_refcount_block(fs, rec->e_blkno, ref_leaf_buf); if (ret) goto out; ocfs2_find_refcount_rec_in_rl(ref_leaf_buf, cpos, len, ret_rec, index); memcpy(ret_buf, ref_leaf_buf, fs->fs_blocksize); out: if (eb_buf) ocfs2_free(&eb_buf); if (ref_leaf_buf) ocfs2_free(&ref_leaf_buf); return ret; } enum ocfs2_ref_rec_contig { REF_CONTIG_NONE = 0, REF_CONTIG_LEFT, REF_CONTIG_RIGHT, REF_CONTIG_LEFTRIGHT, }; static enum ocfs2_ref_rec_contig ocfs2_refcount_rec_adjacent(struct ocfs2_refcount_block *rb, int index) { if ((rb->rf_records.rl_recs[index].r_refcount == rb->rf_records.rl_recs[index + 1].r_refcount) && (rb->rf_records.rl_recs[index].r_cpos + rb->rf_records.rl_recs[index].r_clusters == rb->rf_records.rl_recs[index + 1].r_cpos)) return REF_CONTIG_RIGHT; return REF_CONTIG_NONE; } static enum ocfs2_ref_rec_contig ocfs2_refcount_rec_contig(struct ocfs2_refcount_block *rb, int index) { enum ocfs2_ref_rec_contig ret = REF_CONTIG_NONE; if (index < rb->rf_records.rl_used - 1) ret = ocfs2_refcount_rec_adjacent(rb, index); if (index > 0) { enum ocfs2_ref_rec_contig tmp; tmp = ocfs2_refcount_rec_adjacent(rb, index - 1); if (tmp == REF_CONTIG_RIGHT) { if (ret == REF_CONTIG_RIGHT) ret = REF_CONTIG_LEFTRIGHT; else ret = REF_CONTIG_LEFT; } } return ret; } static void ocfs2_rotate_refcount_rec_left(struct ocfs2_refcount_block *rb, int index) { assert(rb->rf_records.rl_recs[index].r_refcount == rb->rf_records.rl_recs[index+1].r_refcount); rb->rf_records.rl_recs[index].r_clusters += rb->rf_records.rl_recs[index+1].r_clusters; if (index < rb->rf_records.rl_used - 2) memmove(&rb->rf_records.rl_recs[index + 1], &rb->rf_records.rl_recs[index + 2], sizeof(struct ocfs2_refcount_rec) * (rb->rf_records.rl_used - index - 2)); memset(&rb->rf_records.rl_recs[rb->rf_records.rl_used - 1], 0, sizeof(struct ocfs2_refcount_rec)); rb->rf_records.rl_used -= 1; } /* * Merge the refcount rec if we are contiguous with the adjacent recs. */ static void ocfs2_refcount_rec_merge(struct ocfs2_refcount_block *rb, int index) { enum ocfs2_ref_rec_contig contig = ocfs2_refcount_rec_contig(rb, index); if (contig == REF_CONTIG_NONE) return; if (contig == REF_CONTIG_LEFT || contig == REF_CONTIG_LEFTRIGHT) { assert(index > 0); index--; } ocfs2_rotate_refcount_rec_left(rb, index); if (contig == REF_CONTIG_LEFTRIGHT) ocfs2_rotate_refcount_rec_left(rb, index); } /* * Change the refcount indexed by "index" in rb. * If refcount reaches 0, remove it. */ static int ocfs2_change_refcount_rec(ocfs2_filesys *fs, char *ref_leaf_buf, int index, int merge, int change) { struct ocfs2_refcount_block *rb = (struct ocfs2_refcount_block *)ref_leaf_buf; struct ocfs2_refcount_list *rl = &rb->rf_records; struct ocfs2_refcount_rec *rec = &rl->rl_recs[index]; rec->r_refcount += change; if (!rec->r_refcount) { if (index != rl->rl_used - 1) { memmove(rec, rec + 1, (rl->rl_used - index - 1) * sizeof(struct ocfs2_refcount_rec)); memset(&rl->rl_recs[le16_to_cpu(rl->rl_used) - 1], 0, sizeof(struct ocfs2_refcount_rec)); } rl->rl_used -= 1; } else if (merge) ocfs2_refcount_rec_merge(rb, index); return ocfs2_write_refcount_block(fs, rb->rf_blkno, ref_leaf_buf); } static int ocfs2_expand_inline_ref_root(ocfs2_filesys *fs, char *ref_root_buf, char *ret_leaf_buf) { int ret; uint64_t new_blkno; char *new_buf = NULL; struct ocfs2_refcount_block *new_rb; struct ocfs2_refcount_block *root_rb = (struct ocfs2_refcount_block *)ref_root_buf; ret = ocfs2_malloc_block(fs->fs_io, &new_buf); if (ret) return ret; ret = ocfs2_new_refcount_block(fs, &new_blkno, root_rb->rf_blkno, root_rb->rf_generation); if (ret) goto out; ret = ocfs2_read_refcount_block(fs, new_blkno, new_buf); if (ret) goto out; /* * Initialize ocfs2_refcount_block. * It should contain the same refcount information as the * old root. So just memcpy the refcount_list, set the * rf_cpos to 0 and the leaf flag. */ new_rb = (struct ocfs2_refcount_block *)new_buf; memcpy(&new_rb->rf_list, &root_rb->rf_list, fs->fs_blocksize - offsetof(struct ocfs2_refcount_block, rf_list)); new_rb->rf_cpos = 0; new_rb->rf_flags = OCFS2_REFCOUNT_LEAF_FL; /* Now change the root. */ memset(&root_rb->rf_list, 0, fs->fs_blocksize - offsetof(struct ocfs2_refcount_block, rf_list)); root_rb->rf_list.l_count = ocfs2_extent_recs_per_rb(fs->fs_blocksize); root_rb->rf_clusters = 1; root_rb->rf_list.l_next_free_rec = 1; root_rb->rf_list.l_recs[0].e_blkno = new_blkno; root_rb->rf_list.l_recs[0].e_leaf_clusters = 1; root_rb->rf_flags = OCFS2_REFCOUNT_TREE_FL; /* * We write the new allocated refcount block first. If the write * fails, skip update the root. */ ret = ocfs2_write_refcount_block(fs, new_rb->rf_blkno, new_buf); if (ret) goto out; ret = ocfs2_write_refcount_block(fs, root_rb->rf_blkno, ref_root_buf); if (ret) goto out; memcpy(ret_leaf_buf, new_buf, fs->fs_blocksize); out: ocfs2_free(&new_buf); return ret; } static int ocfs2_refcount_rec_no_intersect(struct ocfs2_refcount_rec *prev, struct ocfs2_refcount_rec *next) { if (ocfs2_get_ref_rec_low_cpos(prev) + prev->r_clusters <= ocfs2_get_ref_rec_low_cpos(next)) return 1; return 0; } static int cmp_refcount_rec_by_low_cpos(const void *a, const void *b) { const struct ocfs2_refcount_rec *l = a, *r = b; uint32_t l_cpos = ocfs2_get_ref_rec_low_cpos(l); uint32_t r_cpos = ocfs2_get_ref_rec_low_cpos(r); if (l_cpos > r_cpos) return 1; if (l_cpos < r_cpos) return -1; return 0; } static int cmp_refcount_rec_by_cpos(const void *a, const void *b) { const struct ocfs2_refcount_rec *l = a, *r = b; uint64_t l_cpos = l->r_cpos; uint64_t r_cpos = r->r_cpos; if (l_cpos > r_cpos) return 1; if (l_cpos < r_cpos) return -1; return 0; } /* * The refcount cpos are ordered by their 64bit cpos, * But we will use the low 32 bit to be the e_cpos in the b-tree. * So we need to make sure that this pos isn't intersected with others. * * Note: The refcount block is already sorted by their low 32 bit cpos, * So just try the middle pos first, and we will exit when we find * the good position. */ static int ocfs2_find_refcount_split_pos(struct ocfs2_refcount_list *rl, uint32_t *split_pos, int *split_index) { int num_used = rl->rl_used; int delta, middle = num_used / 2; for (delta = 0; delta < middle; delta++) { /* Let's check delta earlier than middle */ if (ocfs2_refcount_rec_no_intersect( &rl->rl_recs[middle - delta - 1], &rl->rl_recs[middle - delta])) { *split_index = middle - delta; break; } /* For even counts, don't walk off the end */ if ((middle + delta + 1) == num_used) continue; /* Now try delta past middle */ if (ocfs2_refcount_rec_no_intersect( &rl->rl_recs[middle + delta], &rl->rl_recs[middle + delta + 1])) { *split_index = middle + delta + 1; break; } } if (delta >= middle) return OCFS2_ET_NO_SPACE; *split_pos = ocfs2_get_ref_rec_low_cpos(&rl->rl_recs[*split_index]); return 0; } static int ocfs2_divide_leaf_refcount_block(char *ref_leaf_buf, char *new_buf, uint32_t *split_cpos) { int split_index = 0, num_moved, ret; uint32_t cpos = 0; struct ocfs2_refcount_block *rb = (struct ocfs2_refcount_block *)ref_leaf_buf; struct ocfs2_refcount_list *rl = &rb->rf_records; struct ocfs2_refcount_block *new_rb = (struct ocfs2_refcount_block *)new_buf; struct ocfs2_refcount_list *new_rl = &new_rb->rf_records; /* * XXX: Improvement later. * If we know all the high 32 bit cpos is the same, no need to sort. * * In order to make the whole process safe, we do: * 1. sort the entries by their low 32 bit cpos first so that we can * find the split cpos easily. * 2. call ocfs2_tree_insert_extent to insert the new refcount block. * 3. move the refcount rec to the new block. * 4. sort the entries by their 64 bit cpos. * 5. And we will delay the write out of the leaf block after the * extent tree is successfully changed by its caller. */ qsort(&rl->rl_recs, rl->rl_used, sizeof(struct ocfs2_refcount_rec), cmp_refcount_rec_by_low_cpos); ret = ocfs2_find_refcount_split_pos(rl, &cpos, &split_index); if (ret) return ret; new_rb->rf_cpos = cpos; /* move refcount records starting from split_index to the new block. */ num_moved = rl->rl_used - split_index; memcpy(new_rl->rl_recs, &rl->rl_recs[split_index], num_moved * sizeof(struct ocfs2_refcount_rec)); /*ok, remove the entries we just moved over to the other block. */ memset(&rl->rl_recs[split_index], 0, num_moved * sizeof(struct ocfs2_refcount_rec)); /* change old and new rl_used accordingly. */ rl->rl_used -= num_moved; new_rl->rl_used = num_moved; qsort(&rl->rl_recs, rl->rl_used, sizeof(struct ocfs2_refcount_rec), cmp_refcount_rec_by_cpos); qsort(&new_rl->rl_recs, new_rl->rl_used, sizeof(struct ocfs2_refcount_rec), cmp_refcount_rec_by_cpos); *split_cpos = cpos; return 0; } static int ocfs2_new_leaf_refcount_block(ocfs2_filesys *fs, char *ref_root_buf, char *ref_leaf_buf) { int ret; uint32_t new_cpos; uint64_t new_blkno; struct ocfs2_refcount_block *root_rb = (struct ocfs2_refcount_block *)ref_root_buf; char *new_buf = NULL; struct ocfs2_refcount_block *rb; struct ocfs2_extent_tree ref_et; assert(root_rb->rf_flags & OCFS2_REFCOUNT_TREE_FL); ret = ocfs2_malloc_block(fs->fs_io, &new_buf); if (ret) return ret; ret = ocfs2_new_refcount_block(fs, &new_blkno, root_rb->rf_blkno, root_rb->rf_generation); if (ret) goto out; ret = ocfs2_read_refcount_block(fs, new_blkno, new_buf); ret = ocfs2_divide_leaf_refcount_block(ref_leaf_buf, new_buf, &new_cpos); if (ret) goto out; ocfs2_init_refcount_extent_tree(&ref_et, fs, ref_root_buf, root_rb->rf_blkno); ret = ocfs2_tree_insert_extent(fs, &ref_et, new_cpos, new_blkno, 1, 0); if (ret) goto out; /* * Write the old refcount block first. * If the write fails, fsck should be able to remove all * the refcounted clusters we have moved to the new refcount block. */ rb = (struct ocfs2_refcount_block *)ref_leaf_buf; ret = ocfs2_write_refcount_block(fs, rb->rf_blkno, ref_leaf_buf); if (ret) goto out; ret = ocfs2_write_refcount_block(fs, new_blkno, new_buf); if (ret) goto out; out: if (new_buf) ocfs2_free(&new_buf); return ret; } static int ocfs2_expand_refcount_tree(ocfs2_filesys *fs, char *ref_root_buf, char *ref_leaf_buf) { int ret; struct ocfs2_refcount_block *root_rb = (struct ocfs2_refcount_block *)ref_root_buf; struct ocfs2_refcount_block *leaf_rb = (struct ocfs2_refcount_block *)ref_leaf_buf; if (root_rb->rf_blkno == leaf_rb->rf_blkno) { /* * the old root bh hasn't been expanded to a b-tree, * so expand it first. */ ret = ocfs2_expand_inline_ref_root(fs, ref_root_buf, ref_leaf_buf); if (ret) goto out; } /* Now add a new refcount block into the tree.*/ ret = ocfs2_new_leaf_refcount_block(fs, ref_root_buf, ref_leaf_buf); out: return ret; } /* * Adjust the extent rec in b-tree representing ref_leaf_buf. * * Only called when we have inserted a new refcount rec at index 0 * which means ocfs2_extent_rec.e_cpos may need some change. */ static int ocfs2_adjust_refcount_rec(ocfs2_filesys *fs, char *ref_root_buf, char *ref_leaf_buf, struct ocfs2_refcount_rec *rec) { int ret = 0, i; uint32_t new_cpos, old_cpos; struct ocfs2_path *path = NULL; struct ocfs2_extent_list *el; struct ocfs2_extent_tree et; struct ocfs2_refcount_block *rb = (struct ocfs2_refcount_block *)ref_root_buf; uint64_t ref_root_blkno = rb->rf_blkno;; if (!(rb->rf_flags & OCFS2_REFCOUNT_TREE_FL)) goto out; rb = (struct ocfs2_refcount_block *)ref_leaf_buf; old_cpos = rb->rf_cpos; new_cpos = rec->r_cpos & OCFS2_32BIT_POS_MASK; if (old_cpos <= new_cpos) goto out; ocfs2_init_refcount_extent_tree(&et, fs, ref_root_buf, ref_root_blkno); path = ocfs2_new_path_from_et(&et); if (!path) { ret = OCFS2_ET_NO_MEMORY; goto out; } ret = ocfs2_find_path(fs, path, old_cpos); if (ret) goto out; /* change the leaf extent block first. */ el = path_leaf_el(path); for (i = 0; i < el->l_next_free_rec; i++) if (el->l_recs[i].e_cpos == old_cpos) break; assert(i < el->l_next_free_rec); el->l_recs[i].e_cpos = new_cpos; /* change the r_cpos in the leaf block. */ rb->rf_cpos = new_cpos; ret = ocfs2_write_extent_block(fs, path_leaf_blkno(path), path_leaf_buf(path)); if (ret) goto out; ret = ocfs2_write_refcount_block(fs, rb->rf_blkno, ref_leaf_buf); out: ocfs2_free_path(path); return ret; } static int ocfs2_insert_refcount_rec(ocfs2_filesys *fs, char *ref_root_buf, char *ref_leaf_buf, struct ocfs2_refcount_rec *rec, int index, int merge) { int ret; struct ocfs2_refcount_block *rb = (struct ocfs2_refcount_block *)ref_leaf_buf; struct ocfs2_refcount_list *rf_list = &rb->rf_records; assert(!(rb->rf_flags & OCFS2_REFCOUNT_TREE_FL)); if (rf_list->rl_used == rf_list->rl_count) { uint64_t cpos = rec->r_cpos; uint32_t len = rec->r_clusters; ret = ocfs2_expand_refcount_tree(fs, ref_root_buf, ref_leaf_buf); if (ret) goto out; ret = ocfs2_get_refcount_rec(fs, ref_root_buf, cpos, len, NULL, &index, ref_leaf_buf); if (ret) goto out; } if (index < rf_list->rl_used) memmove(&rf_list->rl_recs[index + 1], &rf_list->rl_recs[index], (rf_list->rl_used - index) * sizeof(struct ocfs2_refcount_rec)); rf_list->rl_recs[index] = *rec; rf_list->rl_used += 1; if (merge) ocfs2_refcount_rec_merge(rb, index); ret = ocfs2_write_refcount_block(fs, rb->rf_blkno, ref_leaf_buf); if (ret) goto out; if (index == 0) ret = ocfs2_adjust_refcount_rec(fs, ref_root_buf, ref_leaf_buf, rec); out: return ret; } /* * Split the refcount_rec indexed by "index" in ref_leaf_buf. * This is much simple than our b-tree code. * split_rec is the new refcount rec we want to insert. * If split_rec->r_refcount > 0, we are changing the refcount(in case we * increase refcount or decrease a refcount to non-zero). * If split_rec->r_refcount == 0, we are punching a hole in current refcount * rec( in case we decrease a refcount to zero). */ static int ocfs2_split_refcount_rec(ocfs2_filesys *fs, char *ref_root_buf, char *ref_leaf_buf, struct ocfs2_refcount_rec *split_rec, int index, int merge) { int ret, recs_need; uint32_t len; struct ocfs2_refcount_block *rb = (struct ocfs2_refcount_block *)ref_leaf_buf; struct ocfs2_refcount_list *rf_list = &rb->rf_records; struct ocfs2_refcount_rec *orig_rec = &rf_list->rl_recs[index]; struct ocfs2_refcount_rec *tail_rec = NULL; assert(!(rb->rf_flags & OCFS2_REFCOUNT_TREE_FL)); /* * If we just need to split the header or tail clusters, * no more recs are needed, just split is OK. * Otherwise we at least need one new recs. */ if (!split_rec->r_refcount && (split_rec->r_cpos == orig_rec->r_cpos || split_rec->r_cpos + split_rec->r_clusters == orig_rec->r_cpos + orig_rec->r_clusters)) recs_need = 0; else recs_need = 1; /* * We need one more rec if we split in the middle and the new rec have * some refcount in it. */ if (split_rec->r_refcount && (split_rec->r_cpos != orig_rec->r_cpos && split_rec->r_cpos + split_rec->r_clusters != orig_rec->r_cpos + orig_rec->r_clusters)) recs_need++; /* If the leaf block don't have enough record, expand it. */ if (rf_list->rl_used + recs_need > rf_list->rl_count) { struct ocfs2_refcount_rec tmp_rec; uint64_t cpos = orig_rec->r_cpos; len = orig_rec->r_clusters; ret = ocfs2_expand_refcount_tree(fs, ref_root_buf, ref_leaf_buf); if (ret) goto out; /* * We have to re-get it since now cpos may be moved to * another leaf block. */ ret = ocfs2_get_refcount_rec(fs, ref_root_buf, cpos, len, &tmp_rec, &index, ref_leaf_buf); if (ret) goto out; orig_rec = &rf_list->rl_recs[index]; } /* * We have calculated out how many new records we need and store * in recs_need, so spare enough space first by moving the records * after "index" to the end. */ if (rf_list->rl_used && index != rf_list->rl_used - 1) memmove(&rf_list->rl_recs[index + 1 + recs_need], &rf_list->rl_recs[index + 1], (rf_list->rl_used - index - 1) * sizeof(struct ocfs2_refcount_rec)); len = (orig_rec->r_cpos + orig_rec->r_clusters) - (split_rec->r_cpos + split_rec->r_clusters); /* * If we have "len", the we will split in the tail and move it * to the end of the space we have just spared. */ if (len) { tail_rec = &rf_list->rl_recs[index + recs_need]; memcpy(tail_rec, orig_rec, sizeof(struct ocfs2_refcount_rec)); tail_rec->r_cpos += tail_rec->r_clusters - len; tail_rec->r_clusters = len; } /* * If the split pos isn't the same as the original one, we need to * split in the head. * * Note: We have the chance that split_rec.r_refcount = 0, * recs_need = 0 and len > 0, which means we just cut the head from * the orig_rec and in that case we have done some modification in * orig_rec above, so the check for r_cpos is faked. */ if (split_rec->r_cpos != orig_rec->r_cpos && tail_rec != orig_rec) { len = split_rec->r_cpos - orig_rec->r_cpos; orig_rec->r_clusters = len; index++; } rf_list->rl_used += recs_need; if (split_rec->r_refcount) { rf_list->rl_recs[index] = *split_rec; if (merge) ocfs2_refcount_rec_merge(rb, index); } ret = ocfs2_write_refcount_block(fs, rb->rf_blkno, ref_leaf_buf); out: return ret; } static int __ocfs2_increase_refcount(ocfs2_filesys *fs, char *ref_root_buf, uint64_t cpos, uint32_t len, int merge, int value) { int ret = 0, index; char *ref_leaf_buf = NULL; struct ocfs2_refcount_rec rec; unsigned int set_len = 0; struct ocfs2_refcount_block *root_rb, *rb; ret = ocfs2_malloc_block(fs->fs_io, &ref_leaf_buf); if (ret) return ret; root_rb = (struct ocfs2_refcount_block *)ref_root_buf; rb = (struct ocfs2_refcount_block *)ref_leaf_buf; while (len) { ret = ocfs2_get_refcount_rec(fs, ref_root_buf, cpos, len, &rec, &index, ref_leaf_buf); if (ret) goto out; set_len = rec.r_clusters; /* * Here we may meet with 3 situations: * * 1. If we find an already existing record, and the length * is the same, cool, we just need to increase the r_refcount * and it is OK. * 2. If we find a hole, just insert it with r_refcount = 1. * 3. If we are in the middle of one extent record, split * it. */ if (rec.r_refcount && rec.r_cpos == cpos && set_len <= len) { ret = ocfs2_change_refcount_rec(fs, ref_leaf_buf, index, merge, value); if (ret) goto out; } else if (!rec.r_refcount) { rec.r_refcount = value; ret = ocfs2_insert_refcount_rec(fs, ref_root_buf, ref_leaf_buf, &rec, index, merge); if (ret) goto out; } else { set_len = ocfs2_min((uint64_t)(cpos + len), (uint64_t)(rec.r_cpos + set_len)) - cpos; rec.r_cpos = cpos; rec.r_clusters = set_len; rec.r_refcount += value; ret = ocfs2_split_refcount_rec(fs, ref_root_buf, ref_leaf_buf, &rec, index, merge); if (ret) goto out; } cpos += set_len; len -= set_len; /* In user space, we have to sync the buf by ourselves. */ if (rb->rf_blkno == root_rb->rf_blkno) memcpy(ref_root_buf, ref_leaf_buf, fs->fs_blocksize); } out: ocfs2_free(&ref_leaf_buf); return ret; } errcode_t ocfs2_increase_refcount(ocfs2_filesys *fs, uint64_t ino, uint64_t cpos, uint32_t len) { errcode_t ret; char *ref_root_buf = NULL; char *di_buf = NULL; struct ocfs2_dinode *di; ret = ocfs2_malloc_block(fs->fs_io, &di_buf); if (ret) goto out; ret = ocfs2_read_inode(fs, ino, di_buf); if (ret) goto out; di = (struct ocfs2_dinode *)di_buf; assert(di->i_dyn_features & OCFS2_HAS_REFCOUNT_FL); assert(di->i_refcount_loc); ret = ocfs2_malloc_block(fs->fs_io, &ref_root_buf); if (ret) goto out; ret = ocfs2_read_refcount_block(fs, di->i_refcount_loc, ref_root_buf); if (ret) goto out; ret = __ocfs2_increase_refcount(fs, ref_root_buf, cpos, len, 1, 1); out: if (ref_root_buf) ocfs2_free(&ref_root_buf); if (di_buf) ocfs2_free(&di_buf); return ret; } static int ocfs2_remove_refcount_extent(ocfs2_filesys *fs, char *ref_root_buf, char *ref_leaf_buf) { int ret; struct ocfs2_refcount_block *rb = (struct ocfs2_refcount_block *)ref_leaf_buf; struct ocfs2_refcount_block *root_rb = (struct ocfs2_refcount_block *)ref_root_buf; struct ocfs2_extent_tree et; assert(rb->rf_records.rl_used == 0); ocfs2_init_refcount_extent_tree(&et, fs, ref_root_buf, root_rb->rf_blkno); ret = ocfs2_remove_extent(fs, &et, rb->rf_cpos, 1); if (ret) goto out; ret = ocfs2_delete_refcount_block(fs, rb->rf_blkno); root_rb->rf_clusters -= 1; /* * check whether we need to restore the root refcount block if * there is no leaf extent block at atll. */ if (!root_rb->rf_list.l_next_free_rec) { assert(root_rb->rf_clusters == 0); root_rb->rf_flags = 0; root_rb->rf_parent = 0; root_rb->rf_cpos = 0; memset(&root_rb->rf_records, 0, fs->fs_blocksize - offsetof(struct ocfs2_refcount_block, rf_records)); root_rb->rf_records.rl_count = ocfs2_refcount_recs_per_rb(fs->fs_blocksize); } ret = ocfs2_write_refcount_block(fs, root_rb->rf_blkno, ref_root_buf); out: return ret; } static int ocfs2_decrease_refcount_rec(ocfs2_filesys *fs, char *ref_root_buf, char *ref_leaf_buf, int index, uint64_t cpos, unsigned int len, int value) { int ret; struct ocfs2_refcount_block *rb = (struct ocfs2_refcount_block *)ref_leaf_buf; struct ocfs2_refcount_block *root_rb = (struct ocfs2_refcount_block *)ref_root_buf; struct ocfs2_refcount_rec *rec = &rb->rf_records.rl_recs[index]; assert(cpos >= rec->r_cpos); assert(cpos + len <= rec->r_cpos + rec->r_clusters); if (cpos == rec->r_cpos && len == rec->r_clusters) ret = ocfs2_change_refcount_rec(fs, ref_leaf_buf, index, 1, -value); else { struct ocfs2_refcount_rec split = *rec; split.r_cpos = cpos; split.r_clusters = len; split.r_refcount -= value; ret = ocfs2_split_refcount_rec(fs, ref_root_buf, ref_leaf_buf, &split, index, 1); } if (ret) goto out; /* In user space, we have to sync the buf by ourselves. */ if (rb->rf_blkno == root_rb->rf_blkno) memcpy(ref_root_buf, ref_leaf_buf, fs->fs_blocksize); /* Remove the leaf refcount block if it contains no refcount record. */ if (!rb->rf_records.rl_used && rb->rf_blkno != root_rb->rf_blkno) { ret = ocfs2_remove_refcount_extent(fs, ref_root_buf, ref_leaf_buf); } out: return ret; } static int __ocfs2_decrease_refcount(ocfs2_filesys *fs, char *ref_root_buf, uint64_t cpos, uint32_t len, int delete) { int ret = 0, index = 0; struct ocfs2_refcount_rec rec; unsigned int r_count = 0, r_len; char *ref_leaf_buf = NULL; ret = ocfs2_malloc_block(fs->fs_io, &ref_leaf_buf); if (ret) return ret; while (len) { ret = ocfs2_get_refcount_rec(fs, ref_root_buf, cpos, len, &rec, &index, ref_leaf_buf); if (ret) goto out; r_count = rec.r_refcount; assert(r_count > 0); if (!delete) assert(r_count == 1); r_len = ocfs2_min((uint64_t)(cpos + len), (uint64_t)(rec.r_cpos + rec.r_clusters)) - cpos; ret = ocfs2_decrease_refcount_rec(fs, ref_root_buf, ref_leaf_buf, index, cpos, r_len, 1); if (ret) goto out; if (rec.r_refcount == 1 && delete) { ret = ocfs2_free_clusters(fs, r_len, ocfs2_clusters_to_blocks(fs, cpos)); if (ret) goto out; } cpos += r_len; len -= r_len; } out: ocfs2_free(&ref_leaf_buf); return ret; } errcode_t ocfs2_decrease_refcount(ocfs2_filesys *fs, uint64_t ino, uint32_t cpos, uint32_t len, int delete) { errcode_t ret; char *ref_root_buf = NULL; char *di_buf = NULL; struct ocfs2_dinode *di; ret = ocfs2_malloc_block(fs->fs_io, &di_buf); if (ret) goto out; ret = ocfs2_read_inode(fs, ino, di_buf); if (ret) goto out; di = (struct ocfs2_dinode *)di_buf; assert(di->i_dyn_features & OCFS2_HAS_REFCOUNT_FL); assert(di->i_refcount_loc); ret = ocfs2_malloc_block(fs->fs_io, &ref_root_buf); if (ret) goto out; ret = ocfs2_read_refcount_block(fs, di->i_refcount_loc, ref_root_buf); if (ret) goto out; ret = __ocfs2_decrease_refcount(fs, ref_root_buf, cpos, len, delete); out: if (ref_root_buf) ocfs2_free(&ref_root_buf); if (di_buf) ocfs2_free(&di_buf); return ret; } #define MAX_CONTIG_BYTES 1048576 static inline unsigned int ocfs2_cow_contig_clusters(ocfs2_filesys *fs) { return ocfs2_clusters_in_bytes(fs, MAX_CONTIG_BYTES); } static inline unsigned int ocfs2_cow_contig_mask(ocfs2_filesys *fs) { return ~(ocfs2_cow_contig_clusters(fs) - 1); } /* * Given an extent that starts at 'start' and an I/O that starts at 'cpos', * find an offset (start + (n * contig_clusters)) that is closest to cpos * while still being less than or equal to it. * * The goal is to break the extent at a multiple of contig_clusters. */ static inline unsigned int ocfs2_cow_align_start(ocfs2_filesys *fs, unsigned int start, unsigned int cpos) { assert(start <= cpos); return start + ((cpos - start) & ocfs2_cow_contig_mask(fs)); } /* * Given a cluster count of len, pad it out so that it is a multiple * of contig_clusters. */ static inline unsigned int ocfs2_cow_align_length(ocfs2_filesys *fs, unsigned int len) { unsigned int padded = (len + (ocfs2_cow_contig_clusters(fs) - 1)) & ocfs2_cow_contig_mask(fs); /* Did we wrap? */ if (padded < len) padded = UINT_MAX; return padded; } /* * Calculate out the start and number of virtual clusters we need to to CoW. * * cpos is vitual start cluster position we want to do CoW in a * file and write_len is the cluster length. * max_cpos is the place where we want to stop CoW intentionally. * * Normal we will start CoW from the beginning of extent record cotaining cpos. * We try to break up extents on boundaries of MAX_CONTIG_BYTES so that we * get good I/O from the resulting extent tree. */ static int ocfs2_refcount_cal_cow_clusters(ocfs2_filesys *fs, struct ocfs2_extent_tree *et, uint32_t cpos, uint32_t write_len, uint32_t max_cpos, uint32_t *cow_start, uint32_t *cow_len) { int ret = 0; struct ocfs2_extent_list *el = et->et_root_el; int tree_height = el->l_tree_depth, i; char *eb_buf = NULL; struct ocfs2_extent_block *eb = NULL; struct ocfs2_extent_rec *rec; unsigned int want_clusters, rec_end = 0; int contig_clusters = ocfs2_cow_contig_clusters(fs); int leaf_clusters; assert(cpos + write_len <= max_cpos); ret = ocfs2_malloc_block(fs->fs_io, &eb_buf); if (ret) return ret; if (tree_height > 0) { ret = ocfs2_tree_find_leaf(fs, el, et->et_root_blkno, et->et_root_buf, cpos, &eb_buf); if (ret) goto out; eb = (struct ocfs2_extent_block *) eb_buf; el = &eb->h_list; if (el->l_tree_depth) { ret = OCFS2_ET_CORRUPT_EXTENT_BLOCK; goto out; } } else el = et->et_root_el; *cow_len = 0; for (i = 0; i < el->l_next_free_rec; i++) { rec = &el->l_recs[i]; if (ocfs2_is_empty_extent(rec)) { assert(i == 0); continue; } if (rec->e_cpos + rec->e_leaf_clusters <= cpos) continue; if (*cow_len == 0) { /* * We should find a refcounted record in the * first pass. */ assert(rec->e_flags & OCFS2_EXT_REFCOUNTED); *cow_start = rec->e_cpos; } /* * If we encounter a hole, a non-refcounted record or * pass the max_cpos, stop the search. */ if ((!(rec->e_flags & OCFS2_EXT_REFCOUNTED)) || (*cow_len && rec_end != rec->e_cpos) || (max_cpos <= rec->e_cpos)) break; leaf_clusters = rec->e_leaf_clusters; rec_end = rec->e_cpos + leaf_clusters; if (rec_end > max_cpos) { rec_end = max_cpos; leaf_clusters = rec_end - rec->e_cpos; } /* * How many clusters do we actually need from * this extent? First we see how many we actually * need to complete the write. If that's smaller * than contig_clusters, we try for contig_clusters. */ if (!*cow_len) want_clusters = write_len; else want_clusters = (cpos + write_len) - (*cow_start + *cow_len); if (want_clusters < contig_clusters) want_clusters = contig_clusters; /* * If the write does not cover the whole extent, we * need to calculate how we're going to split the extent. * We try to do it on contig_clusters boundaries. * * Any extent smaller than contig_clusters will be * CoWed in its entirety. */ if (leaf_clusters <= contig_clusters) *cow_len += leaf_clusters; else if (*cow_len || (*cow_start == cpos)) { /* * This extent needs to be CoW'd from its * beginning, so all we have to do is compute * how many clusters to grab. We align * want_clusters to the edge of contig_clusters * to get better I/O. */ want_clusters = ocfs2_cow_align_length(fs, want_clusters); if (leaf_clusters < want_clusters) *cow_len += leaf_clusters; else *cow_len += want_clusters; } else if ((*cow_start + contig_clusters) >= (cpos + write_len)) { /* * Breaking off contig_clusters at the front * of the extent will cover our write. That's * easy. */ *cow_len = contig_clusters; } else if ((rec_end - cpos) <= contig_clusters) { /* * Breaking off contig_clusters at the tail of * this extent will cover cpos. */ *cow_start = rec_end - contig_clusters; *cow_len = contig_clusters; } else if ((rec_end - cpos) <= want_clusters) { /* * While we can't fit the entire write in this * extent, we know that the write goes from cpos * to the end of the extent. Break that off. * We try to break it at some multiple of * contig_clusters from the front of the extent. * Failing that (ie, cpos is within * contig_clusters of the front), we'll CoW the * entire extent. */ *cow_start = ocfs2_cow_align_start(fs, *cow_start, cpos); *cow_len = rec_end - *cow_start; } else { /* * Ok, the entire write lives in the middle of * this extent. Let's try to slice the extent up * nicely. Optimally, our CoW region starts at * m*contig_clusters from the beginning of the * extent and goes for n*contig_clusters, * covering the entire write. */ *cow_start = ocfs2_cow_align_start(fs, *cow_start, cpos); want_clusters = (cpos + write_len) - *cow_start; want_clusters = ocfs2_cow_align_length(fs, want_clusters); if (*cow_start + want_clusters <= rec_end) *cow_len = want_clusters; else *cow_len = rec_end - *cow_start; } /* Have we covered our entire write yet? */ if ((*cow_start + *cow_len) >= (cpos + write_len)) break; /* * If we reach the end of the extent block and don't get enough * clusters, continue with the next extent block if possible. */ if (i + 1 == el->l_next_free_rec && eb && eb->h_next_leaf_blk) { ret = ocfs2_read_extent_block(fs, eb->h_next_leaf_blk, eb_buf); if (ret) goto out; eb = (struct ocfs2_extent_block *)eb_buf; el = &eb->h_list; i = -1; } } out: if (eb_buf) ocfs2_free(&eb_buf); return ret; } static int ocfs2_duplicate_clusters(struct ocfs2_cow_context *context, uint32_t cpos, uint32_t old_cluster, uint32_t new_cluster, uint32_t new_len) { int ret; int i, bpc = context->fs->fs_clustersize / context->fs->fs_blocksize; uint64_t old_block = ocfs2_clusters_to_blocks(context->fs, old_cluster); uint64_t new_block = ocfs2_clusters_to_blocks(context->fs, new_cluster); char *buf = NULL; ret = ocfs2_malloc_blocks(context->fs->fs_io, bpc, &buf); if (ret) return ret; for (i = 0; i < new_len; i++, old_block += bpc, new_block += bpc) { ret = ocfs2_read_blocks(context->fs, old_block, bpc, buf); if (ret) break; ret = io_write_block(context->fs->fs_io, new_block, bpc, buf); if (ret) break; } ocfs2_free(&buf); return ret; } static int __ocfs2_clear_ext_refcount(ocfs2_filesys *fs, struct ocfs2_extent_tree *et, uint32_t cpos, uint32_t p_cluster, uint32_t len, unsigned int ext_flags) { return ocfs2_change_extent_flag(fs, et, cpos, len, ocfs2_clusters_to_blocks(fs, p_cluster), 0, OCFS2_EXT_REFCOUNTED); } static int ocfs2_replace_clusters(struct ocfs2_cow_context *context, uint32_t cpos, uint32_t old, uint32_t new, uint32_t len, unsigned int ext_flags) { int ret; /*If the old clusters is unwritten, no need to duplicate. */ if (!(ext_flags & OCFS2_EXT_UNWRITTEN)) { ret = ocfs2_duplicate_clusters(context, cpos, old, new, len); if (ret) goto out; } ret = __ocfs2_clear_ext_refcount(context->fs, &context->data_et, cpos, new, len, ext_flags); out: return ret; } static int ocfs2_di_get_clusters(struct ocfs2_cow_context *context, uint32_t v_cluster, uint32_t *p_cluster, uint32_t *num_clusters, uint16_t *extent_flags) { ocfs2_cached_inode *cinode = context->cow_object; return ocfs2_get_clusters(cinode, v_cluster, p_cluster, num_clusters, extent_flags); } static int ocfs2_make_clusters_writable(struct ocfs2_cow_context *context, uint32_t cpos, uint32_t p_cluster, uint32_t num_clusters, unsigned int e_flags) { int ret, delete, index; uint32_t new_len; uint64_t start; unsigned int set_len; char *ref_leaf_buf = NULL; struct ocfs2_refcount_rec rec; ret = ocfs2_malloc_block(context->fs->fs_io, &ref_leaf_buf); if (ret) return ret; while (num_clusters) { ret = ocfs2_get_refcount_rec(context->fs, context->ref_root_buf, p_cluster, num_clusters, &rec, &index, ref_leaf_buf); if (ret) goto out; assert(rec.r_refcount); set_len = ocfs2_min((uint64_t)p_cluster + num_clusters, (uint64_t)rec.r_cpos + rec.r_clusters) - p_cluster; /* * There are many different situation here. * 1. If refcount == 1, remove the flag and don't COW. * 2. If refcount > 1, allocate clusters. * Here we may not allocate r_len once at a time, so continue * until we reach num_clusters. */ if (rec.r_refcount == 1) { delete = 0; ret = __ocfs2_clear_ext_refcount(context->fs, &context->data_et, cpos, p_cluster, set_len, e_flags); if (ret) goto out; } else { delete = 1; ret = ocfs2_new_clusters(context->fs, 1, set_len, &start, &new_len); if (ret) goto out; ret = ocfs2_replace_clusters(context, cpos, p_cluster, ocfs2_blocks_to_clusters(context->fs, start), new_len, e_flags); if (ret) goto out; set_len = new_len; } ret = __ocfs2_decrease_refcount(context->fs, context->ref_root_buf, p_cluster, set_len, delete); if (ret) goto out; cpos += set_len; p_cluster += set_len; num_clusters -= set_len; } /* handle any post_cow action. */ if (context->post_refcount && context->post_refcount->func) { ret = context->post_refcount->func(context->fs, context->post_refcount->para); if (ret) goto out; } out: if (ref_leaf_buf) ocfs2_free(&ref_leaf_buf); return ret; } static int ocfs2_replace_cow(struct ocfs2_cow_context *context) { int ret = 0; uint32_t cow_start = context->cow_start, cow_len = context->cow_len; uint32_t p_cluster, num_clusters; uint16_t ext_flags; if (!ocfs2_refcount_tree(OCFS2_RAW_SB(context->fs->fs_super))) return OCFS2_ET_RO_FILESYS; while (cow_len) { ret = context->get_clusters(context, cow_start, &p_cluster, &num_clusters, &ext_flags); if (ret) break; assert(ext_flags & OCFS2_EXT_REFCOUNTED); if (cow_len < num_clusters) num_clusters = cow_len; ret = ocfs2_make_clusters_writable(context, cow_start, p_cluster, num_clusters, ext_flags); if (ret) break; cow_len -= num_clusters; cow_start += num_clusters; } return ret; } /* * Starting at cpos, try to CoW write_len clusters. Don't CoW * past max_cpos. This will stop when it runs into a hole or an * unrefcounted extent. */ static int ocfs2_refcount_cow_hunk(ocfs2_cached_inode *cinode, uint32_t cpos, uint32_t write_len, uint32_t max_cpos) { int ret; uint32_t cow_start = 0, cow_len = 0; struct ocfs2_cow_context context; assert(cinode->ci_inode->i_dyn_features & OCFS2_HAS_REFCOUNT_FL); memset(&context, 0, sizeof(struct ocfs2_cow_context)); ocfs2_init_dinode_extent_tree(&context.data_et, cinode->ci_fs, (char *)cinode->ci_inode, cinode->ci_blkno); ret = ocfs2_refcount_cal_cow_clusters(cinode->ci_fs, &context.data_et, cpos, write_len, max_cpos, &cow_start, &cow_len); if (ret) goto out; assert(cow_len > 0); context.cow_start = cow_start; context.cow_len = cow_len; context.fs = cinode->ci_fs; context.get_clusters = ocfs2_di_get_clusters; context.cow_object = cinode; ret = ocfs2_malloc_block(cinode->ci_fs->fs_io, &context.ref_root_buf); if (ret) goto out; ret = ocfs2_read_refcount_block(cinode->ci_fs, cinode->ci_inode->i_refcount_loc, context.ref_root_buf); if (ret) goto out; ret = ocfs2_replace_cow(&context); ocfs2_free(&context.ref_root_buf); out: return ret; } /* * CoW any and all clusters between cpos and cpos+write_len. * Don't CoW past max_cpos. If this returns successfully, all * clusters between cpos and cpos+write_len are safe to modify. */ errcode_t ocfs2_refcount_cow(ocfs2_cached_inode *cinode, uint32_t cpos, uint32_t write_len, uint32_t max_cpos) { int ret = 0; uint32_t p_cluster, num_clusters; uint16_t ext_flags; while (write_len) { ret = ocfs2_get_clusters(cinode, cpos, &p_cluster, &num_clusters, &ext_flags); if (ret) break; if (write_len < num_clusters) num_clusters = write_len; if (ext_flags & OCFS2_EXT_REFCOUNTED) { ret = ocfs2_refcount_cow_hunk(cinode, cpos, num_clusters, max_cpos); if (ret) break; } write_len -= num_clusters; cpos += num_clusters; } if (!ret) ret = ocfs2_write_cached_inode(cinode->ci_fs, cinode); return ret; } errcode_t ocfs2_refcount_tree_get_rec(ocfs2_filesys *fs, struct ocfs2_refcount_block *rb, uint32_t phys_cpos, uint64_t *p_blkno, uint32_t *e_cpos, uint32_t *num_clusters) { int i; errcode_t ret = 0; char *eb_buf = NULL; struct ocfs2_extent_block *eb; struct ocfs2_extent_rec *rec = NULL; struct ocfs2_extent_list *el = &rb->rf_list; uint64_t e_blkno = 0; if (el->l_tree_depth) { ret = ocfs2_tree_find_leaf(fs, el, rb->rf_blkno, (char *)rb, phys_cpos, &eb_buf); if (ret) goto out; eb = (struct ocfs2_extent_block *)eb_buf; el = &eb->h_list; if (el->l_tree_depth) { ret = OCFS2_ET_INVALID_ARGUMENT; goto out; } } for (i = el->l_next_free_rec - 1; i >= 0; i--) { rec = &el->l_recs[i]; if (rec->e_cpos <= phys_cpos) { e_blkno = rec->e_blkno; break; } } if (!e_blkno) { ret = OCFS2_ET_INVALID_ARGUMENT; goto out; } *p_blkno = rec->e_blkno; *num_clusters = rec->e_leaf_clusters; if (e_cpos) *e_cpos = rec->e_cpos; out: if (eb_buf) ocfs2_free(&eb_buf); return ret; } errcode_t ocfs2_refcount_punch_hole(ocfs2_filesys *fs, uint64_t rf_blkno, uint64_t p_start, uint32_t len) { errcode_t ret; char *root_buf = NULL, *buf = NULL; struct ocfs2_refcount_rec rec; int index; uint32_t dec_len; ret = ocfs2_malloc_block(fs->fs_io, &root_buf); if (ret) goto out; ret = ocfs2_malloc_block(fs->fs_io, &buf); if (ret) goto out; ret = ocfs2_read_refcount_block(fs, rf_blkno, root_buf); if (ret) goto out; while (len) { ret = ocfs2_get_refcount_rec(fs, root_buf, p_start, len, &rec, &index, buf); if (!rec.r_refcount) { /* There is no refcount for p_start. */ len -= rec.r_clusters; p_start += rec.r_clusters; continue; } dec_len = (p_start + len < rec.r_cpos + rec.r_clusters) ? len : (rec.r_cpos + rec.r_clusters - p_start); ret = ocfs2_decrease_refcount_rec(fs, root_buf, buf, index, p_start, dec_len, rec.r_refcount); if (ret) goto out; len -= dec_len; p_start += dec_len; } out: if (root_buf) ocfs2_free(&root_buf); if (buf) ocfs2_free(&buf); return ret; } errcode_t ocfs2_change_refcount(ocfs2_filesys *fs, uint64_t rf_blkno, uint64_t p_start, uint32_t len, uint32_t refcount) { errcode_t ret; char *root_buf = NULL, *buf = NULL; struct ocfs2_refcount_rec rec; int index, value; ret = ocfs2_malloc_block(fs->fs_io, &root_buf); if (ret) goto out; ret = ocfs2_malloc_block(fs->fs_io, &buf); if (ret) goto out; ret = ocfs2_read_refcount_block(fs, rf_blkno, root_buf); if (ret) goto out; ret = ocfs2_get_refcount_rec(fs, root_buf, p_start, len, &rec, &index, buf); assert(rec.r_refcount != refcount && rec.r_cpos <= p_start && rec.r_cpos + rec.r_clusters >= p_start + len); value = refcount; value -= rec.r_refcount; ret = __ocfs2_increase_refcount(fs, root_buf, p_start, len, 1, value); out: if (root_buf) ocfs2_free(&root_buf); if (buf) ocfs2_free(&buf); return ret; } struct xattr_value_obj { errcode_t errcode; uint64_t p_cpos; uint32_t v_cpos; uint32_t clusters; int new_flags; int clear_flags; }; static int change_xattr_refcount(ocfs2_cached_inode *ci, char *xe_buf, uint64_t xe_blkno, struct ocfs2_xattr_entry *xe, char *value_buf, uint64_t value_blkno, void *value, int in_bucket, void *priv_data) { uint32_t p_cluster, num_clusters; uint16_t ext_flags; struct xattr_value_obj *obj = priv_data; struct ocfs2_extent_tree et; ocfs2_root_write_func write_func = NULL; struct ocfs2_xattr_value_root *xv; if (ocfs2_xattr_is_local(xe)) return 0; xv = (struct ocfs2_xattr_value_root *)value; obj->errcode = ocfs2_xattr_get_clusters(ci->ci_fs, &xv->xr_list, value_blkno, value_buf, obj->v_cpos, &p_cluster, &num_clusters, &ext_flags); if (obj->errcode) return OCFS2_XATTR_ERROR; if (p_cluster != obj->p_cpos) return 0; assert(num_clusters >= obj->clusters); if (xe_blkno == ci->ci_inode->i_blkno) write_func = ocfs2_write_inode; else if (xe_blkno == ci->ci_inode->i_xattr_loc) write_func = ocfs2_write_xattr_block; ocfs2_init_xattr_value_extent_tree(&et, ci->ci_fs, value_buf, value_blkno, write_func, xv); obj->errcode = ocfs2_change_extent_flag(ci->ci_fs, &et, obj->v_cpos, obj->clusters, ocfs2_clusters_to_blocks(ci->ci_fs, obj->p_cpos), obj->new_flags, obj->clear_flags); if (obj->errcode) goto out; if (!write_func) { assert(in_bucket); obj->errcode = ocfs2_write_xattr_bucket(ci->ci_fs, xe_blkno, xe_buf); if (obj->errcode) goto out; } return OCFS2_XATTR_ABORT; out: return OCFS2_XATTR_ERROR; } static errcode_t ocfs2_xattr_change_ext_refcount(ocfs2_filesys *fs, ocfs2_cached_inode *ci, uint32_t v_cpos, uint32_t clusters, uint64_t p_cpos, int new_flags, int clear_flags) { int iret; errcode_t ret = 0; struct xattr_value_obj obj = { .v_cpos = v_cpos, .p_cpos = p_cpos, .clusters = clusters, .new_flags = new_flags, .clear_flags = clear_flags, }; iret = ocfs2_xattr_iterate(ci, change_xattr_refcount, &obj); if (iret & OCFS2_XATTR_ERROR) ret = obj.errcode; return ret; } /* * Clear the refcount flag for an extent rec (v_cpos, clusters) of the file. * This extent rec can be found either in dinode or xattr. */ errcode_t ocfs2_change_refcount_flag(ocfs2_filesys *fs, uint64_t i_blkno, uint32_t v_cpos, uint32_t clusters, uint64_t p_cpos, int new_flags, int clear_flags) { errcode_t ret; ocfs2_cached_inode *ci = NULL; struct ocfs2_extent_tree et; uint32_t p_cluster, num_clusters; uint16_t ext_flags; ret = ocfs2_read_cached_inode(fs, i_blkno, &ci); if (ret) goto out; ret = ocfs2_get_clusters(ci, v_cpos, &p_cluster, &num_clusters, &ext_flags); if (ret) goto out; if (p_cluster == p_cpos) { /* OK, the p_cpos is in the dinode. */ assert(num_clusters >= clusters); ocfs2_init_dinode_extent_tree(&et, fs, (char *)ci->ci_inode, i_blkno); ret = ocfs2_change_extent_flag(fs, &et, v_cpos, clusters, ocfs2_clusters_to_blocks(fs, p_cpos), new_flags, clear_flags); goto out; } ret = ocfs2_xattr_change_ext_refcount(fs, ci, v_cpos, clusters, p_cpos, new_flags, clear_flags); out: if (ci) ocfs2_free_cached_inode(fs, ci); return ret; } static errcode_t create_generation(uint32_t *value) { int randfd = 0; int readlen = sizeof(*value); randfd = open("/dev/urandom", O_RDONLY); if (randfd < 0) return errno; if (read(randfd, value, readlen) != readlen) { close(randfd); return errno; } close(randfd); return 0; } errcode_t ocfs2_create_refcount_tree(ocfs2_filesys *fs, uint64_t *refcount_loc) { errcode_t ret; uint32_t generation; ret = create_generation(&generation); if (ret) return ret; return ocfs2_new_refcount_block(fs, refcount_loc, 0, generation); } errcode_t ocfs2_attach_refcount_tree(ocfs2_filesys *fs, uint64_t ino, uint64_t refcount_loc) { errcode_t ret; char *buf = NULL; struct ocfs2_dinode *di; struct ocfs2_refcount_block *rb; ret = ocfs2_malloc_block(fs->fs_io, &buf); if (ret) return ret; /* * We add the rf_count for the tree first so that * if their is any corruption before we attaching * the tree to the inode, we can check it out * easily by RF_COUNT_INVALID. */ ret = ocfs2_read_refcount_block(fs, refcount_loc, buf); if (ret) goto out; rb = (struct ocfs2_refcount_block *)buf; rb->rf_count += 1; ret = ocfs2_write_refcount_block(fs, refcount_loc, buf); if (ret) goto out; ret = ocfs2_read_inode(fs, ino, buf); if (ret) goto out; di = (struct ocfs2_dinode *)buf; assert(!(di->i_dyn_features & OCFS2_HAS_REFCOUNT_FL)); assert(!di->i_refcount_loc); di->i_refcount_loc = refcount_loc; di->i_dyn_features |= OCFS2_HAS_REFCOUNT_FL; ret = ocfs2_write_inode(fs, ino, buf); out: ocfs2_free(&buf); return ret; } errcode_t ocfs2_detach_refcount_tree(ocfs2_filesys *fs, uint64_t ino, uint64_t refcount_loc) { errcode_t ret; char *buf = NULL; struct ocfs2_dinode *di; struct ocfs2_refcount_block *rb; ret = ocfs2_malloc_block(fs->fs_io, &buf); if (ret) return ret; ret = ocfs2_read_refcount_block(fs, refcount_loc, buf); if (ret) goto out; rb = (struct ocfs2_refcount_block *)buf; rb->rf_count += -1; if (!rb->rf_count) { ret = ocfs2_delete_refcount_block(fs, rb->rf_blkno); if (ret) { com_err("refcount", ret, "remove refcount tree <%llu> failed.\n", rb->rf_blkno); goto out; } } else { ret = ocfs2_write_refcount_block(fs, refcount_loc, buf); if (ret) { com_err("refcount", ret, "update refcount tree <%llu> failed.\n", rb->rf_blkno); goto out; } } ret = ocfs2_read_inode(fs, ino, buf); if (ret) { com_err("refcount", ret, "read inode %lu fail, stop setting refcount tree <%llu>.\n", ino, rb->rf_blkno); goto out; } di = (struct ocfs2_dinode *)buf; di->i_refcount_loc = 0; di->i_dyn_features &= ~OCFS2_HAS_REFCOUNT_FL; ret = ocfs2_write_inode(fs, ino, buf); out: ocfs2_free(&buf); return ret; } struct xattr_value_cow_object { struct ocfs2_xattr_value_root *xv; uint64_t xe_blkno; uint64_t value_blkno; char *xe_buf; char *value_buf; }; static int ocfs2_xattr_value_get_clusters(struct ocfs2_cow_context *context, uint32_t v_cluster, uint32_t *p_cluster, uint32_t *num_clusters, uint16_t *extent_flags) { struct xattr_value_cow_object *obj = context->cow_object; return ocfs2_xattr_get_clusters(context->fs, &obj->xv->xr_list, obj->value_blkno, obj->value_buf, v_cluster, p_cluster, num_clusters, extent_flags); } static errcode_t ocfs2_write_xattr_bucket_in_cow(ocfs2_filesys *fs, void *para) { struct xattr_value_cow_object *obj = para; return ocfs2_write_xattr_bucket(fs, obj->xe_blkno, obj->xe_buf); } /* * Do CoW for xattr. */ errcode_t ocfs2_refcount_cow_xattr(ocfs2_cached_inode *ci, char *xe_buf, uint64_t xe_blkno, char *value_buf, uint64_t value_blkno, struct ocfs2_xattr_value_root *xv, uint32_t cpos, uint32_t write_len) { int ret; uint32_t cow_start, cow_len; struct ocfs2_cow_context context; struct ocfs2_post_refcount post_refcount; ocfs2_root_write_func write_func = NULL; struct xattr_value_cow_object value_obj; assert(ci->ci_inode->i_dyn_features & OCFS2_HAS_REFCOUNT_FL); memset(&context, 0, sizeof(struct ocfs2_cow_context)); value_obj.xv = xv; value_obj.xe_blkno = xe_blkno; value_obj.value_blkno = value_blkno; value_obj.xe_buf = xe_buf; value_obj.value_buf = value_buf; /* * Set the corresponding root write function. * If we are in the bucket, write the whole bucket by ourselves. */ if (xe_blkno == ci->ci_inode->i_blkno) write_func = ocfs2_write_inode; else if (xe_blkno == ci->ci_inode->i_xattr_loc) write_func = ocfs2_write_xattr_block; else { /* * We are in a bucket and we can't write the extent tree * root by ourself. Set post_refcount so that the whole * bucket can be written after the CoW succeeds. */ post_refcount.para = &value_obj; post_refcount.func = ocfs2_write_xattr_bucket_in_cow; context.post_refcount = &post_refcount; } ocfs2_init_xattr_value_extent_tree(&context.data_et, ci->ci_fs, value_buf, value_blkno, write_func, xv); ret = ocfs2_refcount_cal_cow_clusters(ci->ci_fs, &context.data_et, cpos, write_len, UINT_MAX, &cow_start, &cow_len); if (ret) goto out; assert(cow_len > 0); context.cow_start = cow_start; context.cow_len = cow_len; context.fs = ci->ci_fs; context.get_clusters = ocfs2_xattr_value_get_clusters; context.cow_object = &value_obj; ret = ocfs2_malloc_block(ci->ci_fs->fs_io, &context.ref_root_buf); if (ret) goto out; ret = ocfs2_read_refcount_block(ci->ci_fs, ci->ci_inode->i_refcount_loc, context.ref_root_buf); if (ret) goto out; ret = ocfs2_replace_cow(&context); if (ret) goto out; if (!write_func) ret = ocfs2_write_xattr_bucket(ci->ci_fs, xe_blkno, xe_buf); out: if (&context.ref_root_buf) ocfs2_free(&context.ref_root_buf); return ret; } ocfs2-tools-ocfs2-tools-1.8.6/libocfs2/refcount.h000066400000000000000000000021751347147137200215670ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * refcount.h * * Copyright (C) 2009 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License version 2 as published by the Free Software Foundation. * * 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. */ #ifndef _LIBOCFS2_REFCOUNT_H_ #define _LIBOCFS2_REFCOUNT_H_ typedef errcode_t (ocfs2_post_refcount_func)(ocfs2_filesys *fs, void *para); /* * Some refcount caller need to do more work after we modify the data b-tree * during refcount operation(including CoW and add refcount flag), and make the * transaction complete. So it must give us this structure so that we can do it * within our transaction. * */ struct ocfs2_post_refcount { ocfs2_post_refcount_func *func; /* real function. */ void *para; }; #endif /* _LIBOCFS2_REFCOUNT_H_ */ ocfs2-tools-ocfs2-tools-1.8.6/libocfs2/slot_map.c000066400000000000000000000271131347147137200215520ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * Copyright (C) 2005 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License, version 2, as published by the Free Software Foundation. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. */ #define _XOPEN_SOURCE 600 /* Triggers XOPEN2K in features.h */ #define _LARGEFILE64_SOURCE #include "ocfs2/byteorder.h" #include "ocfs2/ocfs2.h" /* Just so we can pass it about */ union ocfs2_slot_map_wrapper { struct ocfs2_slot_map *mw_map; struct ocfs2_slot_map_extended *mw_map_extended; }; void ocfs2_swap_slot_map(struct ocfs2_slot_map *sm, int num_slots) { int i; if (!cpu_is_big_endian) return; for (i = 0; i < num_slots; i++) sm->sm_slots[i] = bswap_16(sm->sm_slots[i]); } void ocfs2_swap_slot_map_extended(struct ocfs2_slot_map_extended *se, int num_slots) { int i; if (!cpu_is_big_endian) return; for (i = 0; i < num_slots; i++) se->se_slots[i].es_node_num = bswap_32(se->se_slots[i].es_node_num); } static errcode_t __ocfs2_read_slot_map(ocfs2_filesys *fs, int num_slots, union ocfs2_slot_map_wrapper *wrap) { errcode_t ret; uint64_t blkno; char *slot_map_buf; struct ocfs2_slot_map *sm; struct ocfs2_slot_map_extended *se; int bytes_needed, len; int extended = ocfs2_uses_extended_slot_map(OCFS2_RAW_SB(fs->fs_super)); ret = ocfs2_lookup_system_inode(fs, SLOT_MAP_SYSTEM_INODE, 0, &blkno); if (ret) return ret; ret = ocfs2_read_whole_file(fs, blkno, &slot_map_buf, &len); if (ret) return ret; if (extended) bytes_needed = num_slots * sizeof(struct ocfs2_extended_slot); else bytes_needed = num_slots * sizeof(__le16); if (bytes_needed > len) { ocfs2_free(&slot_map_buf); return OCFS2_ET_SHORT_READ; } if (extended) { se = (struct ocfs2_slot_map_extended *)slot_map_buf; ocfs2_swap_slot_map_extended(se, num_slots); wrap->mw_map_extended = se; } else { sm = (struct ocfs2_slot_map *)slot_map_buf; ocfs2_swap_slot_map(sm, num_slots); wrap->mw_map = sm; } return 0; } errcode_t ocfs2_read_slot_map(ocfs2_filesys *fs, int num_slots, struct ocfs2_slot_map **map_ret) { errcode_t ret; union ocfs2_slot_map_wrapper wrap = { .mw_map = NULL, }; ret = __ocfs2_read_slot_map(fs, num_slots, &wrap); if (ret) return ret; *map_ret = wrap.mw_map; return 0; } errcode_t ocfs2_read_slot_map_extended(ocfs2_filesys *fs, int num_slots, struct ocfs2_slot_map_extended **map_ret) { errcode_t ret; union ocfs2_slot_map_wrapper wrap = { .mw_map_extended = NULL, }; ret = __ocfs2_read_slot_map(fs, num_slots, &wrap); if (ret) return ret; *map_ret = wrap.mw_map_extended; return 0; } static errcode_t __ocfs2_write_slot_map(ocfs2_filesys *fs, int num_slots, union ocfs2_slot_map_wrapper *wrap) { errcode_t ret, tret; ocfs2_cached_inode *ci = NULL; uint64_t blkno; unsigned int size, bytes, wrote, blocks; void *buf = NULL; int extended = ocfs2_uses_extended_slot_map(OCFS2_RAW_SB(fs->fs_super)); ret = ocfs2_lookup_system_inode(fs, SLOT_MAP_SYSTEM_INODE, 0, &blkno); if (ret) goto out; if (extended) size = num_slots * sizeof(struct ocfs2_extended_slot); else size = num_slots * sizeof(__le16); blocks = ocfs2_blocks_in_bytes(fs, size); bytes = blocks << OCFS2_RAW_SB(fs->fs_super)->s_blocksize_bits; ret = ocfs2_malloc_blocks(fs->fs_io, blocks, &buf); if (ret) goto out; memset(buf, 0, bytes); if (extended) { memcpy(buf, wrap->mw_map_extended, size); ocfs2_swap_slot_map_extended(buf, num_slots); } else { memcpy(buf, wrap->mw_map, size); ocfs2_swap_slot_map(buf, num_slots); } ret = ocfs2_read_cached_inode(fs, blkno, &ci); if (ret) goto out; ret = ocfs2_file_write(ci, buf, bytes, 0, &wrote); if (ret) goto out; /* * This is wacky. We have to write a block (bytes), but &wrote * might return only i_size (size). Handle both. */ if ((wrote != bytes) && (wrote != size)) ret = OCFS2_ET_SHORT_WRITE; out: if (ci) { tret = ocfs2_free_cached_inode(fs, ci); /* * The error from free_cached_inode() is only important if * there were no other problems. */ if (!ret) ret = tret; } if (buf) ocfs2_free(&buf); return ret; } errcode_t ocfs2_write_slot_map(ocfs2_filesys *fs, int num_slots, struct ocfs2_slot_map *sm) { union ocfs2_slot_map_wrapper wrap = { .mw_map = sm, }; return __ocfs2_write_slot_map(fs, num_slots, &wrap); } errcode_t ocfs2_write_slot_map_extended(ocfs2_filesys *fs, int num_slots, struct ocfs2_slot_map_extended *se) { union ocfs2_slot_map_wrapper wrap = { .mw_map_extended = se, }; return __ocfs2_write_slot_map(fs, num_slots, &wrap); } static void ocfs2_slot_map_to_data(ocfs2_filesys *fs, struct ocfs2_slot_map_data *md, union ocfs2_slot_map_wrapper *wrap) { int i; struct ocfs2_slot_map *sm = wrap->mw_map; struct ocfs2_slot_map_extended *se = wrap->mw_map_extended; int extended = ocfs2_uses_extended_slot_map(OCFS2_RAW_SB(fs->fs_super)); for (i = 0; i < md->md_num_slots; i++) { if (extended) { if (se->se_slots[i].es_valid) { md->md_slots[i].sd_valid = 1; md->md_slots[i].sd_node_num = se->se_slots[i].es_node_num; } else md->md_slots[i].sd_valid = 0; } else { if (sm->sm_slots[i] != (uint16_t)OCFS2_INVALID_SLOT) { md->md_slots[i].sd_valid = 1; md->md_slots[i].sd_node_num = sm->sm_slots[i]; } else md->md_slots[i].sd_valid = 0; } } } static void ocfs2_slot_data_to_map(ocfs2_filesys *fs, struct ocfs2_slot_map_data *md, union ocfs2_slot_map_wrapper *wrap) { int i; struct ocfs2_slot_map *sm = wrap->mw_map; struct ocfs2_slot_map_extended *se = wrap->mw_map_extended; int extended = ocfs2_uses_extended_slot_map(OCFS2_RAW_SB(fs->fs_super)); for (i = 0; i < md->md_num_slots; i++) { if (extended) { if (md->md_slots[i].sd_valid) { se->se_slots[i].es_valid = 1; se->se_slots[i].es_node_num = md->md_slots[i].sd_node_num; } else se->se_slots[i].es_valid = 0; } else { if (md->md_slots[i].sd_valid) sm->sm_slots[i] = (uint16_t)md->md_slots[i].sd_node_num; else sm->sm_slots[i] = (uint16_t)OCFS2_INVALID_SLOT; } } } static errcode_t ocfs2_alloc_slot_map_data(int num_slots, struct ocfs2_slot_map_data **md_ret) { errcode_t ret; struct ocfs2_slot_map_data *md; ret = ocfs2_malloc0(sizeof(struct ocfs2_slot_map_data) + (sizeof(struct ocfs2_slot_data) * num_slots), &md); if (ret) return ret; md->md_num_slots = num_slots; md->md_slots = (struct ocfs2_slot_data *)((char *)md + sizeof(struct ocfs2_slot_map_data)); *md_ret = md; return 0; } errcode_t ocfs2_load_slot_map(ocfs2_filesys *fs, struct ocfs2_slot_map_data **data_ret) { errcode_t ret; int num_slots = OCFS2_RAW_SB(fs->fs_super)->s_max_slots;; struct ocfs2_slot_map_data *md; union ocfs2_slot_map_wrapper wrap = { .mw_map = NULL, }; ret = ocfs2_alloc_slot_map_data(num_slots, &md); if (ret) return ret; ret = __ocfs2_read_slot_map(fs, num_slots, &wrap); if (ret) { ocfs2_free(&md); return ret; } ocfs2_slot_map_to_data(fs, md, &wrap); *data_ret = md; return 0; } errcode_t ocfs2_store_slot_map(ocfs2_filesys *fs, struct ocfs2_slot_map_data *md) { errcode_t ret; char *slot_map_buf; int bytes; int extended = ocfs2_uses_extended_slot_map(OCFS2_RAW_SB(fs->fs_super)); union ocfs2_slot_map_wrapper wrap; if (extended) bytes = md->md_num_slots * sizeof(struct ocfs2_extended_slot); else bytes = md->md_num_slots * sizeof(__le16); ret = ocfs2_malloc0(bytes, &slot_map_buf); if (ret) return ret; wrap.mw_map = (struct ocfs2_slot_map *)slot_map_buf; ocfs2_slot_data_to_map(fs, md, &wrap); ret = __ocfs2_write_slot_map(fs, md->md_num_slots, &wrap); ocfs2_free(&slot_map_buf); return ret; } struct slotmap_format { int extended; int needed_slots; int actual_slots; unsigned int needed_bytes; ocfs2_cached_inode *ci; }; static errcode_t ocfs2_size_slot_map(ocfs2_filesys *fs, struct slotmap_format *sf) { errcode_t ret ; struct ocfs2_dinode *di; unsigned int clusters; uint64_t new_size; uint64_t blkno; di = sf->ci->ci_inode; blkno = sf->ci->ci_blkno; clusters = sf->needed_bytes + fs->fs_clustersize - 1; clusters = clusters >> OCFS2_RAW_SB(fs->fs_super)->s_clustersize_bits; /* Zero slots not allowed - even local mounts have a slot */ if (!clusters) { ret = OCFS2_ET_INTERNAL_FAILURE; goto out; } /* * We ensure that slotmaps are formatted to the end of the * allocation. If the allocation hasn't changed, we don't have * anything to do. */ if (clusters == di->i_clusters) { ret = 0; goto out; } if (clusters > di->i_clusters) { ret = ocfs2_extend_allocation(fs, blkno, (clusters - di->i_clusters)); if (ret) goto out; /* We don't cache in the library right now, so any * work done in extend_allocation won't be reflected * in our now stale copy. */ ocfs2_free_cached_inode(fs, sf->ci); ret = ocfs2_read_cached_inode(fs, blkno, &sf->ci); if (ret) { sf->ci = NULL; goto out; } di = sf->ci->ci_inode; } else if (clusters < di->i_clusters) { new_size = clusters << OCFS2_RAW_SB(fs->fs_super)->s_clustersize_bits; ret = ocfs2_truncate(fs, blkno, new_size); if (ret) goto out; ocfs2_free_cached_inode(fs, sf->ci); ret = ocfs2_read_cached_inode(fs, blkno, &sf->ci); if (ret) { sf->ci = NULL; goto out; } di = sf->ci->ci_inode; } /* * Now that we've adjusted the allocation, write out the * correct i_size. By design, the slot map's i_size encompasses * the full allocation. */ di->i_size = di->i_clusters << OCFS2_RAW_SB(fs->fs_super)->s_clustersize_bits; di->i_mtime = time(NULL); ret = ocfs2_write_inode(fs, blkno, (char *)di); if (ret) goto out; out: return ret; } errcode_t ocfs2_format_slot_map(ocfs2_filesys *fs) { errcode_t ret; uint64_t blkno; struct slotmap_format sf; struct ocfs2_slot_map_data *md = NULL; ret = ocfs2_lookup_system_inode(fs, SLOT_MAP_SYSTEM_INODE, 0, &blkno); if (ret) goto out; ret = ocfs2_read_cached_inode(fs, blkno, &sf.ci); if (ret) goto out; /* verify it is a system file */ if (!(sf.ci->ci_inode->i_flags & OCFS2_VALID_FL) || !(sf.ci->ci_inode->i_flags & OCFS2_SYSTEM_FL)) { ret = OCFS2_ET_INTERNAL_FAILURE; goto out; } sf.extended = ocfs2_uses_extended_slot_map(OCFS2_RAW_SB(fs->fs_super)); sf.needed_slots = OCFS2_RAW_SB(fs->fs_super)->s_max_slots; if (!sf.extended && (sf.needed_slots > OCFS2_MAX_SLOTS)) { ret = OCFS2_ET_TOO_MANY_SLOTS; goto out; } if (sf.extended) sf.needed_bytes = sf.needed_slots * sizeof(struct ocfs2_extended_slot); else sf.needed_bytes = sf.needed_slots * sizeof(__le16); ret = ocfs2_size_slot_map(fs, &sf); if (ret) goto out; if (sf.extended) sf.actual_slots = sf.ci->ci_inode->i_size / sizeof(struct ocfs2_extended_slot); else sf.actual_slots = sf.ci->ci_inode->i_size / sizeof(__le16); /* This returns an empty map that covers the entire allocation */ ret = ocfs2_alloc_slot_map_data(sf.actual_slots, &md); if (ret) return ret; ret = ocfs2_store_slot_map(fs, md); out: if (sf.ci) ocfs2_free_cached_inode(fs, sf.ci); if (md) ocfs2_free(&md); return ret; } ocfs2-tools-ocfs2-tools-1.8.6/libocfs2/sysfile.c000066400000000000000000000026551347147137200214160ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * sysfile.c * * System inode operations for the OCFS2 userspace library. * * Copyright (C) 2004 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License, version 2, as published by the Free Software Foundation. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. */ #define _XOPEN_SOURCE 600 /* Triggers XOPEN2K in features.h */ #define _LARGEFILE64_SOURCE #include #include "ocfs2/ocfs2.h" errcode_t ocfs2_lookup_system_inode(ocfs2_filesys *fs, int type, int slot_num, uint64_t *blkno) { errcode_t ret; char *buf; ret = ocfs2_malloc0(sizeof(char) * (OCFS2_MAX_FILENAME_LEN + 1), &buf); if (ret) return ret; ocfs2_sprintf_system_inode_name(buf, OCFS2_MAX_FILENAME_LEN, type, slot_num); ret = ocfs2_lookup(fs, fs->fs_sysdir_blkno, buf, strlen(buf), NULL, blkno); ocfs2_free(&buf); return ret; } ocfs2-tools-ocfs2-tools-1.8.6/libocfs2/truncate.c000066400000000000000000000330541347147137200215620ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * truncate.c * * Truncate an OCFS2 inode. For the OCFS2 userspace library. * * Copyright (C) 2004 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License, version 2, as published by the Free Software Foundation. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. */ #define _XOPEN_SOURCE 600 /* Triggers magic in features.h */ #define _LARGEFILE64_SOURCE #include #if HAVE_UNISTD_H #include #endif #include #include #include "ocfs2/ocfs2.h" #include "ocfs2/byteorder.h" struct truncate_ctxt { uint64_t ino; uint64_t new_size_in_clusters; uint32_t new_i_clusters; errcode_t (*free_clusters)(ocfs2_filesys *fs, uint32_t len, uint64_t start_blkno, void *free_data); void *free_data; }; static int ocfs2_truncate_clusters(ocfs2_filesys *fs, struct ocfs2_extent_rec *rec, uint64_t ino, uint32_t len, uint64_t start) { if (!ocfs2_refcount_tree(OCFS2_RAW_SB(fs->fs_super)) || !(rec->e_flags & OCFS2_EXT_REFCOUNTED)) return ocfs2_free_clusters(fs, len, start); assert(ino); return ocfs2_decrease_refcount(fs, ino, ocfs2_blocks_to_clusters(fs, start), len, 1); } /* * Delete and free clusters if needed. This only works with DEPTH_TRAVERSE. */ static int truncate_iterate(ocfs2_filesys *fs, struct ocfs2_extent_rec *rec, int tree_depth, uint32_t ccount, uint64_t ref_blkno, int ref_recno, void *priv_data) { struct truncate_ctxt *ctxt = (struct truncate_ctxt *)priv_data; uint32_t len = 0, new_size_in_clusters = ctxt->new_size_in_clusters; uint64_t start = 0; errcode_t ret; int func_ret = OCFS2_EXTENT_ERROR; char *buf = NULL; struct ocfs2_extent_list *el = NULL; int cleanup_rec = 0; if ((rec->e_cpos + ocfs2_rec_clusters(tree_depth, rec)) <= new_size_in_clusters) return 0; if (rec->e_cpos >= new_size_in_clusters) { /* the rec is entirely outside the new size, free it */ if (!tree_depth) { start = rec->e_blkno; len = ocfs2_rec_clusters(tree_depth, rec); } else { /* here we meet with a full empty extent block, delete * it. The extent list it contains should already be * iterated and all the clusters have been freed. */ ret = ocfs2_delete_extent_block(fs, rec->e_blkno); if (ret) goto bail; } cleanup_rec = 1; } else { /* we're truncating into the middle of the rec */ len = rec->e_cpos + ocfs2_rec_clusters(tree_depth, rec); len -= new_size_in_clusters; if (!tree_depth) { ocfs2_set_rec_clusters(tree_depth, rec, new_size_in_clusters - rec->e_cpos); start = rec->e_blkno + ocfs2_clusters_to_blocks(fs, ocfs2_rec_clusters(tree_depth, rec)); } else { ocfs2_set_rec_clusters(tree_depth, rec, new_size_in_clusters - rec->e_cpos); /* * For a sparse file, we may meet with another * situation here: * The start of the left most extent rec is greater * than the new size we truncate the file to, but the * start of the extent block is less than that size. * In this case, actually all the extent records in * this extent block have been removed. So we have * to remove the extent block also. * In this function, we have to reread the extent list * to see whether the extent block is empty or not. */ ret = ocfs2_malloc_block(fs->fs_io, &buf); if (ret) goto bail; ret = ocfs2_read_extent_block(fs, rec->e_blkno, buf); if (ret) goto bail; el = &((struct ocfs2_extent_block *)buf)->h_list; if (el->l_next_free_rec == 0) { ret = ocfs2_delete_extent_block(fs, rec->e_blkno); if (ret) goto bail; cleanup_rec = 1; } } } if (start) { if (ctxt->free_clusters) ret = ctxt->free_clusters(fs, len, start, ctxt->free_data); else ret = ocfs2_truncate_clusters(fs, rec, ctxt->ino, len, start); if (ret) goto bail; ctxt->new_i_clusters -= len; } func_ret = OCFS2_EXTENT_CHANGED; bail: if (cleanup_rec) memset(rec, 0, sizeof(struct ocfs2_extent_rec)); if (buf) ocfs2_free(&buf); return func_ret; } /* * Zero the area past i_size but still within an allocated * cluster. This avoids exposing nonzero data on subsequent file * extends. */ static errcode_t ocfs2_zero_tail_for_truncate(ocfs2_cached_inode *ci, uint64_t new_size) { errcode_t ret; char *buf = NULL; ocfs2_filesys *fs = ci->ci_fs; uint64_t start_blk, p_blkno, contig_blocks, start_off; int count, byte_counts, bpc = fs->fs_clustersize /fs->fs_blocksize; uint16_t ext_flags; if (new_size == 0) return 0; start_blk = new_size / fs->fs_blocksize; ret = ocfs2_extent_map_get_blocks(ci, start_blk, 1, &p_blkno, &contig_blocks, &ext_flags); if (ret) goto out; /* Tail is a hole. */ if (!p_blkno) goto out; if (ext_flags & OCFS2_EXT_REFCOUNTED) { uint32_t cpos = ocfs2_blocks_to_clusters(fs, start_blk); ret = ocfs2_refcount_cow(ci, cpos, 1, cpos + 1); if (ret) goto out; ret = ocfs2_extent_map_get_blocks(ci, start_blk, 1, &p_blkno, &contig_blocks, &ext_flags); if (ret) goto out; assert(!(ext_flags & OCFS2_EXT_REFCOUNTED) && p_blkno); } /* calculate the total blocks we need to empty. */ count = bpc - (p_blkno & (bpc - 1)); ret = ocfs2_malloc_blocks(fs->fs_io, count, &buf); if (ret) goto out; ret = ocfs2_read_blocks(fs, p_blkno, count, buf); if (ret) goto out; /* empty the content after the new_size and within the same cluster. */ start_off = new_size % fs->fs_blocksize; byte_counts = count * fs->fs_blocksize - start_off; memset(buf + start_off, 0, byte_counts); ret = io_write_block(fs->fs_io, p_blkno, count, buf); out: if (buf) ocfs2_free(&buf); return ret; } /* * This function will truncate the file's cluster which exceeds * the cluster where new_size resides in and empty all the * bytes in the same cluster which exceeds new_size. */ static errcode_t ocfs2_zero_tail_and_truncate_full(ocfs2_filesys *fs, ocfs2_cached_inode *ci, uint64_t new_i_size, uint32_t *new_clusters, errcode_t (*free_clusters)(ocfs2_filesys *fs, uint32_t len, uint64_t start, void *free_data), void *free_data) { errcode_t ret; uint64_t new_size_in_blocks; struct truncate_ctxt ctxt; new_size_in_blocks = ocfs2_blocks_in_bytes(fs, new_i_size); ctxt.ino = ci->ci_blkno; ctxt.new_i_clusters = ci->ci_inode->i_clusters; ctxt.new_size_in_clusters = ocfs2_clusters_in_blocks(fs, new_size_in_blocks); ctxt.free_clusters = free_clusters; ctxt.free_data = free_data; ret = ocfs2_extent_iterate_inode(fs, ci->ci_inode, OCFS2_EXTENT_FLAG_DEPTH_TRAVERSE, NULL, truncate_iterate, &ctxt); if (ret) goto out; ret = ocfs2_zero_tail_for_truncate(ci, new_i_size); if (ret) goto out; if (new_clusters) *new_clusters = ctxt.new_i_clusters; out: return ret; } errcode_t ocfs2_zero_tail_and_truncate(ocfs2_filesys *fs, ocfs2_cached_inode *ci, uint64_t new_i_size, uint32_t *new_clusters) { return ocfs2_zero_tail_and_truncate_full(fs, ci, new_i_size, new_clusters, NULL, NULL); } /* * NOTE: ocfs2_truncate_inline() also handles fast symlink, * since truncating for inline file and fasy symlink are * almost the same thing per se. */ errcode_t ocfs2_truncate_inline(ocfs2_filesys *fs, uint64_t ino, uint64_t new_i_size) { errcode_t ret = 0; char *buf = NULL; struct ocfs2_dinode *di = NULL; struct ocfs2_inline_data *idata = NULL; if (!(fs->fs_flags & OCFS2_FLAG_RW)) return OCFS2_ET_RO_FILESYS; ret = ocfs2_malloc_block(fs->fs_io, &buf); if (ret) return ret; ret = ocfs2_read_inode(fs, ino, buf); if (ret) goto out_free_buf; di = (struct ocfs2_dinode *)buf; if (di->i_size < new_i_size) { ret = EINVAL; goto out_free_buf; } idata = &di->id2.i_data; if (!(di->i_dyn_features & OCFS2_INLINE_DATA_FL) && !(S_ISLNK(di->i_mode) && !di->i_clusters)) { ret = EINVAL; goto out_free_buf; } if (di->i_dyn_features & OCFS2_INLINE_DATA_FL) memset(idata->id_data + new_i_size, 0, di->i_size - new_i_size); else memset(di->id2.i_symlink + new_i_size, 0, di->i_size - new_i_size); di->i_size = new_i_size; ret = ocfs2_write_inode(fs, ino, buf); out_free_buf: if (buf) ocfs2_free(&buf); return ret; } /* XXX care about zeroing new clusters and final partially truncated * clusters */ errcode_t ocfs2_truncate_full(ocfs2_filesys *fs, uint64_t ino, uint64_t new_i_size, errcode_t (*free_clusters)(ocfs2_filesys *fs, uint32_t len, uint64_t start, void *free_data), void *free_data) { errcode_t ret; uint32_t new_clusters; ocfs2_cached_inode *ci = NULL; ret = ocfs2_read_cached_inode(fs, ino, &ci); if (ret) goto out; /* in case of dio crashed, force do trucate since blocks may already * be allocated */ if (ci->ci_inode->i_flags & cpu_to_le32(OCFS2_DIO_ORPHANED_FL)) { ci->ci_inode->i_flags &= ~cpu_to_le32(OCFS2_DIO_ORPHANED_FL); ci->ci_inode->i_dio_orphaned_slot = 0; new_i_size = ci->ci_inode->i_size; goto truncate; } if (ci->ci_inode->i_size == new_i_size) goto out; if (ci->ci_inode->i_size < new_i_size) { ret = ocfs2_extend_file(fs, ino, new_i_size); goto out; } truncate: if ((S_ISLNK(ci->ci_inode->i_mode) && !ci->ci_inode->i_clusters) || (ci->ci_inode->i_dyn_features & OCFS2_INLINE_DATA_FL)) ret = ocfs2_truncate_inline(fs, ino, new_i_size); else { ret = ocfs2_zero_tail_and_truncate_full(fs, ci, new_i_size, &new_clusters, free_clusters, free_data); if (ret) goto out; ci->ci_inode->i_clusters = new_clusters; /* now all the clusters and extent blocks are freed. * only when the file's content is empty, should the tree depth * change. */ if (new_clusters == 0) ci->ci_inode->id2.i_list.l_tree_depth = 0; ci->ci_inode->i_size = new_i_size; ret = ocfs2_write_cached_inode(fs, ci); } if (!ret && !new_i_size && ci->ci_inode->i_refcount_loc && (ci->ci_inode->i_dyn_features & OCFS2_HAS_REFCOUNT_FL)) ret = ocfs2_detach_refcount_tree(fs, ino, ci->ci_inode->i_refcount_loc); out: if (ci) ocfs2_free_cached_inode(fs, ci); return ret; } errcode_t ocfs2_truncate(ocfs2_filesys *fs, uint64_t ino, uint64_t new_i_size) { return ocfs2_truncate_full(fs, ino, new_i_size, NULL, NULL); } errcode_t ocfs2_xattr_value_truncate(ocfs2_filesys *fs, uint64_t ino, struct ocfs2_xattr_value_root *xv) { struct truncate_ctxt ctxt; int changed; struct ocfs2_extent_list *el = &xv->xr_list; ctxt.ino = ino; ctxt.new_i_clusters = xv->xr_clusters; ctxt.new_size_in_clusters = 0; return ocfs2_extent_iterate_xattr(fs, el, xv->xr_last_eb_blk, OCFS2_EXTENT_FLAG_DEPTH_TRAVERSE, truncate_iterate, &ctxt, &changed); } errcode_t ocfs2_xattr_tree_truncate(ocfs2_filesys *fs, struct ocfs2_xattr_tree_root *xt) { struct truncate_ctxt ctxt; int changed; struct ocfs2_extent_list *el = &xt->xt_list; /* * ino is used to find refcount tree, as we never use refcount * in xattr tree, so set it to 0. */ ctxt.ino = 0; ctxt.new_i_clusters = xt->xt_clusters; ctxt.new_size_in_clusters = 0; return ocfs2_extent_iterate_xattr(fs, el, xt->xt_last_eb_blk, OCFS2_EXTENT_FLAG_DEPTH_TRAVERSE, truncate_iterate, &ctxt, &changed); } errcode_t ocfs2_dir_indexed_tree_truncate(ocfs2_filesys *fs, struct ocfs2_dx_root_block *dx_root) { struct truncate_ctxt ctxt; memset(&ctxt, 0, sizeof (struct truncate_ctxt)); ctxt.new_i_clusters = dx_root->dr_clusters; ctxt.new_size_in_clusters = 0; return ocfs2_extent_iterate_dx_root(fs, dx_root, OCFS2_EXTENT_FLAG_DEPTH_TRAVERSE, NULL, truncate_iterate, &ctxt); } #ifdef DEBUG_EXE #include #include #include static uint64_t read_number(const char *num) { uint64_t val; char *ptr; val = strtoull(num, &ptr, 0); if (!ptr || *ptr) return 0; return val; } static void print_usage(void) { fprintf(stderr, "Usage: debug_truncate -i -s device\n"); } extern int opterr, optind; extern char *optarg; int main(int argc, char *argv[]) { errcode_t ret; int c; uint64_t blkno = 0, new_size = 0; ocfs2_filesys *fs; char *device; initialize_ocfs_error_table(); while ((c = getopt(argc, argv, "i:s:")) != EOF) { switch (c) { case 'i': blkno = read_number(optarg); if (blkno <= OCFS2_SUPER_BLOCK_BLKNO) { fprintf(stderr, "Invalid inode block: %s\n", optarg); print_usage(); return 1; } break; case 's': new_size = read_number(optarg); break; default: print_usage(); return 1; break; } } if (optind >= argc) { fprintf(stderr, "Missing device name\n"); print_usage(); return 1; } device = argv[optind]; if (!blkno || !new_size) { print_usage(); return 1; } ret = ocfs2_open(device, OCFS2_FLAG_RW, 0, 0, &fs); if (ret) { com_err(argv[0], ret, "while opening file \"%s\"", device); return ret; } ret = ocfs2_truncate(fs, blkno, new_size); if (ret) com_err(argv[0], ret, "while truncating inode %"PRIu64, blkno); ocfs2_close(fs); return ret; } #endif /* DEBUG_EXE */ ocfs2-tools-ocfs2-tools-1.8.6/libocfs2/unix_io.c000066400000000000000000000645561347147137200214220ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * unix_io.c * * I/O routines for the OCFS2 userspace library. * * Copyright (C) 2002, 2004 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License, version 2, as published by the Free Software Foundation. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * * Portions of this code from e2fsprogs/lib/ext2fs/unix_io.c * Copyright (C) 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, * 2002 by Theodore Ts'o. */ #define _XOPEN_SOURCE 600 /* Triggers ISOC99, UNIX98 in features.h */ #define _LARGEFILE64_SOURCE #define _GNU_SOURCE /* Because libc really doesn't want us using O_DIRECT? */ #include #include #include #include #include #include #include #ifdef __linux__ #include #include #include #include #include #endif #include #include #include "ocfs2/kernel-rbtree.h" #include "ocfs2/ocfs2.h" /* * We do cached I/O in 1MB hunks, so we need this constant. */ #ifndef ONE_MEGABYTE # define ONE_MEGABYTE (1024 * 1024) #endif /* * The cache looks up blocks in two ways: * * 1) If it needs a new block, it gets one off of ic->ic_lru. The blocks * attach to that list via icb->icb_list. * * 2) If it wants to look up an existing block, it gets it from * ic->ic_lookup. The blocks are attached vai icb->icb_node. */ struct io_cache_block { struct rb_node icb_node; struct list_head icb_list; uint64_t icb_blkno; char *icb_buf; }; struct io_cache { size_t ic_nr_blocks; struct list_head ic_lru; struct rb_root ic_lookup; /* Housekeeping */ struct io_cache_block *ic_metadata_buffer; unsigned long ic_metadata_buffer_len; char *ic_data_buffer; unsigned long ic_data_buffer_len; int ic_locked; int ic_use_count; /* stats */ uint32_t ic_hits; uint32_t ic_misses; uint32_t ic_inserts; uint32_t ic_removes; }; struct _io_channel { char *io_name; int io_blksize; int io_flags; int io_error; int io_fd; bool io_nocache; struct io_cache *io_cache; /* stats */ uint64_t io_bytes_read; uint64_t io_bytes_written; }; /* * We open code this because we don't have the ocfs2_filesys to call * ocfs2_blocks_in_bytes(). */ static inline int one_meg_of_blocks(io_channel *channel) { int count = ONE_MEGABYTE + channel->io_blksize - 1; return count / channel->io_blksize; } static errcode_t unix_vec_read_blocks(io_channel *channel, struct io_vec_unit *ivus, int count) { int i; int ret; io_context_t io_ctx; struct iocb *iocb = NULL, **iocbs = NULL; struct io_event *events = NULL; int64_t offset; int submitted, completed = 0; ret = OCFS2_ET_NO_MEMORY; iocb = malloc((sizeof(struct iocb) * count)); iocbs = malloc((sizeof(struct iocb *) * count)); events = malloc((sizeof(struct io_event) * count)); if (!iocb || !iocbs || !events) goto out; memset(&io_ctx, 0, sizeof(io_ctx)); ret = io_queue_init(count, &io_ctx); if (ret) return ret; for (i = 0; i < count; ++i) { offset = ivus[i].ivu_blkno * channel->io_blksize; io_prep_pread(&(iocb[i]), channel->io_fd, ivus[i].ivu_buf, ivus[i].ivu_buflen, offset); iocbs[i] = &iocb[i]; } resubmit: ret = io_submit(io_ctx, count - completed, &iocbs[completed]); if (!ret && (count - completed)) ret = OCFS2_ET_SHORT_READ; if (ret < 0) goto out; submitted = ret; ret = io_getevents(io_ctx, submitted, submitted, events, NULL); if (ret < 0) goto out; completed += submitted; if (completed < count) goto resubmit; out: if (ret >= 0) ret = 0; if (!ret) channel->io_bytes_read += (count * channel->io_blksize); free(iocb); free(iocbs); free(events); io_queue_release(io_ctx); return ret; } static errcode_t unix_io_read_block(io_channel *channel, int64_t blkno, int count, char *data) { int ret; ssize_t size, tot, rd; uint64_t location; /* -ative means count is in bytes */ size = (count < 0) ? -count : count * channel->io_blksize; location = blkno * channel->io_blksize; tot = 0; while (tot < size) { rd = pread64(channel->io_fd, data + tot, size - tot, location + tot); ret = OCFS2_ET_IO; if (rd < 0) { channel->io_error = errno; goto out; } if (!rd) goto out; tot += rd; } ret = 0; out: if (!ret && tot != size) { ret = OCFS2_ET_SHORT_READ; memset(data + tot, 0, size - tot); } channel->io_bytes_read += tot; return ret; } static errcode_t unix_io_write_block_full(io_channel *channel, int64_t blkno, int count, const char *data, int *completed) { int ret; ssize_t size, tot, wr; uint64_t location; /* -ative means count is in bytes */ size = (count < 0) ? -count : count * channel->io_blksize; location = blkno * channel->io_blksize; tot = 0; while (tot < size) { wr = pwrite64(channel->io_fd, data + tot, size - tot, location + tot); ret = OCFS2_ET_IO; if (wr < 0) { channel->io_error = errno; goto out; } if (!wr) goto out; tot += wr; } ret = 0; out: if (completed) *completed = tot / channel->io_blksize; if (!ret && (tot != size)) ret = OCFS2_ET_SHORT_WRITE; channel->io_bytes_written += tot; return ret; } static errcode_t unix_io_write_block(io_channel *channel, int64_t blkno, int count, const char *data) { return unix_io_write_block_full(channel, blkno, count, data, NULL); } /* * See if the rbtree has a block for the given block number. * * The rb_node garbage lets insertion share the search. Trivial callers * pass NULL. */ static struct io_cache_block *io_cache_lookup(struct io_cache *ic, uint64_t blkno) { struct rb_node *p = ic->ic_lookup.rb_node; struct io_cache_block *icb; while (p) { icb = rb_entry(p, struct io_cache_block, icb_node); if (blkno < icb->icb_blkno) { p = p->rb_left; } else if (blkno > icb->icb_blkno) { p = p->rb_right; } else return icb; } return NULL; } static void io_cache_insert(struct io_cache *ic, struct io_cache_block *insert_icb) { struct rb_node **p = &ic->ic_lookup.rb_node; struct rb_node *parent = NULL; struct io_cache_block *icb = NULL; while (*p) { parent = *p; icb = rb_entry(parent, struct io_cache_block, icb_node); if (insert_icb->icb_blkno < icb->icb_blkno) { p = &(*p)->rb_left; icb = NULL; } else if (insert_icb->icb_blkno > icb->icb_blkno) { p = &(*p)->rb_right; icb = NULL; } else assert(0); /* We erased it, remember? */ } rb_link_node(&insert_icb->icb_node, parent, p); rb_insert_color(&insert_icb->icb_node, &ic->ic_lookup); ic->ic_inserts++; } static void io_cache_seen(struct io_cache *ic, struct io_cache_block *icb) { /* Move to the front of the LRU */ list_del(&icb->icb_list); list_add_tail(&icb->icb_list, &ic->ic_lru); } static void io_cache_unsee(struct io_cache *ic, struct io_cache_block *icb) { /* * Move to the end of the LRU. There's no point in removing an * "unseen" buffer from the cache. It's valid, but we want the * next I/O to steal it. */ list_del(&icb->icb_list); list_add(&icb->icb_list, &ic->ic_lru); } static void io_cache_disconnect(struct io_cache *ic, struct io_cache_block *icb) { /* * This icb should longer be looked up. * If icb->icb_blkno is UINT64_MAX, it's already disconnected. */ if (icb->icb_blkno != UINT64_MAX) { rb_erase(&icb->icb_node, &ic->ic_lookup); memset(&icb->icb_node, 0, sizeof(struct rb_node)); icb->icb_blkno = UINT64_MAX; } } static struct io_cache_block *io_cache_pop_lru(struct io_cache *ic) { struct io_cache_block *icb; icb = list_entry(ic->ic_lru.next, struct io_cache_block, icb_list); io_cache_disconnect(ic, icb); ic->ic_removes++; return icb; } /* * Unlike its sync counterpart, this function issues ios even for cached blocks. */ static errcode_t io_cache_vec_read_blocks(io_channel *channel, struct io_vec_unit *ivus, int count, bool nocache) { struct io_cache *ic = channel->io_cache; struct io_cache_block *icb; errcode_t ret = 0; int i, j, blksize = channel->io_blksize; uint64_t blkno; uint32_t numblks; char *buf; /* * Read all blocks. We could extend this to not issue ios for already * cached blocks. But is it worth the effort? */ ret = unix_vec_read_blocks(channel, ivus, count); if (ret) goto out; /* refresh cache */ for (i = 0; i < count; i++) { blkno = ivus[i].ivu_blkno; numblks = ivus[i].ivu_buflen / blksize; buf = ivus[i].ivu_buf; for (j = 0; j < numblks; ++j, ++blkno, buf += blksize) { icb = io_cache_lookup(ic, blkno); if (!icb) { if (nocache) continue; icb = io_cache_pop_lru(ic); icb->icb_blkno = blkno; io_cache_insert(ic, icb); } memcpy(icb->icb_buf, buf, blksize); if (nocache) io_cache_unsee(ic, icb); else io_cache_seen(ic, icb); } } out: return ret; } /* * This relies on the fact that our cache is always up to date. If a * block is in the cache, the same thing is on disk. Even if we re-read * the disk block, we don't need to update the cache. This allows us * to look for optimal I/O sizes; it's better to call one read 1MB of * half-cached blocks than to read every other block. * * If the caller specifies "nocache", we still want to give them anything * we found in the cache, but we want cached blocks moved to the front * of the LRU. That way they get stolen first. */ static errcode_t io_cache_read_blocks(io_channel *channel, int64_t blkno, int count, char *data, bool nocache) { int i, good_blocks; errcode_t ret = 0; struct io_cache *ic = channel->io_cache; struct io_cache_block *icb; /* * Here we check two things: * * 1) Are all the blocks cached? If so, we can skip I/O. * 2) If they are not all cached, we want to start our read at the * first uncached blkno. */ for (good_blocks = 0; good_blocks < count; good_blocks++) { icb = io_cache_lookup(ic, blkno + good_blocks); if (!icb) break; ic->ic_hits++; } /* Read any blocks not in the cache */ if (good_blocks < count) { ic->ic_misses += (count - good_blocks); ret = unix_io_read_block(channel, blkno + good_blocks, count - good_blocks, data + (channel->io_blksize * good_blocks)); if (ret) goto out; } /* Now we sync up the cache with the data buffer */ for (i = 0; i < count; i++, data += channel->io_blksize) { icb = io_cache_lookup(ic, blkno + i); if (i < good_blocks) { /* * We skipped reading this because it was in the * cache. Copy it to the data buffer. */ assert(icb); memcpy(data, icb->icb_buf, channel->io_blksize); } else if (!icb) { if (nocache) continue; /* Steal the LRU buffer */ icb = io_cache_pop_lru(ic); icb->icb_blkno = blkno + i; io_cache_insert(ic, icb); /* * We did I/O into the data buffer, now update * the cache. */ memcpy(icb->icb_buf, data, channel->io_blksize); } /* * What about if ((i >= good_blocks) && icb)? That means * we had the buffer in the cache, but we read it anyway * to get a single I/O. Our cache guarantees that the * contents will match, so we just skip to marking the * buffer seen. */ if (nocache) io_cache_unsee(ic, icb); else io_cache_seen(ic, icb); } out: return ret; } static errcode_t io_cache_read_block(io_channel *channel, int64_t blkno, int count, char *data, bool nocache) { int todo = one_meg_of_blocks(channel); errcode_t ret = 0; /* * We do this in one meg hunks so that each hunk has an * opportunity to be in cache, but we get a good throughput. */ while (count) { if (todo > count) todo = count; ret = io_cache_read_blocks(channel, blkno, todo, data, nocache); if (ret) break; blkno += todo; count -= todo; data += (channel->io_blksize * todo); } return ret; } /* * This relies on the fact that our cache is always up to date. If a * block is in the cache, the same thing is on disk. So here we'll write * a whole stream and update the cache as needed. */ static errcode_t io_cache_write_blocks(io_channel *channel, int64_t blkno, int count, const char *data, bool nocache) { int i, completed = 0; errcode_t ret; struct io_cache *ic = channel->io_cache; struct io_cache_block *icb; /* Get the write out of the way */ ret = unix_io_write_block_full(channel, blkno, count, data, &completed); /* * Now we sync up the cache with the data buffer. We have * to sync up I/O that completed, even if the entire I/O did not. * * In the nocache case, we want to skip blocks that weren't in the * cache, but we want to update blocks that where. Even though * the caller specified "don't cache this", it's already in the * cache. We don't want stale data. */ for (i = 0; i < completed; i++, data += channel->io_blksize) { icb = io_cache_lookup(ic, blkno + i); if (!icb) { if (nocache) continue; /* * Steal the LRU buffer. We can't error here, so * we can safely insert it before we copy the data. */ icb = io_cache_pop_lru(ic); icb->icb_blkno = blkno + i; io_cache_insert(ic, icb); } memcpy(icb->icb_buf, data, channel->io_blksize); if (nocache) io_cache_unsee(ic, icb); else io_cache_seen(ic, icb); } return ret; } static errcode_t io_cache_write_block(io_channel *channel, int64_t blkno, int count, const char *data, bool nocache) { /* * Unlike io_read_cache_block(), we're going to do all of the * I/O no matter what. We keep the separation of * io_cache_write_block() and io_cache_write_blocks() for * consistency. */ return io_cache_write_blocks(channel, blkno, count, data, nocache); } static void io_free_cache(struct io_cache *ic) { if (ic) { if (ic->ic_data_buffer) { if (ic->ic_locked) munlock(ic->ic_data_buffer, ic->ic_data_buffer_len); ocfs2_free(&ic->ic_data_buffer); } if (ic->ic_metadata_buffer) { if (ic->ic_locked) munlock(ic->ic_metadata_buffer, ic->ic_metadata_buffer_len); ocfs2_free(&ic->ic_metadata_buffer); } ocfs2_free(&ic); } } void io_destroy_cache(io_channel *channel) { if (channel->io_cache) { if (!--channel->io_cache->ic_use_count) io_free_cache(channel->io_cache); channel->io_cache = NULL; } } /* * A cache is kind of pointless if it is swappable, right? Let's give * applications the ability to pin the cache memory. This is a separate * call from io_init_cache() because non-privileged users can't do it, and * they still want to create small caches. */ errcode_t io_mlock_cache(io_channel *channel) { int rc; struct io_cache *ic = channel->io_cache; long pages_wanted, avpages; if (!ic) return OCFS2_ET_INVALID_ARGUMENT; if (ic->ic_locked) return 0; /* * We're going to lock our cache pages. We don't want to * request more memory than the system has, though. */ pages_wanted = channel->io_blksize * ic->ic_nr_blocks / getpagesize(); avpages = sysconf(_SC_AVPHYS_PAGES); if (pages_wanted > avpages) return OCFS2_ET_NO_MEMORY; rc = mlock(ic->ic_data_buffer, ic->ic_data_buffer_len); if (!rc) { rc = mlock(ic->ic_metadata_buffer, ic->ic_metadata_buffer_len); if (rc) munlock(ic->ic_data_buffer, ic->ic_data_buffer_len); } if (rc) return OCFS2_ET_NO_MEMORY; ic->ic_locked = 1; return 0; } errcode_t io_init_cache(io_channel *channel, size_t nr_blocks) { int i; struct io_cache *ic; char *dbuf; struct io_cache_block *icb_list; errcode_t ret; ret = ocfs2_malloc0(sizeof(struct io_cache), &ic); if (ret) goto out; ic->ic_nr_blocks = nr_blocks; ic->ic_lookup = RB_ROOT; INIT_LIST_HEAD(&ic->ic_lru); ret = ocfs2_malloc_blocks(channel, nr_blocks, &ic->ic_data_buffer); if (ret) goto out; ic->ic_data_buffer_len = (unsigned long)nr_blocks * channel->io_blksize; ret = ocfs2_malloc0(sizeof(struct io_cache_block) * nr_blocks, &ic->ic_metadata_buffer); if (ret) goto out; ic->ic_metadata_buffer_len = (unsigned long)nr_blocks * sizeof(struct io_cache_block); icb_list = ic->ic_metadata_buffer; dbuf = ic->ic_data_buffer; for (i = 0; i < nr_blocks; i++) { icb_list[i].icb_blkno = UINT64_MAX; icb_list[i].icb_buf = dbuf; dbuf += channel->io_blksize; list_add_tail(&icb_list[i].icb_list, &ic->ic_lru); } ic->ic_use_count = 1; channel->io_cache = ic; out: if (ret) io_free_cache(ic); return ret; } errcode_t io_init_cache_size(io_channel *channel, size_t bytes) { size_t blocks; blocks = (bytes + (channel->io_blksize - 1)) / channel->io_blksize; return io_init_cache(channel, blocks); } size_t io_get_cache_size(io_channel *channel) { if (channel->io_cache) return channel->io_cache->ic_data_buffer_len; return 0; } errcode_t io_share_cache(io_channel *from, io_channel *to) { if (!from->io_cache) return OCFS2_ET_INTERNAL_FAILURE; if (to->io_cache) return OCFS2_ET_INTERNAL_FAILURE; to->io_cache = from->io_cache; from->io_cache->ic_use_count++; return 0; } static errcode_t io_validate_o_direct(io_channel *channel) { errcode_t ret = OCFS2_ET_UNEXPECTED_BLOCK_SIZE; int block_size; char *blk; for (block_size = io_get_blksize(channel); block_size <= OCFS2_MAX_BLOCKSIZE; block_size <<= 1) { io_set_blksize(channel, block_size); ret = ocfs2_malloc_block(channel, &blk); if (ret) break; ret = unix_io_read_block(channel, 0, 1, blk); ocfs2_free(&blk); if (!ret) break; } return ret; } int io_is_device_readonly(io_channel *channel) { errcode_t ret; int dev_ro; ret = ioctl(channel->io_fd, BLKROGET, &dev_ro); if (ret >= 0 && dev_ro) return 1; return 0; } errcode_t io_open(const char *name, int flags, io_channel **channel) { errcode_t ret; io_channel *chan = NULL; #ifdef __linux__ struct stat stat_buf; struct utsname ut; #endif if (!name || !*name) return OCFS2_ET_BAD_DEVICE_NAME; ret = ocfs2_malloc0(sizeof(struct _io_channel), &chan); if (ret) return ret; ret = ocfs2_malloc(strlen(name)+1, &chan->io_name); if (ret) goto out_chan; strcpy(chan->io_name, name); chan->io_blksize = OCFS2_MIN_BLOCKSIZE; chan->io_flags = (flags & OCFS2_FLAG_RW) ? O_RDWR : O_RDONLY; chan->io_nocache = false; if (!(flags & OCFS2_FLAG_BUFFERED)) chan->io_flags |= O_DIRECT; chan->io_error = 0; chan->io_fd = open64(name, chan->io_flags); if (chan->io_fd < 0) { /* chan will be freed, don't bother with chan->io_error */ if (errno == ENOENT) ret = OCFS2_ET_NAMED_DEVICE_NOT_FOUND; else ret = OCFS2_ET_IO; goto out_name; } if (!(flags & OCFS2_FLAG_BUFFERED)) { ret = io_validate_o_direct(chan); if (ret) goto out_close; /* FIXME: bindraw here */ } /* Workaround from e2fsprogs */ #ifdef __linux__ #undef RLIM_INFINITY #if (defined(__alpha__) || ((defined(__sparc__) || defined(__mips__)) && (SIZEOF_LONG == 4))) #define RLIM_INFINITY ((unsigned long)(~0UL>>1)) #else #define RLIM_INFINITY (~0UL) #endif /* * Work around a bug in 2.4.10-2.4.18 kernels where writes to * block devices are wrongly getting hit by the filesize * limit. This workaround isn't perfect, since it won't work * if glibc wasn't built against 2.2 header files. (Sigh.) * */ if ((flags & OCFS2_FLAG_RW) && (uname(&ut) == 0) && ((ut.release[0] == '2') && (ut.release[1] == '.') && (ut.release[2] == '4') && (ut.release[3] == '.') && (ut.release[4] == '1') && (ut.release[5] >= '0') && (ut.release[5] < '8')) && (fstat(chan->io_fd, &stat_buf) == 0) && (S_ISBLK(stat_buf.st_mode))) { struct rlimit rlim; rlim.rlim_cur = rlim.rlim_max = (unsigned long) RLIM_INFINITY; setrlimit(RLIMIT_FSIZE, &rlim); getrlimit(RLIMIT_FSIZE, &rlim); if (((unsigned long) rlim.rlim_cur) < ((unsigned long) rlim.rlim_max)) { rlim.rlim_cur = rlim.rlim_max; setrlimit(RLIMIT_FSIZE, &rlim); } } #endif *channel = chan; return 0; out_close: /* Ignore the return, leave the original error */ close(chan->io_fd); out_name: ocfs2_free(&chan->io_name); out_chan: ocfs2_free(&chan); *channel = NULL; return ret; } errcode_t io_close(io_channel *channel) { errcode_t ret = 0; io_destroy_cache(channel); if (close(channel->io_fd) < 0) ret = errno; ocfs2_free(&channel->io_name); ocfs2_free(&channel); return ret; } int io_get_error(io_channel *channel) { return channel->io_error; } errcode_t io_set_blksize(io_channel *channel, int blksize) { if (blksize % OCFS2_MIN_BLOCKSIZE) return OCFS2_ET_INVALID_ARGUMENT; if (!blksize) blksize = OCFS2_MIN_BLOCKSIZE; if (channel->io_blksize != blksize) channel->io_blksize = blksize; return 0; } int io_get_blksize(io_channel *channel) { return channel->io_blksize; } int io_get_fd(io_channel *channel) { return channel->io_fd; } void io_get_stats(io_channel *channel, struct ocfs2_io_stats *stats) { struct io_cache *ioc = channel->io_cache; memset(stats, 0, sizeof(struct ocfs2_io_stats)); stats->is_bytes_read = channel->io_bytes_read; stats->is_bytes_written = channel->io_bytes_written; if (ioc) { stats->is_cache_hits = ioc->ic_hits; stats->is_cache_misses = ioc->ic_misses; stats->is_cache_inserts = ioc->ic_inserts; stats->is_cache_removes = ioc->ic_removes; } } /* * If a channel is set to 'nocache', it will use the _nocache() functions * even if called via the regular functions. This allows control of * naive code that we don't want to have to carry nocache parameters * around. Smarter code can ignore this function and use the _nocache() * functions directly. */ void io_set_nocache(io_channel *channel, bool nocache) { channel->io_nocache = nocache; } errcode_t io_vec_read_blocks(io_channel *channel, struct io_vec_unit *ivus, int count) { if (channel->io_cache) return io_cache_vec_read_blocks(channel, ivus, count, channel->io_nocache); else return unix_vec_read_blocks(channel, ivus, count); } errcode_t io_read_block(io_channel *channel, int64_t blkno, int count, char *data) { if (channel->io_cache) return io_cache_read_block(channel, blkno, count, data, channel->io_nocache); else return unix_io_read_block(channel, blkno, count, data); } errcode_t io_read_block_nocache(io_channel *channel, int64_t blkno, int count, char *data) { if (channel->io_cache) return io_cache_read_block(channel, blkno, count, data, true); else return unix_io_read_block(channel, blkno, count, data); } errcode_t io_write_block(io_channel *channel, int64_t blkno, int count, const char *data) { if (channel->io_cache) return io_cache_write_block(channel, blkno, count, data, channel->io_nocache); else return unix_io_write_block(channel, blkno, count, data); } errcode_t io_write_block_nocache(io_channel *channel, int64_t blkno, int count, const char *data) { if (channel->io_cache) return io_cache_write_block(channel, blkno, count, data, true); else return unix_io_write_block(channel, blkno, count, data); } #ifdef DEBUG_EXE #include #include #include #include static int64_t read_number(const char *num) { int64_t val; char *ptr; val = strtoll(num, &ptr, 0); if (!ptr || *ptr) return 0; return val; } static void dump_u32(uint32_t *val) { unsigned int i; uint8_t *bytes = (uint8_t *)val; for (i = 0; i < sizeof(uint32_t); i++) fprintf(stdout, "%02X", bytes[i]); } static void dump_block(int64_t blkno, int blksize, char *buf) { size_t i; uint32_t *vals = (uint32_t *)buf; fprintf(stdout, "Dumping block %"PRId64" (%d bytes):\n", blkno, blksize); for (i = 0; i < (blksize / sizeof(uint32_t)); i++) { if (!(i % 4)) { if (i) fprintf(stdout, "\n"); fprintf(stdout, "0x%08zu\t", i * sizeof(uint32_t)); } dump_u32(&vals[i]); fprintf(stdout, " "); } fprintf(stdout, "\n"); } static void print_usage(void) { fprintf(stderr, "Usage: unix_io [-b ] [-c ] [-B ]\n" " \n"); } extern int opterr, optind; extern char *optarg; int main(int argc, char *argv[]) { errcode_t ret; int c; int64_t blkno, count, blksize; char *filename; io_channel *channel; char *blks; /* Some simple defaults */ blksize = 512; blkno = 0; count = 1; initialize_ocfs_error_table(); while((c = getopt(argc, argv, "b:c:B:")) != EOF) { switch (c) { case 'b': blkno = read_number(optarg); if (blkno < 0) { fprintf(stderr, "Invalid blkno: %s\n", optarg); print_usage(); return 1; } break; case 'c': count = read_number(optarg); if (!count) { fprintf(stderr, "Invalid count: %s\n", optarg); print_usage(); return 1; } break; case 'B': blksize = read_number(optarg); if (!blksize) { fprintf(stderr, "Invalid blksize: %s\n", optarg); print_usage(); return 1; } break; default: print_usage(); return 1; break; } } if (blksize % OCFS2_MIN_BLOCKSIZE) { fprintf(stderr, "Invalid blocksize: %"PRId64"\n", blksize); print_usage(); return 1; } if (count < 0) { if (-count > (int64_t)INT_MAX) { fprintf(stderr, "Count is too large: %"PRId64"\n", count); print_usage(); return 1; } count = -count / blksize; } else { if ((count * blksize) > INT_MAX) { fprintf(stderr, "Count is too large: %"PRId64"\n", count); print_usage(); return 1; } } if (optind >= argc) { fprintf(stderr, "Missing filename\n"); print_usage(); return 1; } filename = argv[optind]; ret = io_open(filename, OCFS2_FLAG_RO, &channel); if (ret) { com_err(argv[0], ret, "while opening file \"%s\"", filename); goto out; } ret = ocfs2_malloc_blocks(channel, (int)count, &blks); if (ret) { com_err(argv[0], ret, "while allocating %"PRId64" blocks", count); goto out_channel; } ret = io_read_block(channel, blkno, (int)count, blks); if (ret) { com_err(argv[0], ret, "while reading %"PRId64" blocks at block %"PRId64" (%s)", count, blkno, strerror(io_get_error(channel))); goto out_blocks; } for (c = 0; c < count; c++) dump_block(blkno + c, blksize, blks + (c * blksize)); out_blocks: ocfs2_free(&blks); out_channel: ret = io_close(channel); if (ret) { com_err(argv[0], ret, "while closing file \"%s\"", filename); } out: return 0; } #endif /* DEBUG_EXE */ ocfs2-tools-ocfs2-tools-1.8.6/libocfs2/unlink.c000066400000000000000000000133621347147137200212350ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * unlink.c * * Remove an entry from an OCFS2 directory. For the OCFS2 userspace * library. * * Copyright (C) 2004 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License, version 2, as published by the Free Software Foundation. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * * This code is a port of e2fsprogs/lib/ext2fs/unlink.c * Copyright (C) 1993, 1994, 1997 Theodore Ts'o. */ #define _XOPEN_SOURCE 600 /* Triggers magic in features.h */ #define _LARGEFILE64_SOURCE #include #include #include "ocfs2/ocfs2.h" struct link_struct { const char *name; int namelen; uint64_t inode; int flags; int done; }; #ifdef __TURBOC__ #pragma argsused #endif static int unlink_proc(struct ocfs2_dir_entry *dirent, uint64_t blocknr, int offset, int blocksize, char *buf, void *priv_data) { struct link_struct *ls = (struct link_struct *) priv_data; if (ls->name && ((dirent->name_len & 0xFF) != ls->namelen)) return 0; if (ls->name && strncmp(ls->name, dirent->name, dirent->name_len & 0xFF)) return 0; if (ls->inode && (dirent->inode != ls->inode)) return 0; dirent->inode = 0; ls->done++; return OCFS2_DIRENT_ABORT|OCFS2_DIRENT_CHANGED; } static errcode_t ocfs2_unlink_el(ocfs2_filesys *fs, uint64_t dir, const char *name, uint64_t ino, int flags) { errcode_t ret; struct link_struct ls; ls.name = name; ls.namelen = name ? strlen(name) : 0; ls.inode = ino; ls.flags = 0; ls.done = 0; ret = ocfs2_dir_iterate(fs, dir, 0, 0, unlink_proc, &ls); if (ret) goto out; if (!ls.done) ret = OCFS2_ET_DIR_NO_SPACE; out: return ret; } static errcode_t __ocfs2_delete_entry(ocfs2_filesys *fs, struct ocfs2_dir_entry *de_del, char *dir_buf) { struct ocfs2_dir_entry *de, *pde; int offset = 0; errcode_t ret = 0; pde = NULL; de = (struct ocfs2_dir_entry *)dir_buf; while( offset < fs->fs_blocksize) { if (!ocfs2_check_dir_entry(fs, de, dir_buf, offset)) { ret = OCFS2_ET_DIR_CORRUPTED; goto out; } if (de == de_del) { if (pde) pde->rec_len += de->rec_len; else de->inode = 0; goto out; } if (de->rec_len <= 0) { ret = OCFS2_ET_DIR_CORRUPTED; goto out; } pde = de; offset += de->rec_len; de = (struct ocfs2_dir_entry *)((char *)de + de->rec_len); } out: return ret; } static errcode_t ocfs2_unlink_dx(ocfs2_filesys *fs, uint64_t dir, const char *name, uint64_t ino, int flags) { char *di_buf = NULL, *dx_root_buf = NULL; struct ocfs2_dinode *di; struct ocfs2_dx_root_block *dx_root; struct ocfs2_dx_entry_list *entry_list; struct ocfs2_dir_block_trailer *trailer; int write_dx_leaf = 0; int add_to_free_list = 0; int max_rec_len = 0; struct ocfs2_dir_lookup_result lookup; errcode_t ret; assert(name); ret = ocfs2_malloc_block(fs->fs_io, &di_buf); if (ret) goto out; ret = ocfs2_read_inode(fs, dir, di_buf); if (ret) goto out; di = (struct ocfs2_dinode *)di_buf; ret = ocfs2_malloc_block(fs->fs_io, &dx_root_buf); if (ret) goto out; ret = ocfs2_read_dx_root(fs, di->i_dx_root, dx_root_buf); if (ret) goto out; dx_root = (struct ocfs2_dx_root_block *)dx_root_buf; memset(&lookup, 0, sizeof(struct ocfs2_dir_lookup_result)); ret= ocfs2_dx_dir_search(fs, name, strlen(name), dx_root, &lookup); if (ret) goto out; trailer = ocfs2_dir_trailer_from_block(fs, lookup.dl_leaf); if (trailer->db_free_rec_len == 0) add_to_free_list = 1; ret = __ocfs2_delete_entry(fs, lookup.dl_entry, lookup.dl_leaf); if (ret) goto out; max_rec_len = ocfs2_find_max_rec_len(fs, lookup.dl_leaf); trailer->db_free_rec_len = max_rec_len; if (add_to_free_list) { trailer->db_free_next = dx_root->dr_free_blk; dx_root->dr_free_blk = lookup.dl_leaf_blkno; } ret = ocfs2_write_dir_block(fs, di, lookup.dl_leaf_blkno, lookup.dl_leaf); if (ret) goto out; if (dx_root->dr_flags & OCFS2_DX_FLAG_INLINE) entry_list = &dx_root->dr_entries; else { entry_list = &(lookup.dl_dx_leaf->dl_list); write_dx_leaf = 1; } ocfs2_dx_list_remove_entry(entry_list, lookup.dl_dx_entry_idx); if (write_dx_leaf) { ret = ocfs2_write_dx_leaf(fs, lookup.dl_dx_leaf_blkno, lookup.dl_dx_leaf); if (ret) goto out; } dx_root->dr_num_entries --; ret = ocfs2_write_dx_root(fs, di->i_dx_root, dx_root_buf); if (ret) goto out; ret = ocfs2_write_inode(fs, di->i_blkno, di_buf); out: release_lookup_res(&lookup); if (dx_root_buf) ocfs2_free(&dx_root_buf); if (di_buf) ocfs2_free(&di_buf); return ret; } #ifdef __TURBOC__ #pragma argsused #endif errcode_t ocfs2_unlink(ocfs2_filesys *fs, uint64_t dir, const char *name, uint64_t ino, int flags) { errcode_t ret; char *di_buf = NULL; struct ocfs2_dinode *di; if (!(fs->fs_flags & OCFS2_FLAG_RW)) return OCFS2_ET_RO_FILESYS; ret = ocfs2_malloc_block(fs->fs_io, &di_buf); if (ret) goto out; ret = ocfs2_read_inode(fs, dir, di_buf); if (ret) goto out; di = (struct ocfs2_dinode *)di_buf; if (ocfs2_supports_indexed_dirs(OCFS2_RAW_SB(fs->fs_super)) && (ocfs2_dir_indexed(di))) ret = ocfs2_unlink_dx(fs, dir, name, ino, flags); else ret = ocfs2_unlink_el(fs, dir, name, ino, flags); out: if (di_buf) ocfs2_free(&di_buf); return ret; } ocfs2-tools-ocfs2-tools-1.8.6/libocfs2/xattr.c000066400000000000000000000432651347147137200211040ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * xattr.c * * Copyright (C) 2004, 2008 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License version 2 as published by the Free Software Foundation. * * 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. */ #include #include #include "ocfs2/byteorder.h" #include "ocfs2/ocfs2.h" struct ocfs2_xattr_def_value_root { struct ocfs2_xattr_value_root xv; struct ocfs2_extent_rec er; }; #define OCFS2_XATTR_ROOT_SIZE (sizeof(struct ocfs2_xattr_def_value_root)) uint32_t ocfs2_xattr_uuid_hash(unsigned char *uuid) { uint32_t i, hash = 0; for (i = 0; i < OCFS2_VOL_UUID_LEN; i++) { hash = (hash << OCFS2_HASH_SHIFT) ^ (hash >> (8*sizeof(hash) - OCFS2_HASH_SHIFT)) ^ *uuid++; } return hash; } uint32_t ocfs2_xattr_name_hash(uint32_t uuid_hash, const char *name, int name_len) { /* Get hash value of uuid from super block */ uint32_t hash = uuid_hash; int i; /* hash extended attribute name */ for (i = 0; i < name_len; i++) { hash = (hash << OCFS2_HASH_SHIFT) ^ (hash >> (8*sizeof(hash) - OCFS2_HASH_SHIFT)) ^ *name++; } return hash; } uint16_t ocfs2_xattr_buckets_per_cluster(ocfs2_filesys *fs) { return fs->fs_clustersize / OCFS2_XATTR_BUCKET_SIZE; } uint16_t ocfs2_blocks_per_xattr_bucket(ocfs2_filesys *fs) { return OCFS2_XATTR_BUCKET_SIZE / fs->fs_blocksize; } static void ocfs2_swap_xattr_entry(struct ocfs2_xattr_entry *xe) { xe->xe_name_hash = bswap_32(xe->xe_name_hash); xe->xe_name_offset = bswap_16(xe->xe_name_offset); xe->xe_value_size = bswap_64(xe->xe_value_size); } static void ocfs2_swap_xattr_tree_root(struct ocfs2_xattr_tree_root *xt) { xt->xt_clusters = bswap_32(xt->xt_clusters); xt->xt_last_eb_blk = bswap_64(xt->xt_last_eb_blk); } static void ocfs2_swap_xattr_value_root(struct ocfs2_xattr_value_root *xr) { xr->xr_clusters = bswap_32(xr->xr_clusters); xr->xr_last_eb_blk = bswap_64(xr->xr_last_eb_blk); } static void ocfs2_swap_xattr_block_header(struct ocfs2_xattr_block *xb) { xb->xb_suballoc_slot = bswap_16(xb->xb_suballoc_slot); xb->xb_suballoc_bit = bswap_16(xb->xb_suballoc_bit); xb->xb_fs_generation = bswap_32(xb->xb_fs_generation); xb->xb_blkno = bswap_64(xb->xb_blkno); xb->xb_flags = bswap_16(xb->xb_flags); xb->xb_suballoc_loc = bswap_64(xb->xb_suballoc_loc); } static void ocfs2_swap_xattr_header(struct ocfs2_xattr_header *xh) { if (cpu_is_little_endian) return; xh->xh_count = bswap_16(xh->xh_count); xh->xh_free_start = bswap_16(xh->xh_free_start); xh->xh_name_value_len = bswap_16(xh->xh_name_value_len); xh->xh_num_buckets = bswap_16(xh->xh_num_buckets); } /* * The swap barriers for xattrs are the hardest. The ocfs2_xattr_header * can be at the start of a bucket, inside an xattr block, or at the end * of an inode. Thus, we need to pass obj for the containing object. * On top of that, buckets are always 4K, regardless of blocksize. Thus, * we take objsize as an argument and fake the ocfs2_filesys we pass to * ocfs2_swap_barrier(). * * Much of this is internal to xattr.c, thankfully. The callers of the * pubic ocfs2_swap_xattr*() APIs don't have to worry about objsize! */ static void ocfs2_swap_xattr_entries_to_cpu(ocfs2_filesys *fs, void *obj, size_t objsize, struct ocfs2_xattr_header *xh) { uint16_t i; char *value; ocfs2_filesys fake_fs = { .fs_blocksize = objsize, }; if (cpu_is_little_endian) return; for (i = 0; i < xh->xh_count; i++) { struct ocfs2_xattr_entry *xe = &xh->xh_entries[i]; if (ocfs2_swap_barrier(&fake_fs, obj, xe, sizeof(struct ocfs2_xattr_entry))) break; ocfs2_swap_xattr_entry(xe); value = (char *)xh + xe->xe_name_offset + OCFS2_XATTR_SIZE(xe->xe_name_len); if (!ocfs2_xattr_is_local(xe)) { struct ocfs2_xattr_value_root *xr = (struct ocfs2_xattr_value_root *)value; if (ocfs2_swap_barrier(&fake_fs, obj, xr, OCFS2_XATTR_ROOT_SIZE)) break; ocfs2_swap_xattr_value_root(xr); ocfs2_swap_extent_list_to_cpu(&fake_fs, xh, &xr->xr_list); } else if (ocfs2_swap_barrier(&fake_fs, obj, value, OCFS2_XATTR_SIZE(xe->xe_value_size))) break; } } static void ocfs2_swap_xattr_entries_from_cpu(ocfs2_filesys *fs, void *obj, size_t objsize, struct ocfs2_xattr_header *xh) { uint16_t i; char *value; ocfs2_filesys fake_fs = { .fs_blocksize = objsize, }; if (cpu_is_little_endian) return; for (i = 0; i < xh->xh_count; i++) { struct ocfs2_xattr_entry *xe = &xh->xh_entries[i]; if (ocfs2_swap_barrier(&fake_fs, obj, xe, sizeof(struct ocfs2_xattr_entry))) break; value = (char *)xh + xe->xe_name_offset + OCFS2_XATTR_SIZE(xe->xe_name_len); if (!ocfs2_xattr_is_local(xe)) { struct ocfs2_xattr_value_root *xr = (struct ocfs2_xattr_value_root *)value; if (ocfs2_swap_barrier(&fake_fs, obj, xr, OCFS2_XATTR_ROOT_SIZE)) break; ocfs2_swap_extent_list_from_cpu(&fake_fs, xh, &xr->xr_list); ocfs2_swap_xattr_value_root(xr); } else if (ocfs2_swap_barrier(&fake_fs, obj, value, OCFS2_XATTR_SIZE(xe->xe_value_size))) break; ocfs2_swap_xattr_entry(xe); } } static void __ocfs2_swap_xattrs_to_cpu(ocfs2_filesys *fs, void *obj, size_t objsize, struct ocfs2_xattr_header *xh) { ocfs2_swap_xattr_header(xh); ocfs2_swap_xattr_entries_to_cpu(fs, obj, objsize, xh); } void ocfs2_swap_xattrs_to_cpu(ocfs2_filesys *fs, void *obj, struct ocfs2_xattr_header *xh) { return __ocfs2_swap_xattrs_to_cpu(fs, obj, fs->fs_blocksize, xh); } static void __ocfs2_swap_xattrs_from_cpu(ocfs2_filesys *fs, void *obj, size_t objsize, struct ocfs2_xattr_header *xh) { ocfs2_swap_xattr_entries_from_cpu(fs, obj, objsize, xh); ocfs2_swap_xattr_header(xh); } void ocfs2_swap_xattrs_from_cpu(ocfs2_filesys *fs, void *obj, struct ocfs2_xattr_header *xh) { return __ocfs2_swap_xattrs_from_cpu(fs, obj, fs->fs_blocksize, xh); } void ocfs2_swap_xattr_block_to_cpu(ocfs2_filesys *fs, struct ocfs2_xattr_block *xb) { if (cpu_is_little_endian) return; ocfs2_swap_xattr_block_header(xb); if (!(xb->xb_flags & OCFS2_XATTR_INDEXED)) ocfs2_swap_xattrs_to_cpu(fs, xb, &xb->xb_attrs.xb_header); else { ocfs2_swap_xattr_tree_root(&xb->xb_attrs.xb_root); ocfs2_swap_extent_list_to_cpu(fs, xb, &xb->xb_attrs.xb_root.xt_list); } } void ocfs2_swap_xattr_block_from_cpu(ocfs2_filesys *fs, struct ocfs2_xattr_block *xb) { if (cpu_is_little_endian) return; if (!(xb->xb_flags & OCFS2_XATTR_INDEXED)) ocfs2_swap_xattrs_from_cpu(fs, xb, &xb->xb_attrs.xb_header); else { ocfs2_swap_extent_list_from_cpu(fs, xb, &xb->xb_attrs.xb_root.xt_list); ocfs2_swap_xattr_tree_root(&xb->xb_attrs.xb_root); } ocfs2_swap_xattr_block_header(xb); } errcode_t ocfs2_read_xattr_block(ocfs2_filesys *fs, uint64_t blkno, char *xb_buf) { errcode_t ret = 0; char *blk; struct ocfs2_xattr_block *xb; if ((blkno < OCFS2_SUPER_BLOCK_BLKNO) || (blkno > fs->fs_blocks)) return OCFS2_ET_BAD_BLKNO; ret = ocfs2_malloc_block(fs->fs_io, &blk); if (ret) return ret; ret = ocfs2_read_blocks(fs, blkno, 1, blk); if (ret) goto out; xb = (struct ocfs2_xattr_block *)blk; ret = ocfs2_validate_meta_ecc(fs, blk, &xb->xb_check); if (ret) goto out; if (memcmp(xb->xb_signature, OCFS2_XATTR_BLOCK_SIGNATURE, strlen(OCFS2_XATTR_BLOCK_SIGNATURE))) { ret = OCFS2_ET_BAD_XATTR_BLOCK_MAGIC; goto out; } memcpy(xb_buf, blk, fs->fs_blocksize); xb = (struct ocfs2_xattr_block *)xb_buf; ocfs2_swap_xattr_block_to_cpu(fs, xb); out: ocfs2_free(&blk); return ret; } errcode_t ocfs2_write_xattr_block(ocfs2_filesys *fs, uint64_t blkno, char *xb_buf) { errcode_t ret = 0; char *blk; struct ocfs2_xattr_block *xb; if (!(fs->fs_flags & OCFS2_FLAG_RW)) return OCFS2_ET_RO_FILESYS; if ((blkno < OCFS2_SUPER_BLOCK_BLKNO) || (blkno > fs->fs_blocks)) return OCFS2_ET_BAD_BLKNO; ret = ocfs2_malloc_block(fs->fs_io, &blk); if (ret) return ret; memcpy(blk, xb_buf, fs->fs_blocksize); xb = (struct ocfs2_xattr_block *)blk; ocfs2_swap_xattr_block_from_cpu(fs, xb); ocfs2_compute_meta_ecc(fs, blk, &xb->xb_check); ret = io_write_block(fs->fs_io, blkno, 1, blk); if (!ret) fs->fs_flags |= OCFS2_FLAG_CHANGED; ocfs2_free(&blk); return ret; } errcode_t ocfs2_xattr_get_rec(ocfs2_filesys *fs, struct ocfs2_xattr_block *xb, uint32_t name_hash, uint64_t *p_blkno, uint32_t *e_cpos, uint32_t *num_clusters) { int i; errcode_t ret = 0; char *eb_buf = NULL; struct ocfs2_extent_block *eb; struct ocfs2_extent_rec *rec = NULL; struct ocfs2_extent_list *el = &xb->xb_attrs.xb_root.xt_list; uint64_t e_blkno = 0; if (!(xb->xb_flags & OCFS2_XATTR_INDEXED)) return OCFS2_ET_INVALID_ARGUMENT; if (el->l_tree_depth) { ret = ocfs2_tree_find_leaf(fs, el, xb->xb_blkno, (char *)xb, name_hash, &eb_buf); if (ret) goto out; eb = (struct ocfs2_extent_block *) eb_buf; el = &eb->h_list; if (el->l_tree_depth) { ret = OCFS2_ET_INVALID_ARGUMENT; goto out; } } for (i = el->l_next_free_rec - 1; i >= 0; i--) { rec = &el->l_recs[i]; if (rec->e_cpos <= name_hash) { e_blkno = rec->e_blkno; break; } } if (!e_blkno) { ret = OCFS2_ET_INVALID_ARGUMENT; goto out; } *p_blkno = rec->e_blkno; *num_clusters = rec->e_leaf_clusters; if (e_cpos) *e_cpos = rec->e_cpos; out: if (eb_buf) ocfs2_free(&eb_buf); return ret; } uint16_t ocfs2_xattr_value_real_size(uint16_t name_len, uint16_t value_len) { uint16_t size = 0; if (value_len <= OCFS2_XATTR_INLINE_SIZE) size = OCFS2_XATTR_SIZE(name_len) + OCFS2_XATTR_SIZE(value_len); else size = OCFS2_XATTR_SIZE(name_len) + OCFS2_XATTR_ROOT_SIZE; return size; } uint16_t ocfs2_xattr_min_offset(struct ocfs2_xattr_header *xh, uint16_t size) { int i; uint16_t min_offs = size; for (i = 0 ; i < xh->xh_count; i++) { struct ocfs2_xattr_entry *xe = &xh->xh_entries[i]; size_t offs = xe->xe_name_offset; if (offs < min_offs) min_offs = offs; } return min_offs; } uint16_t ocfs2_xattr_name_value_len(struct ocfs2_xattr_header *xh) { int i; uint16_t total_len = 0; for (i = 0 ; i < xh->xh_count; i++) { struct ocfs2_xattr_entry *xe = &xh->xh_entries[i]; total_len += ocfs2_xattr_value_real_size(xe->xe_name_len, xe->xe_value_size); } return total_len; } errcode_t ocfs2_read_xattr_bucket(ocfs2_filesys *fs, uint64_t blkno, char *bucket_buf) { errcode_t ret = 0; char *bucket; struct ocfs2_xattr_header *xh; int blk_per_bucket = ocfs2_blocks_per_xattr_bucket(fs); ret = ocfs2_malloc_blocks(fs->fs_io, blk_per_bucket, &bucket); if (ret) return ret; ret = ocfs2_read_blocks(fs, blkno, blk_per_bucket, bucket); if (ret) goto out; xh = (struct ocfs2_xattr_header *)bucket; if (ocfs2_meta_ecc(OCFS2_RAW_SB(fs->fs_super)) && !(fs->fs_flags & OCFS2_FLAG_NO_ECC_CHECKS)) { ret = ocfs2_block_check_validate(bucket, OCFS2_XATTR_BUCKET_SIZE, &xh->xh_check); if (ret) goto out; } memcpy(bucket_buf, bucket, OCFS2_XATTR_BUCKET_SIZE); xh = (struct ocfs2_xattr_header *)bucket_buf; __ocfs2_swap_xattrs_to_cpu(fs, xh, OCFS2_XATTR_BUCKET_SIZE, xh); out: ocfs2_free(&bucket); return ret; } errcode_t ocfs2_write_xattr_bucket(ocfs2_filesys *fs, uint64_t blkno, char *bucket_buf) { errcode_t ret = 0; char *bucket; struct ocfs2_xattr_header *xh; int blk_per_bucket = ocfs2_blocks_per_xattr_bucket(fs); if (!(fs->fs_flags & OCFS2_FLAG_RW)) return OCFS2_ET_RO_FILESYS; if ((blkno < OCFS2_SUPER_BLOCK_BLKNO) || (blkno > fs->fs_blocks)) return OCFS2_ET_BAD_BLKNO; ret = ocfs2_malloc_blocks(fs->fs_io, blk_per_bucket, &bucket); if (ret) return ret; memcpy(bucket, bucket_buf, OCFS2_XATTR_BUCKET_SIZE); xh = (struct ocfs2_xattr_header *)bucket; __ocfs2_swap_xattrs_from_cpu(fs, xh, OCFS2_XATTR_BUCKET_SIZE, xh); if (ocfs2_meta_ecc(OCFS2_RAW_SB(fs->fs_super))) ocfs2_block_check_compute(bucket, OCFS2_XATTR_BUCKET_SIZE, &xh->xh_check); ret = io_write_block(fs->fs_io, blkno, blk_per_bucket, bucket); if (!ret) fs->fs_flags |= OCFS2_FLAG_CHANGED; ocfs2_free(&bucket); return ret; } struct xattr_iterate_ctxt { ocfs2_cached_inode *ci; int (*func)(ocfs2_cached_inode *ci, char *xe_buf, uint64_t xe_blkno, struct ocfs2_xattr_entry *xe, char *value_buf, uint64_t value_blkno, void *value, int in_bucket, void *priv_data); errcode_t errcode; void *priv_data; }; static int ocfs2_xattr_iterate_entries(struct xattr_iterate_ctxt *ctxt, char *xattr_buf, uint64_t xe_blkno, struct ocfs2_xattr_header *xh, int is_bucket) { int i, value_offset, block_offset; struct ocfs2_xattr_entry *xe = NULL; int iret = 0; char *value_buf; void *value; for (i = 0 ; i < xh->xh_count; i++) { xe = &xh->xh_entries[i]; value_offset = xe->xe_name_offset + OCFS2_XATTR_SIZE(xe->xe_name_len); block_offset = value_offset / ctxt->ci->ci_fs->fs_blocksize; value_buf = xattr_buf + block_offset * ctxt->ci->ci_fs->fs_blocksize; value = (char *)xh + value_offset; if (ctxt->func) { iret = ctxt->func(ctxt->ci, xattr_buf, xe_blkno, xe, value_buf, xe_blkno + block_offset, value, is_bucket, ctxt->priv_data); if (iret & (OCFS2_XATTR_ABORT | OCFS2_XATTR_ERROR)) break; } } return iret; } static int ocfs2_xattr_iterate_ibody(struct xattr_iterate_ctxt *ctxt) { struct ocfs2_xattr_header *xh = NULL; struct ocfs2_dinode *di = ctxt->ci->ci_inode; if (!(di->i_dyn_features & OCFS2_INLINE_XATTR_FL)) return 0; xh = (struct ocfs2_xattr_header *)((char *)di + ctxt->ci->ci_fs->fs_blocksize - di->i_xattr_inline_size); return ocfs2_xattr_iterate_entries(ctxt, (char *)di, di->i_blkno, xh, 0); } static int ocfs2_xattr_iterate_bucket(struct xattr_iterate_ctxt *ctxt, uint64_t blkno, uint32_t clusters) { int i, iret = 0 ; char *bucket = NULL; struct ocfs2_xattr_header *xh; ocfs2_filesys *fs = ctxt->ci->ci_fs; int blk_per_bucket = ocfs2_blocks_per_xattr_bucket(fs); uint32_t bpc = ocfs2_xattr_buckets_per_cluster(fs); uint32_t num_buckets = clusters * bpc; ctxt->errcode = ocfs2_malloc_blocks(fs->fs_io, blk_per_bucket, &bucket); if (ctxt->errcode) goto out; for (i = 0; i < num_buckets; i++, blkno += blk_per_bucket) { ctxt->errcode = ocfs2_read_xattr_bucket(fs, blkno, bucket); if (ctxt->errcode) goto out; xh = (struct ocfs2_xattr_header *)bucket; /* * The real bucket num in this series of blocks is stored * in the 1st bucket. */ if (i == 0) num_buckets = xh->xh_num_buckets; iret = ocfs2_xattr_iterate_entries(ctxt, bucket, blkno, xh, 1); } out: if (bucket) ocfs2_free(&bucket); if (ctxt->errcode) iret |= OCFS2_XATTR_ERROR; return iret; } static int ocfs2_xattr_iterate_index_block(struct xattr_iterate_ctxt *ctxt, struct ocfs2_xattr_block *xb) { ocfs2_filesys *fs = ctxt->ci->ci_fs; struct ocfs2_extent_list *el = &xb->xb_attrs.xb_root.xt_list; uint32_t name_hash = UINT_MAX, e_cpos = 0, num_clusters = 0; uint64_t p_blkno = 0; int iret = 0; if (!el->l_next_free_rec) return 0; while (name_hash > 0) { ctxt->errcode = ocfs2_xattr_get_rec(fs, xb, name_hash, &p_blkno, &e_cpos, &num_clusters); if (ctxt->errcode) break; iret = ocfs2_xattr_iterate_bucket(ctxt, p_blkno, num_clusters); if (iret & (OCFS2_XATTR_ERROR | OCFS2_XATTR_ABORT)) break; if (e_cpos == 0) break; name_hash = e_cpos - 1; } if (ctxt->errcode) iret |= OCFS2_XATTR_ERROR; return iret; } static int ocfs2_xattr_iterate_block(struct xattr_iterate_ctxt *ctxt) { char *blk = NULL; ocfs2_filesys *fs = ctxt->ci->ci_fs; struct ocfs2_dinode *di = ctxt->ci->ci_inode; struct ocfs2_xattr_block *xb; int iret = 0; if (!di->i_xattr_loc) return 0; ctxt->errcode = ocfs2_malloc_block(fs->fs_io, &blk); if (ctxt->errcode) goto out; ctxt->errcode = ocfs2_read_xattr_block(fs, di->i_xattr_loc, blk); if (ctxt->errcode) goto out; xb = (struct ocfs2_xattr_block *)blk; if (xb->xb_flags & OCFS2_XATTR_INDEXED) iret = ocfs2_xattr_iterate_index_block(ctxt, xb); else { struct ocfs2_xattr_header *header = &xb->xb_attrs.xb_header; iret = ocfs2_xattr_iterate_entries(ctxt, blk, di->i_xattr_loc, header, 0); } out: if (blk) ocfs2_free(&blk); if (ctxt->errcode) iret |= OCFS2_XATTR_ERROR; return iret; } /* * Iterate the xattr entries on inode 'ci'. If 'func' returns * OCFS2_XATTR_ABORT or OCFS2_XATTR_ERROR, stop iteration. * If OCFS2_XATTR_ERROR, return an error from ocfs2_xattr_iterate. * * If you modify an xattr, you must restart your iteration - there is * no guarantee it is in a consistent state. */ errcode_t ocfs2_xattr_iterate(ocfs2_cached_inode *ci, int (*func)(ocfs2_cached_inode *ci, char *xe_buf, uint64_t xe_blkno, struct ocfs2_xattr_entry *xe, char *value_buf, uint64_t value_blkno, void *value, int in_bucket, void *priv_data), void *priv_data) { errcode_t ret = 0; int iret = 0; struct xattr_iterate_ctxt ctxt; if (!ocfs2_support_xattr(OCFS2_RAW_SB(ci->ci_fs->fs_super)) || (!(ci->ci_inode->i_dyn_features & OCFS2_HAS_XATTR_FL))) return 0; ctxt.ci = ci; ctxt.func = func; ctxt.priv_data = priv_data; ctxt.errcode = 0; iret = ocfs2_xattr_iterate_ibody(&ctxt); if (!(iret & (OCFS2_XATTR_ABORT | OCFS2_XATTR_ERROR))) iret = ocfs2_xattr_iterate_block(&ctxt); if (iret & OCFS2_XATTR_ERROR) ret = ctxt.errcode; return ret; } ocfs2-tools-ocfs2-tools-1.8.6/libtools-internal/000077500000000000000000000000001347147137200215225ustar00rootroot00000000000000ocfs2-tools-ocfs2-tools-1.8.6/libtools-internal/.gitignore000066400000000000000000000000511347147137200235060ustar00rootroot00000000000000cscope.* libtools-internal.a *.d debug_* ocfs2-tools-ocfs2-tools-1.8.6/libtools-internal/Makefile000066400000000000000000000016241347147137200231650ustar00rootroot00000000000000TOPDIR = .. include $(TOPDIR)/Preamble.make INCLUDES = -I. -I$(TOPDIR)/include UNINST_LIBRARIES = libtools-internal.a CFLAGS += -fPIC DEFINES += -DVERSION=\"$(VERSION)\" ifneq ($(OCFS2_DEBUG_EXE),) DEBUG_EXE_FILES = $(shell awk '/DEBUG_EXE/{if (k[FILENAME] == 0) {print FILENAME; k[FILENAME] = 1;}}' $(CFILES)) DEBUG_EXE_PROGRAMS = $(addprefix debug_,$(subst .c,,$(DEBUG_EXE_FILES))) .SECONDARY: UNINST_PROGRAMS += $(DEBUG_EXE_PROGRAMS) debug_%.o : %.c $(CC) $(CFLAGS) $(LOCAL_CFLAGS) $(CPPFLAGS) $(LOCAL_CPPFLAGS) \ $(INCLUDES) $(DEFINES) \ -DDEBUG_EXE -o $@ -c $< debug_%: debug_%.o $(LINK) $(COM_ERR_LIBS) endif CFILES = verbose.c progress.c utils.c scandisk.c HFILES = libtools-internal.h OBJS = $(subst .c,.o,$(CFILES)) $(OBJS): $(HFILES) $(HFILES_GEN) libtools-internal.a: $(OBJS) rm -f $@ $(AR) r $@ $^ $(RANLIB) $@ DIST_FILES = $(CFILES) $(HFILES) include $(TOPDIR)/Postamble.make ocfs2-tools-ocfs2-tools-1.8.6/libtools-internal/libtools-internal.h000066400000000000000000000015761347147137200253450ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * libtools-internal.h * * Internal header for libtools-internal. * * Copyright (C) 2008 Oracle. All rights reserved. * * 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. * * 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. */ #ifndef _LIBTOOLS_INTERNAL_H #define _LIBTOOLS_INTERNAL_H int tools_verbosity(void); int tools_is_interactive(void); void tools_progress_clear(void); void tools_progress_restore(void); #endif /* _LIBTOOLS_INTERNAL_H */ ocfs2-tools-ocfs2-tools-1.8.6/libtools-internal/progress.c000066400000000000000000000317031347147137200235360ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * progress.c * * Internal routines progress output. * * Copyright (C) 2008 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License version 2 as published by the Free Software Foundation. * * 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. */ #define _LARGEFILE64_SOURCE #include #include #include #include #include #include #include #include "ocfs2-kernel/kernel-list.h" #include "tools-internal/progress.h" #include "libtools-internal.h" enum progress_length { PROGRESS_TRUNC, PROGRESS_SHORT, PROGRESS_LONG, }; #define TRUNC_LEN 3 #define PERCENTAGE_LEN 5 #define SPINNER_LEN 2 struct tools_progress { struct list_head p_list; enum progress_length p_len; char *p_long_name; unsigned int p_long_name_len; char *p_short_name; unsigned int p_short_name_len; uint64_t p_current; uint64_t p_count; unsigned int p_percent; int p_spinner_pos; }; #define PROGRESS_OPEN "[" #define PROGRESS_SEP " > " #define PROGRESS_CLOSE "]" #define PROGRESS_ELIPS "... " static const char spinner[] = "\\|/-"; static char nextline; static LIST_HEAD(progresses); /* When did we last update the progress */ static unsigned int last_tick; /* Are we displaying progress statistics */ static int progress_on = 0; /* A fake progress structure to pass around when we're disabled */ static struct tools_progress disabled_prog; /* * A buffer for storing the current progress output. That way, we can * replay it. * * If the terminal is 80 characters or less, or we can't allocate an * appropriately sized progbuf, we use a static one. The extra 2 characters * are for nextline and the NUL. */ #define DEFAULT_WIDTH 80 #define PROGBUF_EXTRA 2 static char static_progbuf[DEFAULT_WIDTH + PROGBUF_EXTRA]; static char *progbuf = static_progbuf; static unsigned int progbuf_len = DEFAULT_WIDTH + PROGBUF_EXTRA; /* * If we've updated the progress within the last 1/8th of a second, there * is no point in doing it again. Tick algorithm stolen from e2fsck. */ static int check_tick(void) { unsigned int tick; struct timeval tv; gettimeofday(&tv, NULL); tick = (tv.tv_sec << 3) + (tv.tv_usec / (1000000 / 8)); if (tick == last_tick) return 0; last_tick = tick; return 1; } static unsigned int calc_percent(uint64_t num, uint64_t dem) { double percent = ((double)num) / ((double)dem); return (unsigned int)((100.0 * percent) + 0.5); } /* If the visual percentage hasn't change, there's no point in updating. */ static int check_percent(struct tools_progress *prog) { unsigned int new_percent; /* An unbounded progress always steps */ if (!prog->p_count) return 1; if (prog->p_current >= prog->p_count) prog->p_current = prog->p_count; new_percent = calc_percent(prog->p_current, prog->p_count); if (new_percent == prog->p_percent) return 0; prog->p_percent = new_percent; return 1; } static void step_spinner(struct tools_progress *prog) { prog->p_spinner_pos = (prog->p_spinner_pos + 1) & 3; } static void progress_length_reset(void) { struct list_head *p; struct tools_progress *prog; list_for_each(p, &progresses) { prog = list_entry(p, struct tools_progress, p_list); prog->p_len = PROGRESS_LONG; } } static size_t length_one_prog(struct tools_progress *prog) { size_t len = 0; switch (prog->p_len) { case PROGRESS_LONG: len += prog->p_long_name_len; break; case PROGRESS_SHORT: len += prog->p_short_name_len; break; case PROGRESS_TRUNC: len += TRUNC_LEN; break; default: assert(0); break; } if (prog->p_count) len += PERCENTAGE_LEN; else len += SPINNER_LEN; return len; } static unsigned int progress_length_check(void) { unsigned int len = 0; int first = 1; struct list_head *p; struct tools_progress *prog; assert(!list_empty(&progresses)); list_for_each(p, &progresses) { prog = list_entry(p, struct tools_progress, p_list); if (first) { len += strlen(PROGRESS_OPEN); first = 0; } else len += strlen(PROGRESS_SEP); len += length_one_prog(prog); } len += strlen(PROGRESS_CLOSE); return len; } static int progress_length_shrink(void) { struct list_head *p; struct tools_progress *prog = NULL; enum progress_length len = PROGRESS_LONG; /* * We start from the longest length. We lower that max length * if we see a shorter one. When we then see the boundary (a * longer length after a shorter one), we break out. */ list_for_each(p, &progresses) { prog = list_entry(p, struct tools_progress, p_list); if (len > prog->p_len) len = prog->p_len; else if (len < prog->p_len) break; prog = NULL; } /* * If there was no boundary, all progresses had the same length. * shrink the first one. */ if (!prog) prog = list_entry(progresses.next, struct tools_progress, p_list); /* * If the one we want to shrink already is at PROGRESS_TRUNC, we * can shrink no more. Return false. */ if (prog->p_len == PROGRESS_TRUNC) return 0; prog->p_len--; return 1; } static unsigned int check_display(void) { char *cols = getenv("COLUMNS"); char *tmpbuf; unsigned int tmp, columns = DEFAULT_WIDTH; if (cols) { tmp = atoi(cols); if (tmp) columns = tmp; } tmp = columns + PROGBUF_EXTRA; /* Do we need more space for this width? */ if (tmp > progbuf_len) { tmpbuf = malloc(sizeof(char) * tmp); if (tmpbuf) { progbuf_len = tmp; memset(tmpbuf, 0, tmp); if (progbuf != static_progbuf) free(progbuf); progbuf = tmpbuf; /* * We just grew the buffer, so try long progress * output again. */ progress_length_reset(); } else { /* * We couldn't allocate enough space, so report * what we can actually use. */ columns = progbuf_len - PROGBUF_EXTRA; } } return columns; } static size_t print_one_prog(struct tools_progress *prog, char *buf, size_t len) { int offset = 0; size_t ret; switch (prog->p_len) { case PROGRESS_LONG: ret = snprintf(buf + offset, len - offset, "%s", prog->p_long_name); break; case PROGRESS_SHORT: ret = snprintf(buf + offset, len - offset, "%s", prog->p_short_name); break; case PROGRESS_TRUNC: ret = snprintf(buf + offset, len - offset, "%.*s", TRUNC_LEN, prog->p_short_name); break; default: assert(0); break; } offset += ret; if (prog->p_count) ret = snprintf(buf + offset, len - offset, " %3u%%", prog->p_percent); else ret = snprintf(buf + offset, len - offset, " %c", spinner[prog->p_spinner_pos & 3]); offset += ret; return offset; } static void print_trailer(char *buf, size_t len) { size_t ret; unsigned int offset = 0; ret = snprintf(buf + offset, len - offset, "%s", PROGRESS_CLOSE); offset += ret; assert(offset <= len); ret = snprintf(buf + offset, len - offset, "%c", nextline); assert(ret < (len - offset)); } static void progress_printf(unsigned int columns) { unsigned int offset = 0; size_t ret; int first = 1; struct list_head *p; struct tools_progress *prog = NULL; if (list_empty(&progresses)) return; list_for_each(p, &progresses) { prog = list_entry(p, struct tools_progress, p_list); if (first) { ret = snprintf(progbuf + offset, columns - offset, "%s", PROGRESS_OPEN); first = 0; } else ret = snprintf(progbuf + offset, columns - offset, "%s", PROGRESS_SEP); offset += ret; offset += print_one_prog(prog, progbuf + offset, columns - offset); assert(offset < columns); } /* * From here on out, we use progbuf_len instead of columns. Our * earlier calculations should have gotten this right. */ assert(offset < columns); print_trailer(progbuf + offset, progbuf_len - offset); } static void truncate_printf(unsigned int columns) { struct tools_progress *last = list_entry(progresses.prev, struct tools_progress, p_list); size_t ret, len = length_one_prog(last); unsigned int offset = 0; if ((len + strlen(PROGRESS_CLOSE) + strlen(PROGRESS_ELIPS)) <= columns) { ret = snprintf(progbuf + offset, columns - offset, "%s", PROGRESS_ELIPS); offset += ret; ret = print_one_prog(last, progbuf + offset, columns - offset); offset += ret; assert(offset < columns); print_trailer(progbuf + offset, progbuf_len - offset); } else { /* Give up, no progress */ progbuf[0] = '\0'; } } static void progress_compute(void) { unsigned int columns = check_display(); int truncate = 0; while (progress_length_check() > columns) { truncate = !progress_length_shrink(); if (truncate) break; } if (truncate) truncate_printf(columns); else progress_printf(columns); } static void progress_clear(void) { unsigned int columns = check_display(); memset(progbuf, ' ', columns); snprintf(progbuf + columns, progbuf_len - columns, "%c", nextline); } static void progress_write(void) { printf("%s", progbuf); } static void tools_progress_free(struct tools_progress *prog) { if (prog->p_long_name) free(prog->p_long_name); if (prog->p_short_name) free(prog->p_short_name); free(prog); } static struct tools_progress *tools_progress_alloc(const char *long_name, const char *short_name, uint64_t count) { struct tools_progress *prog; prog = malloc(sizeof(struct tools_progress)); if (!prog) goto out; memset(prog, 0, sizeof(struct tools_progress)); prog->p_long_name = strdup(long_name ? long_name : ""); prog->p_short_name = strdup(short_name ? short_name : long_name); if (!prog->p_long_name || !prog->p_short_name) { tools_progress_free(prog); prog = NULL; goto out; } prog->p_long_name_len = strlen(prog->p_long_name); prog->p_short_name_len = strlen(prog->p_short_name); prog->p_count = count; out: return prog; } /* * API for libtools-internal only */ void tools_progress_clear(void) { if (!progress_on) return; if (list_empty(&progresses)) return; /* * We only need to wipe the line if are doing terminal-based * progress. */ if (nextline != '\r') return; progress_clear(); progress_write(); } void tools_progress_restore(void) { if (!progress_on) return; /* Same here */ if (list_empty(&progresses)) return; if (nextline != '\r') return; progress_compute(); progress_write(); } /* * Public API */ void tools_progress_enable(void) { progress_on = 1; if (!list_empty(&progresses)) return; if (isatty(STDOUT_FILENO)) nextline = '\r'; else nextline = '\n'; } void tools_progress_disable(void) { progress_on = 0; } int tools_progress_enabled(void) { return progress_on; } struct tools_progress *tools_progress_start(const char *long_name, const char *short_name, uint64_t count) { struct tools_progress *prog; if (!progress_on) return &disabled_prog; prog = tools_progress_alloc(long_name, short_name, count); if (!prog) goto out; list_add_tail(&prog->p_list, &progresses); tools_progress_clear(); progress_length_reset(); progress_compute(); progress_write(); out: return prog; } void tools_progress_step(struct tools_progress *prog, unsigned int step) { if (prog == &disabled_prog) return; prog->p_current += step; if (!check_percent(prog)) return; if (!check_tick() && (prog->p_percent != 100) && (!prog->p_count || (prog->p_percent != 0))) return; if (!prog->p_count) step_spinner(prog); progress_compute(); progress_write(); } void tools_progress_stop(struct tools_progress *prog) { if (prog == &disabled_prog) return; tools_progress_clear(); list_del(&prog->p_list); tools_progress_free(prog); if (!list_empty(&progresses)) { progress_length_reset(); tools_progress_restore(); } } #ifdef DEBUG_EXE #include static int run_steps(const char *ln, const char *sn, int count, int (*func)(void)) { int i, ret = 0; struct tools_progress *prog; struct timespec ts = { .tv_nsec = 100000000, }; prog = tools_progress_start(ln, sn, count > 0 ? count : 0); if (!prog) return 1; if (count < 0) count = -count; for (i = 0; i < count; i++) { if (func) ret = func(); if (ret) break; tools_progress_step(prog, 1); nanosleep(&ts, NULL); } tools_progress_stop(prog); return ret; } static int middle(void) { static int try = 0; char lbuf[100], sbuf[100]; try++; snprintf(lbuf, 100, "This is middle %d", try); snprintf(sbuf, 100, "middle%d", try); return run_steps(lbuf, sbuf, -7, NULL); } static int outer(void) { static int try = 0; char lbuf[100], sbuf[100]; try++; snprintf(lbuf, 100, "This is outer %d", try); snprintf(sbuf, 100, "outer%d", try); return run_steps(lbuf, sbuf, 10, middle); } int main(int argc, char *argv[]) { setbuf(stdout, NULL); setbuf(stderr, NULL); tools_progress_enable(); return run_steps("This is a test", "thisis", 5, outer); } #endif ocfs2-tools-ocfs2-tools-1.8.6/libtools-internal/scandisk.c000066400000000000000000000513231347147137200234710ustar00rootroot00000000000000/****************************************************************************** ******************************************************************************* ** ** Copyright (C) 2008 Red Hat, Inc. All rights reserved. ** All rights reserved. ** ** Author: Fabio M. Di Nitto ** ** Original design by: ** Joel Becker ** Fabio M. Di Nitto ** ** This copyrighted material is made available to anyone wishing to use, ** modify, copy, or redistribute it subject to the terms and conditions ** of the GNU General Public License v.2. ** ******************************************************************************* ******************************************************************************/ #include #include #include #include #include #include #include #include #include "tools-internal/scandisk.h" /** search in cache helpers **/ /* * match is 0 for exact match * 1 to see if the string is contained and return the first match */ static struct devnode *find_dev_by_path(struct devnode *startnode, char *path, int match) { struct devnode *nextnode; struct devpath *nextpath; while (startnode) { nextnode = startnode->next; nextpath = startnode->devpath; while (nextpath) { if (match) { if (strstr(nextpath->path, path)) return startnode; } else { if (!strcmp(nextpath->path, path)) return startnode; } nextpath = nextpath->next; } startnode = nextnode; } return 0; } static struct devnode *find_dev_by_majmin(struct devnode *startnode, int maj, int min) { struct devnode *nextnode; while (startnode) { nextnode = startnode->next; if ((startnode->maj == maj) && (startnode->min == min)) return startnode; startnode = nextnode; } return 0; } /** free the cache.. this one is easy ;) **/ /* free all the path associated to one node */ static void flush_dev_list(struct devpath *startpath) { struct devpath *nextpath; while (startpath) { nextpath = startpath->next; free(startpath); startpath = nextpath; } return; } /* free all nodes associated with one devlist */ static void flush_dev_cache(struct devlisthead *devlisthead) { struct devnode *nextnode, *startnode = devlisthead->devnode; while (startnode) { nextnode = startnode->next; flush_dev_list(startnode->devpath); free(startnode); startnode = nextnode; } return; } /** list object allocation helpers **/ /* our only certain keys in the list are maj and min * this function append a devnode obj to devlisthead * and set maj and min */ static struct devnode *alloc_list_obj(struct devlisthead *devlisthead, int maj, int min) { struct devnode *nextnode; nextnode = malloc(sizeof(struct devnode)); if (!nextnode) return 0; memset(nextnode, 0, sizeof(struct devnode)); if (!devlisthead->devnode) devlisthead->devnode = nextnode; else devlisthead->tail->next = nextnode; devlisthead->tail = nextnode; nextnode->maj = maj; nextnode->min = min; return nextnode; } /* really annoying but we have no way to know upfront how * many paths are linked to a certain maj/min combo. * Once we find a device, we know maj/min and this new path. * add_path_obj will add the given path to the devnode */ static int add_path_obj(struct devnode *startnode, const char *path) { struct devpath *nextpath, *startpath; nextpath = malloc(sizeof(struct devpath)); if (!nextpath) return 0; memset(nextpath, 0, sizeof(struct devpath)); if (!startnode->devpath) { startnode->devpath = startpath = nextpath; } else { startpath = startnode->devpath; while (startpath->next) startpath = startpath->next; /* always append what we find */ startpath->next = nextpath; startpath = nextpath; } strncpy(startpath->path, path, MAXPATHLEN - 1); return 1; } /* lsdev needs to add blocks in 2 conditions: if we have a real block device * or if have a symlink to a block device. * this function simply avoid duplicate code around. */ static int add_lsdev_block(struct devlisthead *devlisthead, struct stat *sb, const char *path) { int maj, min; struct devnode *startnode; maj = major(sb->st_rdev); min = minor(sb->st_rdev); startnode = find_dev_by_majmin(devlisthead->devnode, maj, min); if (!startnode) { startnode = alloc_list_obj(devlisthead, maj, min); if (!startnode) return 0; } if (!add_path_obj(startnode, path)) return 0; return 1; } /* check if it is a device or a symlink to a device */ static int dev_is_block(struct stat *sb, char *path) { if (S_ISBLK(sb->st_mode)) return 1; if (S_ISLNK(sb->st_mode)) if (!stat(path, sb)) if (S_ISBLK(sb->st_mode)) return 1; return 0; } /* lsdev does nothing more than ls -lR /dev * dives into dirs (skips hidden directories) * add block devices * parse symlinks * * ret: * 1 on success * -1 for generic errors * -2 -ENOMEM */ static int lsdev(struct devlisthead *devlisthead, const char *path) { int i, n, err = 0; struct dirent **namelist; struct stat sb; char newpath[MAXPATHLEN]; i = scandir(path, &namelist, 0, alphasort); if (i < 0) return -1; for (n = 0; n < i; n++) { if (namelist[n]->d_name[0] != '.') { snprintf(newpath, sizeof(newpath), "%s/%s", path, namelist[n]->d_name); if (!lstat(newpath, &sb)) { if (S_ISDIR(sb.st_mode)) err = lsdev(devlisthead, newpath); if (err < 0) return err; if (dev_is_block(&sb, newpath)) if (!add_lsdev_block (devlisthead, &sb, newpath)) return -2; } } free(namelist[n]); } free(namelist); return 1; } /* * scan /proc/partitions and adds info into the list. * It's able to add nodes if those are not found in sysfs. * * ret: * 0 if we can't scan * -2 -ENOMEM * 1 if everything is ok */ static int scanprocpart(struct devlisthead *devlisthead) { char line[4096]; FILE *fp; int minor, major; unsigned long long blkcnt; char device[128]; struct devnode *startnode; fp = fopen("/proc/partitions", "r"); if (!fp) return 0; while (fgets(line, sizeof(line), fp) != NULL) { if (strlen(line) > 128 + (22)) continue; if (sscanf(line, "%4d %4d %10llu %s", &major, &minor, &blkcnt, device) < 4) continue; /* careful here.. if there is no device, we are scanning the * first two lines that are not useful to us */ if (!strlen(device)) continue; startnode = find_dev_by_majmin(devlisthead->devnode, major, minor); if (!startnode) { startnode = alloc_list_obj(devlisthead, major, minor); if (!startnode) { fclose(fp); return -2; } } startnode->procpart = 1; strncpy(startnode->procname, device, sizeof(startnode->procname) - 1); } fclose(fp); return 1; } /* scan /proc/mdstat and adds info to the list. At this point * all the devices _must_ be already in the list. We don't add anymore * since raids can only be assembled out of existing devices * * ret: * 1 if we could scan * 0 otherwise */ static int scanmdstat(struct devlisthead *devlisthead) { char line[4096]; FILE *fp; char device[16]; char separator[4]; char status[16]; char personality[16]; char firstdevice[16]; char devices[4096]; char *tmp, *next; struct devnode *startnode = NULL; fp = fopen("/proc/mdstat", "r"); if (!fp) return 0; while (fgets(line, sizeof(line), fp) != NULL) { /* i like things to be absolutely clean */ memset(device, 0, sizeof(device)); memset(separator, 0, sizeof(separator)); memset(status, 0, sizeof(status)); memset(personality, 0, sizeof(personality)); memset(firstdevice, 0, sizeof(firstdevice)); memset(devices, 0, sizeof(devices)); if (strlen(line) >= sizeof(line)) continue; /* we only parse stuff that starts with ^md * that's supposed to point to raid */ if (!(line[0] == 'm' && line[1] == 'd')) continue; if (sscanf(line, "%s %s %s %s %s", device, separator, status, personality, firstdevice) < 5) continue; /* scan only raids that are active */ if (strcmp(status, "active")) continue; /* try to find *mdX and set the device as real raid. * if we don't find the device we don't try to set the slaves */ startnode = find_dev_by_path(devlisthead->devnode, device, 1); if (!startnode) continue; startnode->md = 1; /* trunkate the string from sdaX[Y] to sdaX and * copy the whole device string over */ tmp = strstr(firstdevice, "["); if (!tmp) continue; memset(tmp, 0, 1); tmp = strstr(line, firstdevice); if (!tmp) continue; strncpy(devices, tmp, sizeof(devices) - 1); /* if we don't find any slave (for whatever reason) * keep going */ if (!strlen(devices)) continue; tmp = devices; while ((tmp) && ((next = strstr(tmp, " ")) || strlen(tmp))) { char *tmp2; tmp2 = strstr(tmp, "["); if (tmp2) memset(tmp2, 0, 1); startnode = find_dev_by_path(devlisthead->devnode, tmp, 1); if (startnode) startnode->md = 2; tmp = next; if (tmp) tmp++; } } fclose(fp); return 1; } /* scanmapper parses /proc/devices to identify what maj are associated * with device-mapper * * ret: * can't fail for now */ static int scanmapper(struct devlisthead *devlisthead) { struct devnode *startnode; FILE *fp; char line[4096]; char major[4]; char device[64]; int maj, start = 0; fp = fopen("/proc/devices", "r"); if (!fp) return 0; while (fgets(line, sizeof(line), fp) != NULL) { memset(major, 0, sizeof(major)); memset(device, 0, sizeof(device)); if (strlen(line) > sizeof(line)) continue; if (!strncmp(line, "Block devices:", 13)) { start = 1; continue; } if (!start) continue; if (sscanf(line, "%s %s", major, device) < 2) continue; if (!strncmp(device, "device-mapper", 13)) { maj = atoi(major); startnode = devlisthead->devnode; while (startnode) { if (startnode->maj == maj) startnode->mapper = 1; startnode = startnode->next; } } } fclose(fp); return 1; } /* scanpower parses /proc/devices to identify what maj are associated * with powerpath devices */ static int scanpower(struct devlisthead *devlisthead) { struct devnode *startnode; FILE *fp; char line[4096]; char major[4]; char device[64]; int maj, start = 0; int found = 0; fp = fopen("/proc/devices", "r"); if (!fp) return 0; while (fgets(line, sizeof(line), fp) != NULL) { memset(major, 0, 4); memset(device, 0, 64); if (strlen(line) > 4096) continue; if (!strncmp(line, "Block devices:", 13)) { start = 1; continue; } if (!start) continue; sscanf(line, "%s %s", major, device); if (!strncmp(device, "power", 5)) { found = 1; maj = atoi(major); startnode = devlisthead->devnode; while (startnode) { if (startnode->maj == maj) startnode->power = 1; startnode = startnode->next; } } } fclose(fp); return found; } /* scan through the list and execute the custom filter for each entry */ static void run_filter(struct devlisthead *devlisthead, devfilter filter, void *filter_args) { struct devnode *startnode = devlisthead->devnode; while (startnode) { filter(startnode, filter_args); startnode = startnode->next; } return; } /** sysfs helper functions **/ /* /sys/block/sda/dev or /sys/block/sda1/dev exists * the device is real and dev contains maj/min info. * * ret: * 1 on success and set maj/min * 0 if no file is found * -1 if we could not open the file */ static int sysfs_is_dev(char *path, int *maj, int *min) { char newpath[MAXPATHLEN]; struct stat sb; FILE *f; snprintf(newpath, sizeof(newpath), "%s/dev", path); if (!lstat(newpath, &sb)) { f = fopen(newpath, "r"); if (f) { int err; err = fscanf(f, "%d:%d", maj, min); fclose(f); if ((err == EOF) || (err != 2)) return -1; return 1; } else return -1; } return 0; } /* /sys/block/sda/removable tells us if a device can be ejected * from the system or not. This is useful for USB pendrive that are * both removable and disks. * * ret: * 1 if is removable * 0 if not * -1 if we couldn't find the file. */ static int sysfs_is_removable(char *path) { char newpath[MAXPATHLEN]; struct stat sb; int i = -1; FILE *f; snprintf(newpath, sizeof(newpath), "%s/removable", path); if (!lstat(newpath, &sb)) { f = fopen(newpath, "r"); if (f) { int err; err = fscanf(f, "%d\n", &i); fclose(f); if ((err == EOF) || (err != 1)) i = -1; } } return i; } /* we use this function to scan /sys/block/sda{,1}/{holders,slaves} * to know in what position of the foodchain this device is. * NOTE: a device can have both holders and slaves at the same time! * (for example an lvm volume on top of a raid device made of N real disks * * ret: * always return the amount of entries in the dir if successful * or any return value from scandir. */ static int sysfs_has_subdirs_entries(char *path, const char *subdir) { char newpath[MAXPATHLEN]; struct dirent **namelist; struct stat sb; int n, i, count = 0; snprintf(newpath, sizeof(newpath), "%s/%s", path, subdir); if (!lstat(newpath, &sb)) { if (S_ISDIR(sb.st_mode)) { i = scandir(newpath, &namelist, 0, alphasort); if (i < 0) return i; for (n = 0; n < i; n++) { if (namelist[n]->d_name[0] != '.') count++; free(namelist[n]); } free(namelist); } } return count; } /* this is the best approach so far to make sure a block device * is a disk and distinguish it from a cdrom or tape or etc. * What we know for sure is that a type 0 is a disk. * From an old piece code 0xe is an IDE disk and comes from media. * NOTE: we scan also for ../ that while it seems stupid, it will * allow to easily mark partitions as real disks. * (see for example /sys/block/sda/device/type and * /sys/block/sda1/../device/type) * TODO: there might be more cases to evaluate. * * ret: * -2 we were not able to open the file * -1 no path found * 0 we found the path but we have 0 clue on what it is * 1 is a disk */ static int sysfs_is_disk(char *path) { char newpath[MAXPATHLEN]; struct stat sb; int i = -1; FILE *f; snprintf(newpath, sizeof(newpath), "%s/device/type", path); if (!lstat(newpath, &sb)) goto found; snprintf(newpath, sizeof(newpath), "%s/../device/type", path); if (!lstat(newpath, &sb)) goto found; snprintf(newpath, sizeof(newpath), "%s/device/media", path); if (!lstat(newpath, &sb)) goto found; snprintf(newpath, sizeof(newpath), "%s/../device/media", path); if (!lstat(newpath, &sb)) goto found; snprintf(newpath, sizeof(newpath), "%s/device/devtype", path); if (!lstat(newpath, &sb)) return 1; snprintf(newpath, sizeof(newpath), "%s/../device/devtype", path); if (!lstat(newpath, &sb)) return 1; return -1; found: f = fopen(newpath, "r"); if (f) { int err; err = fscanf(f, "%d\n", &i); fclose(f); if ((err == EOF) || (err != 1)) return 0; switch (i) { case 0x0: /* scsi type_disk */ case 0xe: /* found on ide disks from old kernels.. */ i = 1; break; default: i = 0; /* by default we have no clue */ break; } } else i = -2; return i; } /* recursive function that will scan and dive into /sys/block * looking for devices and scanning for attributes. * * ret: * 1 on success * -1 on generic error * -2 -ENOMEM */ static int scansysfs(struct devlisthead *devlisthead, const char *path, int level, int parent_holder) { struct devnode *startnode; int i, n, maj = -1, min = -1, has_holder; struct dirent **namelist; struct stat sb; char newpath[MAXPATHLEN]; i = scandir(path, &namelist, 0, alphasort); if (i < 0) return -1; for (n = 0; n < i; n++) { if (namelist[n]->d_name[0] != '.') { snprintf(newpath, sizeof(newpath), "%s/%s", path, namelist[n]->d_name); /* newer version of sysfs has symlinks follow them */ if (!lstat(newpath, &sb) && level) if (S_ISLNK(sb.st_mode)) if (!stat(newpath, &sb)) if (S_ISBLK(sb.st_mode)) continue; has_holder = parent_holder; if (sysfs_is_dev(newpath, &maj, &min) > 0) { startnode = alloc_list_obj(devlisthead, maj, min); if (!startnode) return -2; startnode->sysfsattrs.sysfs = 1; startnode->sysfsattrs.removable = sysfs_is_removable(newpath); if (!parent_holder) has_holder = sysfs_has_subdirs_entries(newpath, "holders"); startnode->sysfsattrs.holders = has_holder; startnode->sysfsattrs.slaves = sysfs_has_subdirs_entries(newpath, "slaves"); startnode->sysfsattrs.disk = sysfs_is_disk(newpath); } if (!stat(newpath, &sb) && !level) { if (S_ISDIR(sb.st_mode)) if (scansysfs(devlisthead, newpath, 1, has_holder) < 0) return -1; } else if (!lstat(newpath, &sb)) { if (S_ISDIR(sb.st_mode)) if (scansysfs(devlisthead, newpath, 1, has_holder) < 0) return -1; } } free(namelist[n]); } free(namelist); return 1; } /* * devlisthead can be null if you are at init time. pass the old one if you are * updating or scanning.. * * timeout is used only at init time to set the cache timeout value if default * value is not good enough. We might extend its meaning at somepoint. * Anything <= 0 means that the cache does not expire. */ struct devlisthead *scan_for_dev(struct devlisthead *devlisthead, time_t timeout, devfilter filter, void *filter_args) { int res; time_t current; time(¤t); if (devlisthead) { if ((current - devlisthead->cache_timestamp) < devlisthead->cache_timeout) { return devlisthead; } } else { devlisthead = malloc(sizeof(struct devlisthead)); if (!devlisthead) return NULL; memset(devlisthead, 0, sizeof(struct devlisthead)); if (timeout) devlisthead->cache_timeout = timeout; else devlisthead->cache_timeout = DEVCACHETIMEOUT; } flush_dev_cache(devlisthead); devlisthead->cache_timestamp = current; /* it's important we check those 3 errors and abort in case * as it means that we are running out of mem, */ devlisthead->sysfs = res = scansysfs(devlisthead, SYSBLOCKPATH, 0, 0); if (res < -1) goto emergencyout; devlisthead->procpart = res = scanprocpart(devlisthead); if (res < -1) goto emergencyout; devlisthead->lsdev = res = lsdev(devlisthead, DEVPATH); if (res < -1) goto emergencyout; /* from now on we don't alloc mem ourselves but only add info */ devlisthead->mdstat = scanmdstat(devlisthead); devlisthead->mapper = scanmapper(devlisthead); devlisthead->power = scanpower(devlisthead); if (filter) run_filter(devlisthead, filter, filter_args); return devlisthead; emergencyout: free_dev_list(devlisthead); return 0; } /* free everything we used so far */ void free_dev_list(struct devlisthead *devlisthead) { if (devlisthead) { flush_dev_cache(devlisthead); free(devlisthead); } return; } #ifdef DEBUG_EXE #include "ocfs2-kernel/kernel-list.h" #include struct sd_devices { struct list_head sd_list; int sd_maj; int sd_min; char *sd_path; }; struct scan_context { struct list_head devlist; int rescan; }; static void add_to_list(struct list_head *device_list, struct devnode *node) { struct devpath *path; struct sd_devices *sd; int add = 0; path = node->devpath; while (path) { if (node->mapper) add = !strncmp(path->path, "/dev/mapper/", 12); else add = !strncmp(path->path, "/dev/sd", 7); if (add) { sd = malloc(sizeof(struct sd_devices)); if (sd) { sd->sd_maj = node->maj; sd->sd_min = node->min; sd->sd_path = strdup(path->path); list_add_tail(&sd->sd_list, device_list); break; } } path = path->next; } } static void filter_devices(struct devnode *node, void *user_data) { struct scan_context *ctxt = user_data; /* No information in sysfs? Ignore it! */ if (!node->sysfsattrs.sysfs) return; /* Not a disk? Ignore it! */ if (!node->sysfsattrs.disk) return; /* It's part of some other device? Ignore it! */ if (node->sysfsattrs.holders) return; /* * No path in /dev? Well, udev probably hasn't gotten there. Trigger * a rescan */ if (!node->devpath) { ctxt->rescan = 1; return; } add_to_list(&ctxt->devlist, node); } int main(int argc, char **argv) { struct devlisthead *dev = NULL; int delay = 1; struct scan_context scan_ctxt, *ctxt = &scan_ctxt; struct list_head *pos, *pos1; struct sd_devices *sd; INIT_LIST_HEAD(&ctxt->devlist); do { ctxt->rescan = 0; if (delay > 5) break; if (dev) { list_for_each_safe(pos, pos1, &ctxt->devlist) { sd = list_entry(pos, struct sd_devices, sd_list); list_del(pos); free(sd); } free_dev_list(dev); sleep(delay); delay += 2; } dev = scan_for_dev(NULL, 5, filter_devices, ctxt); if (!dev) { printf("error\n"); return -1; } } while (ctxt->rescan); list_for_each(pos, &ctxt->devlist) { sd = list_entry(pos, struct sd_devices, sd_list); printf("%d %d %s\n", sd->sd_maj, sd->sd_min, sd->sd_path); } free_dev_list(dev); return 0; } #endif ocfs2-tools-ocfs2-tools-1.8.6/libtools-internal/utils.c000066400000000000000000000035751347147137200230400ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * utils.c * * Utility functions * * Copyright (C) 2010 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License version 2 as published by the Free Software Foundation. * * 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. */ #define _LARGEFILE64_SOURCE #include #include #include #include #include #include #include #include #include "tools-internal/utils.h" #include "libtools-internal.h" /* * Public API */ char *tools_strchomp(char *str) { int len = strlen(str); char *p; if (!len) return str; p = str + len - 1;; while (isspace(*p) && len--) *p-- = '\0'; return str; } char *tools_strchug(char *str) { int len = strlen(str); char *p = str; if (!len) return str; while (isspace(*p) && len--) p++; if (len) memmove(str, p, len); str[len] = '\0'; return str; } #ifdef DEBUG_EXE typedef char * (*test_func)(char *str); static void do_test(char **s, test_func f) { int i; char tmp[100]; for (i = 0; s[i]; ++i) { strncpy(tmp, s[i], sizeof(tmp)); printf("before:>%s< ", tmp); *(f)(tmp); printf("after:>%s<\n", tmp); } } int main(int argc, char *argv[]) { char *m[] = { "xxx", "xxx \t", "xxx\n", "xx x\n ", NULL }; char *u[] = { "xxx", " \txxx", "\nxxx", " \nx xx", NULL }; setbuf(stdout, NULL); setbuf(stderr, NULL); printf("Testing tools_strchomp():\n"); do_test(m, &tools_strchomp); printf("\nTesting tools_strchug():\n"); do_test(u, &tools_strchug); return 0; } #endif ocfs2-tools-ocfs2-tools-1.8.6/libtools-internal/verbose.c000066400000000000000000000104351347147137200233360ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * verbose.c * * Internal routines for verbose output. * * Copyright (C) 2008 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License version 2 as published by the Free Software Foundation. * * 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. */ #define _LARGEFILE64_SOURCE #define _GNU_SOURCE /* for getopt_long and O_DIRECT */ #include #include #include #include #include #include #include #include "tools-internal/verbose.h" #include "tools-internal/progress.h" #include "libtools-internal.h" static char progname[PATH_MAX] = "(Unknown)"; static int verbosity = 1; static int interactive = 0; static int interactive_answer = 0; void tools_setup_argv0(const char *argv0) { char *pname; char pathtmp[PATH_MAX]; /* This shouldn't care which basename(3) we get */ snprintf(pathtmp, PATH_MAX, "%s", argv0); pname = basename(pathtmp); snprintf(progname, PATH_MAX, "%s", pname); } /* If all verbosity is turned off, make sure com_err() prints nothing. */ static void quiet_com_err(const char *prog, long errcode, const char *fmt, va_list args) { return; } void tools_verbose(void) { verbosity++; if (verbosity == 1) reset_com_err_hook(); } void tools_quiet(void) { if (verbosity == 1) set_com_err_hook(quiet_com_err); verbosity--; } int tools_verbosity(void) { return verbosity; } static void vfverbosef(FILE *f, int level, const char *fmt, va_list args) { if (level <= verbosity) { tools_progress_clear(); vfprintf(f, fmt, args); tools_progress_restore(); } } static void fverbosef(FILE *f, int level, const char *fmt, ...) __attribute__ ((format (printf, 3, 4))); static void fverbosef(FILE *f, int level, const char *fmt, ...) { va_list args; va_start(args, fmt); vfverbosef(f, level, fmt, args); va_end(args); } void verbosef(enum tools_verbosity_level level, const char *fmt, ...) { va_list args; FILE *f = stderr; if (level & VL_FLAG_STDOUT) { f = stdout; level &= ~VL_FLAG_STDOUT; } va_start(args, fmt); vfverbosef(f, level, fmt, args); va_end(args); } void errorf(const char *fmt, ...) { va_list args; va_start(args, fmt); fverbosef(stderr, VL_ERR, "%s: ", progname); vfverbosef(stderr, VL_ERR, fmt, args); va_end(args); } void tcom_err(errcode_t code, const char *fmt, ...) { va_list args; tools_progress_clear(); va_start(args, fmt); com_err_va(progname, code, fmt, args); va_end(args); tools_progress_restore(); } static int vtools_interact(enum tools_verbosity_level level, const char *fmt, va_list args) { char *s, buffer[NAME_MAX]; int progress_enabled = tools_progress_enabled(); if (progress_enabled) { tools_progress_clear(); tools_progress_disable(); } vfverbosef(stderr, level, fmt, args); if (interactive_answer) { fverbosef(stderr, level, "%c\n", interactive_answer); sprintf(buffer, "%c", interactive_answer); s = buffer; } else s = fgets(buffer, sizeof(buffer), stdin); if (progress_enabled) { tools_progress_enable(); tools_progress_restore(); } if (s && *s) { *s = tolower(*s); if (*s == 'y') return 1; } return 0; } void tools_interactive(void) { interactive = 1; } void tools_interactive_yes(void) { interactive_answer = 'y'; } void tools_interactive_no(void) { interactive_answer = 'n'; } int tools_is_interactive(void) { return interactive; } /* Pass this a question without a newline. */ int tools_interact(const char *fmt, ...) { int rc; va_list args; if (!interactive) return 1; va_start(args, fmt); rc = vtools_interact(VL_ERR, fmt, args); va_end(args); return rc; } /* Only for "DON'T DO THIS WITHOUT REALLY CHECKING!" stuff */ int tools_interact_critical(const char *fmt, ...) { int rc; va_list args; va_start(args, fmt); rc = vtools_interact(VL_CRIT, fmt, args); va_end(args); return rc; } void tools_version(void) { verbosef(VL_ERR, "%s %s\n", progname, VERSION); } const char *tools_progname(void) { return progname; } ocfs2-tools-ocfs2-tools-1.8.6/listuuid/000077500000000000000000000000001347147137200177235ustar00rootroot00000000000000ocfs2-tools-ocfs2-tools-1.8.6/listuuid/.gitignore000066400000000000000000000000231347147137200217060ustar00rootroot00000000000000*.sw? listuuid *.d ocfs2-tools-ocfs2-tools-1.8.6/listuuid/Makefile000066400000000000000000000013521347147137200213640ustar00rootroot00000000000000TOPDIR = .. include $(TOPDIR)/Preamble.make INCLUDES = -I$(TOPDIR)/include LIBOCFS2_LIBS = -L$(TOPDIR)/libocfs2 -locfs2 LIBOCFS2_DEPS = $(TOPDIR)/libocfs2/libocfs2.a LIBO2DLM_LIBS = -L$(TOPDIR)/libo2dlm -lo2dlm $(DL_LIBS) LIBO2DLM_DEPS = $(TOPDIR)/libo2dlm/libo2dlm.a ifneq ($(BUILD_FSDLM_SUPPORT),) LIBO2CB_LIBS = -L$(TOPDIR)/libo2cb -lo2cb -ldlm_lt else LIBO2CB_LIBS = -L$(TOPDIR)/libo2cb -lo2cb endif LIBO2CB_DEPS = $(TOPDIR)/libo2cb/libo2cb.a UNINST_PROGRAMS = listuuid CFILES = listuuid.c OBJS = $(subst .c,.o,$(CFILES)) DIST_FILES = $(CFILES) listuuid: $(OBJS) $(LIBOCFS2_DEPS) $(LIBO2DLM_DEPS) $(LIBO2CB_DEPS) $(LINK) $(LIBOCFS2_LIBS) $(LIBO2DLM_LIBS) $(COM_ERR_LIBS) $(UUID_LIBS) $(AIO_LIBS) include $(TOPDIR)/Postamble.make ocfs2-tools-ocfs2-tools-1.8.6/listuuid/listuuid.c000066400000000000000000000121311347147137200217270ustar00rootroot00000000000000/* * listuuid.c * * Lists UUIDs of all the devices * * Copyright (C) 2004 Oracle. All rights reserved. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * * Authors: Sunil Mushran */ #define _LARGEFILE64_SOURCE #define _GNU_SOURCE /* Because libc really doesn't want us using O_DIRECT? */ #include #include #include #include #include #include #include #include #include #include "ocfs2/ocfs2.h" #include "ocfs2-kernel/ocfs1_fs_compat.h" char *device = NULL; char *progname = NULL; int all_devices = 0; char *usage_string = "usage: %s [-a] [device]\n"; /* * ocfs2_partition_list() * */ static errcode_t ocfs2_partition_list (struct list_head *dev_list) { errcode_t ret = 0; FILE *proc; char line[256]; char name[256]; char major[256]; char minor[256]; ocfs2_devices *dev; proc = fopen ("/proc/partitions", "r"); if (proc == NULL) { ret = OCFS2_ET_IO; goto bail; } while (fgets (line, sizeof(line), proc) != NULL) { *major = *minor = *name = '\0'; if (sscanf(line, "%*[ ]%[0-9]%*[ ]%[0-9] %*d %99[^ \t\n]", major, minor, name) != 3) continue; ret = ocfs2_malloc0(sizeof(ocfs2_devices), &dev); if (ret) goto bail; snprintf(dev->dev_name, sizeof(dev->dev_name), "/dev/%s", name); dev->maj_num = strtoul(major, NULL, 0); dev->min_num = strtoul(minor, NULL, 0); list_add_tail(&(dev->list), dev_list); } bail: if (proc) fclose(proc); return ret; } /* * ocfs2_print_uuids() * */ static void ocfs2_print_uuids(struct list_head *dev_list) { ocfs2_devices *dev; struct list_head *pos; char uuid[40]; char devstr[10]; printf("%-20s %7s %-5s %-36s %-s\n", "Device", "maj,min", "FS", "UUID", "Label"); list_for_each(pos, dev_list) { dev = list_entry(pos, ocfs2_devices, list); if (dev->fs_type == 0) continue; uuid_unparse(dev->uuid, uuid); sprintf(devstr, "%3d,%-d", dev->maj_num, dev->min_num); printf("%-20s %-7s %-5s %-36s %s\n", dev->dev_name, devstr, (dev->fs_type == 2 ? "ocfs2" : "ocfs"), uuid, dev->label); } return ; } /* * ocfs2_detect() * */ static errcode_t ocfs2_detect(char *device) { errcode_t ret = 0; struct list_head dev_list; struct list_head *pos1, *pos2; ocfs2_devices *dev; ocfs2_filesys *fs = NULL; char *dev_name; INIT_LIST_HEAD(&(dev_list)); if (device) { ret = ocfs2_malloc0(sizeof(ocfs2_devices), &dev); if (ret) goto bail; strncpy(dev->dev_name, device, sizeof(dev->dev_name)); list_add(&(dev->list), &dev_list); } else { ret = ocfs2_partition_list(&dev_list); if (ret) { com_err(progname, ret, "while reading /proc/partitions"); goto bail; } } list_for_each(pos1, &dev_list) { dev = list_entry(pos1, ocfs2_devices, list); dev_name = dev->dev_name; /* open fs */ fs = NULL; ret = ocfs2_open(dev_name, OCFS2_FLAG_RO, 0, 0, &fs); if (ret) { if (ret == OCFS2_ET_OCFS_REV) dev->fs_type = 1; else { ret = 0; continue; } } else dev->fs_type = 2; /* get uuid for ocfs2 */ if (dev->fs_type == 2) { memcpy(dev->label, OCFS2_RAW_SB(fs->fs_super)->s_label, sizeof(dev->label)); memcpy(dev->uuid, OCFS2_RAW_SB(fs->fs_super)->s_uuid, sizeof(dev->uuid)); } else { if (ocfs2_get_ocfs1_label(dev->dev_name, dev->label, sizeof(dev->label), dev->uuid, sizeof(dev->uuid))) { dev->label[0] = '\0'; memset(dev->uuid, 0, sizeof(dev->uuid)); } } /* close fs */ if (fs) ocfs2_close(fs); } ocfs2_print_uuids(&dev_list); bail: list_for_each_safe(pos1, pos2, &(dev_list)) { dev = list_entry(pos1, ocfs2_devices, list); list_del(&(dev->list)); ocfs2_free(&dev); } return ret; } /* * usage() * */ static void usage(char *progname) { printf(usage_string, progname); return ; } /* * read_options() * */ static int read_options(int argc, char **argv) { int ret = 0; int c; progname = basename(argv[0]); if (argc < 2) { usage(progname); ret = 1; goto bail; } while(1) { c = getopt(argc, argv, "a"); if (c == -1) break; switch (c) { case 'a': /* all devices */ all_devices = 1; break; default: break; } } if (!ret && optind < argc && argv[optind]) device = argv[optind]; bail: return ret; } /* * main() * */ int main(int argc, char **argv) { errcode_t ret = 0; initialize_ocfs_error_table(); ret = read_options (argc, argv); if (ret) goto bail; ret = ocfs2_detect(device); bail: return ret; } ocfs2-tools-ocfs2-tools-1.8.6/mbvendor.m4000066400000000000000000000045261347147137200201460ustar00rootroot00000000000000# MB_VENDOR([VARIABLE]) # --------------------- AC_DEFUN([MB_VENDOR], [AC_MSG_CHECKING([for vendor]) AC_ARG_WITH(vendor, [ --with-vendor=VENDOR Vendor to tailor build defaults and packages to [common]],[ mb_vendor="$withval" if test -x "vendor/${mb_vendor}/vendor.guess"; then if "vendor/${mb_vendor}/vendor.guess" >&AS_MESSAGE_LOG_FD 2>&AS_MESSAGE_LOG_FD; then AC_MSG_RESULT([$mb_vendor]) else AC_MSG_RESULT([not found]) AC_MSG_ERROR([Vendor $mb_vendor not detected]) fi else AC_MSG_RESULT([not supported]) AC_MSG_ERROR([Vendor $mb_vendor not supported]) fi ], [ mb_vendor=`./vendor.guess 2>&AS_MESSAGE_LOG_FD` if test -z "$mb_vendor"; then AC_MSG_RESULT([not found]) else AC_MSG_RESULT([$mb_vendor]) fi ]) dnl Use 2.13 safe ifelse() ifelse([$1], [], [], [ $1="$mb_vendor" AC_SUBST($1) ]) ]) # MB_VENDOR # MB_VENDOR_KERNEL([VARIABLE]) # --------------------- AC_DEFUN([MB_VENDOR_KERNEL], [AC_MSG_CHECKING([for vendor kernel]) AC_ARG_WITH(vendorkernel, [ --with-vendorkernel=KERNELVERSION Vendor kernel version to compile against [detected]], [ mb_vendorkernel="$withval" if test -z "$mb_vendor"; then AC_MSG_RESULT([no vendor]) AC_MSG_ERROR([No vendor specified or discovered]) fi if test -x "vendor/${mb_vendor}/kernel.guess"; then mb_vkinclude="`vendor/${mb_vendor}/kernel.guess build ${mb_vendorkernel} 2>&AS_MESSAGE_LOG_FD`" if test -z "$mb_vkinclude"; then AC_MSG_RESULT([not found]) AC_MSG_ERROR([Vendor kernel $mb_vendorkernel not detected]) else AC_MSG_RESULT([$mb_vkinclude]) fi else AC_MSG_RESULT([not supported]) AC_MSG_ERROR([Vendor $mb_vendor does not support kernel detection]) fi ], [ if test -x "vendor/${mb_vendor}/kernel.guess"; then mb_vkinclude="`vendor/${mb_vendor}/kernel.guess build 2>&AS_MESSAGE_LOG_FD`" if test -z "$mb_vkinclude"; then AC_MSG_RESULT([not found]) else AC_MSG_RESULT([$mb_vkinclude]) fi else mb_vkinclude= AC_MSG_RESULT([not supported]) fi ]) dnl Use 2.13 safe ifelse() ifelse([$1], [], [], [ $1="$mb_vkinclude" AC_SUBST($1) ]) ]) # MB_VENDOR_KERNEL ocfs2-tools-ocfs2-tools-1.8.6/mkfs.ocfs2/000077500000000000000000000000001347147137200200345ustar00rootroot00000000000000ocfs2-tools-ocfs2-tools-1.8.6/mkfs.ocfs2/.gitignore000066400000000000000000000001011347147137200220140ustar00rootroot00000000000000*.sw? stamp-md5 mkfs.ocfs2 cscope* mkfs.ocfs2.8 tunefs.ocfs2 *.d ocfs2-tools-ocfs2-tools-1.8.6/mkfs.ocfs2/Cscope.make000066400000000000000000000004051347147137200221060ustar00rootroot00000000000000.PHONY: cscope cscope: rm -f cscope.* echo "-k" >> cscope.files find . -name '*.[ch]' >>cscope.files find ../libocfs2/ -name '*.[ch]' >>cscope.files find ../libo2cb/ -name '*.[ch]' >>cscope.files find ../libo2dlm/ -name '*.[ch]' >>cscope.files cscope -b ocfs2-tools-ocfs2-tools-1.8.6/mkfs.ocfs2/Makefile000066400000000000000000000016341347147137200215000ustar00rootroot00000000000000TOPDIR = .. include $(TOPDIR)/Preamble.make sbindir = $(root_sbindir) SBIN_PROGRAMS = mkfs.ocfs2 LIBOCFS2_LIBS = -L$(TOPDIR)/libocfs2 -locfs2 LIBOCFS2_DEPS = $(TOPDIR)/libocfs2/libocfs2.a LIBO2CB_LIBS = -L$(TOPDIR)/libo2cb -lo2cb LIBO2CB_DEPS = $(TOPDIR)/libo2cb/libo2cb.a ifneq ($(BUILD_CMAP_SUPPORT),) LIBO2CB_LIBS += -lcmap endif ifneq ($(BUILD_FSDLM_SUPPORT),) LIBO2CB_LIBS += -ldlm_lt endif LIBO2DLM_LIBS = -L$(TOPDIR)/libo2dlm -lo2dlm $(DL_LIBS) LIBO2DLM_DEPS = $(TOPDIR)/libo2dlm/libo2dlm.a INCLUDES = -I$(TOPDIR)/include -I. DEFINES = -DVERSION=\"$(VERSION)\" CFILES = mkfs.c check.c HFILES = mkfs.h OBJS = $(subst .c,.o,$(CFILES)) MANS = mkfs.ocfs2.8 DIST_FILES = $(CFILES) $(HFILES) mkfs.ocfs2.8.in mkfs.ocfs2: $(OBJS) $(LIBOCFS2_DEPS) $(LIBO2DLM_DEPS) $(LIBO2CB_DEPS) $(LINK) $(LIBOCFS2_LIBS) $(LIBO2DLM_LIBS) $(LIBO2CB_LIBS) $(COM_ERR_LIBS) $(UUID_LIBS) $(AIO_LIBS) include $(TOPDIR)/Postamble.make ocfs2-tools-ocfs2-tools-1.8.6/mkfs.ocfs2/check.c000066400000000000000000000250011347147137200212530ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * check.c * * OCFS2 format check utility * * Copyright (C) 2005 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License, version 2, as published by the Free Software Foundation. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * */ #include "mkfs.h" #define WHOAMI "mkfs.ocfs2" int is_classic_stack(char *stack_name) { return !strcmp(stack_name, OCFS2_CLASSIC_CLUSTER_STACK); } /* For ocfs2_fill_cluster_information(). Errors are to be ignored */ void cluster_fill(char **stack_name, char **cluster_name, uint8_t *stack_flags) { errcode_t err; struct o2cb_cluster_desc cluster; *stack_name = NULL; *cluster_name = NULL; *stack_flags = 0; err = o2cb_init(); if (err) return; err = o2cb_running_cluster_desc(&cluster); if (err) return; if (cluster.c_stack) { /* * These were allocated by o2cb_running_cluster_desc(), * the caller will free them. */ *stack_name = cluster.c_stack; *cluster_name = cluster.c_cluster; *stack_flags = cluster.c_flags; } } /* For ocfs2_fill_cluster_information(). Errors are to be ignored */ static void disk_fill(const char *device, char **stack_name, char **cluster_name, uint8_t *stack_flags) { errcode_t err; ocfs2_filesys *fs = NULL; struct o2cb_cluster_desc desc; *stack_name = NULL; *cluster_name = NULL; *stack_flags = 0; err = ocfs2_open(device, OCFS2_FLAG_RO, 0, 0, &fs); if (err) return; if (!ocfs2_clusterinfo_valid(OCFS2_RAW_SB(fs->fs_super))) goto close; err = ocfs2_fill_cluster_desc(fs, &desc); if (err) goto close; *stack_name = strdup(desc.c_stack); *cluster_name = strdup(desc.c_cluster); *stack_flags = desc.c_flags; close: ocfs2_close(fs); } static int check_cluster_compatibility(State *s, char *active, char *other, const char *other_desc) { int ret = -1; if (strlen(other) && strlen(active) && strcmp(active, other)) { fprintf(stderr, "%s cluster (%s) does not match the active cluster " "(%s).\n%s will not be able to determine if this " "operation can be done safely.\n", other_desc, other, active, s->progname); if (!s->force) { fprintf(stderr, "To skip this check, use --force or -F\n"); goto out; } fprintf(stdout, "Format is forced.\n"); } ret = 0; out: return ret; } /* * Try to connect to the cluster and look at the disk to fill in default * cluster values. If we can't connect, that's OK for now. The only errors * are when values are missing or conflict with option arguments. * * This function assumes that each set of cluster stack values (stack and * cluster name) are either both set or both unset. As in, if the user * specifies a cluster stack, he must specify the cluster name too. */ int ocfs2_fill_cluster_information(State *s) { char *user_cluster_name, *user_stack_name, user_value[100]; char *o2cb_cluster_name, *o2cb_stack_name, o2cb_value[100]; char *disk_cluster_name, *disk_stack_name, disk_value[100]; uint8_t user_stack_flags, o2cb_stack_flags, disk_stack_flags; int clusterinfo = 0, userspace = 0; int ret = -1; if (s->mount == MOUNT_LOCAL) return 0; *user_value = *o2cb_value = *disk_value = '\0'; /* get currently active cluster stack */ cluster_fill(&o2cb_stack_name, &o2cb_cluster_name, &o2cb_stack_flags); /* get cluster stack configured on disk */ disk_fill(s->device_name, &disk_stack_name, &disk_cluster_name, &disk_stack_flags); /* cluster stack as provided by the user */ user_stack_name = s->cluster_stack; user_cluster_name = s->cluster_name; user_stack_flags = s->stack_flags; s->cluster_stack = s->cluster_name = NULL; s->stack_flags = 0; /* * If the user specifies global heartbeat, while we can assume o2cb * stack, we still need to find the cluster name. */ if (s->global_heartbeat && !user_stack_name) { if (!o2cb_stack_name) { com_err(s->progname, 0, "Global heartbeat cannot be " "enabled without either starting the o2cb " "cluster stack or providing the cluster stack " "info."); goto out; } if (strcmp(o2cb_stack_name, OCFS2_CLASSIC_CLUSTER_STACK)) { com_err(s->progname, 0, "Global heartbeat is " "incompatible with the active cluster stack " "\"%s\".\n", o2cb_stack_name); goto out; } user_stack_name = strdup(o2cb_stack_name); user_cluster_name = strdup(o2cb_cluster_name); user_stack_flags |= OCFS2_CLUSTER_O2CB_GLOBAL_HEARTBEAT; } /* User specifically asked for clusterinfo */ if (s->feature_flags.opt_incompat & OCFS2_FEATURE_INCOMPAT_CLUSTERINFO) clusterinfo++; /* User specifically asked for usersapce stack */ if (s->feature_flags.opt_incompat & OCFS2_FEATURE_INCOMPAT_USERSPACE_STACK) userspace++; /* merge - easier to compare */ if (user_stack_name && user_cluster_name) { snprintf(user_value, sizeof(user_value), "%s,%s,%d", user_stack_name, user_cluster_name, user_stack_flags); } if (o2cb_stack_name && o2cb_cluster_name) { snprintf(o2cb_value, sizeof(o2cb_value), "%s,%s,%d", o2cb_stack_name, o2cb_cluster_name, o2cb_stack_flags); } if (disk_stack_name && disk_cluster_name) { snprintf(disk_value, sizeof(disk_value), "%s,%s,%d", disk_stack_name, disk_cluster_name, disk_stack_flags); } /* if disk and o2cb are not the same, continue only if force set */ if (check_cluster_compatibility(s, o2cb_value, disk_value, "On disk")) goto out; /* if user and o2cb are not the same, continue only if force set */ if (check_cluster_compatibility(s, o2cb_value, user_value, "User requested")) goto out; if (strlen(user_value)) { s->cluster_stack = strdup(user_stack_name); s->cluster_name = strdup(user_cluster_name); s->stack_flags = user_stack_flags; } else if (strlen(o2cb_value)) { s->cluster_stack = strdup(o2cb_stack_name); s->cluster_name = strdup(o2cb_cluster_name); s->stack_flags = o2cb_stack_flags; } else if (strlen(disk_value)) { s->cluster_stack = strdup(disk_stack_name); s->cluster_name = strdup(disk_cluster_name); s->stack_flags = disk_stack_flags; } else { /* default */ if (clusterinfo || userspace) { fprintf(stderr, "The clusterinfo or userspace stack " "features cannot be enabled. Please rerun with " "the cluster stack details or after starting " "the cluster stack.\n"); goto out; } } /* * If it is the o2cb stack and the user has not specifically asked * for the clusterinfo feature, then go default. */ if (!strlen(user_value) && s->cluster_stack) { if (is_classic_stack(s->cluster_stack) && !clusterinfo && !s->stack_flags) { free(s->cluster_stack); free(s->cluster_name); s->cluster_stack = s->cluster_name = NULL; s->stack_flags = 0; } } if (s->cluster_stack) { fprintf(stdout, "Cluster stack: %s\n" "Cluster name: %s\n" "Stack Flags: 0x%x\n" "NOTE: Feature extended slot map may be enabled\n", s->cluster_stack, s->cluster_name, s->stack_flags); } else fprintf(stdout, "Cluster stack: classic o2cb\n"); ret = 0; out: free(user_cluster_name); free(user_stack_name); free(o2cb_cluster_name); free(o2cb_stack_name); free(disk_cluster_name); free(disk_stack_name); return ret; } int ocfs2_check_volume(State *s) { ocfs2_filesys *fs = NULL; errcode_t ret; int mount_flags; if (s->dry_run) { fprintf(stdout, "Dry run\n"); return 0; } if (ocfs2_fill_cluster_information(s)) return -1; ret = ocfs2_check_if_mounted(s->device_name, &mount_flags); if (ret) { com_err(s->progname, ret, "while determining whether %s is mounted.", s->device_name); return -1; } if (mount_flags & OCFS2_MF_MOUNTED) { fprintf(stderr, "%s is mounted; ", s->device_name); if (s->force) { fputs("overwriting anyway. Hope /etc/mtab is " "incorrect.\n", stderr); return 1; } fputs("will not make a ocfs2 volume here!\n", stderr); return -1; } if (mount_flags & OCFS2_MF_BUSY) { fprintf(stderr, "%s is apparently in use by the system; ", s->device_name); if (s->force) { fputs("format forced anyway.\n", stderr); return 1; } fputs("will not make a ocfs2 volume here!\n", stderr); return -1; } ret = ocfs2_open(s->device_name, OCFS2_FLAG_RW, 0, 0, &fs); if ((ret == OCFS2_ET_UNSUPP_FEATURE) || (ret == OCFS2_ET_RO_UNSUPP_FEATURE)) { com_err(s->progname, ret, "while opening device \"%s\"", s->device_name); if (!s->force) { fprintf(stderr, "As this is an existing OCFS2 volume, it could be mounted on an another node in the cluster.\n" "However, as %s is unable to read the superblock, it cannot detect if the volume is in use or not.\n" "To skip this check, use --force or -F.\n", s->progname); return -1; } else { fprintf(stderr, "WARNING: Cluster check disabled.\n"); return 1; } } else if (ret) { if (ret == OCFS2_ET_OCFS_REV) fprintf(stdout, "Overwriting existing ocfs partition.\n"); return 0; } else fprintf(stdout, "Overwriting existing ocfs2 partition.\n"); if (ocfs2_mount_local(fs)) goto nolock; if (!s->force) { if (s->cluster_stack) { ret = o2cb_setup_stack(s->cluster_stack); if (ret) { com_err(s->progname, ret, "while setting up stack\n"); return -1; } } ret = o2cb_init(); if (ret) { com_err(s->progname, ret, "while initializing the cluster"); return -1; } ret = ocfs2_initialize_dlm(fs, WHOAMI); if (ret) { ocfs2_close(fs); com_err(s->progname, ret, "while initializing the dlm"); fprintf(stderr, "As this is an existing OCFS2 volume, it could be mounted on an another node in the cluster.\n" "However, as %s is unable to initialize the dlm, it cannot detect if the volume is in use or not.\n" "To skip this check, use --force or -F.\n", s->progname); return -1; } ret = ocfs2_lock_down_cluster(fs); if (ret) { ocfs2_shutdown_dlm(fs, WHOAMI); ocfs2_close(fs); com_err(s->progname, ret, "while locking the cluster"); fprintf(stderr, "This volume appears to be in use in the cluster.\n"); return -1; } ocfs2_release_cluster(fs); ocfs2_shutdown_dlm(fs, WHOAMI); } else { fprintf(stderr, "WARNING: Cluster check disabled.\n"); } nolock: ocfs2_close(fs); return 1; } ocfs2-tools-ocfs2-tools-1.8.6/mkfs.ocfs2/mkfs.c000066400000000000000000002323411347147137200211450ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * mkfs.c * * OCFS2 format utility * * Copyright (C) 2004, 2011 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License, version 2, as published by the Free Software Foundation. * * 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. * */ #include "mkfs.h" static State *get_state(int argc, char **argv); static void free_state(State *s); static int get_number(char *arg, uint64_t *res); static void parse_journal_opts(char *progname, const char *opts, uint64_t *journal_size_in_bytes, int *journal64); static void usage(const char *progname); static void version(const char *progname); static void fill_defaults(State *s); static int get_bits(State *s, int num); static uint64_t get_valid_size(uint64_t num, uint64_t lo, uint64_t hi); static void *do_malloc(State *s, size_t size); static void do_pwrite(State *s, const void *buf, size_t count, uint64_t offset); static AllocBitmap *initialize_bitmap(State *s, uint32_t bits, uint32_t unit_bits, const char *name, SystemFileDiskRecord *bm_record); static int find_clear_bits(void *buf, unsigned int size, uint32_t num_bits, uint32_t offset); static int alloc_bytes_from_bitmap(State *s, uint64_t bytes, AllocBitmap *bitmap, uint64_t *start, uint64_t *num); static int alloc_from_bitmap(State *s, uint64_t num_bits, AllocBitmap *bitmap, uint64_t *start, uint64_t *num); static uint64_t alloc_inode(State *s, uint16_t *suballoc_bit); static DirData *alloc_directory(State *s); static void free_directory(DirData *dir); static void add_entry_to_directory(State *s, DirData *dir, char *name, uint64_t byte_off, uint8_t type); static uint32_t blocks_needed(State *s); static uint32_t sys_blocks_needed(uint32_t num_slots); static uint32_t system_dir_blocks_needed(State *s); static void check_32bit_blocks(State *s); static void format_superblock(State *s, SystemFileDiskRecord *rec, SystemFileDiskRecord *root_rec, SystemFileDiskRecord *sys_rec); static void format_file(State *s, SystemFileDiskRecord *rec); static void write_metadata(State *s, SystemFileDiskRecord *rec, void *src); static void write_bitmap_data(State *s, AllocBitmap *bitmap); static void write_directory_data(State *s, DirData *dir); static void write_group_data(State *s, AllocGroup *group); static void format_leading_space(State *s); //static void replacement_journal_create(State *s, uint64_t journal_off); static void open_device(State *s); static void close_device(State *s); static int initial_slots_for_volume(uint64_t size); static void create_generation(State *s); static void init_record(State *s, SystemFileDiskRecord *rec, int type, int mode); static void print_state(State *s); static void clear_both_ends(State *s); static int ocfs2_clusters_per_group(int block_size, int cluster_size_bits); static AllocGroup * initialize_alloc_group(State *s, const char *name, SystemFileDiskRecord *alloc_inode, uint64_t blkno, uint16_t chain, uint16_t cpg, uint16_t bpc); static void free_alloc_group(AllocGroup *group); static void index_system_dirs(State *s, ocfs2_filesys *fs); static void create_lost_found_dir(State *s, ocfs2_filesys *fs); static void format_journals(State *s, ocfs2_filesys *fs); static void format_slotmap(State *s, ocfs2_filesys *fs); static int format_backup_super(State *s, ocfs2_filesys *fs); static void mkfs_compute_meta_ecc(State *s, void *data, struct ocfs2_block_check *bc); extern char *optarg; extern int optind, opterr, optopt; static SystemFileInfo system_files[] = { { "bad_blocks", SFI_OTHER, 1, S_IFREG | 0644 }, { "global_inode_alloc", SFI_CHAIN, 1, S_IFREG | 0644 }, { "slot_map", SFI_OTHER, 1, S_IFREG | 0644 }, { "heartbeat", SFI_HEARTBEAT, 1, S_IFREG | 0644 }, { "global_bitmap", SFI_CLUSTER, 1, S_IFREG | 0644 }, { "aquota.user", SFI_QUOTA, 1, S_IFREG | 0644 }, { "aquota.group", SFI_QUOTA, 1, S_IFREG | 0644 }, { "orphan_dir:%04d", SFI_OTHER, 0, S_IFDIR | 0755 }, { "extent_alloc:%04d", SFI_CHAIN, 0, S_IFREG | 0644 }, { "inode_alloc:%04d", SFI_CHAIN, 0, S_IFREG | 0644 }, { "journal:%04d", SFI_JOURNAL, 0, S_IFREG | 0644 }, { "local_alloc:%04d", SFI_LOCAL_ALLOC, 0, S_IFREG | 0644 }, { "truncate_log:%04d", SFI_TRUNCATE_LOG, 0, S_IFREG | 0644 }, { "aquota.user:%04d", SFI_QUOTA, 0, S_IFREG | 0644 }, { "aquota.group:%04d", SFI_QUOTA, 0, S_IFREG | 0644 }, }; struct fs_type_translation { const char *ft_str; enum ocfs2_mkfs_types ft_type; }; static struct fs_type_translation ocfs2_mkfs_types_table[] = { {"datafiles", OCFS2_MKFSTYPE_DATAFILES}, {"mail", OCFS2_MKFSTYPE_MAIL}, {"vmstore", OCFS2_MKFSTYPE_VMSTORE}, {NULL, OCFS2_MKFSTYPE_DEFAULT}, }; enum { BACKUP_SUPER_OPTION = CHAR_MAX + 1, FEATURE_LEVEL, FEATURES_OPTION, CLUSTER_STACK_OPTION, CLUSTER_NAME_OPTION, GLOBAL_HEARTBEAT_OPTION, }; static uint64_t align_bytes_to_clusters_ceil(State *s, uint64_t bytes) { uint64_t ret = bytes + s->cluster_size - 1; if (ret < bytes) /* deal with wrapping */ ret = UINT64_MAX; ret = ret >> s->cluster_size_bits; ret = ret << s->cluster_size_bits; return ret; } /* * Translate 32 bytes uuid to 36 bytes uuid format. * for example: * 32 bytes uuid: 178BDC83D50241EF94EB474A677D498B * 36 bytes uuid: 178BDC83-D502-41EF-94EB-474A677D498B */ static void translate_uuid(char *uuid_32, char *uuid_36) { int i; char *cp = uuid_32; for (i = 0; i < 36; i++) { if ((i == 8) || (i == 13) || (i == 18) || (i == 23)) { uuid_36[i] = '-'; continue; } uuid_36[i] = *cp++; } } static int is_cluster_info_valid(State *s, char *stack_name, char *cluster_name, int globalhb) { if (!stack_name && !cluster_name && !globalhb) return 1; if (s->mount == MOUNT_LOCAL) { if (stack_name || cluster_name || globalhb) { com_err(s->progname, O2CB_ET_INVALID_STACK_NAME, "; local mount is incompatible with " "the specified cluster attribute"); return 0; } } if (!stack_name || !strlen(stack_name)) { com_err(s->progname, O2CB_ET_INVALID_STACK_NAME, "; missing cluster stack"); return 0; } if (!o2cb_valid_stack_name(stack_name)) { com_err(s->progname, O2CB_ET_INVALID_STACK_NAME, "; unknown cluster stack '%s'", stack_name); return 0; } if (!cluster_name) { com_err(s->progname, O2CB_ET_INVALID_CLUSTER_NAME, "; missing cluster name"); return 0; } if (!strcmp(stack_name, OCFS2_CLASSIC_CLUSTER_STACK)) { if (!o2cb_valid_o2cb_cluster_name(cluster_name)) { com_err(s->progname, O2CB_ET_INVALID_CLUSTER_NAME, "; max %d alpha-numeric characters", OCFS2_CLUSTER_NAME_LEN); return 0; } } else { if (!o2cb_valid_cluster_name(cluster_name)) { com_err(s->progname, O2CB_ET_INVALID_CLUSTER_NAME, "; max %d characters", OCFS2_CLUSTER_NAME_LEN); return 0; } } if (globalhb) { if (strcmp(stack_name, OCFS2_CLASSIC_CLUSTER_STACK)) { com_err(s->progname, O2CB_ET_INVALID_STACK_NAME, "; global heartbeat mode is only applicable to " "the o2cb cluster stack"); return 0; } } return 1; } static void handle_signal (int sig) { switch (sig) { case SIGTERM: case SIGINT: printf("\nProcess Interrupted.\n"); exit(1); } return ; } /* Call this with SIG_BLOCK to block and SIG_UNBLOCK to unblock */ static void block_signals (int how) { sigset_t sigs; sigfillset(&sigs); sigdelset(&sigs, SIGTRAP); sigdelset(&sigs, SIGSEGV); sigprocmask(how, &sigs, (sigset_t *) 0); return ; } /* Is this something to skip for heartbeat-only devices */ static int hb_dev_skip(State *s, int system_inode) { int ret = 0; if (s->hb_dev) { switch (system_inode) { case GLOBAL_BITMAP_SYSTEM_INODE: case GLOBAL_INODE_ALLOC_SYSTEM_INODE: case HEARTBEAT_SYSTEM_INODE: break; default: ret = 1; } } return ret; } static void fill_fake_fs(State *s, ocfs2_filesys *fake_fs, void *buf) { memset(buf, 0, s->blocksize); memset(fake_fs, 0, sizeof(ocfs2_filesys)); fake_fs->fs_super = buf; fake_fs->fs_blocksize = s->blocksize; fake_fs->fs_clustersize = s->cluster_size; OCFS2_RAW_SB(fake_fs->fs_super)->s_feature_incompat = s->feature_flags.opt_incompat; OCFS2_RAW_SB(fake_fs->fs_super)->s_feature_ro_compat = s->feature_flags.opt_ro_compat; OCFS2_RAW_SB(fake_fs->fs_super)->s_feature_compat = s->feature_flags.opt_compat; } static void mkfs_init_dir_trailer(State *s, DirData *dir, void *buf) { char super_buf[OCFS2_MAX_BLOCKSIZE]; ocfs2_filesys fake_fs; struct ocfs2_dir_entry *de; struct ocfs2_dinode fake_di = { .i_blkno = dir->record->fe_off >> s->blocksize_bits, }; uint64_t blkno = dir->record->extent_off; /* Find out how far we are in our directory */ blkno += ((char *)buf) - ((char *)dir->buf); blkno >>= s->blocksize_bits; fill_fake_fs(s, &fake_fs, super_buf); if (ocfs2_supports_dir_trailer(&fake_fs)) { de = buf; de->rec_len = ocfs2_dir_trailer_blk_off(&fake_fs); ocfs2_init_dir_trailer(&fake_fs, &fake_di, blkno, buf); } } /* Should we skip this inode because of features enabled / disabled? */ static int feature_skip(State *s, int system_inode) { switch (system_inode) { case USER_QUOTA_SYSTEM_INODE: case LOCAL_USER_QUOTA_SYSTEM_INODE: return !(s->feature_flags.opt_ro_compat & OCFS2_FEATURE_RO_COMPAT_USRQUOTA); case GROUP_QUOTA_SYSTEM_INODE: case LOCAL_GROUP_QUOTA_SYSTEM_INODE: return !(s->feature_flags.opt_ro_compat & OCFS2_FEATURE_RO_COMPAT_GRPQUOTA); default: return 0; } } static inline uint32_t system_dir_bytes_needed(State *s) { int each = OCFS2_DIR_REC_LEN(SYSTEM_FILE_NAME_MAX); return each * sys_blocks_needed(s->initial_slots); } static void format_quota_files(State *s, ocfs2_filesys *fs) { errcode_t ret; ocfs2_quota_hash *usr_hash = NULL, *grp_hash = NULL; /* Write correct data into quota files */ if (!feature_skip(s, USER_QUOTA_SYSTEM_INODE)) { ret = ocfs2_init_fs_quota_info(fs, USRQUOTA); if (ret) { com_err(s->progname, ret, "while looking up global user quota file"); goto error; } fs->qinfo[USRQUOTA].flags = 0; fs->qinfo[USRQUOTA].qi_info.dqi_syncms = OCFS2_DEF_QUOTA_SYNC; fs->qinfo[USRQUOTA].qi_info.dqi_bgrace = OCFS2_DEF_BLOCK_GRACE; fs->qinfo[USRQUOTA].qi_info.dqi_igrace = OCFS2_DEF_INODE_GRACE; ret = ocfs2_new_quota_hash(&usr_hash); if (ret) { com_err(s->progname, ret, "while creating user quota hash."); goto error; } ret = ocfs2_init_global_quota_file(fs, USRQUOTA); if (ret) { com_err(s->progname, ret, "while creating global user " "quota file"); goto error; } ret = ocfs2_init_local_quota_files(fs, USRQUOTA); if (ret) { com_err(s->progname, ret, "while initializing local user quota files"); goto error; } } if (!feature_skip(s, GROUP_QUOTA_SYSTEM_INODE)) { ret = ocfs2_init_fs_quota_info(fs, GRPQUOTA); if (ret) { com_err(s->progname, ret, "while looking up global group quota file"); goto error; } fs->qinfo[GRPQUOTA].flags = 0; fs->qinfo[GRPQUOTA].qi_info.dqi_syncms = OCFS2_DEF_QUOTA_SYNC; fs->qinfo[GRPQUOTA].qi_info.dqi_bgrace = OCFS2_DEF_BLOCK_GRACE; fs->qinfo[GRPQUOTA].qi_info.dqi_igrace = OCFS2_DEF_INODE_GRACE; ret = ocfs2_new_quota_hash(&grp_hash); if (ret) { com_err(s->progname, ret, "while creating group quota hash."); goto error; } ret = ocfs2_init_global_quota_file(fs, GRPQUOTA); if (ret) { com_err(s->progname, ret, "while creating global group " "quota file"); goto error; } ret = ocfs2_init_local_quota_files(fs, GRPQUOTA); if (ret) { com_err(s->progname, ret, "while initializing local group quota files"); goto error; } } ret = ocfs2_compute_quota_usage(fs, usr_hash, grp_hash); if (ret) { com_err(s->progname, ret, "while computing quota usage"); goto error; } if (usr_hash) { ret = ocfs2_write_release_dquots(fs, USRQUOTA, usr_hash); if (ret) { com_err(s->progname, ret, "while writing user quota usage"); goto error; } ret = ocfs2_free_quota_hash(usr_hash); if (ret) { com_err(s->progname, ret, "while releasing user quota hash"); goto error; } } if (grp_hash) { ret = ocfs2_write_release_dquots(fs, GRPQUOTA, grp_hash); if (ret) { com_err(s->progname, ret, "while writing group quota usage"); goto error; } ret = ocfs2_free_quota_hash(grp_hash); if (ret) { com_err(s->progname, ret, "while releasing group quota hash"); goto error; } } return; error: clear_both_ends(s); exit(1); } static void grow_extent_allocator(State *s, ocfs2_filesys *fs) { errcode_t ret; int i; for (i = 0; i < OCFS2_RAW_SB(fs->fs_super)->s_max_slots; i++) { ret = ocfs2_grow_chain_allocator(fs, EXTENT_ALLOC_SYSTEM_INODE, i, s->extent_alloc_size_in_clusters); if (ret) { com_err(s->progname, ret, "while growing the extent " "allocator for slot %d by %d clusters", i, s->extent_alloc_size_in_clusters); goto error; } } return; error: clear_both_ends(s); exit(1); } static void finish_normal_format(State *s) { errcode_t ret; int num; ocfs2_filesys *fs; /* These routines use libocfs2 to do their work. */ ret = ocfs2_open(s->device_name, OCFS2_FLAG_RW, 0, 0, &fs); if (ret) { com_err(s->progname, ret, "while opening file system for final " "operations."); clear_both_ends(s); exit(1); } /* 8MB should cover an allocator and some other stuff */ ret = io_init_cache_size(fs->fs_io, 8 * 1024 * 1024); if (ret) com_err(s->progname, ret, "while initializing the I/O cache. Continuing " "without a cache (safe, but slower)"); if (!s->no_backup_super) { if (!s->quiet) printf("Writing backup superblock: "); num = format_backup_super(s, fs); if (!s->quiet) printf("%d block(s)\n", num); } if (!s->quiet) printf("Formatting Journals: "); format_journals(s, fs); if (!s->quiet) printf("done\n"); if (!s->quiet) printf("Growing extent allocator: "); grow_extent_allocator(s, fs); if (!s->quiet) printf("done\n"); if (!s->quiet) printf("Formatting slot map: "); format_slotmap(s, fs); if (!s->quiet) printf("done\n"); if (!s->quiet) printf("Formatting quota files: "); format_quota_files(s, fs); if (!s->quiet) printf("done\n"); if (s->dx_dirs && !s->inline_data) { /* * We want to do this after quota, but before adding * any new entries to directories. */ if (!s->quiet) printf("Indexing system directories: "); index_system_dirs(s, fs); if (!s->quiet) printf("done\n"); } if (!s->quiet) printf("Writing lost+found: "); create_lost_found_dir(s, fs); if (!s->quiet) printf("done\n"); ocfs2_close(fs); } static inline int discard_blocks(State *s, uint64_t from, uint64_t count) { uint64_t range[2]; range[0] = from << s->blocksize_bits; range[1] = count << s->blocksize_bits; return ioctl(s->fd, BLKDISCARD, &range); } static int discard_device_blocks(State *s) { uint64_t blocks = s->volume_size_in_blocks; uint64_t count = DISCARD_STEP_MB; uint64_t cur = 0; int retval = 0; count *= (1024 * 1024); count >>= s->blocksize_bits; while (cur < blocks) { if (cur + count > blocks) count = blocks - cur; retval = discard_blocks(s, cur, count); if (retval) { if (!s->quiet && errno != EOPNOTSUPP) com_err(s->progname, 0, "Discard device blocks: %s", strerror(errno)); break; } cur += count; } return retval; } int main(int argc, char **argv) { State *s; SystemFileDiskRecord *record[NUM_SYSTEM_INODES]; SystemFileDiskRecord crap_rec; SystemFileDiskRecord superblock_rec; SystemFileDiskRecord root_dir_rec; SystemFileDiskRecord system_dir_rec; int i, j, num; DirData *orphan_dir[OCFS2_MAX_SLOTS]; DirData *root_dir; DirData *system_dir; uint64_t need; SystemFileDiskRecord *tmprec; char fname[SYSTEM_FILE_NAME_MAX]; setbuf(stdout, NULL); setbuf(stderr, NULL); if (signal(SIGTERM, handle_signal) == SIG_ERR) { fprintf(stderr, "Could not set SIGTERM\n"); exit(1); } if (signal(SIGINT, handle_signal) == SIG_ERR) { fprintf(stderr, "Could not set SIGINT\n"); exit(1); } initialize_ocfs_error_table(); initialize_o2dl_error_table(); initialize_o2cb_error_table(); s = get_state(argc, argv); /* bail if volume already mounted on cluster, etc. */ switch (ocfs2_check_volume(s)) { case -1: free_state(s); return 1; case 1: if (s->prompt) { fprintf(stdout, "Proceed (y/N): "); if (toupper(getchar()) != 'Y') { printf("Aborting operation.\n"); free_state(s); return 1; } } break; case 0: default: break; } open_device(s); fill_defaults(s); create_generation(s); print_state (s); check_32bit_blocks(s); if (s->dry_run) { close_device(s); free_state(s); return 0; } if (s->discard_blocks) discard_device_blocks(s); clear_both_ends(s); init_record(s, &superblock_rec, SFI_OTHER, S_IFREG | 0644); init_record(s, &root_dir_rec, SFI_OTHER, S_IFDIR | 0755); init_record(s, &system_dir_rec, SFI_OTHER, S_IFDIR | 0755); for (i = 0; i < NUM_SYSTEM_INODES; i++) { num = system_files[i].global ? 1 : s->initial_slots; record[i] = do_malloc(s, sizeof(SystemFileDiskRecord) * num); for (j = 0; j < num; j++) { init_record(s, &record[i][j], system_files[i].type, system_files[i].mode); } } root_dir = alloc_directory(s); system_dir = alloc_directory(s); for (i = 0; i < s->initial_slots; ++i) orphan_dir[i] = alloc_directory(s); need = (s->volume_size_in_clusters + 7) >> 3; need = ((need + s->cluster_size - 1) >> s->cluster_size_bits) << s->cluster_size_bits; if (!s->quiet) printf("Creating bitmaps: "); tmprec = &(record[GLOBAL_BITMAP_SYSTEM_INODE][0]); tmprec->extent_off = 0; tmprec->extent_len = need; s->global_bm = initialize_bitmap (s, s->volume_size_in_clusters, s->cluster_size_bits, "global bitmap", tmprec); /* * Now allocate the global inode alloc group */ tmprec = &(record[GLOBAL_INODE_ALLOC_SYSTEM_INODE][0]); need = blocks_needed(s); alloc_bytes_from_bitmap(s, need << s->blocksize_bits, s->global_bm, &(crap_rec.extent_off), &(crap_rec.extent_len)); s->system_group = initialize_alloc_group(s, "system inode group", tmprec, crap_rec.extent_off >> s->blocksize_bits, 0, crap_rec.extent_len >> s->cluster_size_bits, s->cluster_size / s->blocksize); tmprec->group = s->system_group; tmprec->chain_off = tmprec->group->gd->bg_blkno << s->blocksize_bits; fsync(s->fd); if (!s->quiet) printf("done\n"); if (!s->quiet) printf("Initializing superblock: "); superblock_rec.fe_off = (uint64_t)OCFS2_SUPER_BLOCK_BLKNO << s->blocksize_bits; if (!s->inline_data) { alloc_from_bitmap(s, 1, s->global_bm, &root_dir_rec.extent_off, &root_dir_rec.extent_len); root_dir_rec.dir_data = NULL; } else root_dir_rec.dir_data = root_dir; root_dir_rec.fe_off = alloc_inode(s, &root_dir_rec.suballoc_bit); root_dir->record = &root_dir_rec; add_entry_to_directory(s, root_dir, ".", root_dir_rec.fe_off, OCFS2_FT_DIR); add_entry_to_directory(s, root_dir, "..", root_dir_rec.fe_off, OCFS2_FT_DIR); need = system_dir_bytes_needed(s); if (!s->inline_data || need > ocfs2_max_inline_data_with_xattr(s->blocksize, NULL)) { need = system_dir_blocks_needed(s) << s->blocksize_bits; alloc_bytes_from_bitmap(s, need, s->global_bm, &system_dir_rec.extent_off, &system_dir_rec.extent_len); system_dir_rec.dir_data = NULL; } else system_dir_rec.dir_data = system_dir; system_dir_rec.fe_off = alloc_inode(s, &system_dir_rec.suballoc_bit); system_dir->record = &system_dir_rec; add_entry_to_directory(s, system_dir, ".", system_dir_rec.fe_off, OCFS2_FT_DIR); add_entry_to_directory(s, system_dir, "..", system_dir_rec.fe_off, OCFS2_FT_DIR); for (i = 0; i < NUM_SYSTEM_INODES; i++) { if (hb_dev_skip(s, i)) continue; if (feature_skip(s, i)) continue; num = (system_files[i].global) ? 1 : s->initial_slots; for (j = 0; j < num; j++) { record[i][j].fe_off = alloc_inode(s, &(record[i][j].suballoc_bit)); sprintf(fname, system_files[i].name, j); add_entry_to_directory(s, system_dir, fname, record[i][j].fe_off, S_ISDIR(system_files[i].mode) ? OCFS2_FT_DIR : OCFS2_FT_REG_FILE); } } /* back when we initialized the alloc group we hadn't allocated * an inode for the global allocator yet */ tmprec = &(record[GLOBAL_INODE_ALLOC_SYSTEM_INODE][0]); s->system_group->gd->bg_parent_dinode = tmprec->fe_off >> s->blocksize_bits; tmprec = &(record[HEARTBEAT_SYSTEM_INODE][0]); need = (O2NM_MAX_NODES + 1) << s->blocksize_bits; alloc_bytes_from_bitmap(s, need, s->global_bm, &tmprec->extent_off, &tmprec->extent_len); tmprec->file_size = need; if (!hb_dev_skip(s, ORPHAN_DIR_SYSTEM_INODE)) { for (i = 0; i < s->initial_slots; ++i) { tmprec = &record[ORPHAN_DIR_SYSTEM_INODE][i]; orphan_dir[i]->record = tmprec; if (!s->inline_data) { alloc_from_bitmap(s, 1, s->global_bm, &tmprec->extent_off, &tmprec->extent_len); tmprec->dir_data = NULL; } else tmprec->dir_data = orphan_dir[i]; add_entry_to_directory(s, orphan_dir[i], ".", tmprec->fe_off, OCFS2_FT_DIR); add_entry_to_directory(s, orphan_dir[i], "..", system_dir_rec.fe_off, OCFS2_FT_DIR); } } fsync(s->fd); if (!s->quiet) printf("done\n"); if (!s->quiet) printf("Writing system files: "); format_file(s, &root_dir_rec); format_file(s, &system_dir_rec); for (i = 0; i < NUM_SYSTEM_INODES; i++) { if (hb_dev_skip(s, i)) continue; if (feature_skip(s, i)) continue; num = system_files[i].global ? 1 : s->initial_slots; for (j = 0; j < num; j++) { tmprec = &(record[i][j]); format_file(s, tmprec); } } /* OHMYGODTHISISTHEWORSTCODEEVER: We write out the bitmap here * *again* because we did a bunch of allocs above after our * initial write-out. */ tmprec = &(record[GLOBAL_BITMAP_SYSTEM_INODE][0]); format_file(s, tmprec); write_bitmap_data(s, s->global_bm); write_group_data(s, s->system_group); write_directory_data(s, root_dir); write_directory_data(s, system_dir); if (!hb_dev_skip(s, ORPHAN_DIR_SYSTEM_INODE)) { for (i = 0; i < s->initial_slots; ++i) write_directory_data(s, orphan_dir[i]); } tmprec = &(record[HEARTBEAT_SYSTEM_INODE][0]); write_metadata(s, tmprec, NULL); fsync(s->fd); if (!s->quiet) printf("done\n"); if (!s->quiet) printf("Writing superblock: "); block_signals(SIG_BLOCK); format_leading_space(s); format_superblock(s, &superblock_rec, &root_dir_rec, &system_dir_rec); block_signals(SIG_UNBLOCK); if (!s->quiet) printf("done\n"); if (!s->hb_dev) finish_normal_format(s); close_device(s); if (!s->quiet) printf("%s successful\n\n", s->progname); free_directory(root_dir); free_directory(system_dir); for (i = 0; i < s->initial_slots; i++) free_directory(orphan_dir[i]); for (i = 0; i < NUM_SYSTEM_INODES; i++) ocfs2_free(&record[i]); free_state(s); return 0; } static void parse_fs_type_opts(char *progname, const char *typestr, enum ocfs2_mkfs_types *fs_type) { int i; *fs_type = OCFS2_MKFSTYPE_DEFAULT; for(i = 0; ocfs2_mkfs_types_table[i].ft_str; i++) { if (strcmp(typestr, ocfs2_mkfs_types_table[i].ft_str) == 0) { *fs_type = ocfs2_mkfs_types_table[i].ft_type; break; } } if (*fs_type == OCFS2_MKFSTYPE_DEFAULT) { com_err(progname, 0, "Bad fs type option specified."); exit(1); } } static State * get_state(int argc, char **argv) { char *progname; unsigned int blocksize = 0; unsigned int cluster_size = 0; char *vol_label = NULL; char *stack_name = NULL; char *cluster_name = NULL; int globalhb = 0; unsigned int initial_slots = 0; char *dummy; State *s; int c; int verbose = 0, quiet = 0, force = 0, xtool = 0, hb_dev = 0; int show_version = 0, dry_run = 0; char *device_name; char *uuid = NULL, uuid_36[37] = {'\0'}, *uuid_p; int ret; uint64_t val; uint64_t journal_size_in_bytes = 0; int journal64 = 0; enum ocfs2_mkfs_types fs_type = OCFS2_MKFSTYPE_DEFAULT; int mount = -1; int no_backup_super = -1; enum ocfs2_feature_levels level = OCFS2_FEATURE_LEVEL_DEFAULT; ocfs2_fs_options feature_flags = {0,0,0}, reverse_flags = {0,0,0}; int discard_blocks = 1; static struct option long_options[] = { { "block-size", 1, 0, 'b' }, { "cluster-size", 1, 0, 'C' }, { "label", 1, 0, 'L' }, { "node-slots", 1, 0, 'N' }, { "verbose", 0, 0, 'v' }, { "quiet", 0, 0, 'q' }, { "version", 0, 0, 'V' }, { "journal-options", 1, 0, 'J'}, { "heartbeat-device", 0, 0, 'H'}, { "force", 0, 0, 'F'}, { "mount", 1, 0, 'M'}, { "dry-run", 0, 0, 'n' }, { "nodiscard", 0, 0, 'o'}, { "discard", 0, 0, 'O'}, { "no-backup-super", 0, 0, BACKUP_SUPER_OPTION }, { "fs-feature-level=", 1, 0, FEATURE_LEVEL }, { "fs-features=", 1, 0, FEATURES_OPTION }, { "cluster-stack=", 1, 0, CLUSTER_STACK_OPTION }, { "cluster-name=", 1, 0, CLUSTER_NAME_OPTION }, { "global-heartbeat", 0, 0, GLOBAL_HEARTBEAT_OPTION }, { 0, 0, 0, 0} }; if (argc && *argv) progname = basename(argv[0]); else progname = "mkfs.ocfs2"; while (1) { c = getopt_long(argc, argv, "b:C:L:N:J:M:vnqVFHxT:U:", long_options, NULL); if (c == -1) break; switch (c) { case 'b': ret = get_number(optarg, &val); if (ret || val < OCFS2_MIN_BLOCKSIZE || val > OCFS2_MAX_BLOCKSIZE) { com_err(progname, 0, "Specify a blocksize between %d and %d " "in powers of 2", OCFS2_MIN_BLOCKSIZE, OCFS2_MAX_BLOCKSIZE); exit(1); } blocksize = (unsigned int) get_valid_size(val, OCFS2_MIN_BLOCKSIZE, OCFS2_MAX_BLOCKSIZE); break; case 'C': ret = get_number(optarg, &val); if (ret || val < OCFS2_MIN_CLUSTERSIZE || val > OCFS2_MAX_CLUSTERSIZE) { com_err(progname, 0, "Specify a clustersize between %d and " "%d in powers of 2", OCFS2_MIN_CLUSTERSIZE, OCFS2_MAX_CLUSTERSIZE); exit(1); } cluster_size = (unsigned int) get_valid_size(val, OCFS2_MIN_CLUSTERSIZE, OCFS2_MAX_CLUSTERSIZE); break; case 'L': vol_label = strdup(optarg); if (strlen(vol_label) >= OCFS2_MAX_VOL_LABEL_LEN) { com_err(progname, 0, "Volume label too long: must be less " "than %d characters", OCFS2_MAX_VOL_LABEL_LEN); exit(1); } break; case 'M': if (!strncasecmp(optarg, MOUNT_LOCAL_STR, strlen(MOUNT_LOCAL_STR))) mount = MOUNT_LOCAL; else if (!strncasecmp(optarg, MOUNT_CLUSTER_STR, strlen(MOUNT_CLUSTER_STR))) mount = MOUNT_CLUSTER; else { com_err(progname, 0, "Invalid mount type %s", optarg); exit(1); } break; case 'N': initial_slots = strtoul(optarg, &dummy, 0); if (initial_slots > OCFS2_MAX_SLOTS || *dummy != '\0') { com_err(progname, 0, "Initial node slots must be no more " "than %d", OCFS2_MAX_SLOTS); exit(1); } else if (initial_slots < 1) { com_err(progname, 0, "Initial node slots must be at " "least 1"); exit(1); } break; case 'J': parse_journal_opts(progname, optarg, &journal_size_in_bytes, &journal64); break; case 'U': uuid = strdup(optarg); break; case 'H': hb_dev = 1; break; case 'v': verbose = 1; break; case 'n': dry_run = 1; break; case 'q': quiet = 1; break; case 'V': show_version = 1; break; case 'F': force = 1; break; case 'x': xtool = 1; break; case 'T': parse_fs_type_opts(progname, optarg, &fs_type); break; case BACKUP_SUPER_OPTION: no_backup_super = 1; break; case FEATURE_LEVEL: ret = ocfs2_parse_feature_level(optarg, &level); if (ret) { com_err(progname, ret, "when parsing fs-feature-level string"); exit(1); } break; case FEATURES_OPTION: ret = ocfs2_parse_feature(optarg, &feature_flags, &reverse_flags); if (ret) { com_err(progname, ret, "when parsing fs-features string"); exit(1); } break; case CLUSTER_STACK_OPTION: if (!optarg || !strlen(optarg)) { com_err(progname, 0, "Option --cluster-stack requires an argument"); exit(1); } if (!o2cb_valid_stack_name(optarg)) { com_err(progname, O2CB_ET_INVALID_STACK_NAME, "; unknown cluster stack '%s'", optarg); exit(1); } if (stack_name) free(stack_name); stack_name = strdup(optarg); break; case CLUSTER_NAME_OPTION: if (!optarg || !strlen(optarg)) { com_err(progname, 0, "Option --cluster-name requires an argument"); exit(1); } if (cluster_name) free(cluster_name); cluster_name = strdup(optarg); break; case GLOBAL_HEARTBEAT_OPTION: globalhb = 1; break; case 'O': discard_blocks = 1; break; case 'o': discard_blocks = 0; break; default: usage(progname); break; } } if ((optind == argc) && !show_version) usage(progname); srand48(time(NULL)); device_name = argv[optind]; optind++; s = malloc(sizeof(State)); memset(s, 0, sizeof(State)); if (optind < argc) { s->specified_size_in_blocks = strtoull(argv[optind], &dummy, 0); if ((*dummy)) { com_err(progname, 0, "Block count bad - %s", argv[optind]); exit(1); } optind++; } if (optind < argc) usage(progname); if (!quiet || show_version) version(progname); if (show_version) exit(0); s->progname = progname; s->verbose = verbose; s->quiet = quiet; s->force = force; s->dry_run = dry_run; s->discard_blocks = discard_blocks; s->prompt = xtool ? 0 : 1; s->blocksize = blocksize; s->cluster_size = cluster_size; s->vol_label = vol_label; s->initial_slots = initial_slots; s->device_name = strdup(device_name); s->fd = -1; s->format_time = time(NULL); s->journal_size_in_bytes = journal_size_in_bytes; s->journal64 = journal64; s->hb_dev = hb_dev; s->fs_type = fs_type; ret = ocfs2_merge_feature_flags_with_level(&s->feature_flags, fs_type, level, &feature_flags, &reverse_flags); if (ret) { com_err(s->progname, ret, "while reconciling specified features with chosen " "defaults"); exit(1); } if (s->feature_flags.opt_incompat & OCFS2_FEATURE_INCOMPAT_LOCAL_MOUNT) s->mount = MOUNT_LOCAL; else s->mount = MOUNT_CLUSTER; if (s->feature_flags.opt_incompat & OCFS2_FEATURE_INCOMPAT_INLINE_DATA) s->inline_data = 1; else s->inline_data = 0; if (s->feature_flags.opt_compat & OCFS2_FEATURE_COMPAT_BACKUP_SB) s->no_backup_super = 0; else s->no_backup_super = 1; if (s->feature_flags.opt_incompat & OCFS2_FEATURE_INCOMPAT_INDEXED_DIRS) s->dx_dirs = 1; else s->dx_dirs = 0; /* uuid */ if (!uuid) uuid_generate(s->uuid); else { if (strlen(uuid) == 32) { translate_uuid(uuid, uuid_36); uuid_p = uuid_36; } else uuid_p = uuid; /*uuid_parse only support 36 bytes uuid*/ if (uuid_parse(uuid_p, s->uuid)) { com_err(s->progname, 0, "Invalid UUID specified"); exit(1); } printf("\nWARNING!!! OCFS2 uses the UUID to uniquely identify " "a file system.\nHaving two OCFS2 file systems with " "the same UUID could, in the least,\ncause erratic " "behavior, and if unlucky, cause file system damage.\n" "Please choose the UUID with care.\n\n"); free(uuid); } /* Here if the user set these flags explicitly, we will use them and * discard the setting in the features set. */ if (mount != -1) s->mount = mount; if (!is_cluster_info_valid(s, stack_name, cluster_name, globalhb)) exit(1); s->cluster_stack = stack_name; s->cluster_name = cluster_name; if (globalhb) s->stack_flags |= OCFS2_CLUSTER_O2CB_GLOBAL_HEARTBEAT; s->global_heartbeat = globalhb; if (no_backup_super != -1) s->no_backup_super = no_backup_super; return s; } static void free_state(State *s) { int i; ocfs2_free(&s->vol_label); ocfs2_free(&s->device_name); ocfs2_free(&s->cluster_stack); ocfs2_free(&s->cluster_name); if (s->global_bm) { for (i = 0; i < s->nr_cluster_groups; i++) free_alloc_group(s->global_bm->groups[i]); ocfs2_free(&s->global_bm->groups); ocfs2_free(&s->global_bm->name); ocfs2_free(&s->global_bm); } free_alloc_group(s->system_group); ocfs2_free(&s); } static int get_number(char *arg, uint64_t *res) { char *ptr = NULL; uint64_t num; num = strtoull(arg, &ptr, 0); if ((ptr == arg) || (num == UINT64_MAX)) return(-EINVAL); switch (*ptr) { case '\0': break; case 'g': case 'G': num *= 1024; /* FALL THROUGH */ case 'm': case 'M': num *= 1024; /* FALL THROUGH */ case 'k': case 'K': num *= 1024; /* FALL THROUGH */ case 'b': case 'B': break; default: return -EINVAL; } *res = num; return 0; } /* derived from e2fsprogs */ static void parse_journal_opts(char *progname, const char *opts, uint64_t *journal_size_in_bytes, int *journal64) { char *options, *token, *next, *p, *arg; int ret, journal_usage = 0; uint64_t val; int invert; options = strdup(opts); for (token = options; token && *token; token = next) { p = strchr(token, ','); next = NULL; invert = 0; if (p) { *p = '\0'; next = p + 1; } arg = strstr(token, "no"); if (arg == token) { invert = 1; token += strlen("no"); } arg = strchr(token, '='); if (arg) { *arg = '\0'; arg++; } if (strcmp(token, "size") == 0) { if (!arg || invert) { journal_usage++; continue; } ret = get_number(arg, &val); if (ret || val < OCFS2_MIN_JOURNAL_SIZE) { com_err(progname, 0, "Invalid journal size: %s\nSize must " "be greater than %d bytes", arg, OCFS2_MIN_JOURNAL_SIZE); exit(1); } *journal_size_in_bytes = val; } else if (strcmp(token, "block32") == 0) { if (arg) { journal_usage++; continue; } *journal64 = invert; } else if (strcmp(token, "block64") == 0) { if (arg) { journal_usage++; continue; } *journal64 = !invert; } else journal_usage++; } if (journal_usage) { com_err(progname, 0, "Bad journal options specified. Valid journal " "options are:\n" "\tsize=\n" "\t[no]block32\n" "\t[no]block64\n"); exit(1); } free(options); } static void usage(const char *progname) { fprintf(stderr, "usage: %s [-b block-size] [-C cluster-size] " "[-J journal-options]\n\t\t[-L volume-label] [-M mount-type] " "[-N number-of-node-slots]\n\t\t[-T filesystem-type] [-U uuid]" "[-HFnqvV] [--dry-run]" "\n\t\t[--fs-feature-level=[default|max-compat|max-features]] " "\n\t\t[--fs-features=[[no]sparse,...]] [--global-heartbeat]" "\n\t\t[--cluster-stack=stackname] [--cluster-name=clustername]" "\n\t\t[--no-backup-super] device [blocks-count]\n", progname); exit(1); } static void version(const char *progname) { fprintf(stderr, "%s %s\n", progname, VERSION); } static unsigned int journal_size_default(State *s) { unsigned int j_blocks; if (s->volume_size_in_blocks < 32768) j_blocks = OCFS2_MIN_JOURNAL_SIZE / s->blocksize; else if (s->volume_size_in_blocks < 262144) j_blocks = 4096; else { /* Each journal gets ~.625% of the blocks in the file * system, with a min of 16384 and a max of 65536 */ j_blocks = s->volume_size_in_blocks / 160; if (j_blocks < 16384) j_blocks = 16384; else if (j_blocks > 65536) j_blocks = 65536; } return j_blocks; } static unsigned int journal_size_datafiles(void) { return 8192; } static unsigned int journal_size_mail(State *s) { if (s->volume_size_in_blocks < 262144) return 16384; else if (s->volume_size_in_blocks < 524288) return 32768; return 65536; } static unsigned int journal_size_vmstore(State *s) { if (s->volume_size_in_blocks < 262144) return 8192; else if (s->volume_size_in_blocks < 524288) return 16384; return 32768; } static int journal_size_valid(unsigned int j_blocks, State *s) { return (j_blocks * s->initial_slots + 1024) <= s->volume_size_in_blocks; } /* For operations such as mkdir that can require more than a cluster worth * of journal credits, journal size should be greater than cluster size * 8. * The kernel allows the maximum transaction buffer to be 1\4 th of the * journal size and this is further divided by 2 for transaction * reservation support. We calculate minimum journal size here * accordingly and and ceil w.r.t to the cluster size.*/ static unsigned int journal_min_size(uint32_t cluster_size) { return (cluster_size << OCFS2_MIN_CLUSTER_TO_JOURNAL_SIZE_SHIFT) + cluster_size; } /* stolen from e2fsprogs, modified to fit ocfs2 patterns */ static uint64_t figure_journal_size(uint64_t size, State *s) { unsigned int j_blocks; uint64_t ret; unsigned int min_journal_size; if (s->hb_dev) return 0; if (s->volume_size_in_blocks < 2048) { fprintf(stderr, "Filesystem too small for a journal\n"); exit(1); } min_journal_size = journal_min_size(s->cluster_size); if (size > 0) { j_blocks = size >> s->blocksize_bits; /* mke2fs knows about free blocks at this point, but * we don't so lets just take a wild guess as to what * the fs overhead we're looking at will be. */ if (!journal_size_valid(j_blocks, s)) { fprintf(stderr, "Journal size too big for filesystem.\n"); exit(1); } ret = align_bytes_to_clusters_ceil(s, size); /* It is better to fail mkfs than to create a non-functional * filesystem.*/ if (ret < min_journal_size) { fprintf(stderr, "Journal size too small for filesystem.\n"); exit(1); } return ret; } switch (s->fs_type) { case OCFS2_MKFSTYPE_DATAFILES: j_blocks = journal_size_datafiles(); break; case OCFS2_MKFSTYPE_MAIL: j_blocks = journal_size_mail(s); break; case OCFS2_MKFSTYPE_VMSTORE: j_blocks = journal_size_vmstore(s); break; default: j_blocks = journal_size_default(s); break; } ret = align_bytes_to_clusters_ceil(s, j_blocks << s->blocksize_bits); /* If the default journal size is less than the minimum required * size, set the default to the minimum size. Then fail if * the journal size is not valid*/ if (ret < min_journal_size) { ret = min_journal_size; j_blocks = ret >> s->blocksize_bits; if (!journal_size_valid(j_blocks, s)) { fprintf(stderr, "Volume size too small for required " "configuration.\nIncrease volume size or " "reduce cluster size\n"); exit(1); } } return ret; } static uint32_t cluster_size_default(State *s) { uint32_t cluster_size, cluster_size_bits; uint64_t volume_size; for (cluster_size = OCFS2_MIN_CLUSTERSIZE; cluster_size < OCFS2_MAX_CLUSTERSIZE; cluster_size <<= 1) { cluster_size_bits = get_bits(s, cluster_size); volume_size = s->volume_size_in_bytes >> cluster_size_bits; if (volume_size <= CLUSTERS_MAX) break; } return cluster_size; } static uint32_t cluster_size_datafiles(State *s) { uint32_t cluster_size; uint64_t volume_gigs = s->volume_size_in_bytes / (1024 * 1024 * 1024); if (volume_gigs < 2) { com_err(s->progname, 0, "Selected file system type requires a device of at " "least 2 gigabytes\n"); exit(1); } if (volume_gigs < 64) cluster_size = 128; else if (volume_gigs < 96) cluster_size = 256; else if (volume_gigs < 128) cluster_size = 512; else cluster_size = 1024; return cluster_size * 1024; } static uint32_t figure_extent_alloc_size(State *s) { uint32_t cpg; int numgroups; uint64_t unitsize, totalsize; double curr_percent, target_percent; if (!s->initial_slots) return 0; switch (s->fs_type) { case OCFS2_MKFSTYPE_DATAFILES: case OCFS2_MKFSTYPE_VMSTORE: target_percent = 0.3; break; case OCFS2_MKFSTYPE_MAIL: default: target_percent = 0.1; break; } cpg = ocfs2_clusters_per_group(s->blocksize, s->cluster_size_bits); /* size of the allocator across all slots with one group */ unitsize = cpg * s->cluster_size * s->initial_slots; totalsize = unitsize; for (numgroups = 1; ; ++numgroups) { curr_percent = (double)totalsize * 100 / s->volume_size_in_bytes; if (curr_percent >= target_percent) break; totalsize += unitsize; } if (curr_percent > MAX_EXTALLOC_RESERVE_PERCENT) --numgroups; assert(numgroups >= 0); return cpg * numgroups; } static void fill_defaults(State *s) { size_t pagesize; errcode_t err; uint32_t blocksize; int sectsize; uint64_t ret; struct ocfs2_cluster_group_sizes cgs; uint64_t tmp; pagesize = getpagesize(); s->pagesize_bits = get_bits(s, pagesize); err = ocfs2_get_device_sectsize(s->device_name, §size); if (err) { if (err == OCFS2_ET_CANNOT_DETERMINE_SECTOR_SIZE) sectsize = 0; else { com_err(s->progname, err, "while getting hardware sector size of " "device %s", s->device_name); exit(1); } } if (!sectsize) sectsize = OCFS2_MIN_BLOCKSIZE; /* Heartbeat devices use the minimum size, unless specified */ if (!s->blocksize && s->hb_dev) s->blocksize = sectsize; if (s->blocksize) blocksize = s->blocksize; else blocksize = OCFS2_MAX_BLOCKSIZE; if (blocksize < sectsize) { com_err(s->progname, 0, "the block device %s has a hardware sector size (%d) " "that is larger than the selected block size (%u)", s->device_name, sectsize, blocksize); exit(1); } if (!s->volume_size_in_blocks) { err = ocfs2_get_device_size(s->device_name, blocksize, &ret); if (err) { com_err(s->progname, err, "while getting size of device %s", s->device_name); exit(1); } if (s->hb_dev) { uint64_t dev_size = 0; if ((ret * blocksize) > (2 * 1024 * 1024)) { fprintf(stderr, "%s: Warning: Volume larger than required for a heartbeat device\n", s->progname); } /* Blocks for system dir, root dir, * global allocator*/ dev_size = 4; /* Blocks for hb region */ dev_size += OCFS2_MAX_SLOTS; /* Slop for superblock + cluster bitmap */ dev_size += 10; /* Convert to bytes */ dev_size *= blocksize; /* Convert to megabytes */ dev_size = (dev_size + (1024 * 1024) - 1) >> ONE_MB_SHIFT; dev_size <<= ONE_MB_SHIFT; dev_size /= blocksize; if (ret > dev_size) ret = dev_size; } s->volume_size_in_blocks = ret; if (s->specified_size_in_blocks) { if (s->specified_size_in_blocks > s->volume_size_in_blocks) { com_err(s->progname, 0, "%"PRIu64" blocks were specified and " "this is greater than the %"PRIu64" " "blocks that make up %s.\n", s->specified_size_in_blocks, s->volume_size_in_blocks, s->device_name); exit(1); } s->volume_size_in_blocks = s->specified_size_in_blocks; } } s->volume_size_in_bytes = s->volume_size_in_blocks * blocksize; if (!s->blocksize) { if (s->volume_size_in_bytes <= 1024 * 1024 * 3) { s->blocksize = OCFS2_MIN_BLOCKSIZE; } else { int shift = 30; s->blocksize = OCFS2_MAX_BLOCKSIZE; while (s->blocksize > 1024) { if (s->volume_size_in_bytes >= 1U << shift) break; s->blocksize >>= 1; shift--; } } if (!s->specified_size_in_blocks) { err = ocfs2_get_device_size(s->device_name, s->blocksize, &ret); s->volume_size_in_blocks = ret; } else s->volume_size_in_blocks = s->specified_size_in_blocks; s->volume_size_in_bytes = s->volume_size_in_blocks * s->blocksize; } s->blocksize_bits = get_bits(s, s->blocksize); if (!s->cluster_size) { switch (s->fs_type) { case OCFS2_MKFSTYPE_DATAFILES: case OCFS2_MKFSTYPE_VMSTORE: s->cluster_size = cluster_size_datafiles(s); break; default: s->cluster_size = cluster_size_default(s); break; } } s->cluster_size_bits = get_bits(s, s->cluster_size); /* volume size needs to be cluster aligned */ s->volume_size_in_clusters = s->volume_size_in_bytes >> s->cluster_size_bits; tmp = (uint64_t)s->volume_size_in_clusters; s->volume_size_in_bytes = tmp << s->cluster_size_bits; s->volume_size_in_blocks = s->volume_size_in_bytes >> s->blocksize_bits; s->reserved_tail_size = 0; ocfs2_calc_cluster_groups(s->volume_size_in_clusters, s->blocksize, &cgs); s->global_cpg = cgs.cgs_cpg; s->nr_cluster_groups = cgs.cgs_cluster_groups; s->tail_group_bits = cgs.cgs_tail_group_bits; #if 0 printf("volume_size_in_clusters = %u\n", s->volume_size_in_clusters); printf("global_cpg = %u\n", s->global_cpg); printf("nr_cluster_groups = %u\n", s->nr_cluster_groups); printf("tail_group_bits = %u\n", s->tail_group_bits); #endif if (s->hb_dev) s->initial_slots = 0; if (!s->hb_dev && !s->initial_slots) { if (s->mount == MOUNT_LOCAL) s->initial_slots = 1; else s->initial_slots = initial_slots_for_volume(s->volume_size_in_bytes); } if (!s->vol_label) { s->vol_label = strdup(""); } s->journal_size_in_bytes = figure_journal_size(s->journal_size_in_bytes, s); s->extent_alloc_size_in_clusters = figure_extent_alloc_size(s); } static int get_bits(State *s, int num) { int i, bits = 0; for (i = 32; i >= 0; i--) { if (num == (1U << i)) bits = i; } if (bits == 0) { com_err(s->progname, 0, "Could not get bits for number %d", num); exit(1); } return bits; } static uint64_t get_valid_size(uint64_t num, uint64_t lo, uint64_t hi) { uint64_t tmp = lo; for ( ; lo <= hi; lo <<= 1) { if (lo == num) return num; if (lo < num) tmp = lo; else break; } return tmp; } static void * do_malloc(State *s, size_t size) { void *buf; int ret; ret = posix_memalign(&buf, OCFS2_MAX_BLOCKSIZE, size); if (ret != 0) { com_err(s->progname, 0, "Could not allocate %lu bytes of memory", (unsigned long)size); exit(1); } return buf; } static void do_pwrite(State *s, const void *buf, size_t count, uint64_t offset) { ssize_t ret; ret = pwrite64(s->fd, buf, count, offset); if (ret == -1) { com_err(s->progname, 0, "Could not write: %s", strerror(errno)); exit(1); } } static AllocGroup * initialize_alloc_group(State *s, const char *name, SystemFileDiskRecord *alloc_inode, uint64_t blkno, uint16_t chain, uint16_t cpg, uint16_t bpc) { AllocGroup *group; group = do_malloc(s, sizeof(AllocGroup)); memset(group, 0, sizeof(AllocGroup)); group->gd = do_malloc(s, s->blocksize); memset(group->gd, 0, s->blocksize); strcpy((char *)group->gd->bg_signature, OCFS2_GROUP_DESC_SIGNATURE); group->gd->bg_generation = s->vol_generation; group->gd->bg_size = (uint32_t)ocfs2_group_bitmap_size(s->blocksize, 0, 0); group->gd->bg_bits = cpg * bpc; group->gd->bg_chain = chain; group->gd->bg_parent_dinode = alloc_inode->fe_off >> s->blocksize_bits; group->gd->bg_blkno = blkno; /* First bit set to account for the descriptor block */ ocfs2_set_bit(0, group->gd->bg_bitmap); group->gd->bg_free_bits_count = group->gd->bg_bits - 1; alloc_inode->bi.total_bits += group->gd->bg_bits; alloc_inode->bi.used_bits++; group->alloc_inode = alloc_inode; group->name = strdup(name); return group; } static void free_alloc_group(AllocGroup *group) { if (group) { ocfs2_free(&group->name); ocfs2_free(&group->gd); ocfs2_free(&group); } } static AllocBitmap * initialize_bitmap(State *s, uint32_t bits, uint32_t unit_bits, const char *name, SystemFileDiskRecord *bm_record) { AllocBitmap *bitmap; uint64_t blkno; int i, j, cpg, chain, c_to_b_bits; int recs_per_inode = ocfs2_chain_recs_per_inode(s->blocksize); int wrapped = 0; bitmap = do_malloc(s, sizeof(AllocBitmap)); memset(bitmap, 0, sizeof(AllocBitmap)); bitmap->valid_bits = bits; bitmap->unit_bits = unit_bits; bitmap->unit = 1 << unit_bits; bitmap->name = strdup(name); bm_record->file_size = s->volume_size_in_bytes; bm_record->fe_off = 0ULL; bm_record->bi.used_bits = 0; /* this will be set as we add groups. */ bm_record->bi.total_bits = 0; bm_record->bitmap = bitmap; bitmap->bm_record = bm_record; bitmap->groups = do_malloc(s, s->nr_cluster_groups * sizeof(AllocGroup *)); memset(bitmap->groups, 0, s->nr_cluster_groups * sizeof(AllocGroup *)); c_to_b_bits = s->cluster_size_bits - s->blocksize_bits; /* to the next aligned cluster */ s->first_cluster_group = (OCFS2_SUPER_BLOCK_BLKNO + 1); s->first_cluster_group += ((1 << c_to_b_bits) - 1); s->first_cluster_group >>= c_to_b_bits; s->first_cluster_group_blkno = (uint64_t)s->first_cluster_group << c_to_b_bits; bitmap->groups[0] = initialize_alloc_group(s, "stupid", bm_record, s->first_cluster_group_blkno, 0, s->global_cpg, 1); /* The first bit is set by initialize_alloc_group, hence * we start at 1. For this group (which contains the clusters * containing the superblock and first group descriptor), we * have to set these by hand. */ for (i = 1; i <= s->first_cluster_group; i++) { ocfs2_set_bit(i, bitmap->groups[0]->gd->bg_bitmap); bitmap->groups[0]->gd->bg_free_bits_count--; bm_record->bi.used_bits++; } bitmap->groups[0]->chain_total = s->global_cpg; bitmap->groups[0]->chain_free = bitmap->groups[0]->gd->bg_free_bits_count; chain = 1; blkno = (uint64_t) s->global_cpg << (s->cluster_size_bits - s->blocksize_bits); cpg = s->global_cpg; for(i = 1; i < s->nr_cluster_groups; i++) { if (i == (s->nr_cluster_groups - 1)) cpg = s->tail_group_bits; bitmap->groups[i] = initialize_alloc_group(s, "stupid", bm_record, blkno, chain, cpg, 1); if (wrapped) { /* link the previous group to this guy. */ j = i - recs_per_inode; bitmap->groups[j]->gd->bg_next_group = blkno; bitmap->groups[j]->next = bitmap->groups[i]; } bitmap->groups[chain]->chain_total += bitmap->groups[i]->gd->bg_bits; bitmap->groups[chain]->chain_free += bitmap->groups[i]->gd->bg_free_bits_count; blkno += (uint64_t) s->global_cpg << (s->cluster_size_bits - s->blocksize_bits); chain++; if (chain >= recs_per_inode) { chain = 0; wrapped = 1; } } if (!wrapped) bitmap->num_chains = chain; else bitmap->num_chains = recs_per_inode; /* by now, this should be accurate. */ if (bm_record->bi.total_bits != s->volume_size_in_clusters) { fprintf(stderr, "bitmap total and num clusters don't " "match! %u, %u\n", bm_record->bi.total_bits, s->volume_size_in_clusters); exit(1); } return bitmap; } #if 0 static void destroy_bitmap(AllocBitmap *bitmap) { free(bitmap->buf); free(bitmap); } #endif static int find_clear_bits(void *buf, unsigned int size, uint32_t num_bits, uint32_t offset) { uint32_t next_zero, off, count = 0, first_zero = -1; off = offset; while ((size - off + count >= num_bits) && (next_zero = ocfs2_find_next_bit_clear(buf, size, off)) != size) { if (next_zero >= size) break; if (next_zero != off) { first_zero = next_zero; off = next_zero + 1; count = 0; } else { off++; if (count == 0) first_zero = next_zero; } count++; if (count == num_bits) goto bail; } first_zero = -1; bail: if (first_zero != (uint32_t)-1 && first_zero > size) { fprintf(stderr, "erf... first_zero > bitmap->valid_bits " "(%d > %d)", first_zero, size); first_zero = -1; } return first_zero; } static int alloc_bytes_from_bitmap(State *s, uint64_t bytes, AllocBitmap *bitmap, uint64_t *start, uint64_t *num) { uint32_t num_bits = 0; num_bits = (bytes + bitmap->unit - 1) >> bitmap->unit_bits; return alloc_from_bitmap(s, num_bits, bitmap, start, num); } static int alloc_from_bitmap(State *s, uint64_t num_bits, AllocBitmap *bitmap, uint64_t *start, uint64_t *num) { uint32_t start_bit = (uint32_t) - 1; void *buf = NULL; int i, found, chain; AllocGroup *group; struct ocfs2_group_desc *gd = NULL; unsigned int size; found = 0; for(i = 0; i < bitmap->num_chains && !found; i++) { group = bitmap->groups[i]; do { gd = group->gd; if (gd->bg_free_bits_count >= num_bits) { buf = gd->bg_bitmap; size = gd->bg_bits; start_bit = find_clear_bits(buf, size, num_bits, 0); found = 1; break; } group = group->next; } while (group); } if (start_bit == (uint32_t)-1) { com_err(s->progname, 0, "Could not allocate %"PRIu64" bits from %s bitmap", num_bits, bitmap->name); exit(1); } if (gd->bg_blkno == s->first_cluster_group_blkno) *start = (uint64_t) start_bit; else *start = (uint64_t) start_bit + ((gd->bg_blkno << s->blocksize_bits) >> s->cluster_size_bits); *start = *start << bitmap->unit_bits; *num = ((uint64_t)num_bits) << bitmap->unit_bits; gd->bg_free_bits_count -= num_bits; chain = gd->bg_chain; bitmap->groups[chain]->chain_free -= num_bits; bitmap->bm_record->bi.used_bits += num_bits; #if 0 printf("alloc requested %"PRIu64" bits, given len = %"PRIu64", at " "start = %"PRIu64". used_bits = %u\n", num_bits, *num, *start, bitmap->bm_record->bi.used_bits); #endif while (num_bits--) { ocfs2_set_bit(start_bit, buf); start_bit++; } return 0; } static int alloc_from_group(State *s, uint16_t count, AllocGroup *group, uint64_t *start_blkno, uint16_t *num_bits) { uint16_t start_bit, end_bit; start_bit = ocfs2_find_first_bit_clear(group->gd->bg_bitmap, group->gd->bg_bits); while (start_bit < group->gd->bg_bits) { end_bit = ocfs2_find_next_bit_set(group->gd->bg_bitmap, group->gd->bg_bits, start_bit); if ((end_bit - start_bit) >= count) { *num_bits = count; for (*num_bits = 0; *num_bits < count; *num_bits += 1) { ocfs2_set_bit(start_bit + *num_bits, group->gd->bg_bitmap); } group->gd->bg_free_bits_count -= *num_bits; group->alloc_inode->bi.used_bits += *num_bits; *start_blkno = group->gd->bg_blkno + start_bit; return 0; } start_bit = end_bit; } com_err(s->progname, 0, "Could not allocate %"PRIu16" bits from %s alloc group", count, group->name); exit(1); return 1; } static uint64_t alloc_inode(State *s, uint16_t *suballoc_bit) { uint64_t ret; uint16_t num; alloc_from_group(s, 1, s->system_group, &ret, &num); *suballoc_bit = (int)(ret - s->system_group->gd->bg_blkno); /* Did I mention I hate this code? */ return (ret << s->blocksize_bits); } static DirData * alloc_directory(State *s) { DirData *dir; dir = do_malloc(s, sizeof(DirData)); memset(dir, 0, sizeof(DirData)); return dir; } static void free_directory(DirData *dir) { ocfs2_free(&dir->buf); ocfs2_free(&dir); } static void add_entry_to_directory(State *s, DirData *dir, char *name, uint64_t byte_off, uint8_t type) { struct ocfs2_dir_entry *de, *de1; int new_rec_len; void *new_buf, *p; int new_size, rec_len, real_len; new_rec_len = OCFS2_DIR_REC_LEN(strlen(name)); if (dir->buf) { de = (struct ocfs2_dir_entry *)(dir->buf + dir->last_off); rec_len = de->rec_len; real_len = OCFS2_DIR_REC_LEN(de->name_len); if ((de->inode == 0 && rec_len >= new_rec_len) || (rec_len >= real_len + new_rec_len)) { if (de->inode) { de1 =(struct ocfs2_dir_entry *) ((char *) de + real_len); de1->rec_len = de->rec_len - real_len; de->rec_len = real_len; de = de1; } goto got_it; } new_size = dir->record->file_size + s->blocksize; } else { new_size = s->blocksize; } new_buf = memalign(s->blocksize, new_size); if (new_buf == NULL) { com_err(s->progname, 0, "Failed to grow directory"); exit(1); } if (dir->buf) { memcpy(new_buf, dir->buf, dir->record->file_size); free(dir->buf); p = new_buf + dir->record->file_size; memset(p, 0, s->blocksize); } else { p = new_buf; memset(new_buf, 0, new_size); } dir->buf = new_buf; dir->record->file_size = new_size; de = (struct ocfs2_dir_entry *)p; de->inode = 0; de->rec_len = s->blocksize; if (!s->inline_data || !dir->record->dir_data) mkfs_init_dir_trailer(s, dir, p); got_it: de->name_len = strlen(name); de->inode = byte_off >> s->blocksize_bits; de->file_type = type; strcpy(de->name, name); dir->last_off = ((char *)de - (char *)dir->buf); if (type == OCFS2_FT_DIR) dir->record->links++; } static uint32_t blocks_needed(State *s) { uint32_t num; num = SUPERBLOCK_BLOCKS; num += ROOTDIR_BLOCKS; num += SYSDIR_BLOCKS; num += LOSTDIR_BLOCKS; num += sys_blocks_needed(MAX(32, s->initial_slots)); return num; } static uint32_t sys_blocks_needed(uint32_t num_slots) { uint32_t num = 0; uint32_t cnt = sizeof(system_files) / sizeof(SystemFileInfo); int i; for (i = 0; i < cnt; ++i) { if (system_files[i].global) ++num; else num += num_slots; } return num; } static uint32_t system_dir_blocks_needed(State *s) { int each = OCFS2_DIR_REC_LEN(SYSTEM_FILE_NAME_MAX); int entries_per_block = s->blocksize / each; return (sys_blocks_needed(s->initial_slots) + entries_per_block - 1) / entries_per_block; } #if 0 /* This breaks stuff that depends on volume_size_in_clusters and * volume_size_in_blocks, and I'm not even sure it's necessary. If * needed, this sort of calculation should be done before * fill_defaults where we calculate a bunch of other things based on * #blocks and #clusters. */ static void adjust_volume_size(State *s) { uint32_t max; uint64_t vsize = s->volume_size_in_bytes - (MIN_RESERVED_TAIL_BLOCKS << s->blocksize_bits); max = MAX(s->pagesize_bits, s->blocksize_bits); max = MAX(max, s->cluster_size_bits); vsize >>= max; vsize <<= max; s->volume_size_in_blocks = vsize >> s->blocksize_bits; s->volume_size_in_clusters = vsize >> s->cluster_size_bits; s->reserved_tail_size = s->volume_size_in_bytes - vsize; s->volume_size_in_bytes = vsize; } #endif /* this will go away once we have patches to jbd to support 64bit blocks. * ocfs2 will only fail mounts when it finds itself asked to mount a large * device in a kernel that doesn't have a smarter jbd. */ static void check_32bit_blocks(State *s) { uint64_t max = UINT32_MAX; if (s->journal64) return; if (s->volume_size_in_blocks <= max) return; fprintf(stderr, "ERROR: jbd can only store block numbers in 32 bits. " "%s can hold %"PRIu64" blocks which overflows this limit. If " "you have a new enough Ocfs2 with JBD2 support, you can try " "formatting with the \"-Jblock64\" option to turn on support " "for this size block device.\n" "Otherwise, consider increasing the block size or " "decreasing the device size.\n", s->device_name, s->volume_size_in_blocks); exit(1); } static void mkfs_swap_inode_from_cpu(State *s, struct ocfs2_dinode *di) { ocfs2_filesys fake_fs; char super_buf[OCFS2_MAX_BLOCKSIZE]; fill_fake_fs(s, &fake_fs, super_buf); ocfs2_swap_inode_from_cpu(&fake_fs, di); } static void mkfs_swap_group_desc_from_cpu(State *s, struct ocfs2_group_desc *gd) { ocfs2_filesys fake_fs; char super_buf[OCFS2_MAX_BLOCKSIZE]; fill_fake_fs(s, &fake_fs, super_buf); ocfs2_swap_group_desc_from_cpu(&fake_fs, gd); } static void mkfs_swap_group_desc_to_cpu(State *s, struct ocfs2_group_desc *gd) { ocfs2_filesys fake_fs; char super_buf[OCFS2_MAX_BLOCKSIZE]; fill_fake_fs(s, &fake_fs, super_buf); ocfs2_swap_group_desc_to_cpu(&fake_fs, gd); } static void format_superblock(State *s, SystemFileDiskRecord *rec, SystemFileDiskRecord *root_rec, SystemFileDiskRecord *sys_rec) { struct ocfs2_dinode *di; uint64_t super_off = rec->fe_off; di = do_malloc(s, s->blocksize); memset(di, 0, s->blocksize); strcpy((char *)di->i_signature, OCFS2_SUPER_BLOCK_SIGNATURE); di->i_suballoc_slot = (__u16)OCFS2_INVALID_SLOT; di->i_suballoc_bit = (__u16)-1; di->i_generation = s->vol_generation; di->i_fs_generation = s->vol_generation; di->i_atime = 0; di->i_ctime = s->format_time; di->i_mtime = s->format_time; di->i_blkno = super_off >> s->blocksize_bits; di->i_flags = OCFS2_VALID_FL | OCFS2_SYSTEM_FL | OCFS2_SUPER_BLOCK_FL; di->i_clusters = s->volume_size_in_clusters; di->id2.i_super.s_major_rev_level = OCFS2_MAJOR_REV_LEVEL; di->id2.i_super.s_minor_rev_level = OCFS2_MINOR_REV_LEVEL; di->id2.i_super.s_root_blkno = root_rec->fe_off >> s->blocksize_bits; di->id2.i_super.s_system_dir_blkno = sys_rec->fe_off >> s->blocksize_bits; di->id2.i_super.s_mnt_count = 0; di->id2.i_super.s_max_mnt_count = OCFS2_DFL_MAX_MNT_COUNT; di->id2.i_super.s_state = 0; di->id2.i_super.s_errors = 0; di->id2.i_super.s_lastcheck = s->format_time; di->id2.i_super.s_checkinterval = OCFS2_DFL_CHECKINTERVAL; di->id2.i_super.s_creator_os = OCFS2_OS_LINUX; di->id2.i_super.s_blocksize_bits = s->blocksize_bits; di->id2.i_super.s_clustersize_bits = s->cluster_size_bits; di->id2.i_super.s_max_slots = s->initial_slots; di->id2.i_super.s_first_cluster_group = s->first_cluster_group_blkno; if (s->hb_dev) { s->feature_flags.opt_incompat = OCFS2_FEATURE_INCOMPAT_HEARTBEAT_DEV; s->feature_flags.opt_compat = OCFS2_FEATURE_COMPAT_JBD2_SB; s->feature_flags.opt_ro_compat = 0; } if (s->mount == MOUNT_LOCAL) s->feature_flags.opt_incompat |= OCFS2_FEATURE_INCOMPAT_LOCAL_MOUNT; if (s->cluster_stack) { s->feature_flags.opt_incompat |= OCFS2_FEATURE_INCOMPAT_EXTENDED_SLOT_MAP; /* Selectively enable clusterinfo or userspace stack */ if (!(s->feature_flags.opt_incompat & OCFS2_FEATURE_INCOMPAT_CLUSTERINFO)) { if (!is_classic_stack(s->cluster_stack)) s->feature_flags.opt_incompat |= OCFS2_FEATURE_INCOMPAT_USERSPACE_STACK; else s->feature_flags.opt_incompat |= OCFS2_FEATURE_INCOMPAT_CLUSTERINFO; } memcpy(di->id2.i_super.s_cluster_info.ci_stack, s->cluster_stack, OCFS2_STACK_LABEL_LEN); memcpy(di->id2.i_super.s_cluster_info.ci_cluster, s->cluster_name, OCFS2_CLUSTER_NAME_LEN); di->id2.i_super.s_cluster_info.ci_stackflags = s->stack_flags; } /* * we clear the "backup_sb" here since it should be written by * format_backup_super, not by us. And we have already set the * "s->no_backup_super" according to the features in get_state, * so it is safe to clear the flag here. */ s->feature_flags.opt_compat &= ~OCFS2_FEATURE_COMPAT_BACKUP_SB; if (s->feature_flags.opt_incompat & OCFS2_FEATURE_INCOMPAT_XATTR) di->id2.i_super.s_xattr_inline_size = OCFS2_MIN_XATTR_INLINE_SIZE; di->id2.i_super.s_feature_incompat = s->feature_flags.opt_incompat; di->id2.i_super.s_feature_compat = s->feature_flags.opt_compat; di->id2.i_super.s_feature_ro_compat = s->feature_flags.opt_ro_compat; strcpy((char *)di->id2.i_super.s_label, s->vol_label); memcpy(di->id2.i_super.s_uuid, s->uuid, OCFS2_VOL_UUID_LEN); /* s_uuid_hash is also used by Indexed Dirs */ if (s->feature_flags.opt_incompat & OCFS2_FEATURE_INCOMPAT_XATTR || s->feature_flags.opt_incompat & OCFS2_FEATURE_INCOMPAT_INDEXED_DIRS) di->id2.i_super.s_uuid_hash = ocfs2_xattr_uuid_hash(s->uuid); if (s->feature_flags.opt_incompat & OCFS2_FEATURE_INCOMPAT_INDEXED_DIRS) { di->id2.i_super.s_dx_seed[0] = mrand48(); di->id2.i_super.s_dx_seed[1] = mrand48(); di->id2.i_super.s_dx_seed[2] = mrand48(); } mkfs_swap_inode_from_cpu(s, di); mkfs_compute_meta_ecc(s, di, &di->i_check); do_pwrite(s, di, s->blocksize, super_off); free(di); } /* * This function is in libocfs2/alloc.c. Needless to add, when * changing code here, update the same in alloc.c too. */ static int ocfs2_clusters_per_group(int block_size, int cluster_size_bits) { int megabytes; switch (block_size) { case 4096: case 2048: megabytes = 4; break; case 1024: megabytes = 2; break; case 512: default: megabytes = 1; break; } return (megabytes << ONE_MB_SHIFT) >> cluster_size_bits; } static void format_file(State *s, SystemFileDiskRecord *rec) { struct ocfs2_dinode *di; int mode, i; uint32_t clusters; AllocBitmap *bitmap; mode = rec->mode; clusters = (rec->extent_len + s->cluster_size - 1) >> s->cluster_size_bits; di = do_malloc(s, s->blocksize); memset(di, 0, s->blocksize); strcpy((char *)di->i_signature, OCFS2_INODE_SIGNATURE); di->i_generation = s->vol_generation; di->i_fs_generation = s->vol_generation; di->i_suballoc_slot = (__u16)OCFS2_INVALID_SLOT; di->i_suballoc_bit = rec->suballoc_bit; di->i_blkno = rec->fe_off >> s->blocksize_bits; di->i_uid = 0; di->i_gid = 0; di->i_size = rec->file_size; di->i_mode = mode; di->i_links_count = rec->links; di->i_flags = rec->flags; di->i_atime = di->i_ctime = di->i_mtime = s->format_time; di->i_dtime = 0; di->i_clusters = clusters; if (rec->flags & OCFS2_LOCAL_ALLOC_FL) { di->id2.i_lab.la_size = ocfs2_local_alloc_size(s->blocksize); goto write_out; } if (rec->flags & OCFS2_DEALLOC_FL) { di->id2.i_dealloc.tl_count = ocfs2_truncate_recs_per_inode(s->blocksize); goto write_out; } if (rec->flags & OCFS2_BITMAP_FL) { di->id1.bitmap1.i_used = rec->bi.used_bits; di->id1.bitmap1.i_total = rec->bi.total_bits; } if (rec->cluster_bitmap) { di->id2.i_chain.cl_count = ocfs2_chain_recs_per_inode(s->blocksize); di->id2.i_chain.cl_cpg = ocfs2_group_bitmap_size(s->blocksize, 0, 0) * 8; di->id2.i_chain.cl_bpc = 1; if (s->nr_cluster_groups > ocfs2_chain_recs_per_inode(s->blocksize)) { di->id2.i_chain.cl_next_free_rec = di->id2.i_chain.cl_count; } else di->id2.i_chain.cl_next_free_rec = s->nr_cluster_groups; di->i_clusters = s->volume_size_in_clusters; bitmap = rec->bitmap; for(i = 0; i < bitmap->num_chains; i++) { di->id2.i_chain.cl_recs[i].c_blkno = bitmap->groups[i]->gd->bg_blkno; di->id2.i_chain.cl_recs[i].c_free = bitmap->groups[i]->chain_free; di->id2.i_chain.cl_recs[i].c_total = bitmap->groups[i]->chain_total; } goto write_out; } if (rec->flags & OCFS2_CHAIN_FL) { di->id2.i_chain.cl_count = ocfs2_chain_recs_per_inode(s->blocksize); di->id2.i_chain.cl_cpg = ocfs2_clusters_per_group(s->blocksize, s->cluster_size_bits); di->id2.i_chain.cl_bpc = s->cluster_size / s->blocksize; di->id2.i_chain.cl_next_free_rec = 0; if (rec->chain_off) { di->id2.i_chain.cl_next_free_rec = 1; di->id2.i_chain.cl_recs[0].c_free = rec->group->gd->bg_free_bits_count; di->id2.i_chain.cl_recs[0].c_total = rec->group->gd->bg_bits; di->id2.i_chain.cl_recs[0].c_blkno = rec->chain_off >> s->blocksize_bits; di->id2.i_chain.cl_cpg = rec->group->gd->bg_bits / di->id2.i_chain.cl_bpc; di->i_clusters = di->id2.i_chain.cl_cpg; di->i_size = di->i_clusters << s->cluster_size_bits; } goto write_out; } di->id2.i_list.l_count = ocfs2_extent_recs_per_inode(s->blocksize); di->id2.i_list.l_next_free_rec = 0; di->id2.i_list.l_tree_depth = 0; if (rec->extent_len) { di->id2.i_list.l_next_free_rec = 1; di->id2.i_list.l_recs[0].e_cpos = 0; ocfs2_set_rec_clusters(0, &di->id2.i_list.l_recs[0], clusters); di->id2.i_list.l_recs[0].e_blkno = rec->extent_off >> s->blocksize_bits; } else if (S_ISDIR(di->i_mode) && s->inline_data && rec->dir_data) { DirData *dir = rec->dir_data; struct ocfs2_dir_entry *de = (struct ocfs2_dir_entry *)(dir->buf + dir->last_off); int dir_len = dir->last_off + OCFS2_DIR_REC_LEN(de->name_len); if (dir_len > ocfs2_max_inline_data_with_xattr(s->blocksize, di)) { com_err(s->progname, 0, "Inline a dir which shouldn't be inline.\n"); clear_both_ends(s); exit(1); } de->rec_len -= s->blocksize - ocfs2_max_inline_data_with_xattr(s->blocksize, di); memset(&di->id2, 0, s->blocksize - offsetof(struct ocfs2_dinode, id2)); di->id2.i_data.id_count = ocfs2_max_inline_data_with_xattr(s->blocksize, di); memcpy(di->id2.i_data.id_data, dir->buf, dir_len); di->i_dyn_features |= OCFS2_INLINE_DATA_FL; di->i_size = ocfs2_max_inline_data_with_xattr(s->blocksize, di); } write_out: mkfs_swap_inode_from_cpu(s, di); mkfs_compute_meta_ecc(s, di, &di->i_check); do_pwrite(s, di, s->blocksize, rec->fe_off); free(di); } static void write_metadata(State *s, SystemFileDiskRecord *rec, void *src) { void *buf; buf = do_malloc(s, rec->extent_len); memset(buf, 0, rec->extent_len); if (src) memcpy(buf, src, rec->file_size); do_pwrite(s, buf, rec->extent_len, rec->extent_off); free(buf); } static void write_bitmap_data(State *s, AllocBitmap *bitmap) { int i; uint64_t parent_blkno; struct ocfs2_group_desc *gd, *gd_buf; char *buf = NULL; buf = do_malloc(s, s->cluster_size); memset(buf, 0, s->cluster_size); parent_blkno = bitmap->bm_record->fe_off >> s->blocksize_bits; for(i = 0; i < s->nr_cluster_groups; i++) { gd = bitmap->groups[i]->gd; if (strcmp((char *)gd->bg_signature, OCFS2_GROUP_DESC_SIGNATURE)) { fprintf(stderr, "bad group descriptor!\n"); exit(1); } /* Ok, we didn't get a chance to fill in the parent * blkno until now. */ gd->bg_parent_dinode = parent_blkno; memcpy(buf, gd, s->blocksize); gd_buf = (struct ocfs2_group_desc *)buf; mkfs_swap_group_desc_from_cpu(s, gd_buf); mkfs_compute_meta_ecc(s, buf, &gd_buf->bg_check); do_pwrite(s, buf, s->cluster_size, gd->bg_blkno << s->blocksize_bits); } free(buf); } static void write_group_data(State *s, AllocGroup *group) { uint64_t blkno = group->gd->bg_blkno; mkfs_swap_group_desc_from_cpu(s, group->gd); mkfs_compute_meta_ecc(s, group->gd, &group->gd->bg_check); do_pwrite(s, group->gd, s->blocksize, blkno << s->blocksize_bits); mkfs_swap_group_desc_to_cpu(s, group->gd); } static void mkfs_swap_dir(State *s, DirData *dir, errcode_t (*swap_entry_func)(void *buf, uint64_t bytes)) { char *p = dir->buf; unsigned int offset = 0; unsigned int end = s->blocksize; char super_buf[OCFS2_MAX_BLOCKSIZE]; ocfs2_filesys fake_fs; struct ocfs2_dir_block_trailer *trailer; if (!dir->record->extent_len) return; fill_fake_fs(s, &fake_fs, super_buf); if (!s->inline_data || !dir->record->dir_data) if (ocfs2_supports_dir_trailer(&fake_fs)) end = ocfs2_dir_trailer_blk_off(&fake_fs); while (offset < dir->record->file_size) { trailer = ocfs2_dir_trailer_from_block(&fake_fs, p); swap_entry_func(p, end); if (end != s->blocksize) ocfs2_swap_dir_trailer(trailer); /* Remember, this does nothing if the feature isn't set */ ocfs2_compute_meta_ecc(&fake_fs, p, &trailer->db_check); offset += s->blocksize; p += s->blocksize; } } static void mkfs_swap_dir_from_cpu(State *s, DirData *dir) { mkfs_swap_dir(s, dir, ocfs2_swap_dir_entries_from_cpu); } static void mkfs_swap_dir_to_cpu(State *s, DirData *dir) { mkfs_swap_dir(s, dir, ocfs2_swap_dir_entries_to_cpu); } static void write_directory_data(State *s, DirData *dir) { if (!dir->record->extent_len) return; if (dir->buf) mkfs_swap_dir_from_cpu(s, dir); write_metadata(s, dir->record, dir->buf); if (dir->buf) mkfs_swap_dir_to_cpu(s, dir); } static void format_leading_space(State *s) { int num_blocks = 2, size; struct ocfs1_vol_disk_hdr *hdr; struct ocfs1_vol_label *lbl; void *buf; char *p; size = num_blocks << s->blocksize_bits; p = buf = do_malloc(s, size); memset(buf, 2, size); hdr = buf; strcpy((char *)hdr->signature, "this is an ocfs2 volume"); strcpy((char *)hdr->mount_point, "this is an ocfs2 volume"); p += 512; lbl = (struct ocfs1_vol_label *)p; strcpy((char *)lbl->label, "this is an ocfs2 volume"); strcpy((char *)lbl->cluster_name, "this is an ocfs2 volume"); do_pwrite(s, buf, size, 0); free(buf); } static void open_device(State *s) { s->fd = open64(s->device_name, O_RDWR | O_DIRECT); if (s->fd == -1) { com_err(s->progname, 0, "Could not open device %s: %s", s->device_name, strerror (errno)); exit(1); } } static void close_device(State *s) { fsync(s->fd); close(s->fd); s->fd = -1; } static int initial_slots_for_volume(uint64_t size) { size >>= ONE_GB_SHIFT; if (size < 2) return 2; else if (size < 10) return 4; else if (size < 1024) return 8; else return 16; } static void create_generation(State *s) { int randfd = 0; int readlen = sizeof(s->vol_generation); if ((randfd = open("/dev/urandom", O_RDONLY)) == -1) { com_err(s->progname, 0, "Error opening /dev/urandom: %s", strerror(errno)); exit(1); } if (read(randfd, &s->vol_generation, readlen) != readlen) { com_err(s->progname, 0, "Error reading from /dev/urandom: %s", strerror(errno)); exit(1); } close(randfd); } static void init_record(State *s, SystemFileDiskRecord *rec, int type, int mode) { memset(rec, 0, sizeof(SystemFileDiskRecord)); rec->mode = mode; rec->links = S_ISDIR(mode) ? 0 : 1; rec->bi.used_bits = rec->bi.total_bits = 0; rec->flags = (OCFS2_VALID_FL | OCFS2_SYSTEM_FL); switch (type) { case SFI_JOURNAL: rec->flags |= OCFS2_JOURNAL_FL; break; case SFI_LOCAL_ALLOC: rec->flags |= (OCFS2_BITMAP_FL|OCFS2_LOCAL_ALLOC_FL); break; case SFI_HEARTBEAT: rec->flags |= OCFS2_HEARTBEAT_FL; break; case SFI_CLUSTER: rec->cluster_bitmap = 1; case SFI_CHAIN: rec->flags |= (OCFS2_BITMAP_FL|OCFS2_CHAIN_FL); break; case SFI_TRUNCATE_LOG: rec->flags |= OCFS2_DEALLOC_FL; break; case SFI_QUOTA: rec->flags |= OCFS2_QUOTA_FL; break; case SFI_OTHER: break; } } static void print_state(State *s) { int i; char buf[PATH_MAX] = "\0"; uint64_t extsize = 0; uint32_t numgrps = 0; if (s->quiet) return; if (s->extent_alloc_size_in_clusters) { numgrps = s->extent_alloc_size_in_clusters / ocfs2_clusters_per_group(s->blocksize, s->cluster_size_bits); extsize = (uint64_t)s->extent_alloc_size_in_clusters * s->cluster_size; } ocfs2_snprint_feature_flags(buf, PATH_MAX, &s->feature_flags); if (s->fs_type != OCFS2_MKFSTYPE_DEFAULT) { for(i = 0; ocfs2_mkfs_types_table[i].ft_str; i++) { if (ocfs2_mkfs_types_table[i].ft_type == s->fs_type) { printf("Filesystem Type of %s\n", ocfs2_mkfs_types_table[i].ft_str); break; } } } printf("Label: %s\n", s->vol_label); printf("Features: %s\n", buf); printf("Block size: %u (%u bits)\n", s->blocksize, s->blocksize_bits); printf("Cluster size: %u (%u bits)\n", s->cluster_size, s->cluster_size_bits); printf("Volume size: %"PRIu64" (%u clusters) (%"PRIu64" blocks)\n", s->volume_size_in_bytes, s->volume_size_in_clusters, s->volume_size_in_blocks); printf("Cluster groups: %u (tail covers %u clusters, rest cover %u " "clusters)\n", s->nr_cluster_groups, s->tail_group_bits, s->global_cpg); printf("Extent allocator size: %"PRIu64" (%u groups)\n", extsize, numgrps); if (s->hb_dev) printf("Heartbeat device\n"); else printf("Journal size: %"PRIu64"\n", s->journal_size_in_bytes); printf("Node slots: %u\n", s->initial_slots); } static void clear_both_ends(State *s) { char *buf = NULL; buf = do_malloc(s, CLEAR_CHUNK); memset(buf, 0, CLEAR_CHUNK); /* start of volume */ do_pwrite(s, buf, CLEAR_CHUNK, 0); /* end of volume */ do_pwrite(s, buf, CLEAR_CHUNK, (s->volume_size_in_bytes - CLEAR_CHUNK)); free(buf); return ; } static void index_system_dirs(State *s, ocfs2_filesys *fs) { errcode_t ret; int i, num_slots = OCFS2_RAW_SB(fs->fs_super)->s_max_slots; uint64_t orphan_dir_blkno; /* Start with the root directory */ ret = ocfs2_dx_dir_build(fs, fs->fs_root_blkno); if (ret) { com_err(s->progname, ret, "while indexing root directory"); goto bail; } for (i = 0; i < num_slots; i++) { ret = ocfs2_lookup_system_inode(fs, ORPHAN_DIR_SYSTEM_INODE, i, &orphan_dir_blkno); if (ret) { com_err(s->progname, ret, "while looking up orphan dir %d for indexing", i); goto bail; } ret = ocfs2_dx_dir_build(fs, orphan_dir_blkno); if (ret) { com_err(s->progname, ret, "while indexing root directory"); goto bail; } } return; bail: clear_both_ends(s); exit(1); } static void create_lost_found_dir(State *s, ocfs2_filesys *fs) { errcode_t ret; uint64_t lost_found_blkno; ret = ocfs2_new_inode(fs, &lost_found_blkno, S_IFDIR | 0755); if (ret) { com_err(s->progname, ret, "while creating lost+found"); goto bail; } ret = ocfs2_init_dir(fs, lost_found_blkno, fs->fs_root_blkno); if (ret) { com_err(s->progname, ret, "while adding lost+found dir data"); goto bail; } ret = ocfs2_link(fs, fs->fs_root_blkno, "lost+found", lost_found_blkno, OCFS2_FT_DIR); if (ret) { com_err(s->progname, ret, "while linking lost+found to the " "root directory"); goto bail; } return ; bail: clear_both_ends(s); exit(1); } static void format_journals(State *s, ocfs2_filesys *fs) { errcode_t ret; int i; uint32_t journal_size_in_clusters; uint64_t blkno; char jrnl_file[40]; ocfs2_fs_options features = { .opt_incompat = s->journal64 ? JBD2_FEATURE_INCOMPAT_64BIT : 0, }; journal_size_in_clusters = s->journal_size_in_bytes >> OCFS2_RAW_SB(fs->fs_super)->s_clustersize_bits; for(i = 0; i < OCFS2_RAW_SB(fs->fs_super)->s_max_slots; i++) { snprintf (jrnl_file, sizeof(jrnl_file), ocfs2_system_inodes[JOURNAL_SYSTEM_INODE].si_name, i); ret = ocfs2_lookup(fs, fs->fs_sysdir_blkno, jrnl_file, strlen(jrnl_file), NULL, &blkno); if (ret) { com_err(s->progname, ret, "while looking up journal filename \"%.*s\"", (int)strlen(jrnl_file), jrnl_file); goto error; } ret = ocfs2_make_journal(fs, blkno, journal_size_in_clusters, &features); if (ret) { com_err(s->progname, ret, "while formatting journal \"%.*s\"", (int)strlen(jrnl_file), jrnl_file); goto error; } } return; error: clear_both_ends(s); exit(1); } static void format_slotmap(State *s, ocfs2_filesys *fs) { errcode_t ret; ret = ocfs2_format_slot_map(fs); if (ret) { com_err(s->progname, ret, "while formatting the slot map"); clear_both_ends(s); exit(1); } } static int format_backup_super(State *s, ocfs2_filesys *fs) { errcode_t ret; size_t len; uint64_t blocks[OCFS2_MAX_BACKUP_SUPERBLOCKS]; len = ocfs2_get_backup_super_offsets(fs, blocks, ARRAY_SIZE(blocks)); ret = ocfs2_set_backup_super_list(fs, blocks, len); if (ret) { com_err(s->progname, ret, "while backing up superblock."); goto error; } OCFS2_SET_COMPAT_FEATURE(OCFS2_RAW_SB(fs->fs_super), OCFS2_FEATURE_COMPAT_BACKUP_SB); /* ocfs2_set_backup_super_list() wrote the backups */ ret = ocfs2_write_primary_super(fs); if (ret) { com_err(s->progname, ret, "while updating superblock."); goto error; } return len; error: clear_both_ends(s); exit(1); } static void mkfs_compute_meta_ecc(State *s, void *data, struct ocfs2_block_check *bc) { if (s->feature_flags.opt_incompat & OCFS2_FEATURE_INCOMPAT_META_ECC) ocfs2_block_check_compute(data, s->blocksize, bc); } ocfs2-tools-ocfs2-tools-1.8.6/mkfs.ocfs2/mkfs.h000066400000000000000000000120441347147137200211460ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * mkfs2.h * * Definitions, function prototypes, etc. * * Copyright (C) 2004, 2005 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License, version 2, as published by the Free Software Foundation. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * */ #define _LARGEFILE64_SOURCE #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ocfs2/ocfs2.h" #include "ocfs2/bitops.h" #include "ocfs2-kernel/ocfs1_fs_compat.h" #include #include #ifndef MAX #define MAX(a, b) (((a) > (b)) ? (a) : (b)) #endif #define MOUNT_LOCAL 1 #define MOUNT_CLUSTER 2 #define MOUNT_LOCAL_STR "local" #define MOUNT_CLUSTER_STR "cluster" #define MIN_RESERVED_TAIL_BLOCKS 8 #define SUPERBLOCK_BLOCKS 3 #define ROOTDIR_BLOCKS 1 #define SYSDIR_BLOCKS 1 #define LOSTDIR_BLOCKS 1 #define CLEAR_CHUNK 1048576 #define OCFS2_OS_LINUX 0 #define OCFS2_OS_HURD 1 #define OCFS2_OS_MASIX 2 #define OCFS2_OS_FREEBSD 3 #define OCFS2_OS_LITES 4 #define OCFS2_DFL_MAX_MNT_COUNT 20 #define OCFS2_DFL_CHECKINTERVAL 0 #define SYSTEM_FILE_NAME_MAX 40 #define ONE_MB_SHIFT 20 #define ONE_GB_SHIFT 30 #define BITMAP_AUTO_MAX 786432 #define AUTO_CLUSTERSIZE 65536 #define CLUSTERS_MAX (UINT32_MAX - 1) #define MAX_EXTALLOC_RESERVE_PERCENT 5 #define DISCARD_STEP_MB 2048 #if defined(__linux__) && !defined(BLKDISCARD) #define BLKDISCARD _IO(0x12,119) #endif enum { SFI_JOURNAL, SFI_CLUSTER, SFI_LOCAL_ALLOC, SFI_HEARTBEAT, SFI_CHAIN, SFI_TRUNCATE_LOG, SFI_QUOTA, SFI_OTHER }; typedef struct _SystemFileInfo SystemFileInfo; struct _SystemFileInfo { char *name; int type; int global; int mode; }; struct BitInfo { uint32_t used_bits; uint32_t total_bits; }; typedef struct _AllocGroup AllocGroup; typedef struct _SystemFileDiskRecord SystemFileDiskRecord; typedef struct _DirData DirData; struct _AllocGroup { char *name; struct ocfs2_group_desc *gd; SystemFileDiskRecord *alloc_inode; uint32_t chain_free; uint32_t chain_total; struct _AllocGroup *next; }; struct _SystemFileDiskRecord { uint64_t fe_off; uint16_t suballoc_bit; uint64_t extent_off; uint64_t extent_len; uint64_t file_size; uint64_t chain_off; AllocGroup *group; struct BitInfo bi; struct _AllocBitmap *bitmap; int flags; int links; int mode; int cluster_bitmap; /* record the dir entry so that inline dir can be stored with file. */ DirData *dir_data; }; typedef struct _AllocBitmap AllocBitmap; struct _AllocBitmap { AllocGroup **groups; uint32_t valid_bits; uint32_t unit; uint32_t unit_bits; char *name; uint64_t fe_disk_off; SystemFileDiskRecord *bm_record; SystemFileDiskRecord *alloc_record; int num_chains; }; struct _DirData { void *buf; int last_off; SystemFileDiskRecord *record; }; typedef struct _State State; struct _State { char *progname; int verbose; int quiet; int force; int prompt; int hb_dev; int mount; int no_backup_super; int inline_data; int dx_dirs; int dry_run; int discard_blocks; uint32_t blocksize; uint32_t blocksize_bits; uint32_t cluster_size; uint32_t cluster_size_bits; uint64_t specified_size_in_blocks; uint64_t volume_size_in_bytes; uint32_t volume_size_in_clusters; uint64_t volume_size_in_blocks; uint32_t pagesize_bits; uint64_t reserved_tail_size; unsigned int initial_slots; uint64_t journal_size_in_bytes; int journal64; uint32_t extent_alloc_size_in_clusters; char *vol_label; char *device_name; unsigned char uuid[OCFS2_VOL_UUID_LEN]; char *cluster_stack; char *cluster_name; uint8_t stack_flags; int global_heartbeat; uint32_t vol_generation; int fd; time_t format_time; AllocBitmap *global_bm; AllocGroup *system_group; uint32_t nr_cluster_groups; uint16_t global_cpg; uint16_t tail_group_bits; uint32_t first_cluster_group; uint64_t first_cluster_group_blkno; ocfs2_fs_options feature_flags; enum ocfs2_mkfs_types fs_type; }; int is_classic_stack(char *stack_name); void cluster_fill(char **stack_name, char **cluster_name, uint8_t *stack_flags); int ocfs2_fill_cluster_information(State *s); int ocfs2_check_volume(State *s); ocfs2-tools-ocfs2-tools-1.8.6/mkfs.ocfs2/mkfs.ocfs2.8.in000066400000000000000000000412221347147137200225060ustar00rootroot00000000000000.TH "mkfs.ocfs2" "8" "January 2012" "Version @VERSION@" "OCFS2 Manual Pages" .SH "NAME" mkfs.ocfs2 \- Creates an \fIOCFS2\fR file system. .SH "SYNOPSIS" \fBmkfs.ocfs2\fR [\fB\-b\fR \fIblock\-size\fR] [\fB\-C\fR \fIcluster\-size\fR] [\fB\-L\fR \fIvolume\-label\fR] [\fB\-M\fR \fImount-type\fR] [\fB\-N\fR \fInumber\-of\-nodes\fR] [\fB\-J\fR \fIjournal\-options\fR] [\fB\-\-fs\-features=\fR\fI[no]sparse...\fR] [\fB\-\-fs\-feature\-level=\fR\fIfeature\-level\fR] [\fB\-T\fR \fIfilesystem\-type\fR] [\fB\-\-cluster\-stack=\fR\fIstackname\fR] [\fB\-\-cluster\-name=\fR\fIclustername\fR] [\fB\-\-global\-heartbeat\fR] [\fB\-\-discard | \-\-nodiscard\fR] [\fB\-FqvV\fR] \fIdevice\fR [\fIblocks-count\fI] .SH "DESCRIPTION" .PP \fBmkfs.ocfs2\fR is used to create an \fIOCFS2\fR file system on a \fIdevice\fR, usually a partition on a shared disk. In order to prevent data loss, \fBmkfs.ocfs2\fR will not format an existing \fIOCFS2\fR volume if it detects that it is mounted on another node in the cluster. This tool requires the cluster service to be online. .SH "OPTIONS" .TP \fB\-b, \-\-block\-size\fR \fIblock\-size\fR Valid block size values are 512, 1K, 2K and 4K bytes per block. If omitted, a value will be heuristically determined based on the expected usage of the file system (see the \fB\-T\fR option). A block size of 512 bytes is never recommended. Choose 1K, 2K or 4K. .TP \fB\-C, \-\-cluster\-size\fR \fIcluster\-size\fR Valid cluster size values are 4K, 8K, 16K, 32K, 64K, 128K, 256K, 512K and 1M. If omitted, a value will be heuristically determined based on the expected usage of the file system (see the \fB\-T\fR option). For volumes expected to store large files, like database files, while a cluster size of 128K or more is recommended, one can opt for a smaller size as long as that value is not smaller than the database block size. For others, use 4K. .TP \fB\-F, \-\-force\fR For existing \fIOCFS2\fR volumes, \fImkfs.ocfs2\fR ensures the volume is not mounted on any node in the cluster before formatting. For that to work, \fImkfs.ocfs2\fR expects the cluster service to be online. Specify this option to disable this check. .TP \fB\-J, \-\-journal-options\fR \fIoptions\fR Create the journal using options specified on the command\-line. Journal options are comma separated, and may take an argument using the equals ('=') sign. The following options are supported: .RS .TP \fBsize\fR=\fIjournal\-size\fR Create a journal of size \fIjournal\-size\fR. Minimum size is 4M. If omitted, a value is heuristically determined based upon the file system size. .TP \fBblock32\fR Use a standard 32bit journal. The journal will be able to access up to 2^32-1 blocks. This is the default. It has been the journal format for \fIOCFS2\fR volumes since the beginning. The journal is compatible with all versions of \fIOCFS2\fR. Prepending \fBno\fR is equivalent to the \fBblock64\fR journal option. .TP \fBblock64\fR Use a 64bit journal. The journal will be able to access up to 2^64-1 blocks. This allows large filesystems that can extend to the theoretical limits of \fIOCFS2\fR. It requires a new-enough filesystem driver that uses the new journalled block device, \fBJBD2\fR. Prepending \fBno\fR is equivalent to the \fBblock32\fR journal option. .RE .TP \fB\-L, \-\-label\fR \fIvolume\-label\fR Set the volume label for the file system. This is useful for mounting\-by\-label. Limit the label to under 64 bytes. .TP \fB\-M, \-\-mount\fR \fImount\-type\fR Valid types are \fIlocal\fR and \fIcluster\fR. Local mount allows users to mount the volume without the cluster overhead and works only with \fIOCFS2\fR bundled with Linux kernels 2.6.20 or later. Defaults to \fIcluster\fR. .TP \fB\-N, \-\-node\-slots\fR \fInumber\-of\-node\-slots\fR Valid number ranges from 1 to 255. This number specifies the maximum number of nodes that can concurrently mount the partition. If omitted, the number depends on volume size, for volume size < 2G, it's 2, for 2G <= size < 10G, it's 4, for 10G <= size < 1T, it's 8, for other size, it's 16. The number of slots can be later tuned up or down using \fItunefs.ocfs2\fR. .TP \fB\-T\fR \fIfilesystem\-type\fR Specify how the filesystem is going to be used, so that \fImkfs.ocfs2\fR can chose optimal filesystem parameters for that use. The supported filesystem types are: .RS .TP \fBmail\fR Appropriate for file systems that will host lots of small files. .TP \fBdatafiles\fR Appropriate for file systems that will host a relatively small number of very large files. .TP \fBvmstore\fR Appropriate for file systems that will host Virtual machine images. .RE .TP \fB\-\-fs\-features=\fR\fR\fI[no]sparse...\fR Turn specific file system features on or off. A comma separated list of feature flags can be provided, and \fImkfs.ocfs2\fR will try to create the file system with those features set according to the list. To turn a feature on, include it in the list. To turn a feature off, prepend \fBno\fR to the name. Choices here will override individual features set via the \fB\-\-fs\-feature\-level\fR option. \fBRefer to the section titled feature compatibility before selecting specific features.\fR The following flags are supported: .RS .TP \fBbackup-super\fR \fImkfs.ocfs2\fR, by default, makes up to 6 backup copies of the super block at offsets 1G, 4G, 16G, 64G, 256G and 1T depending on the size of the volume. This can be useful in disaster recovery. This feature is fully compatible with all versions of the file system and generally should not be disabled. .TP \fBlocal\fR Create the file system as a local mount, so that it can be mounted without a cluster stack. .TP \fBsparse\fR Enable support for sparse files. With this, \fIOCFS2\fR can avoid allocating (and zeroing) data to fill holes. Turn this feature on if you can, otherwise extends and some writes might be less performant. .TP \fBunwritten\fR Enable unwritten extents support. With this turned on, an application can request that a range of clusters be pre-allocated within a file. \fIOCFS2\fR will mark those extents with a special flag so that expensive data zeroing doesn't have to be performed. Reads and writes to a pre-allocated region act as reads and writes to a hole, except a write will not fail due to lack of data allocation. This feature requires \fBsparse\fR file support to be turned on. .TP \fBinline-data\fR Enable inline-data support. If this feature is turned on, \fIOCFS2\fR will store small files and directories inside the inode block. Data is transparently moved out to an extent when it no longer fits inside the inode block. In some cases, this can also make a positive impact on cold-cache directory and file operations. .TP \fBextended-slotmap\fR The slot-map is a hidden file on an \fIOCFS2\fR fs which is used to map mounted nodes to system file resources. The extended slot map allows a larger range of possible node numbers, which is useful for userspace cluster stacks. If required, this feature is automatically turned on by \fImkfs.ocfs2\fR. .TP \fBmetaecc\fR Enables metadata checksums. With this enabled, the file system computes and stores the checksums in all metadata blocks. It also computes and stores an error correction code capable of fixing single bit errors. .TP \fBrefcount\fR Enables creation of reference counted trees. With this enabled, the file system allows users to create inode-based snapshots and clones known as \fBreflinks\fR. .TP \fBxattr\fR Enable extended attributes support. With this enabled, users can attach name:value pairs to objects within the file system. In \fIOCFS2\fR, the names can be up to 255 bytes in length, terminated by the first NUL byte. While it is not required, printable names (ASCII) are recommended. The values can be up to 64KB of arbitrary binary data. Attributes can be attached to all types of inodes: regular files, directories, symbolic links, device nodes, etc. This feature is required for users wanting to use extended security facilities like POSIX ACLs or SELinux. .TP \fBusrquota\fR Enable user quota support. With this feature enabled, filesystem will track amount of space and number of inodes (files, directories, symbolic links) each user owns. It is then possible to limit the maximum amount of space or inodes user can have. See a documentation of quota-tools package for more details. .TP \fBgrpquota\fR Enable group quota support. With this feature enabled, filesystem will track amount of space and number of inodes (files, directories, symbolic links) each group owns. It is then possible to limit the maximum amount of space or inodes user can have. See a documentation of quota-tools package for more details. .TP \fBindexed-dirs\fR Enable directory indexing support. With this feature enabled, the file system creates indexed tree for non-inline directory entries. For large scale directories, directory entry lookup performance from the indexed tree is faster then from the legacy directory blocks. .TP \fBdiscontig-bg\fR Enables discontiguous block groups. With this feature enabled, the file system is able to grow the inode and the extent allocators even when there is no contiguous free chunk available. It allows the file system to grow the allocators in smaller (discontiguous) chunks. .TP \fBclusterinfo\fR Enables storing the cluster stack information in the superblock. This feature is needed to support userspace cluster stacks and the global heartbeat mode in the \fBo2cb\fR cluster stack. If needed, this feature is automatically turned on by \fImkfs.ocfs2\fR. .RE .TP \fB\-\-fs\-feature\-level=\fR\fR\fIfeature\-level\fR Choose from a set of pre-determined file-system features. This option is designed to allow users to conveniently choose a set of file system features which fits their needs. There is no downside to trying a set of features which your module might not support - if it won't mount the new file system simply reformat at a lower level. Feature levels can be fine-tuned via the \fB\-\-fs\-features\fR option. Currently, there are 3 types of feature levels: .RS .TP \fBmax-compat\fR Chooses fewer features but ensures that the file system can be mounted from older versions of the \fIOCFS2\fR module. .TP \fBdefault\fR The default feature set tries to strike a balance between providing new features and maintaining compatibility with relatively recent versions of \fIOCFS2\fR. It currently enables \fBsparse\fR, \fBunwritten\fR, \fBinline-data\fR, \fBxattr\fR, \fBindexed-dirs\fR, \fBdiscontig-bg\fR, \fBrefcount\fR, \fBextended-slotmap\fR and \fBclusterinfo\fR. .TP \fBmax-features\fR Choose the maximum amount of features available. This will typically provide the best performance from \fIOCFS2\fR at the expense of creating a file system that is only compatible with very recent versions of the \fIOCFS2\fR kernel module. .RE .TP \fB\-\-cluster\-stack\fR Specify the cluster stack. This option is normally not required as \fImkfs.ocfs2\fR chooses the currently active cluster stack. It is required only if the cluster stack is not online and the user wishes to use a stack other than the default, \fBo2cb\fR. Other supported cluster stacks are \fBpcmk\fR (Pacemaker) and \fBcman\fR (rgmanager). Once set, \fIOCFS2\fR will only allow mounting the volume if the active cluster stack and cluster name matches the one specified on-disk. .TP \fB\-\-cluster\-name\fR Specify the name of the cluster. This option is mandatory if the user has specified a \fIcluster\-stack\fR. This name is restricted to a max of 16 characters. Additionally, the \fBo2cb\fR cluster stack allows only alpha-numeric characters. .TP \fB\-\-global\-heartbeat\fR Enable the global heartbeat mode of the \fBo2cb\fR cluster stack. This option is not required if the \fBo2cb\fR cluster stack with global heartbeat is online as \fImkfs.ocfs2\fR will detect the active stack. However, if the cluster stack is not up, then this option is required alongwith \fIcluster\-stack\fR and \fIcluster\-name\fR. For more, refer to \fBo2cb(7)\fR. .TP \fB\-\-discard\fR Attempt to discard blocks at mkfs time (discarding blocks initially is useful on solid state devices and sparse / thin-provisioned storage). When the device advertises that discard also zeroes data (any subsequent read after the discard and before write returns zero), then mark all not-yet-zeroed blocks as zeroed. This significantly speeds up filesystem initialization. This is set as default. .TP \fB\-\-nodiscard\fR Do not attempt to discard blocks at mkfs time. .TP \fB\-\-no-backup-super\fR This option is deprecated, please use \fB--fs-features=nobackup-super\fR instead. .TP \fB\-n, --dry-run\fR Display the heuristically determined values without overwriting the existing file system. .TP \fB\-q, \-\-quiet\fR Quiet mode. .TP \fB\-U\fR \fIuuid\fR Specify a custom UUID in the plain (2A4D1C581FAA42A1A41D26EFC90C1315) or traditional (2a4d1c58-1faa-42a1-a41d-26efc90c1315) format. This option in \fBnot\fR recommended because the file system uses the UUID to uniquely identify a file system. \fBIf more than one file system were to have the same UUID, one is very likely to encounter erratic behavior, if not, outright file system corruption.\fR .TP \fB\-v, \-\-verbose\fR Verbose mode. .TP \fB\-V, \-\-version\fR Print version and exit. .TP \fIblocks-count\fR Usually \fBmkfs.ocfs2\fR automatically determines the size of the given device and creates a file system that uses all of the available space on the device. This optional argument specifies that the file system should only consume the given number of file system blocks (see \fB-b\fR) on the device. .SH "FEATURE COMPATIBILITY" This section lists the file system features that have been added to the \fIOCFS2\fR file system and the version that it first appeared in. The table below lists the versions of the mainline Linux kernel and ocfs2-tools. Users should use this information to enable only those features that are available in the file system that they are using. Before enabling new features, users are advised to review to the section titled \fBfeature values\fR. .TS CENTER ALLBOX; LB LB LB LI C C. Feature Kernel Version Tools Version local Linux 2.6.20 ocfs2-tools 1.2 sparse Linux 2.6.22 ocfs2-tools 1.4 unwritten Linux 2.6.23 ocfs2-tools 1.4 inline-data Linux 2.6.24 ocfs2-tools 1.4 extended-slotmap Linux 2.6.27 ocfs2-tools 1.6 metaecc Linux 2.6.29 ocfs2-tools 1.6 grpquota Linux 2.6.29 ocfs2-tools 1.6 usrquota Linux 2.6.29 ocfs2-tools 1.6 xattr Linux 2.6.29 ocfs2-tools 1.6 indexed-dirs Linux 2.6.30 ocfs2-tools 1.6 refcount Linux 2.6.32 ocfs2-tools 1.6 discontig-bg Linux 2.6.35 ocfs2-tools 1.6 clusterinfo Linux 2.6.37 ocfs2-tools 1.8 .TE .TS ; L. Users can query the features enabled in the file system as follows: .TE .nf .ft 6 # tunefs.ocfs2 -Q "Label: %V\\nFeatures: %H %O\\n" /dev/sdg1 Label: apache_files_10 Features: sparse inline-data unwritten .ft .fi .SH "FEATURE VALUES" This section lists the hex values that are associated with the file system features. This information is useful when debugging mount failures that are due to feature incompatibility. When a user attempts to mount an \fBOCFS2\fR volume that has features enabled that are not supported by the running file system software, it will fail with an error like: .nf .ps 9 .ft 6 ERROR: couldn't mount because of unsupported optional features (200). .ft .ps .fi By referring to the table below, it becomes apparent that the user attempted to mount a volume with the \fIxattr\fR (extended attributes) feature enabled with a version of the file system software that did not support it. At this stage, the user has the option of either upgrading the file system software, or, disabling that on-disk feature using \fBtunefs.ocfs2\fR. Some features allow the file system to be mounted with an older version of the software provided the mount is read-only. If a user attempts to mount such a volume in a read-write mode, it will fail with an error like: .nf .ps 9 .ft 6 ERROR: couldn't mount RDWR because of unsupported optional features (1). .ft .ps .fi This error indicates that the volume had the \fIunwritten\fR RO compat feature enabled. This volume can be mounted by an older file system software only in the read-only mode. In this case, the user has the option of either mounting the volume with the \fIro\fR mount option, or, disabling that on-disk feature using \fBtunefs.ocfs2\fR. .TS CENTER ALLBOX; LI LI LI LI C C. Feature Category Hex value local Incompat 8 sparse Incompat 10 inline-data Incompat 40 extended-slotmap Incompat 100 xattr Incompat 200 indexed-dirs Incompat 400 metaecc Incompat 800 refcount Incompat 1000 discontig-bg Incompat 2000 clusterinfo Incompat 4000 unwritten RO Compat 1 usrquota RO Compat 2 grpquota RO Compat 4 .TE .SH "SEE ALSO" .BR debugfs.ocfs2(8) .BR fsck.ocfs2(8) .BR mount.ocfs2(8) .BR mounted.ocfs2(8) .BR o2cb(7) .BR o2cluster(8) .BR o2image(8) .BR o2info(1) .BR tunefs.ocfs2(8) .SH "AUTHORS" Oracle Corporation .SH "COPYRIGHT" Copyright \(co 2004, 2012 Oracle. All rights reserved. ocfs2-tools-ocfs2-tools-1.8.6/mkinstalldirs000077500000000000000000000055611347147137200206760ustar00rootroot00000000000000#! /bin/sh # mkinstalldirs --- make directory hierarchy scriptversion=2003-11-08.23 # Original author: Noah Friedman # Created: 1993-05-16 # Public domain. # # This file is maintained in Automake, please report # bugs to or send patches to # . errstatus=0 dirmode="" usage="\ Usage: mkinstalldirs [-h] [--help] [--version] [-m MODE] DIR ... Create each directory DIR (with mode MODE, if specified), including all leading file name components. Report bugs to ." # process command line arguments while test $# -gt 0 ; do case $1 in -h | --help | --h*) # -h for help echo "$usage" exit 0 ;; -m) # -m PERM arg shift test $# -eq 0 && { echo "$usage" 1>&2; exit 1; } dirmode=$1 shift ;; --version) echo "$0 $scriptversion" exit 0 ;; --) # stop option processing shift break ;; -*) # unknown option echo "$usage" 1>&2 exit 1 ;; *) # first non-opt arg break ;; esac done for file do if test -d "$file"; then shift else break fi done case $# in 0) exit 0 ;; esac case $dirmode in '') if mkdir -p -- . 2>/dev/null; then echo "mkdir -p -- $*" exec mkdir -p -- "$@" else # On NextStep and OpenStep, the `mkdir' command does not # recognize any option. It will interpret all options as # directories to create, and then abort because `.' already # exists. test -d ./-p && rmdir ./-p test -d ./-- && rmdir ./-- fi ;; *) if mkdir -m "$dirmode" -p -- . 2>/dev/null; then echo "mkdir -m $dirmode -p -- $*" exec mkdir -m "$dirmode" -p -- "$@" else # Clean up after NextStep and OpenStep mkdir. for d in ./-m ./-p ./-- "./$dirmode"; do test -d $d && rmdir $d done fi ;; esac 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 else if test ! -z "$dirmode"; then echo "chmod $dirmode $pathcomp" lasterr="" chmod "$dirmode" "$pathcomp" || lasterr=$? if test ! -z "$lasterr"; then errstatus=$lasterr fi fi fi fi pathcomp="$pathcomp/" done done exit $errstatus # Local Variables: # mode: shell-script # sh-indentation: 2 # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-end: "$" # End: ocfs2-tools-ocfs2-tools-1.8.6/mount.ocfs2/000077500000000000000000000000001347147137200202365ustar00rootroot00000000000000ocfs2-tools-ocfs2-tools-1.8.6/mount.ocfs2/.gitignore000066400000000000000000000000551347147137200222260ustar00rootroot00000000000000*.sw? mount.ocfs2 mount.ocfs2.8 *.d cscope.* ocfs2-tools-ocfs2-tools-1.8.6/mount.ocfs2/CREDITS000066400000000000000000000004361347147137200212610ustar00rootroot00000000000000 The following files have been copied from util-linux-2.12p/mount: fstab.[ch] mntent.[ch] mount_constants.h paths.h realpath.[ch] sundries.[ch] xmalloc.[ch] The following files have code which has been copied from one or more files in util-linux-2.12p/mount: opts.[ch] nls.h ocfs2-tools-ocfs2-tools-1.8.6/mount.ocfs2/Cscope.make000066400000000000000000000004441347147137200223130ustar00rootroot00000000000000.PHONY: cscope cscope: rm -f cscope.* echo "-k" >> cscope.files echo "-I inc" >> cscope.files find . -name '*.[ch]' >>cscope.files find ../libocfs2/ -name '*.[ch]' >>cscope.files find ../libo2cb/ -name '*.[ch]' >>cscope.files find ../libo2dlm/ -name '*.[ch]' >>cscope.files cscope -b ocfs2-tools-ocfs2-tools-1.8.6/mount.ocfs2/Makefile000066400000000000000000000021571347147137200217030ustar00rootroot00000000000000TOPDIR = .. include $(TOPDIR)/Preamble.make sbindir = $(root_sbindir) SBIN_PROGRAMS = mount.ocfs2 INCLUDES = -I$(TOPDIR)/include LIBOCFS2_LIBS = -L$(TOPDIR)/libocfs2 -locfs2 LIBOCFS2_DEPS = $(TOPDIR)/libocfs2/libocfs2.a LIBO2DLM_LIBS = -L$(TOPDIR)/libo2dlm -lo2dlm $(DL_LIBS) LIBO2DLM_DEPS = $(TOPDIR)/libo2dlm/libo2dlm.a LIBO2CB_LIBS = -L$(TOPDIR)/libo2cb -lo2cb LIBO2CB_DEPS = $(TOPDIR)/libo2cb/libo2cb.a ifneq ($(BUILD_FSDLM_SUPPORT),) LIBO2CB_LIBS += -ldlm_lt endif ifneq ($(BUILD_CMAP_SUPPORT),) LIBO2CB_LIBS += -lcmap endif DEFINES = -DVERSION=\"$(VERSION)\" CORE_CFILES = fstab.c mntent.c realpath.c sundries.c xmalloc.c opts.c MOUNT_CFILES = mount.ocfs2.c HFILES = $(subst .c,.h,$(MOUNT_CFILES) $(CORE_CFILES)) HFILES += mount_constants.h nls.h paths.h MOUNT_OBJS = $(subst .c,.o,$(MOUNT_CFILES) $(CORE_CFILES)) MANS = mount.ocfs2.8 DIST_FILES = $(CORE_CFILES) $(MOUNT_CFILES) \ $(HFILES) $(addsuffix .in,$(MANS)) mount.ocfs2: $(MOUNT_OBJS) $(LIBOCFS2_DEPS) $(LIBO2DLM_DEPS) $(LIBO2CB_DEPS) $(LINK) $(LIBOCFS2_LIBS) $(LIBO2DLM_LIBS) $(LIBO2CB_LIBS) $(COM_ERR_LIBS) $(AIO_LIBS) include $(TOPDIR)/Postamble.make ocfs2-tools-ocfs2-tools-1.8.6/mount.ocfs2/fstab.c000066400000000000000000000402411347147137200215020ustar00rootroot00000000000000/* 1999-02-22 Arkadiusz Mikiewicz * - added Native Language Support * Sun Mar 21 1999 - Arnaldo Carvalho de Melo * - fixed strerr(errno) in gettext calls */ #include #include #include #include #include #include "mntent.h" #include "fstab.h" #include "sundries.h" #include "xmalloc.h" /* #include "mount_blkid.h" -- OCFS2 modification */ #include "paths.h" #include "nls.h" #define streq(s, t) (strcmp ((s), (t)) == 0) #define PROC_MOUNTS "/proc/mounts" /* Information about mtab. ------------------------------------*/ static int have_mtab_info = 0; static int var_mtab_does_not_exist = 0; static int var_mtab_is_a_symlink = 0; static void get_mtab_info(void) { struct stat mtab_stat; if (!have_mtab_info) { if (lstat(MOUNTED, &mtab_stat)) var_mtab_does_not_exist = 1; else if (S_ISLNK(mtab_stat.st_mode)) var_mtab_is_a_symlink = 1; have_mtab_info = 1; } } int mtab_does_not_exist(void) { get_mtab_info(); return var_mtab_does_not_exist; } int mtab_is_a_symlink(void) { get_mtab_info(); return var_mtab_is_a_symlink; } int mtab_is_writable() { static int ret = -1; /* Should we write to /etc/mtab upon an update? Probably not if it is a symlink to /proc/mounts, since that would create a file /proc/mounts in case the proc filesystem is not mounted. */ if (mtab_is_a_symlink()) return 0; if (ret == -1) { int fd = open(MOUNTED, O_RDWR | O_CREAT, 0644); if (fd >= 0) { close(fd); ret = 1; } else ret = 0; } return ret; } /* Contents of mtab and fstab ---------------------------------*/ struct mntentchn mounttable, fstab; static int got_mtab = 0; /* static int got_fstab = 0; -- OCFS2 modification */ static void read_mounttable(void); /*, read_fstab(void); -- OCFS2 modification */ struct mntentchn * mtab_head() { if (!got_mtab) read_mounttable(); return &mounttable; } #if 0 /* OCFS2 modification */ struct mntentchn * fstab_head() { if (!got_fstab) read_fstab(); return &fstab; } #endif static void my_free(const void *s) { if (s) free((void *) s); } static void discard_mntentchn(struct mntentchn *mc0) { struct mntentchn *mc, *mc1; for (mc = mc0->nxt; mc && mc != mc0; mc = mc1) { mc1 = mc->nxt; my_free(mc->m.mnt_fsname); my_free(mc->m.mnt_dir); my_free(mc->m.mnt_type); my_free(mc->m.mnt_opts); free(mc); } } static void read_mntentchn(mntFILE *mfp, const char *fnam, struct mntentchn *mc0) { struct mntentchn *mc = mc0; struct my_mntent *mnt; while ((mnt = my_getmntent(mfp)) != NULL) { if (!streq(mnt->mnt_type, MNTTYPE_IGNORE)) { mc->nxt = (struct mntentchn *) xmalloc(sizeof(*mc)); mc->nxt->prev = mc; mc = mc->nxt; mc->m = *mnt; mc->nxt = mc0; } } mc0->prev = mc; if (ferror(mfp->mntent_fp)) { int errsv = errno; error(_("warning: error reading %s: %s"), fnam, strerror (errsv)); mc0->nxt = mc0->prev = NULL; } my_endmntent(mfp); } /* * Read /etc/mtab. If that fails, try /proc/mounts. * This produces a linked list. The list head mounttable is a dummy. * Return 0 on success. */ static void read_mounttable() { mntFILE *mfp; const char *fnam; struct mntentchn *mc = &mounttable; got_mtab = 1; mc->nxt = mc->prev = NULL; fnam = MOUNTED; mfp = my_setmntent (fnam, "r"); if (mfp == NULL || mfp->mntent_fp == NULL) { int errsv = errno; fnam = PROC_MOUNTS; mfp = my_setmntent (fnam, "r"); if (mfp == NULL || mfp->mntent_fp == NULL) { error(_("warning: can't open %s: %s"), MOUNTED, strerror (errsv)); return; } if (verbose) printf (_("mount: could not open %s - " "using %s instead\n"), MOUNTED, PROC_MOUNTS); } read_mntentchn(mfp, fnam, mc); } #if 0 /* OCFS2 modification */ static void read_fstab() { mntFILE *mfp = NULL; const char *fnam; struct mntentchn *mc = &fstab; got_fstab = 1; mc->nxt = mc->prev = NULL; fnam = _PATH_FSTAB; mfp = my_setmntent (fnam, "r"); if (mfp == NULL || mfp->mntent_fp == NULL) { int errsv = errno; error(_("warning: can't open %s: %s"), _PATH_FSTAB, strerror (errsv)); return; } read_mntentchn(mfp, fnam, mc); } #endif /* Given the name NAME, try to find it in mtab. */ struct mntentchn * getmntfile (const char *name) { struct mntentchn *mc, *mc0; mc0 = mtab_head(); for (mc = mc0->nxt; mc && mc != mc0; mc = mc->nxt) if (streq(mc->m.mnt_dir, name) || streq(mc->m.mnt_fsname, name)) return mc; return NULL; } /* * Given the directory name NAME, and the place MCPREV we found it last time, * try to find more occurrences. */ struct mntentchn * getmntdirbackward (const char *name, struct mntentchn *mcprev) { struct mntentchn *mc, *mc0; mc0 = mtab_head(); if (!mcprev) mcprev = mc0; for (mc = mcprev->prev; mc && mc != mc0; mc = mc->prev) if (streq(mc->m.mnt_dir, name)) return mc; return NULL; } /* * Given the device name NAME, and the place MCPREV we found it last time, * try to find more occurrences. */ struct mntentchn * getmntdevbackward (const char *name, struct mntentchn *mcprev) { struct mntentchn *mc, *mc0; mc0 = mtab_head(); if (!mcprev) mcprev = mc0; for (mc = mcprev->prev; mc && mc != mc0; mc = mc->prev) if (streq(mc->m.mnt_fsname, name)) return mc; return NULL; } /* * Given the name NAME, check that it occurs precisely once as dir or dev. */ int is_mounted_once(const char *name) { struct mntentchn *mc, *mc0; int ct = 0; mc0 = mtab_head(); for (mc = mc0->prev; mc && mc != mc0; mc = mc->prev) if (streq(mc->m.mnt_dir, name) || streq(mc->m.mnt_fsname, name)) ct++; return (ct == 1); } /* Given the name FILE, try to find the option "loop=FILE" in mtab. */ struct mntentchn * getmntoptfile (const char *file) { struct mntentchn *mc, *mc0; const char *opts, *s; int l; if (!file) return NULL; l = strlen(file); mc0 = mtab_head(); for (mc = mc0->nxt; mc && mc != mc0; mc = mc->nxt) if ((opts = mc->m.mnt_opts) != NULL && (s = strstr(opts, "loop=")) && !strncmp(s+5, file, l) && (s == opts || s[-1] == ',') && (s[l+5] == 0 || s[l+5] == ',')) return mc; return NULL; } #if 0 /* OCFS2 modification */ static int has_label(const char *device, const char *label) { const char *devlabel; int ret; devlabel = mount_get_volume_label_by_spec(device); ret = !strcmp(label, devlabel); /* free(devlabel); */ return ret; } static int has_uuid(const char *device, const char *uuid){ const char *devuuid; int ret; devuuid = mount_get_devname_by_uuid(device); ret = !strcmp(uuid, devuuid); /* free(devuuid); */ return ret; } /* Find the entry (SPEC,FILE) in fstab */ struct mntentchn * getfsspecfile (const char *spec, const char *file) { struct mntentchn *mc, *mc0; mc0 = fstab_head(); /* first attempt: names occur precisely as given */ for (mc = mc0->nxt; mc && mc != mc0; mc = mc->nxt) if (streq(mc->m.mnt_dir, file) && streq(mc->m.mnt_fsname, spec)) return mc; /* second attempt: names found after symlink resolution */ for (mc = mc0->nxt; mc && mc != mc0; mc = mc->nxt) if ((streq(mc->m.mnt_dir, file) || streq(canonicalize(mc->m.mnt_dir), file)) && (streq(mc->m.mnt_fsname, spec) || streq(canonicalize(mc->m.mnt_fsname), spec))) return mc; /* third attempt: names found after LABEL= or UUID= resolution */ for (mc = mc0->nxt; mc && mc != mc0; mc = mc->nxt) { if (!strncmp (mc->m.mnt_fsname, "LABEL=", 6) && (streq(mc->m.mnt_dir, file) || streq(canonicalize(mc->m.mnt_dir), file))) { if (has_label(spec, mc->m.mnt_fsname+6)) return mc; } if (!strncmp (mc->m.mnt_fsname, "UUID=", 5) && (streq(mc->m.mnt_dir, file) || streq(canonicalize(mc->m.mnt_dir), file))) { if (has_uuid(spec, mc->m.mnt_fsname+5)) return mc; } } return NULL; } /* Find the dir FILE in fstab. */ struct mntentchn * getfsfile (const char *file) { struct mntentchn *mc, *mc0; mc0 = fstab_head(); for (mc = mc0->nxt; mc && mc != mc0; mc = mc->nxt) if (streq(mc->m.mnt_dir, file)) return mc; return NULL; } /* Find the device SPEC in fstab. */ struct mntentchn * getfsspec (const char *spec) { struct mntentchn *mc, *mc0; mc0 = fstab_head(); for (mc = mc0->nxt; mc && mc != mc0; mc = mc->nxt) if (streq(mc->m.mnt_fsname, spec)) return mc; return NULL; } /* Find the uuid UUID in fstab. */ struct mntentchn * getfsuuidspec (const char *uuid) { struct mntentchn *mc, *mc0; mc0 = fstab_head(); for (mc = mc0->nxt; mc && mc != mc0; mc = mc->nxt) if (strncmp (mc->m.mnt_fsname, "UUID=", 5) == 0 && streq(mc->m.mnt_fsname + 5, uuid)) return mc; return NULL; } /* Find the label LABEL in fstab. */ struct mntentchn * getfsvolspec (const char *label) { struct mntentchn *mc, *mc0; mc0 = fstab_head(); for (mc = mc0->nxt; mc && mc != mc0; mc = mc->nxt) if (strncmp (mc->m.mnt_fsname, "LABEL=", 6) == 0 && streq(mc->m.mnt_fsname + 6, label)) return mc; return NULL; } #endif /* Updating mtab ----------------------------------------------*/ /* Flag for already existing lock file. */ static int we_created_lockfile = 0; /* Flag to indicate that signals have been set up. */ static int signals_have_been_setup = 0; /* Ensure that the lock is released if we are interrupted. */ extern char *strsignal(int sig); /* not always in */ static void handler (int sig) { die(EX_USER, "%s", strsignal(sig)); } static void setlkw_timeout (int sig) { /* nothing, fcntl will fail anyway */ } /* Remove lock file. */ void unlock_mtab (void) { if (we_created_lockfile) { unlink (MOUNTED_LOCK); we_created_lockfile = 0; } } /* Create the lock file. The lock file will be removed if we catch a signal or when we exit. */ /* The old code here used flock on a lock file /etc/mtab~ and deleted this lock file afterwards. However, as rgooch remarks, that has a race: a second mount may be waiting on the lock and proceed as soon as the lock file is deleted by the first mount, and immediately afterwards a third mount comes, creates a new /etc/mtab~, applies flock to that, and also proceeds, so that the second and third mount now both are scribbling in /etc/mtab. The new code uses a link() instead of a creat(), where we proceed only if it was us that created the lock, and hence we always have to delete the lock afterwards. Now the use of flock() is in principle superfluous, but avoids an arbitrary sleep(). */ /* Where does the link point to? Obvious choices are mtab and mtab~~. HJLu points out that the latter leads to races. Right now we use mtab~. instead. Use 20 as upper bound for the length of %d. */ #define MOUNTLOCK_LINKTARGET MOUNTED_LOCK "%d" #define MOUNTLOCK_LINKTARGET_LTH (sizeof(MOUNTED_LOCK)+20) void lock_mtab (void) { int tries = 3; char linktargetfile[MOUNTLOCK_LINKTARGET_LTH]; at_die = unlock_mtab; if (!signals_have_been_setup) { int sig = 0; struct sigaction sa; sa.sa_handler = handler; sa.sa_flags = 0; sigfillset (&sa.sa_mask); while (sigismember (&sa.sa_mask, ++sig) != -1 && sig != SIGCHLD) { if (sig == SIGALRM) sa.sa_handler = setlkw_timeout; else sa.sa_handler = handler; sigaction (sig, &sa, (struct sigaction *) 0); } signals_have_been_setup = 1; } sprintf(linktargetfile, MOUNTLOCK_LINKTARGET, getpid ()); /* Repeat until it was us who made the link */ while (!we_created_lockfile) { struct flock flock; int errsv, fd, i, j; i = open (linktargetfile, O_WRONLY|O_CREAT, 0); if (i < 0) { int errsv = errno; /* linktargetfile does not exist (as a file) and we cannot create it. Read-only filesystem? Too many files open in the system? Filesystem full? */ die (EX_FILEIO, _("can't create lock file %s: %s " "(use -n flag to override)"), linktargetfile, strerror (errsv)); } close(i); j = link(linktargetfile, MOUNTED_LOCK); errsv = errno; (void) unlink(linktargetfile); if (j == 0) we_created_lockfile = 1; if (j < 0 && errsv != EEXIST) { die (EX_FILEIO, _("can't link lock file %s: %s " "(use -n flag to override)"), MOUNTED_LOCK, strerror (errsv)); } fd = open (MOUNTED_LOCK, O_WRONLY); if (fd < 0) { int errsv = errno; /* Strange... Maybe the file was just deleted? */ if (errno == ENOENT && tries-- > 0) continue; die (EX_FILEIO, _("can't open lock file %s: %s " "(use -n flag to override)"), MOUNTED_LOCK, strerror (errsv)); } flock.l_type = F_WRLCK; flock.l_whence = SEEK_SET; flock.l_start = 0; flock.l_len = 0; if (j == 0) { /* We made the link. Now claim the lock. */ if (fcntl (fd, F_SETLK, &flock) == -1) { if (verbose) { int errsv = errno; printf(_("Can't lock lock file %s: %s\n"), MOUNTED_LOCK, strerror (errsv)); } /* proceed anyway */ } } else { static int tries = 0; /* Someone else made the link. Wait. */ alarm(LOCK_TIMEOUT); if (fcntl (fd, F_SETLKW, &flock) == -1) { int errsv = errno; die (EX_FILEIO, _("can't lock lock file %s: %s"), MOUNTED_LOCK, (errno == EINTR) ? _("timed out") : strerror (errsv)); } alarm(0); /* Limit the number of iterations - maybe there still is some old /etc/mtab~ */ if (tries++ > 3) { if (tries > 5) die (EX_FILEIO, _("Cannot create link %s\n" "Perhaps there is a stale lock file?\n"), MOUNTED_LOCK); sleep(1); } } close(fd); } } /* * Update the mtab. * Used by umount with null INSTEAD: remove the last DIR entry. * Used by mount upon a remount: update option part, * and complain if a wrong device or type was given. * [Note that often a remount will be a rw remount of / * where there was no entry before, and we'll have to believe * the values given in INSTEAD.] */ void update_mtab (const char *dir, struct my_mntent *instead) { mntFILE *mfp, *mftmp; const char *fnam = MOUNTED; struct mntentchn mtabhead; /* dummy */ struct mntentchn *mc, *mc0, *absent = NULL; if (mtab_does_not_exist() || mtab_is_a_symlink()) return; lock_mtab(); /* having locked mtab, read it again */ mc0 = mc = &mtabhead; mc->nxt = mc->prev = NULL; mfp = my_setmntent(fnam, "r"); if (mfp == NULL || mfp->mntent_fp == NULL) { int errsv = errno; error (_("cannot open %s (%s) - mtab not updated"), fnam, strerror (errsv)); goto leave; } read_mntentchn(mfp, fnam, mc); /* find last occurrence of dir */ for (mc = mc0->prev; mc && mc != mc0; mc = mc->prev) if (streq(mc->m.mnt_dir, dir)) break; if (mc && mc != mc0) { if (instead == NULL) { /* An umount - remove entry */ if (mc && mc != mc0) { mc->prev->nxt = mc->nxt; mc->nxt->prev = mc->prev; free(mc); } } else { /* A remount */ mc->m.mnt_opts = instead->mnt_opts; } } else if (instead) { /* not found, add a new entry */ absent = xmalloc(sizeof(*absent)); absent->m = *instead; absent->nxt = mc0; absent->prev = mc0->prev; mc0->prev = absent; if (mc0->nxt == NULL) mc0->nxt = absent; } /* write chain to mtemp */ mftmp = my_setmntent (MOUNTED_TEMP, "w"); if (mftmp == NULL || mftmp->mntent_fp == NULL) { int errsv = errno; error (_("cannot open %s (%s) - mtab not updated"), MOUNTED_TEMP, strerror (errsv)); goto leave; } for (mc = mc0->nxt; mc && mc != mc0; mc = mc->nxt) { if (my_addmntent(mftmp, &(mc->m)) == 1) { int errsv = errno; die (EX_FILEIO, _("error writing %s: %s"), MOUNTED_TEMP, strerror (errsv)); } } discard_mntentchn(mc0); if (fchmod (fileno (mftmp->mntent_fp), S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) < 0) { int errsv = errno; fprintf(stderr, _("error changing mode of %s: %s\n"), MOUNTED_TEMP, strerror (errsv)); } my_endmntent (mftmp); { /* * If mount is setuid and some non-root user mounts sth, * then mtab.tmp might get the group of this user. Copy uid/gid * from the present mtab before renaming. */ struct stat sbuf; if (stat (MOUNTED, &sbuf) == 0) if (chown (MOUNTED_TEMP, sbuf.st_uid, sbuf.st_gid) < 0) { int errsv = errno; fprintf(stderr, _("error changing ownership of %s: %s\n"), MOUNTED_TEMP, strerror (errsv)); } } /* rename mtemp to mtab */ if (rename (MOUNTED_TEMP, MOUNTED) < 0) { int errsv = errno; fprintf(stderr, _("can't rename %s to %s: %s\n"), MOUNTED_TEMP, MOUNTED, strerror(errsv)); } leave: unlock_mtab(); } ocfs2-tools-ocfs2-tools-1.8.6/mount.ocfs2/fstab.h000066400000000000000000000017151347147137200215120ustar00rootroot00000000000000#include "mntent.h" int mtab_is_writable(void); int mtab_does_not_exist(void); int mtab_is_a_symlink(void); int is_mounted_once(const char *name); struct mntentchn { struct mntentchn *nxt, *prev; struct my_mntent m; }; struct mntentchn *mtab_head (void); struct mntentchn *getmntfile (const char *name); struct mntentchn *getmntoptfile (const char *file); struct mntentchn *getmntdirbackward (const char *dir, struct mntentchn *mc); struct mntentchn *getmntdevbackward (const char *dev, struct mntentchn *mc); #if 0 /* OCFS2 modification */ struct mntentchn *fstab_head (void); struct mntentchn *getfsfile (const char *file); struct mntentchn *getfsspec (const char *spec); struct mntentchn *getfsspecfile (const char *spec, const char *file); struct mntentchn *getfsuuidspec (const char *uuid); struct mntentchn *getfsvolspec (const char *label); #endif void lock_mtab (void); void unlock_mtab (void); void update_mtab (const char *special, struct my_mntent *with); ocfs2-tools-ocfs2-tools-1.8.6/mount.ocfs2/mntent.c000066400000000000000000000112571347147137200217150ustar00rootroot00000000000000/* Private version of the libc *mntent() routines. */ /* Note slightly different prototypes. */ /* 1999-02-22 Arkadiusz Mikiewicz * - added Native Language Support */ #include #include /* for index */ #include /* for isdigit */ #include /* for umask */ #include "mntent.h" #include "sundries.h" /* for xmalloc */ #include "nls.h" /* Unfortunately the classical Unix /etc/mtab and /etc/fstab do not handle directory names containing spaces. Here we mangle them, replacing a space by \040. What do other Unices do? */ static unsigned char need_escaping[] = { ' ', '\t', '\n', '\\' }; static char * mangle(const char *s) { char *ss, *sp; int n; n = strlen(s); ss = sp = xmalloc(4*n+1); while(1) { for (n = 0; n < sizeof(need_escaping); n++) { if (*s == need_escaping[n]) { *sp++ = '\\'; *sp++ = '0' + ((*s & 0300) >> 6); *sp++ = '0' + ((*s & 070) >> 3); *sp++ = '0' + (*s & 07); goto next; } } *sp++ = *s; if (*s == 0) break; next: s++; } return ss; } static int is_space_or_tab (char c) { return (c == ' ' || c == '\t'); } static char * skip_spaces(char *s) { while (is_space_or_tab(*s)) s++; return s; } static char * skip_nonspaces(char *s) { while (*s && !is_space_or_tab(*s)) s++; return s; } #define isoctal(a) (((a) & ~7) == '0') /* returns malloced pointer - no more strdup required */ static char * unmangle(char *s) { char *ret, *ss, *sp; ss = skip_nonspaces(s); ret = sp = xmalloc(ss-s+1); while(s != ss) { if (*s == '\\' && isoctal(s[1]) && isoctal(s[2]) && isoctal(s[3])) { *sp++ = 64*(s[1] & 7) + 8*(s[2] & 7) + (s[3] & 7); s += 4; } else *sp++ = *s++; } *sp = 0; return ret; } /* * fstat'ing the file and allocating a buffer holding all of it * may be a bad idea: if the file is /proc/mounts, the stat * returns 0. * (On the other hand, mangling and unmangling is meaningless * for /proc/mounts.) */ mntFILE * my_setmntent (const char *file, char *mode) { mntFILE *mfp = xmalloc(sizeof(*mfp)); mode_t old_umask = umask(077); mfp->mntent_fp = fopen(file, mode); umask(old_umask); mfp->mntent_file = xstrdup(file); mfp->mntent_errs = (mfp->mntent_fp == NULL); mfp->mntent_softerrs = 0; mfp->mntent_lineno = 0; return mfp; } void my_endmntent (mntFILE *mfp) { if (mfp) { if (mfp->mntent_fp) fclose(mfp->mntent_fp); if (mfp->mntent_file) free(mfp->mntent_file); free(mfp); } } int my_addmntent (mntFILE *mfp, struct my_mntent *mnt) { char *m1, *m2, *m3, *m4; int res; if (fseek (mfp->mntent_fp, 0, SEEK_END)) return 1; /* failure */ m1 = mangle(mnt->mnt_fsname); m2 = mangle(mnt->mnt_dir); m3 = mangle(mnt->mnt_type); m4 = mangle(mnt->mnt_opts); res = fprintf (mfp->mntent_fp, "%s %s %s %s %d %d\n", m1, m2, m3, m4, mnt->mnt_freq, mnt->mnt_passno); free(m1); free(m2); free(m3); free(m4); return (res < 0) ? 1 : 0; } /* Read the next entry from the file fp. Stop reading at an incorrect entry. */ struct my_mntent * my_getmntent (mntFILE *mfp) { static char buf[4096]; static struct my_mntent me; char *s; again: if (mfp->mntent_errs || mfp->mntent_softerrs >= ERR_MAX) return NULL; /* read the next non-blank non-comment line */ do { if (fgets (buf, sizeof(buf), mfp->mntent_fp) == NULL) return NULL; mfp->mntent_lineno++; s = index (buf, '\n'); if (s == NULL) { /* Missing final newline? Otherwise extremely */ /* long line - assume file was corrupted */ if (feof(mfp->mntent_fp)) { fprintf(stderr, _("[mntent]: warning: no final " "newline at the end of %s\n"), mfp->mntent_file); s = index (buf, 0); } else { mfp->mntent_errs = 1; goto err; } } *s = 0; if (--s >= buf && *s == '\r') *s = 0; s = skip_spaces(buf); } while (*s == '\0' || *s == '#'); me.mnt_fsname = unmangle(s); s = skip_nonspaces(s); s = skip_spaces(s); me.mnt_dir = unmangle(s); s = skip_nonspaces(s); s = skip_spaces(s); me.mnt_type = unmangle(s); s = skip_nonspaces(s); s = skip_spaces(s); me.mnt_opts = unmangle(s); s = skip_nonspaces(s); s = skip_spaces(s); if (isdigit(*s)) { me.mnt_freq = atoi(s); while(isdigit(*s)) s++; } else me.mnt_freq = 0; if(*s && !is_space_or_tab(*s)) goto err; s = skip_spaces(s); if(isdigit(*s)) { me.mnt_passno = atoi(s); while(isdigit(*s)) s++; } else me.mnt_passno = 0; if(*s && !is_space_or_tab(*s)) goto err; /* allow more stuff, e.g. comments, on this line */ return &me; err: mfp->mntent_softerrs++; fprintf(stderr, _("[mntent]: line %d in %s is bad%s\n"), mfp->mntent_lineno, mfp->mntent_file, (mfp->mntent_errs || mfp->mntent_softerrs >= ERR_MAX) ? _("; rest of file ignored") : ""); goto again; } ocfs2-tools-ocfs2-tools-1.8.6/mount.ocfs2/mntent.h000066400000000000000000000010441347147137200217130ustar00rootroot00000000000000#ifndef MY_MNTENT_H #define MY_MNTENT_H struct my_mntent { const char *mnt_fsname; const char *mnt_dir; const char *mnt_type; const char *mnt_opts; int mnt_freq; int mnt_passno; }; #define ERR_MAX 5 typedef struct mntFILEstruct { FILE *mntent_fp; char *mntent_file; int mntent_lineno; int mntent_errs; int mntent_softerrs; } mntFILE; mntFILE *my_setmntent (const char *file, char *mode); void my_endmntent (mntFILE *mfp); int my_addmntent (mntFILE *mfp, struct my_mntent *mnt); struct my_mntent *my_getmntent (mntFILE *mfp); #endif ocfs2-tools-ocfs2-tools-1.8.6/mount.ocfs2/mount.ocfs2.8.in000066400000000000000000000132761347147137200231220ustar00rootroot00000000000000.TH "mount.ocfs2" "8" "January 2012" "Version @VERSION@" "OCFS2 Manual Pages" .SH "NAME" mount.ocfs2 \- mount an \fIOCFS2\fR filesystem .SH "SYNOPSIS" \fBmount.ocfs2\fR [\fI\-vn\fR] [\fB\-o options\fR] \fIdevice\fR \fIdir\fR .SH "DESCRIPTION" .PP \fBmount.ocfs2\fR mounts an \fIOCFS2\fR filesystem at \fIdir\fR. It is usually invoked indirectly by the \fBmount(8)\fR command. .SH "OPTIONS" .TP \fB\_netdev\fR Indicates that the file system resides on a device that requires network access (used to prevent the system from attempting to mount these filesystems until the network has been enabled on the system). \fBmount.ocfs2(8)\fR transparently appends this option during mount. However, users mounting the volume via /etc/fstab must explicitly specify this mount option to delay the system from mounting the volume until after the network has been enabled. .TP \fBnoatime\fR The file system will not update access time. .TP \fBrelatime\fR The file system will update atime only if the on-disk atime is older than mtime or ctime. .TP \fBstrictatime,atime\_quantum=nrsec\fR The file system will always perform atime updates, but the minimum update interval is specified by atime_quantum which defaults to 60 secs. Set it to zero to always update atime. These two options need work together. .TP \fB[no]acl\fR Enables / disables POSIX ACLs (access control lists) support. .TP \fB[no]user_xattr\fR Enables / disables extended user attributes. .TP \fBcommit=nrsec\fR Sync all data and metadata every nrsec seconds. The default value is 5 seconds. Zero means default. .TP \fBdata=[ordered|writeback]\fR Specifies the handling of file data during metadata journalling. .RS .TP \fBordered\fR This is the default mode. Data is flushed to disk before the corresponding meta-data is committed to the journal. .TP \fBwriteback\fR Data ordering is not preserved - data may be flushed to disk after the corresponding meta-data is committed to the journal. This is rumored to be the higher-throughput option. While it guarantees internal file system integrity, it can allow old data to appear in files after a crash and journal recovery. .RE .TP \fBerrors=[remount-ro|errors=panic|errors=continue]\fR Specifies the behavior when an on-disk corruption is encountered. .RS .TP \fBremount-ro\fR This is the default mode. The file system is remounted read-only. .TP \fBpanic\fR The system is halted via panic. .RE .RS .TP \fBcontinue\fR Ignore errors. Just log error message, return error code to the calling process and continue. .RE .TP \fBlocalflocks\fR This disables cluster-aware \fBflock(2)\fR. .TP \fBcoherency=[full|coherency]\fR Specifies the extent of coherency for the cached file data across the cluster. This mount option works with Linux kernel \fB2.6.37\fR and later. .RS .TP \fBfull\fR This is the default mode. The file system ensures the cached file data is coherent across the cluster for all IO modes. .TP \fBbuffered\fR The file system only ensures the cached file data coherency for buffered mode IOs. It does not perform IO serialization for direct IOs. This allows multiple nodes to perform concurrent direct IOs to the same file. This is the recommended mode for volumes hosting \fIdatabase\fR files. .RE .TP \fBresv_level=level\fR Specifies the level of allocation reservation for files. The higher the value, the more aggressive it is. Valid values are between 0 (reservation off) to 8 (maximum space for reservation). It defaults to 2. This mount option works with Linux kernel \fB2.6.35\fR and later. .TP \fBdir_resv_level=level\fR By default, directory reservation scales with file reserveration. Users should rarely need to change this value. If the file allocation reservation is turned off, this option will have no effect. This mount option works with Linux kernel \fB2.6.35\fR and later. .TP \fBinode64\fR Indicates that the file system can create inodes at any location in the volume, including those which will result in inode numbers greater than 4 billion. .TP \fB[no]intr\fR Specifies whether a signal can interrupt IOs. It is disabled by default. .TP \fBro\fR Mount the file system read-only. .TP \fBrw\fR Mount the file system read-write. .SH "NOTES" .PP To mount and umount a \fIOCFS2\fR volume, do: .nf .ft 6 # mount /dev/sda1 /mount/path ... # umount /mount/path .ft .fi Users mounting a clustered volume should be aware of the following: .in +4n 1. The cluster stack must to be online for a clustered mount to succeed. 2. The clustered mount operation is not instantaneous; it must wait for the node to join the DLM domain. 3. Likewise, clustered umount is also not instantaneous; it involves migrating all mastered lock-resources to the other nodes in the cluster. .in If the mount fails, detailed errors can be found via \fBdmesg(8)\fR. These might include incorrect cluster configuration (say, a missing node or incorrect IP address) or a firewall interfering with \fBo2cb\fR network traffic. Check the configuration as listed in \fBo2cb(7)\fR or the man page of the active cluster stack. To auto-mount volumes on startup, the file system tools include an \fIocfs2\fR init service. This runs after the \fIo2cb\fR init service has started the cluster. The \fIocfs2\fR init service mounts all \fIOCFS2\fR volumes listed in /etc/fstab. .nf .ft 6 # chkconfig --add o2cb o2cb 0:off 1:off 2:on 3:on 4:off 5:on 6:off $ chkconfig --add ocfs2 o2cb 0:off 1:off 2:on 3:on 4:off 5:on 6:off $ cat /etc/fstab ... /dev/sda1 /u01 ocfs2 _netdev,defaults 0 0 ... .ft .fi .SH "SEE ALSO" .BR debugfs.ocfs2(8) .BR fsck.ocfs2(8) .BR mkfs.ocfs2(8) .BR mounted.ocfs2(8) .BR o2cb(7) .BR o2cluster(8) .BR o2image(8) .BR o2info(1) .BR tunefs.ocfs2(8) .SH "AUTHORS" Oracle Corporation .SH "COPYRIGHT" Copyright \(co 2004, 2012 Oracle. All rights reserved. ocfs2-tools-ocfs2-tools-1.8.6/mount.ocfs2/mount.ocfs2.c000066400000000000000000000244341347147137200225660ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * mount.ocfs2.c Mounts ocfs2 volume * * Copyright (C) 2005 Oracle. All rights reserved. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * */ #include "mount.ocfs2.h" #define OCFS2_CLUSTER_STACK_ARG "cluster_stack=" int verbose = 0; int mount_quiet = 0; char *progname = NULL; static int nomtab = 0; struct mount_options { char *dev; char *dir; char *opts; int flags; char *xtra_opts; char *type; }; static void handle_signal(int sig) { switch (sig) { case SIGTERM: case SIGINT: printf("\nmount interrupted\n"); exit(1); } } static void read_options(int argc, char **argv, struct mount_options *mo) { int c; progname = basename(argv[0]); if (argc < 2) return; while(1) { c = getopt(argc, argv, "vno:t:"); if (c == -1) break; switch (c) { case 'v': ++verbose; break; case 'n': ++nomtab; break; case 'o': if (optarg) mo->opts = xstrdup(optarg); break; case 't': if (optarg) mo->type = xstrdup(optarg); break; default: break; } } if (optind < argc && argv[optind]) mo->dev = xstrdup(argv[optind]); ++optind; if (optind < argc && argv[optind]) mo->dir = xstrdup(argv[optind]); } /* * For local mounts, add heartbeat=none. * For userspace clusterstack, add cluster_stack=xxxx. * For o2cb with local heartbeat, add heartbeat=local. * For o2cb with global heartbeat, add heartbeat=global. */ static errcode_t add_mount_options(ocfs2_filesys *fs, struct o2cb_cluster_desc *cluster, char **optstr) { char *add, *extra = NULL; char stackstr[strlen(OCFS2_CLUSTER_STACK_ARG) + OCFS2_STACK_LABEL_LEN + 1]; struct ocfs2_super_block *sb = OCFS2_RAW_SB(fs->fs_super); if (ocfs2_mount_local(fs) || ocfs2_is_hard_readonly(fs)) { add = OCFS2_HB_NONE; goto addit; } if (cluster->c_stack && strcmp(cluster->c_stack, OCFS2_CLASSIC_CLUSTER_STACK)) { snprintf(stackstr, sizeof(stackstr), "%s%s", OCFS2_CLUSTER_STACK_ARG, cluster->c_stack); add = stackstr; goto addit; } if (ocfs2_cluster_o2cb_global_heartbeat(sb)) { add = OCFS2_HB_GLOBAL; goto addit; } add = OCFS2_HB_LOCAL; addit: if (*optstr && *(*optstr)) extra = xstrconcat3(*optstr, ",", add); else extra = xstrndup(add, strlen(add)); if (!extra) return OCFS2_ET_NO_MEMORY; *optstr = extra; return 0; } /* * Code based on similar function in util-linux-2.12p/mount/mount.c * */ static void print_one(const struct my_mntent *me) { if (mount_quiet) return ; printf ("%s on %s", me->mnt_fsname, me->mnt_dir); if (me->mnt_type != NULL && *(me->mnt_type) != '\0') printf (" type %s", me->mnt_type); if (me->mnt_opts) printf (" (%s)", me->mnt_opts); printf ("\n"); } static void my_free(const void *s) { if (s) free((void *) s); } /* * Code based on similar function in util-linux-2.12p/mount/mount.c * */ static void update_mtab_entry(char *spec, char *node, char *type, char *opts, int flags, int freq, int pass) { struct my_mntent mnt; mnt.mnt_fsname = canonicalize (spec); mnt.mnt_dir = canonicalize (node); mnt.mnt_type = type; mnt.mnt_opts = opts; mnt.mnt_freq = freq; mnt.mnt_passno = pass; /* We get chatty now rather than after the update to mtab since the mount succeeded, even if the write to /etc/mtab should fail. */ if (verbose) print_one (&mnt); if (!nomtab && mtab_is_writable()) { if (flags & MS_REMOUNT) update_mtab (mnt.mnt_dir, &mnt); else { mntFILE *mfp; lock_mtab(); mfp = my_setmntent(MOUNTED, "a+"); if (mfp == NULL || mfp->mntent_fp == NULL) { com_err(progname, OCFS2_ET_IO, "%s, %s", MOUNTED, strerror(errno)); } else { if ((my_addmntent (mfp, &mnt)) == 1) { com_err(progname, OCFS2_ET_IO, "%s, %s", MOUNTED, strerror(errno)); } } my_endmntent(mfp); unlock_mtab(); } } my_free(mnt.mnt_fsname); my_free(mnt.mnt_dir); } static int process_options(struct mount_options *mo) { if (!mo->dev) { com_err(progname, OCFS2_ET_BAD_DEVICE_NAME, " "); return -1; } if (!mo->dir) { com_err(progname, OCFS2_ET_INVALID_ARGUMENT, "no mountpoint specified"); return -1; } if (mo->type && strcmp(mo->type, OCFS2_FS_NAME)) { com_err(progname, OCFS2_ET_UNKNOWN_FILESYSTEM, "%s", mo->type); return -1; } if (mo->opts) parse_opts(mo->opts, &mo->flags, &mo->xtra_opts); return 0; } static int run_hb_ctl(const char *hb_ctl_path, const char *device, const char *arg) { int ret = 0; int child_status; char * argv[5]; pid_t child; child = fork(); if (child < 0) { ret = errno; goto bail; } if (!child) { argv[0] = (char *) hb_ctl_path; argv[1] = (char *) arg; argv[2] = "-d"; argv[3] = (char *) device; argv[4] = NULL; ret = execv(argv[0], argv); ret = errno; exit(ret); } else { ret = waitpid(child, &child_status, 0); if (ret < 0) { ret = errno; goto bail; } ret = WEXITSTATUS(child_status); } bail: return ret; } static void change_local_hb_io_priority(ocfs2_filesys *fs, char *dev) { struct ocfs2_super_block *sb = OCFS2_RAW_SB(fs->fs_super); char hb_ctl_path[PATH_MAX]; errcode_t ret; if (ocfs2_mount_local(fs)) return; if (ocfs2_userspace_stack(sb)) return; if (ocfs2_cluster_o2cb_global_heartbeat(sb)) return; ret = o2cb_get_hb_ctl_path(hb_ctl_path, sizeof(hb_ctl_path)); if (!ret) run_hb_ctl(hb_ctl_path, dev, "-P"); } int main(int argc, char **argv) { errcode_t ret = 0; struct mount_options mo; ocfs2_filesys *fs = NULL; struct o2cb_cluster_desc cluster; struct o2cb_region_desc desc; int clustered = 1; int group_join = 0; struct stat statbuf; const char *spec; char *opts_string = NULL; initialize_ocfs_error_table(); initialize_o2dl_error_table(); initialize_o2cb_error_table(); setbuf(stdout, NULL); setbuf(stderr, NULL); if (signal(SIGTERM, handle_signal) == SIG_ERR) { fprintf(stderr, "Could not set SIGTERM\n"); exit(1); } if (signal(SIGINT, handle_signal) == SIG_ERR) { fprintf(stderr, "Could not set SIGINT\n"); exit(1); } memset(&mo, 0, sizeof(mo)); read_options (argc, argv, &mo); ret = process_options(&mo); if (ret) goto bail; ret = ocfs2_open(mo.dev, OCFS2_FLAG_RO, 0, 0, &fs); //O_EXCL? if (ret) { com_err(progname, ret, "while opening device %s", mo.dev); goto bail; } clustered = (0 == ocfs2_mount_local(fs)); if (ocfs2_is_hard_readonly(fs) && (clustered || !(mo.flags & MS_RDONLY))) { ret = OCFS2_ET_IO; com_err(progname, ret, "while mounting read-only device in %s mode", (clustered ? "clustered" : "read-write")); goto bail; } if (verbose) printf("device=%s\n", mo.dev); ret = o2cb_setup_stack((char *)OCFS2_RAW_SB(fs->fs_super)->s_cluster_info.ci_stack); if (ret) { com_err(progname, ret, "while setting up stack\n"); goto bail; } if (clustered) { ret = o2cb_init(); if (ret) { com_err(progname, ret, "while trying initialize cluster"); goto bail; } ret = ocfs2_fill_cluster_desc(fs, &cluster); if (ret) { com_err(progname, ret, "while trying to determine cluster information"); goto bail; } ret = ocfs2_fill_heartbeat_desc(fs, &desc); if (ret) { com_err(progname, ret, "while trying to determine heartbeat information"); goto bail; } desc.r_persist = 1; desc.r_service = OCFS2_FS_NAME; } ret = add_mount_options(fs, &cluster, &mo.xtra_opts); if (ret) { com_err(progname, ret, "while adding mount options"); goto bail; } /* validate mount dir */ if (lstat(mo.dir, &statbuf)) { com_err(progname, 0, "mount directory %s does not exist", mo.dir); goto bail; } else if (stat(mo.dir, &statbuf)) { com_err(progname, 0, "mount directory %s is a broken symbolic " "link", mo.dir); goto bail; } else if (!S_ISDIR(statbuf.st_mode)) { com_err(progname, 0, "mount directory %s is not a directory", mo.dir); goto bail; } block_signals (SIG_BLOCK); if (clustered && !(mo.flags & MS_REMOUNT)) { ret = o2cb_begin_group_join(&cluster, &desc); if (ret) { block_signals (SIG_UNBLOCK); com_err(progname, ret, "while trying to join the group"); goto bail; } group_join = 1; } spec = canonicalize(mo.dev); ret = mount(spec, mo.dir, OCFS2_FS_NAME, mo.flags & ~MS_NOSYS, mo.xtra_opts); if (ret) { ret = errno; if (group_join) { /* We ignore the return code because the mount * failure is the important error. * complete_group_join() will handle cleaning up */ o2cb_complete_group_join(&cluster, &desc, errno); } block_signals (SIG_UNBLOCK); if (ret == -EROFS) com_err(progname, OCFS2_ET_RO_FILESYS, "while mounting %s " "on %s, please try fixing this by fsck.ocfs2 and then " "retry mounting", mo.dev, mo.dir); else com_err(progname, OCFS2_ET_INTERNAL_FAILURE, "while mounting %s on %s. Check 'dmesg' for more " "information on this error %d.", mo.dev, mo.dir, (int)ret); goto bail; } if (group_join) { ret = o2cb_complete_group_join(&cluster, &desc, 0); if (ret) { com_err(progname, ret, "while completing group join (WARNING)"); /* * XXX: GFS2 allows the mount to continue, so we * will do the same. I don't know how clean that * is, but I don't have a better solution. */ ret = 0; } } change_local_hb_io_priority(fs, mo.dev); opts_string = fix_opts_string(((mo.flags & ~MS_NOMTAB) | (clustered ? MS_NETDEV : 0)), mo.xtra_opts, NULL); update_mtab_entry(mo.dev, mo.dir, OCFS2_FS_NAME, opts_string, mo.flags, 0, 0); block_signals (SIG_UNBLOCK); bail: if (fs) ocfs2_close(fs); if (mo.dev) free(mo.dev); if (mo.dir) free(mo.dir); if (mo.opts) free(mo.opts); if (mo.xtra_opts) free(mo.xtra_opts); if (mo.type) free(mo.type); if (opts_string) free(opts_string); return ret ? 1 : 0; } ocfs2-tools-ocfs2-tools-1.8.6/mount.ocfs2/mount.ocfs2.h000066400000000000000000000026471347147137200225750ustar00rootroot00000000000000/* * mount.ocfs2.h Definitions, etc. * * Copyright (C) 2004 Oracle. All rights reserved. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * */ #define _LARGEFILE64_SOURCE #define _GNU_SOURCE /* Because libc really doesn't want us using O_DIRECT? */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "fstab.h" #include "nls.h" #include "paths.h" #include "realpath.h" #include "sundries.h" #include "xmalloc.h" #include "mntent.h" #include "mount_constants.h" #include "opts.h" #include "ocfs2/ocfs2.h" #include "ocfs2/bitops.h" ocfs2-tools-ocfs2-tools-1.8.6/mount.ocfs2/mount_constants.h000066400000000000000000000045411347147137200236510ustar00rootroot00000000000000#ifndef MS_RDONLY #define MS_RDONLY 1 /* Mount read-only */ #endif #ifndef MS_NOSUID #define MS_NOSUID 2 /* Ignore suid and sgid bits */ #endif #ifndef MS_NODEV #define MS_NODEV 4 /* Disallow access to device special files */ #endif #ifndef MS_NOEXEC #define MS_NOEXEC 8 /* Disallow program execution */ #endif #ifndef MS_SYNCHRONOUS #define MS_SYNCHRONOUS 16 /* Writes are synced at once */ #endif #ifndef MS_REMOUNT #define MS_REMOUNT 32 /* Alter flags of a mounted FS */ #endif #ifndef MS_MANDLOCK #define MS_MANDLOCK 64 /* Allow mandatory locks on an FS */ #endif #ifndef MS_DIRSYNC #define MS_DIRSYNC 128 /* Directory modifications are synchronous */ #endif #ifndef MS_ACTION_MASK #define MS_ACTION_MASK 0x380 /* Remount, but new filesystem may be different from old. Atomic (i.e. there is no interval when nothing is mounted at the mountpoint). If new fs differs from the old one and old is busy - -EBUSY. */ #define MS_REPLACE 0x080 /* 128 */ /* After, Before: as soon as we get unions these will add a new member in the end or beginning of the chain. Fail if there is a stack on the mountpoint. */ #define MS_AFTER 0x100 /* 256 */ #define MS_BEFORE 0x180 /* Over: if nothing mounted on a mountpoint - same as if none of these flags had been set; if we have a union with more than one element - fail; if we have a stack or plain mount - mount atop of it, forming a stack. */ #define MS_OVER 0x200 /* 512 */ #endif #ifndef MS_NOATIME #define MS_NOATIME 0x400 /* 1024: Do not update access times. */ #endif #ifndef MS_NODIRATIME #define MS_NODIRATIME 0x800 /* 2048: Don't update directory access times */ #endif #ifndef MS_BIND #define MS_BIND 0x1000 /* 4096: Mount existing tree also elsewhere */ #endif #ifndef MS_MOVE #define MS_MOVE 0x2000 /* 8192: Atomically move tree */ #endif #ifndef MS_REC #define MS_REC 0x4000 /* 16384: Recursive loopback */ #endif #ifndef MS_VERBOSE #define MS_VERBOSE 0x8000 /* 32768 */ #endif #ifndef MS_RELATIME #define MS_RELATIME (1<<21) /* Update atime relative to mtime/ctime. */ #endif #ifndef MS_STRICTATIME #define MS_STRICTATIME (1<<24) /* Strict atime semantics */ #endif /* * Magic mount flag number. Had to be or-ed to the flag values. */ #ifndef MS_MGC_VAL #define MS_MGC_VAL 0xC0ED0000 /* magic flag number to indicate "new" flags */ #endif #ifndef MS_MGC_MSK #define MS_MGC_MSK 0xffff0000 /* magic flag number mask */ #endif ocfs2-tools-ocfs2-tools-1.8.6/mount.ocfs2/nls.h000066400000000000000000000020161347147137200212020ustar00rootroot00000000000000/* * nls.h * * Code extracted from util-linux-2.12p/mount/nls.h * * Copyright (C) 2005 Oracle. All rights reserved. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * */ # undef bindtextdomain # define bindtextdomain(Domain, Directory) /* empty */ # undef textdomain # define textdomain(Domain) /* empty */ # define _(Text) (Text) # define N_(Text) (Text) ocfs2-tools-ocfs2-tools-1.8.6/mount.ocfs2/opts.c000066400000000000000000000176571347147137200214070ustar00rootroot00000000000000/* * opts.c Parses options for mount.ocfs2.c * * Code has been extracted from util-linux-2.12a/mount/mount.c * * Copyright (C) 2005 Oracle. All rights reserved. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * */ #include "mount.ocfs2.h" #include #include #include /* Map from -o and fstab option strings to the flag argument to mount(2). */ struct opt_map { const char *opt; /* option name */ int skip; /* skip in mtab option string */ int inv; /* true if flag value should be inverted */ int mask; /* flag mask value */ }; static const struct opt_map opt_map[] = { { "defaults", 0, 0, 0 }, /* default options */ { "ro", 1, 0, MS_RDONLY }, /* read-only */ { "rw", 1, 1, MS_RDONLY }, /* read-write */ { "exec", 0, 1, MS_NOEXEC }, /* permit execution of binaries */ { "noexec", 0, 0, MS_NOEXEC }, /* don't execute binaries */ { "suid", 0, 1, MS_NOSUID }, /* honor suid executables */ { "nosuid", 0, 0, MS_NOSUID }, /* don't honor suid executables */ { "dev", 0, 1, MS_NODEV }, /* interpret device files */ { "nodev", 0, 0, MS_NODEV }, /* don't interpret devices */ { "sync", 0, 0, MS_SYNCHRONOUS}, /* synchronous I/O */ { "async", 0, 1, MS_SYNCHRONOUS}, /* asynchronous I/O */ { "dirsync", 0, 0, MS_DIRSYNC}, /* synchronous directory modifications */ { "remount", 0, 0, MS_REMOUNT}, /* Alter flags of mounted FS */ { "bind", 0, 0, MS_BIND }, /* Remount part of tree elsewhere */ { "auto", 0, 1, MS_NOAUTO }, /* Can be mounted using -a */ { "noauto", 0, 0, MS_NOAUTO }, /* Can only be mounted explicitly */ { "users", 0, 0, MS_USERS }, /* Allow ordinary user to mount */ { "nousers", 0, 1, MS_USERS }, /* Forbid ordinary user to mount */ { "user", 0, 0, MS_USER }, /* Allow ordinary user to mount */ { "nouser", 0, 1, MS_USER }, /* Forbid ordinary user to mount */ { "owner", 0, 0, MS_OWNER }, /* Let the owner of the device mount */ { "noowner", 0, 1, MS_OWNER }, /* Device owner has no special privs */ { "group", 0, 0, MS_GROUP }, /* Let the group of the device mount */ { "nogroup", 0, 1, MS_GROUP }, /* Device group has no special privs */ { "_netdev", 0, 0, MS_NETDEV }, /* Device accessible only via network */ { "comment", 0, 0, MS_COMMENT}, /* fstab comment only (kudzu,_netdev)*/ /* add new options here */ { "pamconsole", 0, 0, MS_PAMCONSOLE }, /* Allow users at console to mount */ { "nopamconsole", 0, 1, MS_PAMCONSOLE }, /* Console user has no special privs */ #ifdef MS_NOSUB { "sub", 0, 1, MS_NOSUB }, /* allow submounts */ { "nosub", 0, 0, MS_NOSUB }, /* don't allow submounts */ #endif #ifdef MS_SILENT { "quiet", 0, 0, MS_SILENT }, /* be quiet */ { "loud", 0, 1, MS_SILENT }, /* print out messages. */ #endif { "mand", 0, 0, MS_MANDLOCK }, /* Allow mandatory locks on this FS */ { "nomand", 0, 1, MS_MANDLOCK }, /* Forbid mandatory locks on this FS */ { "loop", 1, 0, MS_LOOP }, /* use a loop device */ { "noatime", 0, 0, MS_NOATIME }, /* Do not update access time */ { "relatime", 0, 0, MS_RELATIME }, /* only update atime if previous */ /* atime is older than mtime/ctime */ { "diratime", 0, 1, MS_NODIRATIME }, /* Update dir access times */ { "nodiratime", 0, 0, MS_NODIRATIME },/* Do not update dir access times */ { "strictatime", 0, 0, MS_STRICTATIME },/* Strict atime semantics */ { "kudzu", 0, 0, MS_COMMENT }, /* Silently remove this option (backwards compat use only) */ { "managed", 0, 0, MS_COMMENT }, /* Silently remove this option */ { NULL, 0, 0, 0 } }; static char *opt_loopdev, *opt_vfstype, *opt_offset, *opt_encryption, *opt_speed, *opt_comment; static struct string_opt_map { char *tag; int skip; char **valptr; } string_opt_map[] = { { "loop=", 0, &opt_loopdev }, { "vfs=", 1, &opt_vfstype }, { "offset=", 0, &opt_offset }, { "encryption=", 0, &opt_encryption }, { "speed=", 0, &opt_speed }, { "comment=", 1, &opt_comment }, { NULL, 0, NULL } }; static void clear_string_opts(void) { struct string_opt_map *m; for (m = &string_opt_map[0]; m->tag; m++) *(m->valptr) = NULL; } static int parse_string_opt(char *s) { struct string_opt_map *m; int lth; for (m = &string_opt_map[0]; m->tag; m++) { lth = strlen(m->tag); if (!strncmp(s, m->tag, lth)) { *(m->valptr) = xstrdup(s + lth); return 1; } } return 0; } /* * Look for OPT in opt_map table and return mask value. * If OPT isn't found, tack it onto extra_opts (which is non-NULL). * For the options uid= and gid= replace user or group name by its value. */ static inline void parse_opt (const char *opt, int *mask, char *extra_opts, int len) { const struct opt_map *om; for (om = opt_map; om->opt != NULL; om++) if (streq (opt, om->opt)) { if (om->inv) *mask &= ~om->mask; else *mask |= om->mask; if ((om->mask == MS_USER || om->mask == MS_USERS || om->mask == MS_PAMCONSOLE) && !om->inv) *mask |= MS_SECURE; if ((om->mask == MS_OWNER || om->mask == MS_GROUP) && !om->inv) *mask |= MS_OWNERSECURE; #ifdef MS_SILENT if (om->mask == MS_SILENT && om->inv) { mount_quiet = 1; verbose = 0; } #endif return; } len -= strlen(extra_opts); if (*extra_opts && --len > 0) strcat(extra_opts, ","); /* convert nonnumeric ids to numeric */ if (!strncmp(opt, "uid=", 4) && !isdigit(opt[4])) { struct passwd *pw = getpwnam(opt+4); char uidbuf[20]; if (pw) { sprintf(uidbuf, "uid=%d", pw->pw_uid); if ((len -= strlen(uidbuf)) > 0) strcat(extra_opts, uidbuf); return; } } if (!strncmp(opt, "gid=", 4) && !isdigit(opt[4])) { struct group *gr = getgrnam(opt+4); char gidbuf[20]; if (gr) { sprintf(gidbuf, "gid=%d", gr->gr_gid); if ((len -= strlen(gidbuf)) > 0) strcat(extra_opts, gidbuf); return; } } if ((len -= strlen(opt)) > 0) strcat(extra_opts, opt); } /* Take -o options list and compute 4th and 5th args to mount(2). flags gets the standard options (indicated by bits) and extra_opts all the rest */ void parse_opts (char *options, int *flags, char **extra_opts) { *flags = 0; *extra_opts = NULL; clear_string_opts(); if (options != NULL) { char *opts = xstrdup(options); char *opt; int len = strlen(opts) + 20; *extra_opts = xmalloc(len); **extra_opts = '\0'; for (opt = strtok (opts, ","); opt; opt = strtok (NULL, ",")) if (!parse_string_opt (opt)) parse_opt (opt, flags, *extra_opts, len); free(opts); } #if 0 if (readonly) *flags |= MS_RDONLY; if (readwrite) *flags &= ~MS_RDONLY; *flags |= mounttype; #endif } /* Try to build a canonical options string. */ char *fix_opts_string (int flags, const char *extra_opts, const char *user) { const struct opt_map *om; const struct string_opt_map *m; char *new_opts; new_opts = (flags & MS_RDONLY) ? xstrndup("ro", 2) : xstrndup("rw", 2); for (om = opt_map; om->opt != NULL; om++) { if (om->skip) continue; if (om->inv || !om->mask || (flags & om->mask) != om->mask) continue; new_opts = xstrconcat3(new_opts, ",", om->opt); flags &= ~om->mask; } for (m = &string_opt_map[0]; m->tag; m++) { if (!m->skip && *(m->valptr)) new_opts = xstrconcat4(new_opts, ",", m->tag, *(m->valptr)); } if (extra_opts && *extra_opts) { new_opts = xstrconcat3(new_opts, ",", extra_opts); } if (user) { new_opts = xstrconcat3(new_opts, ",user=", user); } return new_opts; } ocfs2-tools-ocfs2-tools-1.8.6/mount.ocfs2/opts.h000066400000000000000000000035231347147137200213770ustar00rootroot00000000000000/* * opts.h Definitions, function prototypes, etc. * * Copyright (C) 2005 Oracle. All rights reserved. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * */ /* Custom mount options for our own purposes. */ /* Maybe these should now be freed for kernel use again */ #define MS_NOAUTO 0x80000000 #define MS_USERS 0x40000000 #define MS_USER 0x20000000 #define MS_OWNER 0x10000000 #define MS_GROUP 0x08000000 #define MS_PAMCONSOLE 0x04000000 #define MS_NETDEV 0x00040000 #define MS_COMMENT 0x00020000 #define MS_LOOP 0x00010000 /* Options that we keep the mount system call from seeing. */ #define MS_NOSYS (MS_NOAUTO|MS_USERS|MS_USER|MS_COMMENT|MS_LOOP|MS_PAMCONSOLE|MS_NETDEV) /* Options that we keep from appearing in the options field in the mtab. */ #define MS_NOMTAB (MS_REMOUNT|MS_NOAUTO|MS_USERS|MS_USER|MS_PAMCONSOLE) /* Options that we make ordinary users have by default. */ #define MS_SECURE (MS_NOEXEC|MS_NOSUID|MS_NODEV) /* Options that we make owner-mounted devices have by default */ #define MS_OWNERSECURE (MS_NOSUID|MS_NODEV) void parse_opts (char *options, int *flags, char **extra_opts); char *fix_opts_string (int flags, const char *extra_opts, const char *user); ocfs2-tools-ocfs2-tools-1.8.6/mount.ocfs2/paths.h000066400000000000000000000004071347147137200215270ustar00rootroot00000000000000#include #define _PATH_FSTAB "/etc/fstab" #ifdef _PATH_MOUNTED #define MOUNTED_LOCK _PATH_MOUNTED "~" #define MOUNTED_TEMP _PATH_MOUNTED ".tmp" #else #define MOUNTED_LOCK "/etc/mtab~" #define MOUNTED_TEMP "/etc/mtab.tmp" #endif #define LOCK_TIMEOUT 10 ocfs2-tools-ocfs2-tools-1.8.6/mount.ocfs2/realpath.c000066400000000000000000000063711347147137200222110ustar00rootroot00000000000000/* * realpath.c -- canonicalize pathname by removing symlinks * Copyright (C) 1993 Rick Sladkey * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library 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 Library Public License for more details. */ #define resolve_symlinks /* * This routine is part of libc. We include it nevertheless, * since the libc version has some security flaws. */ #include /* for PATH_MAX */ #ifndef PATH_MAX #define PATH_MAX 8192 #endif #include #include #include #include "realpath.h" #include "sundries.h" /* for xstrdup */ #define MAX_READLINKS 32 char * myrealpath(const char *path, char *resolved_path, int maxreslth) { int readlinks = 0; char *npath; char link_path[PATH_MAX+1]; int n; char *buf = NULL; npath = resolved_path; /* If it's a relative pathname use getcwd for starters. */ if (*path != '/') { if (!getcwd(npath, maxreslth-2)) return NULL; npath += strlen(npath); if (npath[-1] != '/') *npath++ = '/'; } else { *npath++ = '/'; path++; } /* Expand each slash-separated pathname component. */ while (*path != '\0') { /* Ignore stray "/" */ if (*path == '/') { path++; continue; } if (*path == '.' && (path[1] == '\0' || path[1] == '/')) { /* Ignore "." */ path++; continue; } if (*path == '.' && path[1] == '.' && (path[2] == '\0' || path[2] == '/')) { /* Backup for ".." */ path += 2; while (npath > resolved_path+1 && (--npath)[-1] != '/') ; continue; } /* Safely copy the next pathname component. */ while (*path != '\0' && *path != '/') { if (npath-resolved_path > maxreslth-2) { errno = ENAMETOOLONG; goto err; } *npath++ = *path++; } /* Protect against infinite loops. */ if (readlinks++ > MAX_READLINKS) { errno = ELOOP; goto err; } /* See if last pathname component is a symlink. */ *npath = '\0'; n = readlink(resolved_path, link_path, PATH_MAX); if (n < 0) { /* EINVAL means the file exists but isn't a symlink. */ if (errno != EINVAL) goto err; } else { #ifdef resolve_symlinks /* Richard Gooch dislikes sl resolution */ int m; /* Note: readlink doesn't add the null byte. */ link_path[n] = '\0'; if (*link_path == '/') /* Start over for an absolute symlink. */ npath = resolved_path; else /* Otherwise back up over this component. */ while (*(--npath) != '/') ; /* Insert symlink contents into path. */ m = strlen(path); if (buf) free(buf); buf = xmalloc(m + n + 1); memcpy(buf, link_path, n); memcpy(buf + n, path, m + 1); path = buf; #endif } *npath++ = '/'; } /* Delete trailing slash but don't whomp a lone slash. */ if (npath != resolved_path+1 && npath[-1] == '/') npath--; /* Make sure it's null terminated. */ *npath = '\0'; if (buf) free(buf); return resolved_path; err: if (buf) free(buf); return NULL; } ocfs2-tools-ocfs2-tools-1.8.6/mount.ocfs2/realpath.h000066400000000000000000000001071347147137200222050ustar00rootroot00000000000000extern char *myrealpath(const char *path, char *resolved_path, int m); ocfs2-tools-ocfs2-tools-1.8.6/mount.ocfs2/sundries.c000066400000000000000000000152371347147137200222460ustar00rootroot00000000000000/* * Support functions. Exported functions are prototyped in sundries.h. * sundries.c,v 1.1.1.1 1993/11/18 08:40:51 jrs Exp * * added fcntl locking by Kjetil T. (kjetilho@math.uio.no) - aeb, 950927 * * 1999-02-22 Arkadiusz Mikiewicz * - added Native Language Support * */ #include #include #include #include /* for MNTTYPE_SWAP */ #include #include "fstab.h" #include "sundries.h" #include "realpath.h" /* #include "nfsmount.h" -- OCFS2 modification */ #include "nls.h" char * xstrndup (const char *s, int n) { char *t; if (s == NULL) die (EX_SOFTWARE, _("bug in xstrndup call")); t = xmalloc(n+1); strncpy(t,s,n); t[n] = 0; return t; } char * xstrconcat2 (const char *s, const char *t) { char *res; if (!s) s = ""; if (!t) t = ""; res = xmalloc(strlen(s) + strlen(t) + 1); strcpy(res, s); strcat(res, t); return res; } /* frees its first arg - typical use: s = xstrconcat3(s,t,u); */ char * xstrconcat3 (const char *s, const char *t, const char *u) { char *res; if (!s) s = ""; if (!t) t = ""; if (!u) u = ""; res = xmalloc(strlen(s) + strlen(t) + strlen(u) + 1); strcpy(res, s); strcat(res, t); strcat(res, u); free((void *) s); return res; } /* frees its first arg - typical use: s = xstrconcat4(s,t,u,v); */ char * xstrconcat4 (const char *s, const char *t, const char *u, const char *v) { char *res; if (!s) s = ""; if (!t) t = ""; if (!u) u = ""; if (!v) v = ""; res = xmalloc(strlen(s) + strlen(t) + strlen(u) + strlen(v) + 1); strcpy(res, s); strcat(res, t); strcat(res, u); strcat(res, v); free((void *) s); return res; } /* Call this with SIG_BLOCK to block and SIG_UNBLOCK to unblock. */ void block_signals (int how) { sigset_t sigs; sigfillset (&sigs); sigdelset(&sigs, SIGTRAP); sigdelset(&sigs, SIGSEGV); sigprocmask (how, &sigs, (sigset_t *) 0); } /* Non-fatal error. Print message and return. */ /* (print the message in a single printf, in an attempt to avoid mixing output of several threads) */ void error (const char *fmt, ...) { va_list args; char *fmt2; if (mount_quiet) return; fmt2 = xstrconcat2 (fmt, "\n"); va_start (args, fmt); vfprintf (stderr, fmt2, args); va_end (args); free (fmt2); } /* True if fstypes match. Null *TYPES means match anything, except that swap types always return false. */ /* Accept nonfs,proc,devpts and nonfs,noproc,nodevpts with the same meaning. */ int matching_type (const char *type, const char *types) { int no; /* negated types list */ int len; const char *p; if (streq (type, MNTTYPE_SWAP)) return 0; if (types == NULL) return 1; no = 0; if (!strncmp(types, "no", 2)) { no = 1; types += 2; } /* Does type occur in types, separated by commas? */ len = strlen(type); p = types; while(1) { if (!strncmp(p, "no", 2) && !strncmp(p+2, type, len) && (p[len+2] == 0 || p[len+2] == ',')) return 0; if (strncmp(p, type, len) == 0 && (p[len] == 0 || p[len] == ',')) return !no; p = index(p,','); if (!p) break; p++; } return no; } /* Returns 1 if needle found or noneedle not found in haystack * Otherwise returns 0 */ static int check_option(const char *haystack, const char *needle) { const char *p, *r; int len, needle_len, this_len; int no; no = 0; if (!strncmp(needle, "no", 2)) { no = 1; needle += 2; } needle_len = strlen(needle); len = strlen(haystack); for (p = haystack; p < haystack+len; p++) { r = strchr(p, ','); if (r) { this_len = r-p; } else { this_len = strlen(p); } if (this_len != needle_len) { p += this_len; continue; } if (strncmp(p, needle, this_len) == 0) return !no; /* foo or nofoo was found */ p += this_len; } return no; /* foo or nofoo was not found */ } /* Returns 1 if each of the test_opts options agrees with the entire * list of options. * Returns 0 if any noopt is found in test_opts and opt is found in options. * Returns 0 if any opt is found in test_opts but is not found in options. * Unlike fs type matching, nonetdev,user and nonetdev,nouser have * DIFFERENT meanings; each option is matched explicitly as specified. */ int matching_opts (const char *options, const char *test_opts) { const char *p, *r; char *q; int len, this_len; if (test_opts == NULL) return 1; len = strlen(test_opts); q = alloca(len+1); if (q == NULL) die (EX_SYSERR, _("not enough memory")); for (p = test_opts; p < test_opts+len; p++) { r = strchr(p, ','); if (r) { this_len = r-p; } else { this_len = strlen(p); } if (!this_len) continue; /* if two ',' appear in a row */ strncpy(q, p, this_len); q[this_len] = '\0'; if (!check_option(options, q)) return 0; /* any match failure means failure */ p += this_len; } /* no match failures in list means success */ return 1; } /* * Converts private "dm-N" names to "/dev/mapper/" * * Since 2.6.29 (patch 784aae735d9b0bba3f8b9faef4c8b30df3bf0128) kernel sysfs * provides the real DM device names in /sys/block//dm/name */ static char * canonicalize_dm_name(const char *ptname) { FILE *f; size_t sz; char path[256], name[256], *res = NULL; char *s; snprintf(path, sizeof(path), "/sys/block/%s/dm/name", ptname); f = fopen(path, "r"); if (!f) return NULL; /* read "\n" from sysfs */ s = fgets(name, sizeof(name), f); sz = strlen(name); if (s && sz > 1) { name[sz - 1] = '\0'; snprintf(path, sizeof(path), "/dev/mapper/%s", name); res = strdup(path); } fclose(f); return res; } /* Make a canonical pathname from PATH. Returns a freshly malloced string. It is up the *caller* to ensure that the PATH is sensible. i.e. canonicalize ("/dev/fd0/.") returns "/dev/fd0" even though ``/dev/fd0/.'' is not a legal pathname for ``/dev/fd0''. Anything we cannot parse we return unmodified. */ char * canonicalize (const char *path) { char canonical[PATH_MAX+2]; char *p; if (path == NULL) return NULL; #if 1 if (streq(path, "none") || streq(path, "proc") || streq(path, "devpts")) return xstrdup(path); #endif if (!myrealpath(path, canonical, PATH_MAX+1)) return xstrdup(path); p = strrchr(canonical, '/'); if (p && strncmp(p, "/dm-", 4) == 0 && isdigit(*(p + 4))) { p = canonicalize_dm_name(p+1); if (p) return p; } return xstrdup(canonical); } ocfs2-tools-ocfs2-tools-1.8.6/mount.ocfs2/sundries.h000066400000000000000000000032301347147137200222410ustar00rootroot00000000000000/* * sundries.h * Support function prototypes. Functions are in sundries.c. */ #include #include #include #include #include #include #if !defined(bool_t) && !defined(__GLIBC__) #include #endif extern int mount_quiet; extern int verbose; extern int sloppy; #define streq(s, t) (strcmp ((s), (t)) == 0) /* Functions in sundries.c that are used in mount.c and umount.c */ void block_signals (int how); char *canonicalize (const char *path); void error (const char *fmt, ...); int matching_type (const char *type, const char *types); int matching_opts (const char *options, const char *test_opts); void *xmalloc (size_t size); char *xstrdup (const char *s); char *xstrndup (const char *s, int n); char *xstrconcat2 (const char *, const char *); char *xstrconcat3 (const char *, const char *, const char *); char *xstrconcat4 (const char *, const char *, const char *, const char *); void die (int errcode, const char *fmt, ...); #ifdef HAVE_NFS int nfsmount (const char *spec, const char *node, int *flags, char **orig_opts, char **opt_args, int *version, int running_bg); #endif /* exit status - bits below are ORed */ #define EX_USAGE 1 /* incorrect invocation or permission */ #define EX_SYSERR 2 /* out of memory, cannot fork, ... */ #define EX_SOFTWARE 4 /* internal mount bug or wrong version */ #define EX_USER 8 /* user interrupt */ #define EX_FILEIO 16 /* problems writing, locking, ... mtab/fstab */ #define EX_FAIL 32 /* mount failure */ #define EX_SOMEOK 64 /* some mount succeeded */ #define EX_BG 256 /* retry in background (internal only) */ ocfs2-tools-ocfs2-tools-1.8.6/mount.ocfs2/xmalloc.c000066400000000000000000000015611347147137200220440ustar00rootroot00000000000000#include #include #include /* strdup() */ #include "xmalloc.h" #include "nls.h" /* _() */ #include "sundries.h" /* EX_SYSERR */ void (*at_die)(void) = NULL; /* Fatal error. Print message and exit. */ void die(int err, const char *fmt, ...) { va_list args; va_start(args, fmt); vfprintf(stderr, fmt, args); fprintf(stderr, "\n"); va_end(args); if (at_die) (*at_die)(); exit(err); } static void die_if_null(void *t) { if (t == NULL) die(EX_SYSERR, _("not enough memory")); } void * xmalloc (size_t size) { void *t; if (size == 0) return NULL; t = malloc(size); die_if_null(t); return t; } void * xrealloc (void *p, size_t size) { void *t; t = realloc(p, size); die_if_null(t); return t; } char * xstrdup (const char *s) { char *t; if (s == NULL) return NULL; t = strdup(s); die_if_null(t); return t; } ocfs2-tools-ocfs2-tools-1.8.6/mount.ocfs2/xmalloc.h000066400000000000000000000003561347147137200220520ustar00rootroot00000000000000#include #include extern void *xmalloc(size_t size); extern void *xrealloc(void *p, size_t size); extern char *xstrdup(const char *s); extern void die(int err, const char *fmt, ...); extern void (*at_die)(void); ocfs2-tools-ocfs2-tools-1.8.6/mounted.ocfs2/000077500000000000000000000000001347147137200205475ustar00rootroot00000000000000ocfs2-tools-ocfs2-tools-1.8.6/mounted.ocfs2/.gitignore000066400000000000000000000000731347147137200225370ustar00rootroot00000000000000mounted.ocfs2 stamp-md5 *.sw? cscope.* *.d mounted.ocfs2.8 ocfs2-tools-ocfs2-tools-1.8.6/mounted.ocfs2/Cscope.make000066400000000000000000000004051347147137200226210ustar00rootroot00000000000000.PHONY: cscope cscope: rm -f cscope.* echo "-k" >> cscope.files find . -name '*.[ch]' >>cscope.files find ../libocfs2/ -name '*.[ch]' >>cscope.files find ../libo2cb/ -name '*.[ch]' >>cscope.files find ../libo2dlm/ -name '*.[ch]' >>cscope.files cscope -b ocfs2-tools-ocfs2-tools-1.8.6/mounted.ocfs2/Makefile000066400000000000000000000020501347147137200222040ustar00rootroot00000000000000TOPDIR = .. include $(TOPDIR)/Preamble.make LIBOCFS2_LIBS = -L$(TOPDIR)/libocfs2 -locfs2 LIBOCFS2_DEPS = $(TOPDIR)/libocfs2/libocfs2.a LIBO2DLM_LIBS = -L$(TOPDIR)/libo2dlm -lo2dlm $(DL_LIBS) LIBO2DLM_DEPS = $(TOPDIR)/libo2dlm/libo2dlm.a LIBO2CB_LIBS = -L$(TOPDIR)/libo2cb -lo2cb LIBO2CB_DEPS = $(TOPDIR)/libo2cb/libo2cb.a ifneq ($(BUILD_FSDLM_SUPPORT),) LIBO2CB_LIBS += -ldlm_lt endif ifneq ($(BUILD_CMAP_SUPPORT),) LIBO2CB_LIBS += -lcmap endif LIBTOOLS_INTERNAL_LIBS = -L$(TOPDIR)/libtools-internal -ltools-internal LIBTOOLS_INTERNAL_DEPS = $(TOPDIR)/libtools-internal/libtools-internal.a sbindir = $(root_sbindir) SBIN_PROGRAMS = mounted.ocfs2 INCLUDES = -I$(TOPDIR)/include CFILES = mounted.c OBJS = $(subst .c,.o,$(CFILES)) MANS = mounted.ocfs2.8 DIST_FILES = $(CFILES) mounted.ocfs2.8.in mounted.ocfs2: $(OBJS) $(LIBOCFS2_DEPS) $(LIBO2DLM_DEPS) $(LIBO2CB_DEPS) ${LIBTOOLS_INTERNAL_DEPS} $(LINK) $(LIBOCFS2_LIBS) $(LIBO2DLM_LIBS) $(LIBO2CB_LIBS) ${LIBTOOLS_INTERNAL_DEPS} $(COM_ERR_LIBS) $(UUID_LIBS) $(AIO_LIBS) include $(TOPDIR)/Postamble.make ocfs2-tools-ocfs2-tools-1.8.6/mounted.ocfs2/mounted.c000066400000000000000000000322241347147137200223710ustar00rootroot00000000000000/* * mounted.c * * ocfs2 mount detect utility * * Copyright (C) 2004, 2011 Oracle. All rights reserved. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * */ #define _LARGEFILE64_SOURCE #define _GNU_SOURCE /* Because libc really doesn't want us using O_DIRECT? */ #include #include #include #include #include #include #include #include #include #include #include #include #include "ocfs2-kernel/kernel-list.h" #include "ocfs2/ocfs2.h" #include "ocfs2/byteorder.h" #include "tools-internal/verbose.h" #undef max #define max(a,b) ((a) > (b) ? (a) : (b)) #undef min #define min(a,b) ((a) < (b) ? (a) : (b)) static int quick_detect = 1; /* default */ static char *device = NULL; static char *progname = NULL; static char *usage_string = "usage: %s [-dfv] [device]\n" " -d quick detect\n" " -f full detect\n" " -v verbose\n"; static void get_max_widths(struct list_head *dev_list, int *dev_width, int *cluster_width) { ocfs2_devices *dev; struct list_head *pos; list_for_each(pos, dev_list) { dev = list_entry(pos, ocfs2_devices, list); if (dev->fs_type != 2) continue; *dev_width = max(*dev_width, strlen(dev->dev_name)); *cluster_width = max(*cluster_width, strlen(dev->cluster)); } } static void print_nodes(ocfs2_devices *dev, char **names, unsigned int length_of_names) { int i, start = 1; unsigned int node_num; struct ocfs2_slot_map_data *map = dev->map; for (i = 0; i < map->md_num_slots; i++) { if (!map->md_slots[i].sd_valid) continue; if (start) start = 0; else printf(", "); node_num = map->md_slots[i].sd_node_num; if (node_num >= length_of_names) printf("Unknown"); else if (names && names[node_num] && *(names[node_num])) printf("%s", names[node_num]); else printf("%d", node_num); } } static void print_full_detect(struct list_head *dev_list) { ocfs2_devices *dev; struct list_head *pos; char **node_names = NULL; char **cluster_names = NULL; char *nodes[O2NM_MAX_NODES]; char flag; int i = 0, dev_width = 7, cluster_width = 7; uint16_t num; get_max_widths(dev_list, &dev_width, &cluster_width); memset(nodes, 0, sizeof(nodes)); o2cb_list_clusters(&cluster_names); if (cluster_names && *cluster_names) { o2cb_list_nodes(*cluster_names, &node_names); /* sort the names according to the node number */ while(node_names && node_names[i] && *(node_names[i])) { if (o2cb_get_node_num(*cluster_names, node_names[i], &num)) break; if (num >= O2NM_MAX_NODES) break; nodes[num] = node_names[i]; ++i; } } printf("%-*s %-5s %-*s %c %-s\n", dev_width, "Device", "Stack", cluster_width, "Cluster", 'F', "Nodes"); list_for_each(pos, dev_list) { dev = list_entry(pos, ocfs2_devices, list); if (dev->fs_type != 2) continue; flag = ' '; if (!strcmp(dev->stack, OCFS2_CLASSIC_CLUSTER_STACK)) { if (dev->stackflags & OCFS2_CLUSTER_O2CB_GLOBAL_HEARTBEAT) flag = 'G'; } printf("%-*s %-5s %-*s %c ", dev_width, dev->dev_name, dev->stack, cluster_width, dev->cluster, flag); if (dev->errcode) { fflush(stdout); com_err("Unknown", dev->errcode, " "); } else { if (dev->hb_dev) printf("Heartbeat device"); else if (dev->mount_flags & OCFS2_MF_MOUNTED_CLUSTER) print_nodes(dev, nodes, O2NM_MAX_NODES); else printf("Not mounted"); printf("\n"); } } if (node_names) o2cb_free_nodes_list(node_names); if (cluster_names) o2cb_free_cluster_list(cluster_names); } static void print_quick_detect(struct list_head *dev_list) { ocfs2_devices *dev; struct list_head *pos; char uuid[OCFS2_VOL_UUID_LEN * 2 + 1]; int i, dev_width = 7, cluster_width = 7; char *p, flag; get_max_widths(dev_list, &dev_width, &cluster_width); printf("%-*s %-5s %-*s %c %-32s %-s\n", dev_width, "Device", "Stack", cluster_width, "Cluster", 'F', "UUID", "Label"); list_for_each(pos, dev_list) { dev = list_entry(pos, ocfs2_devices, list); if (dev->fs_type != 2) continue; for (i = 0, p = uuid; i < OCFS2_VOL_UUID_LEN; i++) { snprintf(p, 3, "%02X", dev->uuid[i]); p += 2; } flag = ' '; if (!strcmp(dev->stack, OCFS2_CLASSIC_CLUSTER_STACK)) { if (dev->stackflags & OCFS2_CLUSTER_O2CB_GLOBAL_HEARTBEAT) flag = 'G'; } printf("%-*s %-5s %-*s %c %-32s %-s\n", dev_width, dev->dev_name, dev->stack, cluster_width, dev->cluster, flag, uuid, dev->label); } } static void scan_dir_for_dev(char *dirname, dev_t devno, char **devname) { DIR *dir; struct dirent *dp; char path[PATH_MAX]; int dirlen; struct stat st; dir = opendir(dirname); if (dir == NULL) return; dirlen = strlen(dirname) + 2; while ((dp = readdir(dir)) != 0) { if (dirlen + strlen(dp->d_name) >= sizeof(path)) continue; if (dp->d_name[0] == '.' && ((dp->d_name[1] == 0) || ((dp->d_name[1] == '.') && (dp->d_name[2] == 0)))) continue; sprintf(path, "%s/%s", dirname, dp->d_name); if (stat(path, &st) < 0) continue; if (S_ISBLK(st.st_mode) && st.st_rdev == devno) { *devname = strdup(path); break; } } closedir(dir); return; } static void free_partition_list(struct list_head *dev_list) { struct list_head *pos1, *pos2; ocfs2_devices *dev; list_for_each_safe(pos1, pos2, dev_list) { dev = list_entry(pos1, ocfs2_devices, list); if (dev->map) ocfs2_free(&dev->map); list_del(&(dev->list)); ocfs2_free(&dev); } } static void list_rm_device(struct list_head *dev_list, int major, int minor) { struct list_head *pos1, *pos2; ocfs2_devices *dev; list_for_each_safe(pos1, pos2, dev_list) { dev = list_entry(pos1, ocfs2_devices, list); if ((dev->maj_num == major) && (dev->min_num == minor)) { if (dev->map) ocfs2_free(&dev->map); list_del(&(dev->list)); ocfs2_free(&dev); } } } static int is_partition(int major, int minor) { char path[PATH_MAX + 1]; struct stat info; snprintf(path, sizeof(path), "/sys/dev/block/%d:%d/partition", major, minor); return !stat(path, &info); } static int find_whole_disk_minor(int major, int minor) { #ifndef SCSI_BLK_MAJOR #ifdef SCSI_DISK0_MAJOR #ifdef SCSI_DISK8_MAJOR #define SCSI_DISK_MAJOR(M) ((M) == SCSI_DISK0_MAJOR || \ ((M) >= SCSI_DISK1_MAJOR && (M) <= SCSI_DISK7_MAJOR) || \ ((M) >= SCSI_DISK8_MAJOR && (M) <= SCSI_DISK15_MAJOR)) #else #define SCSI_DISK_MAJOR(M) ((M) == SCSI_DISK0_MAJOR || \ ((M) >= SCSI_DISK1_MAJOR && (M) <= SCSI_DISK7_MAJOR)) #endif /* defined(SCSI_DISK8_MAJOR) */ #define SCSI_BLK_MAJOR(M) (SCSI_DISK_MAJOR((M)) || (M) == SCSI_CDROM_MAJOR) #else #define SCSI_BLK_MAJOR(M) ((M) == SCSI_DISK_MAJOR || (M) == SCSI_CDROM_MAJOR) #endif /* defined(SCSI_DISK0_MAJOR) */ #endif /* defined(SCSI_BLK_MAJOR) */ if (major == HD_MAJOR) return (minor - (minor%64)); if (SCSI_BLK_MAJOR(major)) return (minor - (minor%16)); /* FIXME: Catch all */ return 0; } static errcode_t build_partition_list(struct list_head *dev_list, char *device) { errcode_t ret = 0; FILE *proc = NULL; char line[512]; char name[512]; char *devname = NULL; int major, minor; ocfs2_devices *dev; uint64_t numblocks; if (device) { ret = ocfs2_malloc0(sizeof(ocfs2_devices), &dev); if (ret) goto bail; strncpy(dev->dev_name, device, sizeof(dev->dev_name)); list_add(&(dev->list), dev_list); return 0; } proc = fopen ("/proc/partitions", "r"); if (proc == NULL) { ret = OCFS2_ET_IO; goto bail; } while (fgets (line, sizeof(line), proc) != NULL) { if (sscanf(line, "%d %d %*d %99[^ \t\n]", &major, &minor, name) != 3) continue; ret = ocfs2_malloc0(sizeof(ocfs2_devices), &dev); if (ret) goto bail; /* * Try to translate private device-mapper dm- names * to standard /dev/mapper/. */ if (!strncmp(name, "dm-", 3) && isdigit(name[3])) { devname = NULL; scan_dir_for_dev("/dev/mapper", makedev(major, minor), &devname); if (devname) { snprintf(dev->dev_name, sizeof(dev->dev_name), "%s", devname); free(devname); } else snprintf(dev->dev_name, sizeof(dev->dev_name), "/dev/%s", name); } else { snprintf(dev->dev_name, sizeof(dev->dev_name), "/dev/%s", name); } /* skip devices smaller than 1M */ if (ocfs2_get_device_size(dev->dev_name, 4096, &numblocks)) { verbosef(VL_DEBUG, "Unable to get size of %s\n", dev->dev_name); ocfs2_free(&dev); continue; } if (numblocks <= (1024 * 1024 / 4096)) { verbosef(VL_DEBUG, "Skipping small device %s\n", dev->dev_name); ocfs2_free(&dev); continue; } if (is_partition(major, minor)) { int whole_minor = find_whole_disk_minor(major, minor); list_rm_device(dev_list, major, whole_minor); } dev->maj_num = major; dev->min_num = minor; list_add_tail(&(dev->list), dev_list); } bail: if (proc) fclose(proc); return ret; } static void usage(char *progname) { printf(usage_string, progname); } static int read_options(int argc, char **argv) { int ret = 0; int c; progname = argv[0]; if (argc < 2) { usage(progname); ret = 1; goto bail; } while(1) { c = getopt(argc, argv, "dfv"); if (c == -1) break; switch (c) { case 'd': /* quick detect*/ quick_detect = 1; break; case 'f': /* full detect*/ quick_detect = 0; break; case 'v': tools_verbose(); break; default: break; } } if (!ret && optind < argc && argv[optind]) device = argv[optind]; bail: return ret; } static ssize_t do_pread(int fd, void *buf, size_t count, off_t offset) { ssize_t rd = 0, ret; while (1) { ret = pread(fd, buf + rd, count - rd, offset + rd); if (ret > 0) rd += ret; if (ret <= 0 || rd == count) break; } if (rd) return rd; return ret; } static void populate_sb_info(ocfs2_devices *dev, struct ocfs2_super_block *sb) { uint32_t incompat; if (!sb) return; dev->fs_type = 2; memcpy(dev->label, sb->s_label, sizeof(dev->label)); memcpy(dev->uuid, sb->s_uuid, sizeof(dev->uuid)); incompat = le32_to_cpu(sb->s_feature_incompat); memcpy(dev->label, sb->s_label, sizeof(dev->label)); memcpy(dev->uuid, sb->s_uuid, sizeof(dev->uuid)); #define CLUSTERINFO_VALID (OCFS2_FEATURE_INCOMPAT_USERSPACE_STACK | \ OCFS2_FEATURE_INCOMPAT_CLUSTERINFO) if (incompat & OCFS2_FEATURE_INCOMPAT_LOCAL_MOUNT) snprintf(dev->stack, sizeof(dev->stack), "%s", "None"); else if (incompat & CLUSTERINFO_VALID) { snprintf(dev->stack, sizeof(dev->stack), "%.*s", OCFS2_STACK_LABEL_LEN, sb->s_cluster_info.ci_stack); snprintf(dev->cluster, sizeof(dev->cluster), "%.*s", OCFS2_CLUSTER_NAME_LEN, sb->s_cluster_info.ci_cluster); dev->stackflags = sb->s_cluster_info.ci_stackflags; } else snprintf(dev->stack, sizeof(dev->stack), "%s", OCFS2_CLASSIC_CLUSTER_STACK); } static void do_quick_detect(struct list_head *dev_list) { int fd = -1, ret; char buf[512]; struct ocfs2_dinode *di; uint32_t offset; struct list_head *pos; ocfs2_devices *dev; list_for_each(pos, dev_list) { dev = list_entry(pos, ocfs2_devices, list); verbosef(VL_APP, "Probing device %s\n", dev->dev_name); fd = open(dev->dev_name, O_RDONLY); if (fd < 0) { verbosef(VL_DEBUG, "Device %s open failed with '%s'\n", dev->dev_name, strerror(errno)); continue; } /* ignore error but log if in verbose */ ret = posix_fadvise(fd, 0, (1024 * 1024), POSIX_FADV_DONTNEED); if (ret < 0) { verbosef(VL_DEBUG, "Buffer cache free for device %s " "failed with '%s'\n", dev->dev_name, strerror(errno)); } for (offset = 1; offset <= 8; offset <<= 1) { ret = do_pread(fd, buf, sizeof(buf), (offset * 1024)); if (ret < sizeof(buf)) break; di = (struct ocfs2_dinode *)buf; if (!memcmp(di->i_signature, OCFS2_SUPER_BLOCK_SIGNATURE, strlen(OCFS2_SUPER_BLOCK_SIGNATURE))) { populate_sb_info(dev, &di->id2.i_super); break; } } close(fd); } } static void do_full_detect(struct list_head *dev_list) { ocfs2_check_heartbeats(dev_list, 1); } int main(int argc, char **argv) { errcode_t ret = 0; struct list_head dev_list; initialize_ocfs_error_table(); initialize_o2dl_error_table(); initialize_o2cb_error_table(); INIT_LIST_HEAD(&(dev_list)); ret = read_options(argc, argv); if (ret) goto bail; o2cb_init(); ret = build_partition_list(&dev_list, device); if (ret) { com_err(progname, ret, "while building partition list"); goto bail; } if (quick_detect) { do_quick_detect(&dev_list); print_quick_detect(&dev_list); } else { do_full_detect(&dev_list); print_full_detect(&dev_list); } free_partition_list(&dev_list); bail: return ret; } ocfs2-tools-ocfs2-tools-1.8.6/mounted.ocfs2/mounted.ocfs2.8.in000066400000000000000000000050461347147137200237400ustar00rootroot00000000000000.TH "mounted.ocfs2" "8" "January 2012" "Version @VERSION@" "OCFS2 Manual Pages" .SH "NAME" mounted.ocfs2 \- Detects all \fIOCFS2\fR volumes on a system. .SH "SYNOPSIS" \fBmounted.ocfs2\fR [\fB\-d\fR] [\fB\-f\fR] [\fIdevice\fR] .SH "DESCRIPTION" .PP \fBmounted.ocfs2\fR is used to detect \fIOCFS2\fR volume(s) on a system. When run without specifying a \fIdevice\fR, it scans all the partitions listed in /proc/partitions. .SH "OPTIONS" .TP \fB\-d\fR Lists the \fIOCFS2\fR volumes along with their labels and uuids. It also lists the cluster stack, cluster name and the cluster flags. The possible cluster stacks are \fBo2cb\fR, \fBpcmk\fR and \fBcman\fR. \fBNone\fR indicates a local mount or a non-clustered volume. A \fBG\fR cluster flag indicates \fIglobal-heartbeat\fR for the \fBo2cb\fR cluster stack. .TP \fB\-f\fR Lists the \fIOCFS2\fR volumes along with the list of nodes that have mounted the volume. .SH "NOTES" As this utility gathers information without taking any cluster locks, the information listed in the full detect mode could be stale. This is only problematic for volumes that were not cleanly umounted by the last node. Such volumes will show up mounted (as per this utility) on one or more nodes but are in fact not mounted on any node. Such volumes are awaiting slot-recovery which is auto-performed on the next mount (or file system check). .SH "EXAMPLES" To view the list of \fIOCFS2\fR volumes, do: .nf .ps 9 .ft 6 # mounted.ocfs2 -d Device Stack Cluster F UUID Label /dev/sdc1 None 23878C320CF3478095D1318CB5C99EED localmount /dev/sdd1 o2cb webcluster G 8AB016CD59FC4327A2CDAB69F08518E3 webvol /dev/sdg1 o2cb webcluster G 77D95EF51C0149D2823674FCC162CF8B logsvol /dev/sdh1 o2cb webcluster G BBA1DBD0F73F449384CE75197D9B7098 scratch /dev/sdk1 o2cb webcluster G DCDA2845177F4D59A0F2DCD8DE507CC3 hb1 .ft .ps .fi To view the list of nodes that have potentially (see notes) mounted the \fIOCFS2\fR volumes, do: .nf .ps 9 .ft 6 # mounted.ocfs2 -f Device Stack Cluster F Nodes /dev/sdc1 None /dev/sdd1 o2cb webcluster G node1, node3, node10 /dev/sdg1 o2cb webcluster G node1, node3, node10 /dev/sdh1 o2cb webcluster G Not mounted /dev/sdk1 o2cb webcluster G node1, node3, node10 .ft .ps .fi .SH "SEE ALSO" .BR debugfs.ocfs2(8) .BR fsck.ocfs2(8) .BR mkfs.ocfs2(8) .BR mount.ocfs2(8) .BR o2cluster(8) .BR o2image(8) .BR o2info(1) .BR tunefs.ocfs2(8) .SH "AUTHORS" Oracle Corporation .SH "COPYRIGHT" Copyright \(co 2004, 2012 Oracle. All rights reserved. ocfs2-tools-ocfs2-tools-1.8.6/o2cb.pc.in000066400000000000000000000003651347147137200176430ustar00rootroot00000000000000prefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=@includedir@ Name: o2cb Description: Library for accessing the ocfs2 cluster base (o2cb) Version: @VERSION@ Requires: com_err Libs: -L${libdir} -lo2cb Cflags: -I${includedir} ocfs2-tools-ocfs2-tools-1.8.6/o2cb_ctl/000077500000000000000000000000001347147137200175505ustar00rootroot00000000000000ocfs2-tools-ocfs2-tools-1.8.6/o2cb_ctl/.gitignore000066400000000000000000000001221347147137200215330ustar00rootroot00000000000000*.sw? *.d clusterbo o2cb_ctl o2cb_ctl.8 cscope.* o2cb o2cb.8 ocfs2.cluster.conf.5 ocfs2-tools-ocfs2-tools-1.8.6/o2cb_ctl/Cscope.make000066400000000000000000000011341347147137200216220ustar00rootroot00000000000000.PHONY: cscope cscope: rm -f cscope.* echo "-k" >> cscope.files echo "-I inc" >> cscope.files find . -maxdepth 2 -name '*.c' -print >>cscope.files find . -maxdepth 2 -name '*.h' -print >>cscope.files find ../libocfs2/ -maxdepth 2 -name '*.c' -print >>cscope.files find ../libocfs2/ -maxdepth 2 -name '*.h' -print >>cscope.files find ../libo2cb/ -maxdepth 2 -name '*.c' -print >>cscope.files find ../libo2cb/ -maxdepth 2 -name '*.h' -print >>cscope.files find ../libo2dlm/ -maxdepth 2 -name '*.c' -print >>cscope.files find ../libo2dlm/ -maxdepth 2 -name '*.h' -print >>cscope.files cscope -b ocfs2-tools-ocfs2-tools-1.8.6/o2cb_ctl/Makefile000066400000000000000000000051271347147137200212150ustar00rootroot00000000000000TOPDIR = .. include $(TOPDIR)/Preamble.make sbindir = $(root_sbindir) SBIN_PROGRAMS = o2cb_ctl o2cb INCLUDES = -I$(TOPDIR)/include LIBTOOLS_INTERNAL_LIBS = -L$(TOPDIR)/libtools-internal -ltools-internal LIBTOOLS_INTERNAL_DEPS = $(TOPDIR)/libtools-internal/libtools-internal.a LIBOCFS2_LIBS = -L$(TOPDIR)/libocfs2 -locfs2 LIBOCFS2_DEPS = $(TOPDIR)/libocfs2/libocfs2.a LIBO2CB_LIBS = -L$(TOPDIR)/libo2cb -lo2cb LIBO2CB_DEPS = $(TOPDIR)/libo2cb/libo2cb.a ifneq ($(BUILD_FSDLM_SUPPORT),) LIBO2CB_LIBS += -ldlm_lt endif ifneq ($(BUILD_CMAP_SUPPORT),) LIBO2CB_LIBS += -lcmap endif LIBO2DLM_LIBS = -L$(TOPDIR)/libo2dlm -lo2dlm $(DL_LIBS) LIBO2DLM_DEPS = $(TOPDIR)/libo2dlm/libo2dlm.a ifndef OCFS2_DYNAMIC_CTL LDFLAGS += -static endif DEFINES = -DVERSION=\"$(VERSION)\" O2CB_CONFIG_CFILES = o2cb_config.c jconfig.c jiterator.c O2CB_CTL_CFILES = ${O2CB_CONFIG_CFILES} o2cb_ctl.c O2CB_CFILES = ${O2CB_CONFIG_CFILES} o2cbtool.c op_cluster.c op_node.c op_heartbeat.c \ op_lists.c op_register.c op_start.c o2cb_scandisk.c op_status.c o2cbutils.c O2CB_CTL_OBJS = $(subst .c,.o,$(O2CB_CTL_CFILES)) O2CB_OBJS = $(subst .c,.o,$(O2CB_CFILES)) HFILES = o2cb_config.h jconfig.h jiterator.h o2cbtool.h o2cb_scandisk.h CFILES = $(O2CB_CTL_CFILES) $(O2CB_CFILES) MANS = o2cb_ctl.8 o2cb.8 ocfs2.cluster.conf.5 DIST_FILES = $(CFILES) $(HFILES) o2cb_ctl.8.in o2cb.8.in ocfs2.cluster.conf.5.in DIST_RULES = dist-subdircreate jconfig_CPPFLAGS = $(GLIB_CFLAGS) -DG_DISABLE_DEPRECATED jiterator_CPPFLAGS = $(GLIB_CFLAGS) -DG_DISABLE_DEPRECATED o2cb_config_CPPFLAGS = $(GLIB_CFLAGS) -DG_DISABLE_DEPRECATED o2cb_ctl_CPPFLAGS = $(GLIB_CFLAGS) -DG_DISABLE_DEPRECATED o2cbtool_CPPFLAGS = $(GLIB_CFLAGS) -DG_DISABLE_DEPRECATED op_cluster_CPPFLAGS = $(GLIB_CFLAGS) -DG_DISABLE_DEPRECATED op_node_CPPFLAGS = $(GLIB_CFLAGS) -DG_DISABLE_DEPRECATED op_heartbeat_CPPFLAGS = $(GLIB_CFLAGS) -DG_DISABLE_DEPRECATED op_lists_CPPFLAGS = $(GLIB_CFLAGS) -DG_DISABLE_DEPRECATED op_register_CPPFLAGS = $(GLIB_CFLAGS) -DG_DISABLE_DEPRECATED op_start_CPPFLAGS = $(GLIB_CFLAGS) -DG_DISABLE_DEPRECATED o2cb_scandisk_CPPFLAGS = $(GLIB_CFLAGS) -DG_DISABLE_DEPRECATED op_status_CPPFLAGS = $(GLIB_CFLAGS) -DG_DISABLE_DEPRECATED o2cbutils_CPPFLAGS = $(GLIB_CFLAGS) -DG_DISABLE_DEPRECATED o2cb_ctl: $(O2CB_CTL_OBJS) $(LIBOCFS2_DEPS) $(LIBO2CB_DEPS) $(LINK) $(LIBO2CB_LIBS) $(GLIB_LIBS) $(LIBOCFS2_LIBS) $(COM_ERR_LIBS) $(AIO_LIBS) o2cb: $(O2CB_OBJS) $(LIBOCFS2_DEPS) $(LIBO2CB_DEPS) ${LIBO2DLM_DEPS} ${LIBTOOLS_INTERNAL_DEPS} $(LINK) $(LIBO2CB_LIBS) $(GLIB_LIBS) $(LIBOCFS2_LIBS) ${LIBO2DLM_LIBS} ${LIBTOOLS_INTERNAL_LIBS} $(COM_ERR_LIBS) $(AIO_LIBS) include $(TOPDIR)/Postamble.make ocfs2-tools-ocfs2-tools-1.8.6/o2cb_ctl/jconfig.c000066400000000000000000001362721347147137200213460ustar00rootroot00000000000000/* * jconfig.c * * Routines for handling the config file format of * Helpers::JConfig. * * Copyright (C) 2002 Joel Becker * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License version 2 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include #include #include #include #include #include /* Check G_LOG_DOMAIN before including glib.h */ #ifndef G_LOG_DOMAIN #define G_LOG_DOMAIN "JConfig" #endif /* G_LOG_DOMAIN */ #include #include "jiterator.h" #include "jconfig.h" /* * Defines */ #define CF_SCAN_WHITESPACE " \t" /* * Types */ typedef struct _JOutputCtxt JOutputCtxt; /* * Structures */ struct _JConfigCtxt { JConfig *cf; JConfigStanza *cfs; gboolean verbose; gboolean error; }; struct _JConfigStanza { gchar *stanza_name; GHashTable *attrs; }; struct _JConfig { gchar *filename; GList *stanza_names; GHashTable *stanzas; }; struct _JOutputCtxt { FILE *output; gboolean success; }; /* * File globals */ static const GScannerConfig test_config = { "" /* cset_skip_characters */, ( G_CSET_a_2_z "_0123456789" G_CSET_A_2_Z ) /* cset_identifier_first */, ( G_CSET_a_2_z "_0123456789" G_CSET_A_2_Z ) /* cset_identifier_nth */, "" /* cpair_comment_single */, TRUE /* case_sensitive */, FALSE /* skip_comment_multi */, FALSE /* skip_comment_single */, FALSE /* scan_comment_multi */, TRUE /* scan_identifier */, TRUE /* scan_identifier_1char */, FALSE /* scan_identifier_NULL */, TRUE /* scan_symbols */, FALSE /* scan_binary */, FALSE /* scan_octal */, FALSE /* scan_float */, FALSE /* scan_hex */, FALSE /* scan_hex_dollar */, FALSE /* scan_string_sq */, FALSE /* scan_string_dq */, FALSE /* numbers_2_int */, FALSE /* int_2_float */, FALSE /* identifier_2_string */, FALSE /* char_2_token */, TRUE /* symbol_2_token */, FALSE /* scope_0_fallback */ }; /* * Forward declarations */ static JConfig *j_config_parse_any(JConfigCtxt *cfc, const gchar *input_name, gint input_fd, const gchar *input_string, gint input_len); static void j_config_parse_base(GScanner *scanner, JConfigCtxt *cfc); static void j_config_parse_to_eol(GScanner *scanner); static void j_config_parse_comment(GScanner *scanner); static void j_config_parse_stanza_name(GScanner *scanner, JConfigCtxt *cfc); static void j_config_parse_white_start(GScanner *scanner, JConfigCtxt *cfc); static void j_config_parse_stanza_attr(GScanner *scanner, JConfigCtxt *cfc); static void j_config_foreach_attr_print(gpointer key, gpointer value, gpointer o_ctxt); static void j_config_foreach_stanza_print(gpointer key, gpointer value, gpointer o_ctxt); static void j_config_attr_names_foreach(gpointer key, gpointer value, gpointer user_data); static void j_config_free_stanza(JConfigStanza *cfs); static JConfig *j_config_config_new(void); static JConfigStanza *j_config_stanza_new(void); static void j_config_free_stanza_node(gpointer key, gpointer value, gpointer thrway); static void j_config_free_config_node(gpointer key, gpointer value, gpointer thrway); static void j_config_free_stanza_proxy(gpointer data, gpointer thrway); static void j_config_foreach_attr_append(gpointer key, gpointer value, gpointer fbuffer); static void j_config_foreach_stanza_append(gpointer key, gpointer value, gpointer fbuffer); /* * Functions */ /* * JConfigCtxt *j_config_new_context() * * Returns a new JConfigCtxt. */ JConfigCtxt *j_config_new_context(void) { JConfigCtxt *cfc; cfc = g_new(JConfigCtxt, 1); if (cfc == NULL) { g_warning("Unable to create a JConfigCtxt structure: %s\n", g_strerror(errno)); return(NULL); } cfc->cfs = NULL; cfc->cf = NULL; cfc->verbose = TRUE; cfc->error = FALSE; return cfc; } /* j_config_context_new() */ /* * void j_config_context_free(JConfigCtxt *cfc) * * Frees a JConfigCtxt. Does *not* free the associated JConfig. */ void j_config_context_free(JConfigCtxt *cfc) { g_return_if_fail(cfc != NULL); g_free(cfc); } /* j_config_context_free() */ /* * gboolean j_config_context_get_error(JConfigCtxt *cfc) * * Set zero for continuing past errors, nonzero to fail. */ gboolean j_config_context_get_error(JConfigCtxt *cfc) { g_return_val_if_fail(cfc != NULL, TRUE); return cfc->error; } /* j_config_context_get_error() */ /* * void j_config_context_set_verbose(JConfigCtxt *cfc, gboolean verbose) * * Set zero for quiet, nonzero for verbose. */ void j_config_context_set_verbose(JConfigCtxt *cfc, gboolean verbose) { g_return_if_fail(cfc != NULL); cfc->verbose = verbose; } /* j_config_context_set_verbose() */ /* * static JConfig *j_config_parse_any(JConfigCtxt *cfc, * const gchar *input_name, * gint input_fd, * const gchar *input_string, * gint input_len) * * The real initial parsing routine. * Creates the JConfig and structures and then * calls j_config_parse_base(); * * some of this is ripped from gtkrc.c */ static JConfig *j_config_parse_any(JConfigCtxt *cfc, const gchar *input_name, gint input_fd, const gchar *input_string, gint input_len) { GScanner *scanner; scanner = g_scanner_new((GScannerConfig *)&test_config); if (input_fd >= 0) { g_assert(input_string == NULL); g_scanner_input_file(scanner, input_fd); } else { g_assert(input_string != NULL); g_assert(input_len >= 0); g_scanner_input_text(scanner, input_string, input_len); } scanner->input_name = input_name; cfc->cf = j_config_config_new(); if (cfc->cf == NULL) { if (cfc->verbose) { g_warning("Unable to create a JConfig structure: %s\n", g_strerror(errno)); } cfc->error = TRUE; g_scanner_destroy(scanner); return(NULL); } cfc->cf->filename = g_strdup(input_name); j_config_parse_base(scanner, cfc); g_scanner_destroy(scanner); return(cfc->cf); } /* j_config_parse_any() */ /* * static void j_config_parse_base(GScanner *scanner, JConfigCtxt *cfc) * * The config file is line oriented. In this scope, it is assumed * that the scanner is situated at the begining of a line _always_. * As such, every sub-function called must return with the scanner * at the beginning of a line (or EOF); * * This function runs through line by line, deciding what * sub-function should handle each line. */ static void j_config_parse_base(GScanner *scanner, JConfigCtxt *cfc) { gboolean done; GTokenValue *value; value = &scanner->next_value; done = FALSE; while (done == FALSE) { GTokenType token; token = g_scanner_peek_next_token(scanner); switch (token) { case G_TOKEN_EOF: done = TRUE; break; case G_TOKEN_NONE: /* will this ever happen? */ break; case G_TOKEN_ERROR: /* should do something here */ break; case G_TOKEN_CHAR: if (strchr(CF_SCAN_WHITESPACE, value->v_char) != NULL) j_config_parse_white_start(scanner, cfc); else if (value->v_char == '#') j_config_parse_comment(scanner); else if (value->v_char == '\n') { #ifdef DEBUG g_print("Newline\n"); #endif /* DEBUG */ g_scanner_get_next_token(scanner); if (cfc->cfs != NULL) cfc->cfs = NULL; } else { if (cfc->verbose) { g_warning("Invalid character in stanza name: %c\n", value->v_char); } cfc->error = TRUE; j_config_parse_to_eol(scanner); if (cfc->cfs != NULL) cfc->cfs = NULL; } break; case G_TOKEN_SYMBOL: /* Another one I don't think I should get */ break; case G_TOKEN_IDENTIFIER: j_config_parse_stanza_name(scanner, cfc); break; default: if (cfc->verbose) g_warning("Unknown token\n"); cfc->error = TRUE; j_config_parse_to_eol(scanner); if (cfc->cfs != NULL) cfc->cfs = NULL; break; } } } /* j_config_parse_base() */ /* * static void j_config_parse_to_eol(GScanner *scanner) * * This is a simple function that just finds the end of a line, * throwing away anything it sees. */ static void j_config_parse_to_eol(GScanner *scanner) { gboolean done; GTokenType token; GTokenValue *value; value = &scanner->value; done = FALSE; while (done == FALSE) { token = g_scanner_peek_next_token(scanner); if (token == G_TOKEN_EOF) done = TRUE; else { token = g_scanner_get_next_token(scanner); if ((token = G_TOKEN_CHAR) && (value->v_char == '\n')) done = TRUE; } } } /* j_config_parse_to_eol() */ /* * static void j_config_parse_comment(GScanner *scanner) * * Parses a comment out of the file, throwing it away. */ static void j_config_parse_comment(GScanner *scanner) { GTokenType token; GTokenValue *value; value = &scanner->value; token = g_scanner_get_next_token(scanner); g_assert(token == G_TOKEN_CHAR); g_assert(value->v_char == '#'); j_config_parse_to_eol(scanner); #ifdef DEBUG g_print("Skipped comment\n"); #endif /* DEBUG */ } /* j_config_parse_comment() */ /* * static void j_config_parse_stanza_name(GScanner *scanner, * JConfigCtxt *cfc) * * If a line starts with a non-whitespace character, it signifies a * new stanza. Stanza names are defined by the Perl-style regular * expression /^\w+:\s*\n/. Just by virtue of entering this function, * any current stanza is closed. * * The gotos are because I decided it was cleaner than a * g_free() at every error. */ static void j_config_parse_stanza_name(GScanner *scanner, JConfigCtxt *cfc) { gboolean done; gchar *stanza_name; GTokenType token; GTokenValue *value; value = &scanner->value; token = g_scanner_get_next_token(scanner); g_assert(token == G_TOKEN_IDENTIFIER); if (cfc->cfs != NULL) cfc->cfs = NULL; stanza_name = g_strdup(value->v_identifier); token = g_scanner_peek_next_token(scanner); if (token == G_TOKEN_EOF) { if (cfc->verbose) g_warning("Invalid stanza name declaration: missing ':'\n"); cfc->error = TRUE; goto stanza_name_free; } token = g_scanner_get_next_token(scanner); if (token != G_TOKEN_CHAR) { if (cfc->verbose) g_warning("Invalid stanza name declaration\n"); cfc->error = TRUE; j_config_parse_to_eol(scanner); goto stanza_name_free; } if (value->v_char == '\n') { if (cfc->verbose) g_warning("Invalid stanza name declaration: missing ':'\n"); cfc->error = TRUE; goto stanza_name_free; } if (value->v_char != ':') { if (cfc->verbose) { g_warning("Invalid character in stanza name declaration: %c\n", value->v_char); } cfc->error = TRUE; j_config_parse_to_eol(scanner); goto stanza_name_free; } done = FALSE; while (done == FALSE) { token = g_scanner_peek_next_token(scanner); if (token == G_TOKEN_EOF) done = TRUE; else if (token == G_TOKEN_CHAR) { token = g_scanner_get_next_token(scanner); if (value->v_char == '\n') done = TRUE; else if (strchr(CF_SCAN_WHITESPACE, value->v_char) == NULL) { if (cfc->verbose) g_warning("Trailing garbage on stanza name declaration\n"); cfc->error = TRUE; j_config_parse_to_eol(scanner); goto stanza_name_free; } } else { if (cfc->verbose) g_warning("Trailing garbage on stanza name declaration\n"); cfc->error = TRUE; j_config_parse_to_eol(scanner); goto stanza_name_free; } } cfc->cfs = j_config_add_stanza(cfc->cf, stanza_name); #ifdef DEBUG g_print("New stanza: %s\n", stanza_name); #endif /* DEBUG */ stanza_name_free: g_free(stanza_name); } /* j_config_parse_stanza_name() */ /* * static void j_config_parse_white_start(GScanner *scanner, * JConfigCtxt *cfc) * * If a line starts with whitespace, it is either a blank line, or an * attribute line. We skip whitespace lines. * If it looks like an attribute line, we call the proper function. */ static void j_config_parse_white_start(GScanner *scanner, JConfigCtxt *cfc) { gboolean done; GTokenType token; GTokenValue *value; value = &scanner->value; token = g_scanner_get_next_token(scanner); g_assert(token == G_TOKEN_CHAR); g_assert(strchr(CF_SCAN_WHITESPACE, value->v_char) != NULL); done = FALSE; while (done == FALSE) { token = g_scanner_peek_next_token(scanner); switch (token) { case G_TOKEN_EOF: done = TRUE; break; case G_TOKEN_IDENTIFIER: j_config_parse_stanza_attr(scanner, cfc); done = TRUE; break; case G_TOKEN_CHAR: token = g_scanner_get_next_token(scanner); if (value->v_char == '\n') { #ifdef DEBUG g_print("Skipped whitespace line\n"); #endif /* DEBUG */ if (cfc->cfs != NULL) cfc->cfs = NULL; done = TRUE; } else if (strchr(CF_SCAN_WHITESPACE, value->v_char) == NULL) { if (cfc->verbose) { g_warning("Invalid character in attribute name: %c\n", value->v_char); } cfc->error = TRUE; j_config_parse_to_eol(scanner); done = TRUE; } break; default: break; } } } /* j_config_parse_white_start() */ /* * static void j_config_parse_stanza_attr(GScanner *scanner, * JConfigCtxt *cfc) * * An attribute is of the form =. * is defined by the Perl-style regular expression * /^\s+\w+\s* /. Note that the leading whitespace (/^\s+/) has been * stripped by j_config_parse_white_start() already. * is defined as /\s*.*\n/. The leading whitespace * (between the "=" and the first non-whitespace character in * ) is stripped, as is the newline. * * This now supports continuation lines. An attribute line ending in * '\' will cause a to be entered into the buffer * and the next line will be treated as part of the attribute. */ static void j_config_parse_stanza_attr(GScanner *scanner, JConfigCtxt *cfc) { gboolean done, multi_line; gchar *attr_name; GString *attr_value; GTokenType token; GTokenValue *value; enum { CF_STATE_ID, CF_STATE_EQUAL, CF_STATE_VALUE } cur_state; value = &scanner->value; token = g_scanner_get_next_token(scanner); g_assert(token == G_TOKEN_IDENTIFIER); if (cfc->cfs == NULL) { if (cfc->verbose) g_warning("Attributes require a matching stanza\n"); cfc->error = TRUE; j_config_parse_to_eol(scanner); return; } attr_name = g_strdup(value->v_identifier); attr_value = g_string_new(NULL); cur_state = CF_STATE_ID; done = FALSE; multi_line = FALSE; while (done == FALSE) { token = g_scanner_peek_next_token(scanner); if (token == G_TOKEN_EOF) { if (cur_state == CF_STATE_ID) { if (cfc->verbose) g_warning("Invalid attribute: missing '='\n"); cfc->error = TRUE; goto attribute_free; } done = TRUE; } else if (token == G_TOKEN_CHAR) { token = g_scanner_get_next_token(scanner); if (value->v_char == '\n') { if (cur_state == CF_STATE_ID) { if (cfc->verbose) g_warning("Invalid attribute: missing '='\n"); cfc->error = TRUE; goto attribute_free; } if (multi_line == FALSE) done = TRUE; else { g_string_append_c(attr_value, value->v_char); multi_line = FALSE; } } else if (multi_line == TRUE) { g_string_append_c(attr_value, '\\'); multi_line = FALSE; if (value->v_char == '\\') multi_line = TRUE; else g_string_append_c(attr_value, value->v_char); } else if (cur_state == CF_STATE_ID) { if (value->v_char == '=') cur_state = CF_STATE_EQUAL; else if (strchr(CF_SCAN_WHITESPACE, value->v_char) == NULL) { if (cfc->verbose) g_warning("Invalid attribute: expecting '='\n"); cfc->error = TRUE; j_config_parse_to_eol(scanner); goto attribute_free; } } else if (cur_state == CF_STATE_EQUAL) { if (strchr(CF_SCAN_WHITESPACE, value->v_char) == NULL) { cur_state = CF_STATE_VALUE; if (value->v_char == '\\') multi_line = TRUE; else g_string_append_c(attr_value, value->v_char); } } else { if (value->v_char == '\\') multi_line = TRUE; else g_string_append_c(attr_value, value->v_char); } } else { token = g_scanner_get_next_token(scanner); if (cur_state == CF_STATE_ID) { if (cfc->verbose) g_warning("Invalid attribute: expecting '='\n"); cfc->error = TRUE; j_config_parse_to_eol(scanner); return; } else if (cur_state == CF_STATE_EQUAL) cur_state = CF_STATE_VALUE; if (token != G_TOKEN_IDENTIFIER) { if (cfc->verbose) g_warning("Error parsing value\n"); cfc->error = TRUE; j_config_parse_to_eol(scanner); goto attribute_free; } if (multi_line == TRUE) { g_string_append_c(attr_value, '\\'); multi_line = FALSE; } g_string_append(attr_value, value->v_identifier); } } j_config_set_attribute(cfc->cfs, attr_name, attr_value->str); #ifdef DEBUG g_print("Attribute: \"%s\" = \"%s\"\n", attr_name, attr_value->str); #endif /* DEBUG */ attribute_free: g_free(attr_name); g_string_free(attr_value, TRUE); } /* * static JConfig *j_config_config_new() * * Allocates a new JConfig structure */ static JConfig *j_config_config_new(void) { JConfig *cf; cf = g_new(JConfig, 1); if (cf == NULL) return(NULL); cf->filename = NULL; cf->stanza_names = NULL; cf->stanzas = g_hash_table_new(g_str_hash, g_str_equal); if (cf->stanzas == NULL) { g_free(cf); return(NULL); } return(cf); } /* j_config_config_new() */ /* * static JConfigStanza *j_config_stanza_new() * * Allocates a new JConfigStanza structure */ static JConfigStanza *j_config_stanza_new(void) { JConfigStanza *cfs; cfs = g_new(JConfigStanza, 1); if (cfs == NULL) return(NULL); cfs->stanza_name = NULL; cfs->attrs = g_hash_table_new(g_str_hash, g_str_equal); if (cfs->attrs == NULL) { g_free(cfs); return(NULL); } return(cfs); } /* j_config_stanza_new() */ /* * static void j_config_foreach_attr_print(gpointer key, * gpointer value, * gpointer o_ctxt) * * Prints each attribute -> value pair. */ static void j_config_foreach_attr_print(gpointer key, gpointer value, gpointer o_ctxt) { gchar delimiter[2] = {'\n', '\0'}; gchar **output_lines; gboolean first_line; gint i, rc; JOutputCtxt *ctxt; ctxt = (JOutputCtxt *)o_ctxt; if (ctxt->success == FALSE) return; if ((value == NULL) || (((gchar *)value)[0] == '\0')) { rc = fprintf(ctxt->output, "\t%s =\n", (gchar *)key); if (rc < 1) ctxt->success = FALSE; return; } output_lines = g_strsplit(value, delimiter, 0); if (output_lines == NULL) { #if DEBUG g_warning("Unable to allocate memory for multiline attribute: %s\n", g_strerror(errno)); #endif ctxt->success = FALSE; return; } first_line = TRUE; rc = 0; for (i = 0; output_lines[i] != NULL; i++) { if (first_line == TRUE) { rc = fprintf(ctxt->output, "\t%s = %s", (gchar *)key, output_lines[i]); first_line = FALSE; } else rc = fprintf(ctxt->output, "\\\n%s", output_lines[i]); } g_strfreev(output_lines); if (rc < 1) { ctxt->success = FALSE; return; } rc = fprintf(ctxt->output, "\n"); if (rc < 1) ctxt->success = FALSE; } /* j_config_foreach_attr_print() */ /* * static void j_config_foreach_stanza_print(gpointer key, * gpointer value, * gpointer o_ctxt) * * Runs through each stanza, printing the header and * calling j_config_foreach_attr_print() on the attributes. */ static void j_config_foreach_stanza_print(gpointer key, gpointer value, gpointer o_ctxt) { gint rc; GList *elem; JConfigStanza *cfs; JOutputCtxt *ctxt; elem = (GList *)value; ctxt = (JOutputCtxt *)o_ctxt; if (ctxt->success == FALSE) return; while (elem) { cfs = (JConfigStanza *)elem->data; rc = fprintf(ctxt->output, "%s:\n", (gchar *)key); if (rc < 1) { ctxt->success = FALSE; break; } g_hash_table_foreach(cfs->attrs, j_config_foreach_attr_print, o_ctxt); rc = fprintf(ctxt->output, "\n"); if (rc < 1) { ctxt->success = FALSE; break; } elem = g_list_next(elem); } } /* j_config_foreach_stanza_print() */ /* * gboolean j_config_dump_file(JConfig *cf, * const gchar *o_ctxt) * * Prints the configuration. * * FIXME: This needs to do more work to protect the resulting file from * ENOSPC, etc. */ gboolean j_config_dump_file(JConfig *cf, const gchar *output_file) { gint rc; JOutputCtxt ctxt; g_return_val_if_fail(cf != NULL, FALSE); g_return_val_if_fail(output_file != NULL, FALSE); ctxt.success = TRUE; if (strcmp(output_file, "-") == 0) ctxt.output = stdout; else ctxt.output = fopen(output_file, "w"); if (ctxt.output == NULL) { #if DEBUG g_warning("Unable to open file \"%s\" for writing: %s\n", output_file, g_strerror(errno)); #endif return(FALSE); } g_hash_table_foreach(cf->stanzas, j_config_foreach_stanza_print, &ctxt); if (strcmp(output_file, "-") != 0) { rc = fclose(ctxt.output); if (rc != 0) ctxt.success = FALSE; } return(ctxt.success); } /* j_config_dump_file() */ /* * JIterator *j_config_get_stanza_names(JConfig *cf) * * Returns a list of stanzas contained in this config */ JIterator *j_config_get_stanza_names(JConfig *cf) { g_return_val_if_fail(cf != NULL, NULL); return(j_iterator_new_from_list(cf->stanza_names)); } /* j_config_get_stanza_names() */ /* * JConfigStanza *j_config_get_stanza_nth(JConfig *cf, * const gchar *stanza_name, * guint n) * * Retreives the nth stanza with the given name, or NULL if * that does not exist */ JConfigStanza *j_config_get_stanza_nth(JConfig *cf, const gchar *stanza_name, guint n) { JConfigStanza *cfs; GList *elem; g_return_val_if_fail(cf != NULL, NULL); g_return_val_if_fail(stanza_name != NULL, NULL); elem = g_hash_table_lookup(cf->stanzas, stanza_name); if (elem == NULL) return(NULL); elem = g_list_nth(elem, n); cfs = elem != NULL ? (JConfigStanza *)elem->data : NULL; return(cfs); } /* j_config_get_stanza_nth() */ /* * gchar *j_config_get_stanza_name(JConfigStanza *cfs) * * Returns the stanza's name */ gchar *j_config_get_stanza_name(JConfigStanza *cfs) { g_return_val_if_fail(cfs != NULL, NULL); return(g_strdup(cfs->stanza_name)); } /* j_config_get_stanza_name() */ /* * JConfigStanza *j_config_add_stanza(JConfig *cf, * const gchar *stanza_name); * * Adds a new stanza to the configuration structure */ JConfigStanza *j_config_add_stanza(JConfig *cf, const gchar *stanza_name) { JConfigStanza *cfs; GList *elem; g_return_val_if_fail(cf != NULL, NULL); g_return_val_if_fail(stanza_name != NULL, NULL); cfs = j_config_stanza_new(); if (cfs == NULL) { #if DEBUG g_warning("Unable to allocate memory for a new stanza: %s\n", g_strerror(errno)); #endif return NULL; } cfs->stanza_name = g_strdup(stanza_name); elem = (GList *)g_hash_table_lookup(cf->stanzas, stanza_name); if (elem == NULL) { cf->stanza_names = g_list_append(cf->stanza_names, g_strdup(stanza_name)); elem = g_list_append(NULL, cfs); g_hash_table_insert(cf->stanzas, g_strdup(stanza_name), elem); } else elem = g_list_append(elem, cfs); return(cfs); } /* j_config_add_stanza() */ /* * void j_config_delete_stanza(JConfig *cf, * JConfigStanza *cfs) * * Removes the stanza pointed to by cfs from the configuration. */ void j_config_delete_stanza(JConfig *cf, JConfigStanza *cfs) { GList *elem, *new_elem; gpointer orig_key; gchar *orig_data; g_return_if_fail(cf != NULL); g_return_if_fail(cfs != NULL); elem = (GList *)g_hash_table_lookup(cf->stanzas, cfs->stanza_name); if (elem == NULL) { #if DEBUG g_warning("Unable to remove stanza - no such class: %s\n", cfs->stanza_name); #endif return; } new_elem = g_list_remove(elem, cfs); if (elem == NULL) { if (g_hash_table_lookup_extended(cf->stanzas, cfs->stanza_name, &orig_key, NULL)) { g_hash_table_remove(cf->stanzas, cfs->stanza_name); g_free(orig_key); } elem = cf->stanza_names; while (elem != NULL) { orig_data = (gchar *)elem->data; if (strcmp(cfs->stanza_name, orig_data) == 0) { cf->stanza_names = g_list_remove(cf->stanza_names, elem->data); g_free(orig_data); break; } elem = g_list_next(elem); } } else if (elem != new_elem) { if (g_hash_table_lookup_extended(cf->stanzas, cfs->stanza_name, &orig_key, NULL)) { g_hash_table_remove(cf->stanzas, cfs->stanza_name); g_free(orig_key); } g_hash_table_insert(cf->stanzas, g_strdup(cfs->stanza_name), new_elem); } j_config_free_stanza(cfs); } /* j_config_delete_stanza() */ /* * void j_config_delete_stanza_nth(JConfig *cf, * const gchar *stanza_name, * guint n) * * Removes the nth stanza of class stanza_name from the * configuration. */ void j_config_delete_stanza_nth(JConfig *cf, const gchar *stanza_name, guint n) { JConfigStanza *cfs; g_return_if_fail(cf != NULL); g_return_if_fail(stanza_name != NULL); cfs = j_config_get_stanza_nth(cf, stanza_name, n); if (cfs == NULL) return; j_config_delete_stanza(cf, cfs); } /* j_config_delete_stanza() */ /* * JConfig *j_config_parse_file_with_context(JConfigCtxt *cfc, * const gchar *filename) * * Parses a config file */ JConfig *j_config_parse_file_with_context(JConfigCtxt *cfc, const gchar *filename) { gint fd; JConfig *cf; g_return_val_if_fail(cfc != NULL, NULL); g_return_val_if_fail(filename != NULL, NULL); if (strcmp(filename, "-") == 0) fd = STDIN_FILENO; else fd = open(filename, O_RDONLY); if (fd < 0) { if (cfc->verbose) { g_warning("Unable to open file \"%s\": %s\n", filename, g_strerror(errno)); } cfc->error = TRUE; return(NULL); } cf = j_config_parse_any(cfc, filename, fd, NULL, 0); if (strcmp(filename, "-") != 0) close(fd); return(cf); } /* j_config_parse_file_with_context() */ /* * JConfig *j_config_parse_file(const gchar *filename) * * Parses a config file, creating the context. */ JConfig *j_config_parse_file(const gchar *filename) { JConfigCtxt *cfc; JConfig *cf = NULL; cfc = j_config_new_context(); if (cfc) { cf = j_config_parse_file_with_context(cfc, filename); if (cfc->cfs) j_config_free_stanza(cfc->cfs); j_config_context_free(cfc); } return(cf); } /* j_config_parse_file() */ /* * JConfig *j_config_parse_memory_with_context(JConfigCtxt *cfc, * gchar *buffer, * gint buf_len) * * Parses a config from a text buffer */ JConfig *j_config_parse_memory_with_context(JConfigCtxt *cfc, gchar *buffer, gint buf_len) { JConfig *cf; g_return_val_if_fail(cfc != NULL, NULL); g_return_val_if_fail(buffer != NULL, NULL); if (buf_len < 0) buf_len = 0; cf = j_config_parse_any(cfc, "memory", -1, buffer, buf_len); return(cf); } /* j_config_parse_memory() */ /* * JConfig *j_config_parse_memory(gchar *buffer, gint buf_len) * * Parses a buffer, creating the context. */ JConfig *j_config_parse_memory(gchar *buffer, gint buf_len) { JConfigCtxt *cfc; JConfig *cf = NULL; cfc = j_config_new_context(); if (cfc) { cf = j_config_parse_memory_with_context(cfc, buffer, buf_len); if (cfc->cfs) j_config_free_stanza(cfc->cfs); j_config_context_free(cfc); } return(cf); } /* j_config_parse_file() */ /* * JConfigMatch *j_config_match_build(guint num_matches, ...) * * Convenience function to build an array of JConfigMatch structures. * num_matches is the number of key, value pairs passed to this * function. The number of varargs passed should therefore be double * num_matches. eg: * * j_config_match_build(2, "foo", "bar", "quuz", "quuuz"); * * This function builds a match that requires: * * foo = bar * quuz = quuuz * * Strings passed in are *NOT* copied. They are merely referenced. * Because of this, the returned array of JConfigMatch structures can * be freed with g_free(). The caller must take care not to change or * destroy the referened strings until they are done with the * array of JConfigMatch structures. */ JConfigMatch *j_config_match_build(guint num_matches, ...) { guint i; va_list args; JConfigMatch *matches; matches = g_new0(JConfigMatch, num_matches); va_start(args, num_matches); for (i = 0; i < num_matches; i++) { matches[i].type = J_CONFIG_MATCH_VALUE; matches[i].name = va_arg(args, gchar *); if (matches[i].name == NULL) { g_free(matches); matches = NULL; break; } matches[i].value = va_arg(args, gchar *); } va_end(args); return(matches); } /* j_config_match_build() */ /* * JIterator *j_config_get_stanzas(JConfig *cf, * const gchar *stanza_name, * JConfigMatch *matches, * guint num_matches) * * Gets the list of stanzas with a given name. Optionally * return only those that staitisfy matches. matches is an array * of JConfigMatch structures. num_matches describes the number * of items in the array. This array can be built by the caller * explicitly, or the caller can use the convenience function * j_config_match_build(). If num_matches is 0, matches can be * NULL. */ JIterator *j_config_get_stanzas(JConfig *cf, const gchar *stanza_name, JConfigMatch *matches, guint num_matches) { gint i; JConfigStanza *cfs; gchar *value; GList *elem, *tmp; JIterator *iter; g_return_val_if_fail(cf != NULL, NULL); g_return_val_if_fail(stanza_name != NULL, NULL); g_return_val_if_fail((num_matches == 0) || (matches != NULL), NULL); elem = (GList *)g_hash_table_lookup(cf->stanzas, stanza_name); if (num_matches > 0) { tmp = NULL; for (; elem != NULL; elem = g_list_next(elem)) { cfs = (JConfigStanza *)elem->data; g_assert(cfs != NULL); for (i = 0; i < num_matches; i++) { g_assert(matches[i].name != NULL); value = j_config_get_attribute(cfs, matches[i].name); if (value != NULL) { if (matches[i].value == NULL) { if (value[0] != '\0') { g_free(value); break; } } else if (strcmp(value, matches[i].value) != 0) { g_free(value); break; } g_free(value); } else { if (matches[i].value != NULL) break; } } if (i >= num_matches) /* All tests succeeded */ tmp = g_list_prepend(tmp, cfs); } elem = g_list_reverse(tmp); iter = j_iterator_new_from_list(elem); g_list_free(elem); } else iter = j_iterator_new_from_list(elem); return(iter); } /* j_config_get_stanzas() */ /* * void j_config_set_attribute(JConfigStanza *cfs, * const gchar *attr_name, * const gchar *attr_value) * * Sets the value of a given attribute name */ void j_config_set_attribute(JConfigStanza *cfs, const gchar *attr_name, const gchar *attr_value) { g_return_if_fail(cfs != NULL); g_return_if_fail(attr_name != NULL); j_config_delete_attribute(cfs, attr_name); g_hash_table_insert(cfs->attrs, g_strdup(attr_name), g_strdup(attr_value)); } /* j_config_set_attribute() */ /* * static void j_config_attr_names_foreach(gpointer key, * gpointer value, * gpointer user_data) * * Foreach function to build the attribute names list. */ static void j_config_attr_names_foreach(gpointer key, gpointer value, gpointer user_data) { GList **list = (GList **)user_data; *list = g_list_append(*list, key); } /* j_config_attr_names_foreach() */ /* * JIterator *j_config_get_attribute_names(JConfigStanza *cfs) * * Returns an iterator over every attribute name in the stanza. */ JIterator *j_config_get_attribute_names(JConfigStanza *cfs) { JIterator *iter; GList *attr_names = NULL; g_return_val_if_fail(cfs != NULL, NULL); g_hash_table_foreach(cfs->attrs, j_config_attr_names_foreach, &attr_names); iter = j_iterator_new_from_list(attr_names); g_list_free(attr_names); return(iter); } /* j_config_get_attribute_names() */ /* * gchar *j_config_get_attribute(JConfigStanza *cfs, * const gchar *attr_name) * * Retrieves the value of a given attribute name */ gchar *j_config_get_attribute(JConfigStanza *cfs, const gchar *attr_name) { gchar *s; g_return_val_if_fail(cfs != NULL, NULL); g_return_val_if_fail(attr_name != NULL, NULL); s = (gchar *)g_hash_table_lookup(cfs->attrs, attr_name); s = g_strdup(s); if (s) g_strchomp(s); return(s); } /* j_config_get_attribute() */ /* * void j_config_delete_attribute(JConfigStanza *cfs, * const gchar *attr_name) * * Removes the attribute attr_name from the stanza pointed to * by cfs. */ void j_config_delete_attribute(JConfigStanza *cfs, const gchar *attr_name) { gpointer orig_key, orig_value; g_return_if_fail(cfs != NULL); g_return_if_fail(attr_name != NULL); if (g_hash_table_lookup_extended(cfs->attrs, attr_name, &orig_key, &orig_value)) { g_hash_table_remove(cfs->attrs, attr_name); g_free(orig_key); g_free(orig_value); } } /* j_config_delete_attribute() */ /* * static void j_config_free_stanza_node(gpointer key, * gpointer value, * gpointer thrway) * * Removes a node from a stanza */ static void j_config_free_stanza_node(gpointer key, gpointer value, gpointer thrway) { g_free(key); g_free(value); } /* j_config_free_stanza_node() */ /* * static void j_config_free_stanza(JConfigStanza *cfs) * * Clears a config file stanza */ static void j_config_free_stanza(JConfigStanza *cfs) { g_return_if_fail(cfs != NULL); g_hash_table_foreach(cfs->attrs, j_config_free_stanza_node, NULL); g_hash_table_destroy(cfs->attrs); g_free(cfs->stanza_name); g_free(cfs); } /* j_config_free_stanza() */ /* * static void j_config_free_stanza_proxy(gpointer data, * gpointer thrway) * * A proxy to call j_config_free_stanza() as a GFunc() */ static void j_config_free_stanza_proxy(gpointer elem_data, gpointer thrway) { j_config_free_stanza(elem_data); } /* j_config_free_stanza_proxy() */ /* * static void j_config_free_config_node(gpointer key, * gpointer value, * gpointer thrway) * * Removes a node from a config */ static void j_config_free_config_node(gpointer key, gpointer value, gpointer thrway) { GList *elem; elem = (GList *)value; g_list_foreach(elem, j_config_free_stanza_proxy, NULL); g_list_free(elem); g_free(key); } /* j_config_free_config_node() */ /* * void j_config_free(JConfig *cf) * * Deletes a config file tree */ void j_config_free(JConfig *cf) { g_return_if_fail(cf != NULL); GList *list; g_hash_table_foreach(cf->stanzas, j_config_free_config_node, NULL); g_hash_table_destroy(cf->stanzas); g_free(cf->filename); list = cf->stanza_names; while (list) { g_free(list->data); list->data = NULL; list = list->next; } g_list_free(cf->stanza_names); g_free(cf); } /* j_config_free() */ /* * static void j_config_foreach_attr_append(gpointer key, * gpointer value, * gpointer fbuffer) * * Prints each attribute -> value pair. */ static void j_config_foreach_attr_append(gpointer key, gpointer value, gpointer fbuffer) { gchar delimiter[2] = {'\n', '\0'}; gchar **output_lines; gboolean first_line; gint i; GString *buffer; g_return_if_fail(fbuffer != NULL); buffer = (GString *)fbuffer; if ((value == NULL) || (((gchar *)value)[0] == '\0')) { g_string_append_printf(buffer, "\t%s =\n", (gchar *)key); return; } output_lines = g_strsplit(value, delimiter, 0); if (output_lines == NULL) { g_warning("Unable to allocate memory for multiline attribute: %s\n", g_strerror(errno)); return; } first_line = TRUE; for (i = 0; output_lines[i] != NULL; i++) { if (first_line == TRUE) { g_string_append_printf(buffer, "\t%s = %s", (gchar *)key, output_lines[i]); first_line = FALSE; } else g_string_append_printf(buffer, "\\\n%s", output_lines[i]); } g_string_append_printf(buffer, "\n"); g_strfreev(output_lines); } /* j_config_foreach_attr_append() */ /* * static void j_config_foreach_stanza_append(gpointer key, * gpointer value, * gpointer fbuffer) * * Runs through each stanza, printing the header and * calling j_config_foreach_attr_append() on the attributes. */ static void j_config_foreach_stanza_append(gpointer key, gpointer value, gpointer fbuffer) { GList *elem; JConfigStanza *cfs; GString *buffer; g_return_if_fail(fbuffer != NULL); buffer = (GString *)fbuffer; elem = (GList *)value; while (elem) { cfs = (JConfigStanza *)elem->data; g_string_append_printf(buffer, "%s:\n", (gchar *)key); g_hash_table_foreach(cfs->attrs, j_config_foreach_attr_append, fbuffer); g_string_append(buffer, "\n"); elem = g_list_next(elem); } } /* j_config_foreach_stanza_append() */ /* * gchar *j_config_dump_memory(JConfig *cf) * * Prints the configuration to an in-memory string. */ gchar *j_config_dump_memory(JConfig *cf) { gchar *output_text; GString *output; g_return_val_if_fail(cf != NULL, NULL); output = g_string_new(NULL); if (output == NULL) return(NULL); g_hash_table_foreach(cf->stanzas, j_config_foreach_stanza_append, (gpointer)output); output_text = output->str; g_string_free(output, FALSE); return(output_text); } /* j_config_dump_memory() */ ocfs2-tools-ocfs2-tools-1.8.6/o2cb_ctl/jconfig.h000066400000000000000000000064321347147137200213450ustar00rootroot00000000000000/* * jconfig.h * * Header file for configuration file parser * * Copyright (C) 2002 Joel Becker * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License version 2 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef __JCONFIG_H #define __JCONFIG_H /* * Type definitions */ typedef struct _JConfigStanza JConfigStanza; typedef struct _JConfig JConfig; typedef struct _JConfigMatch JConfigMatch; typedef struct _JConfigCtxt JConfigCtxt; /* * Enums */ typedef enum _JConfigMatchType { J_CONFIG_MATCH_VALUE = 0, } JConfigMatchType; /* * Structures */ struct _JConfigMatch { JConfigMatchType type; gchar *name; gchar *value; }; /* * Functions */ JConfig *j_config_parse_file(const gchar *filename); JConfig *j_config_parse_memory(gchar *buffer, gint buf_len); JConfigCtxt *j_config_new_context(void); void j_config_context_free(JConfigCtxt *cfc); void j_config_context_set_verbose(JConfigCtxt *cfc, gboolean verbose); gboolean j_config_context_get_error(JConfigCtxt *cfc); JConfig *j_config_parse_file_with_context(JConfigCtxt *cfc, const gchar *filename); JConfig *j_config_parse_memory_with_context(JConfigCtxt *cfc, gchar *buffer, gint buf_len); JIterator *j_config_get_stanza_names(JConfig *cf); JConfigMatch *j_config_match_build(guint num_matches, ...); JIterator *j_config_get_stanzas(JConfig *cf, const gchar *stanza_name, JConfigMatch *matches, guint num_matches); JConfigStanza *j_config_get_stanza_nth(JConfig *cf, const gchar *stanza_name, guint n); gchar *j_config_get_stanza_name(JConfigStanza *cfs); JConfigStanza *j_config_add_stanza(JConfig *cf, const gchar *stanza_name); void j_config_delete_stanza(JConfig *cf, JConfigStanza *cfs); void j_config_delete_stanza_nth(JConfig *cf, const gchar *stanza_name, guint n); JIterator *j_config_get_attribute_names(JConfigStanza *cfs); gchar *j_config_get_attribute(JConfigStanza *cfs, const gchar *attr_name); void j_config_set_attribute(JConfigStanza *cfs, const gchar *attr_name, const gchar *attr_value); void j_config_delete_attribute(JConfigStanza *cfs, const gchar *attr_name); gboolean j_config_dump_file(JConfig *cf, const gchar *output_file); gchar *j_config_dump_memory(JConfig *cf); void j_config_free(JConfig *cf); #endif /* __JCONFIG_H */ ocfs2-tools-ocfs2-tools-1.8.6/o2cb_ctl/jiterator.c000066400000000000000000000104621347147137200217220ustar00rootroot00000000000000/* * jiterator.c * * Code for opaque iterators. * * Copyright (C) 2001 Oracle Corporation, Joel Becker * * All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have recieved a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. */ /* * MT safe */ #include #include #include "jiterator.h" /* Structures */ struct _JIterator { gpointer context; JIteratorFunc has_more_func; JIteratorFunc get_next_func; GDestroyNotify notify_func; }; static gpointer j_iterator_list_has_more (gpointer context); static gpointer j_iterator_list_get_next (gpointer context); static void j_iterator_list_destroy_notify (gpointer context); /* Enumerations */ JIterator* j_iterator_new (gpointer context, JIteratorFunc has_more_func, JIteratorFunc get_next_func, GDestroyNotify notify_func) { JIterator *iterator; iterator = g_new (JIterator, 1); iterator->context = context; iterator->has_more_func = has_more_func; iterator->get_next_func = get_next_func; iterator->notify_func = notify_func; return iterator; } /* j_iterator_new() */ JIterator* j_iterator_new_from_list (GList *init_list) { JIterator *iterator; GList *list_copy, *header; /* The list is copied here, the caller is responsible for the * data items. On _free(), the list is removed and the data items * left alone. If the caller wants different semantics, the * caller can specify their own functions with _new() */ list_copy = g_list_copy(init_list); /* This is a header element to refer to the list */ header = g_list_prepend(NULL, (gpointer) list_copy); iterator = j_iterator_new ((gpointer) header, j_iterator_list_has_more, j_iterator_list_get_next, j_iterator_list_destroy_notify); return(iterator); } /* j_iterator_new_from_list() */ gboolean j_iterator_has_more (JIterator *iterator) { gpointer result; g_return_val_if_fail(iterator != NULL, FALSE); result = (*iterator->has_more_func) (iterator->context); return (gboolean) GPOINTER_TO_INT(result); } /* j_iterator_has_more() */ gpointer j_iterator_get_next (JIterator *iterator) { gpointer result; g_return_val_if_fail (iterator != NULL, NULL); result = (*iterator->get_next_func) (iterator->context); return result; } /* j_iterator_get_next() */ void j_iterator_free (JIterator *iterator) { (*iterator->notify_func) (iterator->context); g_free(iterator); } /* j_iterator_free() */ static gpointer j_iterator_list_has_more (gpointer context) { GList *header, *elem; gboolean result; g_return_val_if_fail(context != NULL, GINT_TO_POINTER(FALSE)); header = (GList *) context; elem = (GList *) header->data; result = (elem != NULL); return (gpointer) GINT_TO_POINTER(result); } /* j_iterator_list_has_more() */ static gpointer j_iterator_list_get_next (gpointer context) { GList *header, *elem; gpointer result; g_return_val_if_fail(context != NULL, NULL); header = (GList *) context; elem = (GList *) header->data; /* User should have called has_more() */ g_return_val_if_fail(elem != NULL, NULL); result = elem->data; header->data = (gpointer) g_list_next(elem); g_list_free_1(elem); return result; } /* j_iterator_list_get_next() */ static void j_iterator_list_destroy_notify (gpointer context) { GList *header, *elem; g_return_if_fail (context != NULL); header = (GList *) context; elem = (GList *) header->data; g_list_free(elem); /* NULL if at end of list */ g_list_free(header); } /* j_iterator_list_destroy_notify() */ ocfs2-tools-ocfs2-tools-1.8.6/o2cb_ctl/jiterator.h000066400000000000000000000025561347147137200217340ustar00rootroot00000000000000/* * jiterator.h * * Prototypes for JIterator * * Copyright (C) 2002 Joel Becker * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License version 2 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef __JITERATOR_H #define __JITERATOR_H /* Typedefs */ typedef struct _JIterator JIterator; typedef gpointer (*JIteratorFunc) (gpointer context); /* Functions */ JIterator* j_iterator_new(gpointer context, JIteratorFunc has_more_func, JIteratorFunc get_next_func, GDestroyNotify notify_func); JIterator* j_iterator_new_from_list(GList *init_list); gboolean j_iterator_has_more(JIterator *iterator); gpointer j_iterator_get_next(JIterator *iterator); void j_iterator_free(JIterator *iterator); #endif /* __JITERATOR_H */ ocfs2-tools-ocfs2-tools-1.8.6/o2cb_ctl/o2cb.8.in000066400000000000000000000104241347147137200210740ustar00rootroot00000000000000.TH "o2cb" "8" "January 2012" "Version @VERSION@" "OCFS2 Manual Pages" .SH "NAME" o2cb \- Cluster registration utility for the \fIO2CB\fR cluster stack. .SH "SYNOPSIS" \fBo2cb\fR [\fB--config-file\fR=path] [-\fBh\fR|\fB--help\fR] [-\fBv\fR|\fB--verbose\fR] [\fB-V\fR|\fB--version\fR] \fICOMMAND\fR [\fIARGS\fR] .SH "DESCRIPTION" .PP \fBo2cb(8)\fR is used to add, remove and list the information in the \fBO2CB\fR cluster configuration file. This utility is also used to register and unregister the cluster, as well as start and stop global heartbeat. The default location of the configuration file, /etc/ocfs2/cluster.conf, can be overridden using the --config-file option. .SH "OPTIONS" .TP \fB--config-file\fR \fIconfig-file\fR Specify a path to the configuration file. If not provided, it will use the default path of /etc/ocfs2/cluster.conf. .TP \fB-v, --verbose\fR Verbose mode. .TP \fB-h, --help\fR Help. .TP \fB-V, --version\fR Show version and exit. .SH "O2CB COMMANDS" .TP \fBadd-cluster\fR \fIcluster-name\fR Adds a cluster to the configuration file. The \fBO2CB\fR configuration file can hold multiple clusters. However, only one cluster can be active at any time. .TP \fBremove-cluster\fR \fIcluster-name\fR Removes a cluster from the configuration file. This command removes all the nodes and heartbeat regions assigned to the cluster. .TP \fBadd-node\fR \fIcluster-name\fR \fInode-name\fR [\fB--ip\fR \fIip-address\fR] [\fB--port\fR \fIport\fR] [\fB--number\fR \fInode-number\fR] Adds a node to the cluster in the configuration file. It accepts three optional arguments. If not provided, the \fIip-address\fR defaults to the one assigned to the \fInode-name\fR, \fIport\fR to 7777, and \fInode-number\fR to the lowest unused node number. .TP \fBremove-node\fR \fIcluster-name\fR \fInode-name\fR Removes a node from the cluster in the configuration file. .TP \fBadd-heartbeat\fR \fIcluster-name\fR [\fIuuid\fR|\fIdevice\fR] Adds a heartbeat region to the cluster in the configuration file. .TP \fBremove-heartbeat\fR \fIcluster-name\fR [\fIuuid\fR|\fIdevice\fR] Removes a heartbeat region from the cluster in the configuration file. .TP \fBheartbeat-mode\fR \fIcluster-name\fR [\fBlocal\fR|\fBglobal\fR] Sets the heartbeat mode for the cluster in the configuration file. .TP \fBlist-clusters\fR Lists all the cluster names in the configuration file. .TP \fBlist-cluster\fR \fIcluster-name\fR \fB--oneline\fR Lists all the nodes and heartbeat regions associated with the cluster in the configuration file. .TP \fBlist-nodes\fR \fIcluster-name\fR \fB--oneline\fR Lists all the nodes associated with the cluster in the configuration file. .TP \fBlist-heartbeats\fR \fIcluster-name\fR \fB--oneline\fR Lists all the heartbeat regions associated with the cluster in the configuration file. .TP \fBregister-cluster\fR \fIcluster-name\fR Registers the cluster listed in the configuration file with configfs. If called when the cluster is already registered, it will update configfs with the current configuration. .TP \fBunregister-cluster\fR \fIcluster-name\fR Unregisters the cluster from configfs. .TP \fBstart-heartbeat\fR \fIcluster-name\fR Starts global heartbeat on all regions for the cluster as listed in the configuration file. If repeated, it will start heartbeat on new regions and stop on regions since removed. It will silently exit if global heartbeat has not been enabled. .TP \fBstop-heartbeat\fR \fIcluster-name\fR Stops global heartbeat on all regions for the cluster. It will silently exit if global heartbeat has not been enabled. .TP \fBcluster-status\fR \fI[cluster-name]\fR Shows whether the given cluster is offline or online. If no cluster is provided, it shows the currently active cluster, if any. .SH "EXAMPLE" To create a cluster, mycluster having two nodes, node1 and node2, do: .nf .ft 6 $ o2cb add-cluster mycluster $ o2cb add-node mycluster node1 --ip 10.10.10.1 $ o2cb add-node mycluster node2 --ip 10.10.10.2 .ft .fi To specify a global heartbeat device, /dev/sda1, do: .nf .ft 6 $ o2cb add-heartbeat mycluster /dev/sda1 .ft .fi To enable global heartbeat, do: .nf .ft 6 $ o2cb heartbeat-mode mycluster global .ft .fi .SH "SEE ALSO" .BR o2cb(7) .BR o2cb.sysconfig(5) .BR ocfs2.cluster.conf(5) .SH "AUTHORS" Oracle Corporation .SH "COPYRIGHT" Copyright \(co 2010, 2012 Oracle. All rights reserved. ocfs2-tools-ocfs2-tools-1.8.6/o2cb_ctl/o2cb_config.c000066400000000000000000000545661347147137200221060ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 4; -*- * * o2cb_config.c * * Configuration management routines for the o2cb_ctl utility. * * Copyright (C) 2004 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License, version 2, as published by the Free Software Foundation. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. */ #include #include #include #include #include #include #include #include #include "jiterator.h" #include "jconfig.h" #include "o2cb_config.h" #include "o2cb/o2cb.h" struct _O2CBConfig { GList *co_clusters; gboolean co_valid; }; struct _O2CBCluster { gchar *c_name; gchar *c_hb_mode; guint c_num_nodes; GList *c_nodes; GList *c_heartbeat; }; struct _O2CBNode { guint n_number; gchar *n_name; gchar *n_addr; guint n_port; }; struct _O2CBHeartbeat { gchar *h_region; }; gchar *valid_heartbeat_modes[] = { O2CB_LOCAL_HEARTBEAT_TAG, O2CB_GLOBAL_HEARTBEAT_TAG, NULL, }; static void o2cb_node_free(O2CBNode *node); O2CBConfig *o2cb_config_initialize(void) { O2CBConfig *config; config = g_new(O2CBConfig, 1); if (!config) return NULL; config->co_clusters = NULL; config->co_valid = FALSE; return config; } /* o2cb_config_initialize() */ static gint o2cb_cluster_fill_heartbeat(O2CBCluster *cluster, JConfigStanza *cfs) { O2CBHeartbeat *hb; gchar *region; gint rc; rc = -EINVAL; region = j_config_get_attribute(cfs, "region"); if (!region || !*region) goto out_error; hb = o2cb_cluster_add_heartbeat(cluster, region); if (!hb) return -ENOMEM; rc = 0; out_error: g_free(region); return rc; } /* o2cb_config_fill_heartbeat() */ static gint o2cb_cluster_fill_node(O2CBCluster *cluster, JConfigStanza *cfs) { O2CBNode *node; gchar *num_s, *name, *addr, *port_s; gchar *ptr; gulong val; gint rc; num_s = addr = port_s = NULL; rc = -EINVAL; name = j_config_get_attribute(cfs, "name"); if (!name || !*name) goto out_error; /* NB: _add_node() gives us a node number, but we're going to * override it, because we know better. */ node = o2cb_cluster_add_node(cluster, name); if (!node) { rc = -ENOMEM; goto out_error; } rc = -EINVAL; num_s = j_config_get_attribute(cfs, "number"); if (!num_s || !*num_s) goto out_error; val = strtoul(num_s, &ptr, 10); if (!ptr || *ptr) goto out_error; rc = -ERANGE; if ((val == ULONG_MAX) || (val >= INT_MAX)) goto out_error; o2cb_node_set_number(node, val); rc = -EINVAL; addr = j_config_get_attribute(cfs, "ip_address"); if (!addr || !*addr) goto out_error; rc = o2cb_node_set_ip_string(node, addr); if (rc) goto out_error; rc = -EINVAL; port_s = j_config_get_attribute(cfs, "ip_port"); if (!port_s || !*port_s) goto out_error; val = strtoul(port_s, &ptr, 10); if (!ptr || *ptr) goto out_error; rc = -ERANGE; if ((val == ULONG_MAX) || (val > UINT_MAX)) goto out_error; o2cb_node_set_port(node, val); rc = 0; out_error: g_free(num_s); g_free(name); g_free(addr); g_free(port_s); return rc; } /* o2cb_config_fill_node() */ static gint o2cb_config_fill_cluster(O2CBConfig *config, JConfig *cf, JConfigStanza *c_cfs) { gint rc; gulong val; gchar *count = NULL, *ptr, *hb_mode = NULL; O2CBCluster *cluster; JIterator *iter; JConfigStanza *n_cfs; JConfigMatch match = {J_CONFIG_MATCH_VALUE, "cluster", NULL}; /* cluster: name */ rc = -ENOENT; match.value = j_config_get_attribute(c_cfs, "name"); if (!match.value && !*match.value) goto out_error; cluster = o2cb_config_add_cluster(config, match.value); if (!cluster) goto out_error; /* cluster: heartbeat_mode */ rc = -ENOMEM; hb_mode = j_config_get_attribute(c_cfs, "heartbeat_mode"); if (!hb_mode) hb_mode = g_strdup(O2CB_LOCAL_HEARTBEAT_TAG); if (!hb_mode) goto out_error; rc = o2cb_cluster_set_heartbeat_mode(cluster, hb_mode); if (rc) goto out_error; /* node: */ rc = -ENOMEM; iter = j_config_get_stanzas(cf, "node", &match, 1); if (!iter) goto out_error; rc = 0; while (j_iterator_has_more(iter)) { n_cfs = (JConfigStanza *)j_iterator_get_next(iter); rc = o2cb_cluster_fill_node(cluster, n_cfs); if (rc) break; } j_iterator_free(iter); if (rc) goto out_error; /* cluster: node_count */ rc = -EINVAL; count = j_config_get_attribute(c_cfs, "node_count"); if (!count || !*count) goto out_error; val = strtoul(count, &ptr, 10); if (!ptr || *ptr) goto out_error; rc = -ERANGE; if ((val == ULONG_MAX) || (val > UINT_MAX)) goto out_error; cluster->c_num_nodes = val; /* heartbeat: */ rc = -ENOMEM; iter = j_config_get_stanzas(cf, "heartbeat", &match, 1); if (!iter) goto out_error; rc = 0; while (j_iterator_has_more(iter)) { n_cfs = (JConfigStanza *)j_iterator_get_next(iter); rc = o2cb_cluster_fill_heartbeat(cluster, n_cfs); if (rc) break; } j_iterator_free(iter); if (rc) goto out_error; rc = 0; out_error: g_free(hb_mode); g_free(match.value); g_free(count); return rc; } /* o2cb_config_fill_cluster() */ static gint o2cb_config_fill(O2CBConfig *config, JConfig *cf) { int rc; JIterator *iter; JConfigStanza *c_cfs; rc = -ENOMEM; iter = j_config_get_stanzas(cf, "cluster", NULL, 0); if (!iter) goto out_error; rc = 0; while (j_iterator_has_more(iter)) { c_cfs = (JConfigStanza *)j_iterator_get_next(iter); rc = o2cb_config_fill_cluster(config, cf, c_cfs); if (rc) break; } j_iterator_free(iter); out_error: return rc; } gint o2cb_config_load(const gchar *filename, O2CBConfig **config) { gint rc; JConfigCtxt *ctxt; JConfig *cf; struct stat stat_buf; rc = stat(filename, &stat_buf); if (rc) { rc = -errno; if (rc != -ENOENT) return rc; cf = j_config_parse_memory("", strlen("")); if (!cf) return -ENOMEM; } else { ctxt = j_config_new_context(); if (!ctxt) return -ENOMEM; j_config_context_set_verbose(ctxt, FALSE); cf = j_config_parse_file_with_context(ctxt, filename); if (j_config_context_get_error(ctxt)) { if (cf) { j_config_free(cf); cf = NULL; } } j_config_context_free(ctxt); if (!cf) return -EIO; } *config = o2cb_config_initialize(); if (*config) { rc = o2cb_config_fill(*config, cf); if (rc) { o2cb_config_free(*config); *config = NULL; } else (*config)->co_valid = TRUE; } j_config_free(cf); return rc; } /* o2cb_config_load() */ static gint o2cb_heartbeat_store(JConfig *cf, O2CBCluster *cluster, O2CBHeartbeat *hb) { JConfigStanza *cfs; cfs = j_config_add_stanza(cf, "heartbeat"); if (!cfs) return -ENOMEM; j_config_set_attribute(cfs, "cluster", cluster->c_name); j_config_set_attribute(cfs, "region", hb->h_region); return 0; } /* o2cb_heartbeat_store() */ static gint o2cb_node_store(JConfig *cf, O2CBCluster *cluster, O2CBNode *node) { gchar *val; JConfigStanza *cfs; cfs = j_config_add_stanza(cf, "node"); if (!cfs) return -ENOMEM; j_config_set_attribute(cfs, "cluster", cluster->c_name); j_config_set_attribute(cfs, "name", node->n_name); j_config_set_attribute(cfs, "ip_address", node->n_addr); val = g_strdup_printf("%u", node->n_port); if (!val) return -ENOMEM; j_config_set_attribute(cfs, "ip_port", val); g_free(val); val = g_strdup_printf("%u", node->n_number); if (!val) return -ENOMEM; j_config_set_attribute(cfs, "number", val); g_free(val); return 0; } /* o2cb_node_store() */ static gint o2cb_cluster_store(JConfig *cf, O2CBCluster *cluster) { int rc; gchar *count; GList *list; JConfigStanza *cfs; O2CBNode *node; O2CBHeartbeat *hb; cfs = j_config_add_stanza(cf, "cluster"); j_config_set_attribute(cfs, "name", cluster->c_name); j_config_set_attribute(cfs, "heartbeat_mode", cluster->c_hb_mode); count = g_strdup_printf("%u", cluster->c_num_nodes); j_config_set_attribute(cfs, "node_count", count); g_free(count); rc = 0; list = cluster->c_heartbeat; while (list) { hb = (O2CBHeartbeat *)list->data; rc = o2cb_heartbeat_store(cf, cluster, hb); if (rc) break; list = list->next; } list = cluster->c_nodes; while (list) { node = (O2CBNode *)list->data; rc = o2cb_node_store(cf, cluster, node); if (rc) break; list = list->next; } return rc; } static gint write_file(const gchar *text, const gchar *filename) { int rc, fd; GString *template; FILE *file; size_t written, len; rc = mkdir("/etc/ocfs2", 0755); if (rc) { rc = -errno; if (rc == -EEXIST) rc = 0; else goto out; } rc = -ENOMEM; template = g_string_new(filename); if (!template) goto out; g_string_append(template, "XXXXXX"); fd = mkstemp(template->str); rc = -errno; if (fd < 0) goto out_free; file = fdopen(fd, "w"); if (!file) { rc = -errno; close(fd); goto out_unlink; } len = strlen(text); written = fwrite(text, sizeof(char), len, file); if (written != len) { if (feof(file)) rc = -EIO; else if (ferror(file)) rc = -errno; else rc = -EIO; fclose(file); goto out_unlink; } fchmod(fd, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); rc = fclose(file); if (rc) { rc = -errno; goto out_unlink; } rc = rename(template->str, filename); if (rc) rc = -errno; out_unlink: if (rc) unlink(template->str); out_free: g_string_free(template, TRUE); out: return rc; } gint o2cb_config_store(O2CBConfig *config, const gchar *filename) { int rc; char *text; JConfig *cf; O2CBCluster *cluster; GList *list; cf = j_config_parse_memory("", strlen("")); if (!cf) return -ENOMEM; rc = 0; list = config->co_clusters; while (list) { cluster = (O2CBCluster *)list->data; rc = o2cb_cluster_store(cf, cluster); if (rc) goto out; list = list->next; } rc = -ENOMEM; text = j_config_dump_memory(cf); if (!text) goto out; rc = write_file(text, filename); g_free(text); out: j_config_free(cf); return rc; } /* o2cb_config_store() */ static void o2cb_node_free(O2CBNode *node) { if (node->n_name) g_free(node->n_name); if (node->n_addr) g_free(node->n_addr); g_free(node); } /* o2cb_node_free() */ static void o2cb_heartbeat_free(O2CBHeartbeat *hb) { if (hb->h_region) g_free(hb->h_region); g_free(hb); } /* o2cb_heartbeat_free() */ static void o2cb_cluster_free(O2CBCluster *cluster) { GList *list; O2CBNode *node; O2CBHeartbeat *hb; while (cluster->c_nodes) { list = cluster->c_nodes; node = (O2CBNode *)list->data; cluster->c_nodes = g_list_delete_link(list, list); o2cb_node_free(node); } while (cluster->c_heartbeat) { list = cluster->c_heartbeat; hb = (O2CBHeartbeat *)list->data; cluster->c_heartbeat = g_list_delete_link(list, list); o2cb_heartbeat_free(hb); } if (cluster->c_name) g_free(cluster->c_name); g_free(cluster); } /* o2cb_cluster_free() */ static void _o2cb_cluster_free(gpointer data, gpointer user_data) { o2cb_cluster_free((O2CBCluster *)data); } void o2cb_config_free(O2CBConfig *config) { g_list_foreach(config->co_clusters, _o2cb_cluster_free, NULL); g_list_free(config->co_clusters); g_free(config); } O2CBCluster *o2cb_config_add_cluster(O2CBConfig *config, const gchar *name) { O2CBCluster *cluster; g_return_val_if_fail(config != NULL, NULL); cluster = o2cb_config_get_cluster_by_name(config, name); if (cluster) return NULL; cluster = g_new(O2CBCluster, 1); cluster->c_name = g_strdup(name); cluster->c_hb_mode = g_strdup(O2CB_LOCAL_HEARTBEAT_TAG); cluster->c_num_nodes = 0; cluster->c_nodes = NULL; cluster->c_heartbeat = NULL; config->co_clusters = g_list_append(config->co_clusters, cluster); config->co_valid = TRUE; return cluster; } /* o2cb_cluster_add_cluster() */ gint o2cb_config_remove_cluster(O2CBConfig *config, const gchar *name) { O2CBCluster *cluster; g_return_val_if_fail(config != NULL, -1); cluster = o2cb_config_get_cluster_by_name(config, name); if (!cluster) return -ENOENT; config->co_clusters = g_list_remove(config->co_clusters, cluster); o2cb_cluster_free(cluster); return 0; } O2CBCluster *o2cb_config_get_cluster_by_name(O2CBConfig *config, const gchar *name) { GList *list; O2CBCluster *cluster; g_return_val_if_fail(config != NULL, NULL); list = config->co_clusters; while (list) { cluster = (O2CBCluster *)list->data; if (!strcmp(cluster->c_name, name)) return cluster; list = list->next; } return NULL; } /* o2cb_config_get_cluster_by_name() */ JIterator *o2cb_config_get_clusters(O2CBConfig *config) { g_return_val_if_fail(config != NULL, NULL); return j_iterator_new_from_list(config->co_clusters); } /* o2cb_config_get_clusters() */ gchar *o2cb_cluster_get_name(O2CBCluster *cluster) { g_return_val_if_fail(cluster != NULL, NULL); return g_strdup(cluster->c_name); } /* o2cb_cluster_get_name() */ gint o2cb_cluster_set_name(O2CBCluster *cluster, const gchar *name) { gchar *new_name; new_name = g_strdup(name); if (!new_name) return -ENOMEM; g_free(cluster->c_name); cluster->c_name = new_name; return 0; } /* o2cb_config_set_cluster_name() */ gchar *o2cb_cluster_get_heartbeat_mode(O2CBCluster *cluster) { g_return_val_if_fail(cluster != NULL, NULL); return g_strdup(cluster->c_hb_mode); } /* o2cb_cluster_get_hb_mode() */ gint o2cb_cluster_set_heartbeat_mode(O2CBCluster *cluster, const gchar *hb_mode) { gchar *new_hb_mode; gint i, valid = 0; if (cluster->c_hb_mode && !g_ascii_strcasecmp(hb_mode, cluster->c_hb_mode)) return 0; for (i = 0; valid_heartbeat_modes[i]; i++) { if (!g_ascii_strcasecmp(hb_mode, valid_heartbeat_modes[i])) { valid = 1; break; } } if (!valid) return -EINVAL; new_hb_mode = g_ascii_strdown(hb_mode, -1); if (!new_hb_mode) return -ENOMEM; g_free(cluster->c_hb_mode); cluster->c_hb_mode = new_hb_mode; return 0; } /* o2cb_config_set_cluster_hb_mode() */ JIterator *o2cb_cluster_get_heartbeat_regions(O2CBCluster *cluster) { g_return_val_if_fail(cluster != NULL, NULL); return j_iterator_new_from_list(cluster->c_heartbeat); } /* o2cb_cluster_get_heartbeat_regions() */ O2CBHeartbeat *o2cb_cluster_get_heartbeat_by_region(O2CBCluster *cluster, const gchar *region) { GList *list; O2CBHeartbeat *hb; g_return_val_if_fail(cluster != NULL, NULL); list = cluster->c_heartbeat; while (list) { hb = (O2CBHeartbeat *)list->data; if (!strcmp(hb->h_region, region)) return hb; list = list->next; } return NULL; } /* o2cb_cluster_get_heartbeat_by_region() */ gint o2cb_cluster_remove_heartbeat(O2CBCluster *cluster, const gchar *region) { O2CBHeartbeat *hb; g_return_val_if_fail(cluster != NULL, -1); hb = o2cb_cluster_get_heartbeat_by_region(cluster, region); if (!hb) return -1; cluster->c_heartbeat = g_list_remove(cluster->c_heartbeat, hb); g_free(hb->h_region); g_free(hb); return 0; } /* o2cb_cluster_remove_heartbeat() */ O2CBHeartbeat *o2cb_cluster_add_heartbeat(O2CBCluster *cluster, const gchar *region) { O2CBHeartbeat *hb; g_return_val_if_fail(cluster != NULL, NULL); hb = o2cb_cluster_get_heartbeat_by_region(cluster, region); if (hb) return NULL; hb = g_new(O2CBHeartbeat, 1); hb->h_region = g_strdup(region); cluster->c_heartbeat = g_list_append(cluster->c_heartbeat, hb); return hb; } /* o2cb_cluster_add_heartbeat() */ gchar *o2cb_heartbeat_get_region(O2CBHeartbeat *heartbeat) { g_return_val_if_fail(heartbeat != NULL, NULL); return g_strdup(heartbeat->h_region); } /* o2cb_heartbeat_get_region */ guint o2cb_cluster_get_node_count(O2CBCluster *cluster) { g_return_val_if_fail(cluster != NULL, 0); return cluster->c_num_nodes; } JIterator *o2cb_cluster_get_nodes(O2CBCluster *cluster) { g_return_val_if_fail(cluster != NULL, NULL); return j_iterator_new_from_list(cluster->c_nodes); } /* o2cb_cluster_get_nodes() */ O2CBNode *o2cb_cluster_get_node(O2CBCluster *cluster, guint n) { GList *list; O2CBNode *node; g_return_val_if_fail(cluster != NULL, NULL); list = cluster->c_nodes; while (list) { node = (O2CBNode *)list->data; if (node->n_number == n) return node; list = list->next; } return NULL; } /* o2cb_cluster_get_node() */ O2CBNode *o2cb_cluster_get_node_by_name(O2CBCluster *cluster, const gchar *name) { GList *list; O2CBNode *node; g_return_val_if_fail(cluster != NULL, NULL); list = cluster->c_nodes; while (list) { node = (O2CBNode *)list->data; if (!strcmp(node->n_name, name)) return node; list = list->next; } return NULL; } /* o2cb_cluster_get_node_by_name() */ O2CBNode *o2cb_cluster_add_node(O2CBCluster *cluster, const gchar *name) { O2CBNode *node; g_return_val_if_fail(cluster != NULL, NULL); node = o2cb_cluster_get_node_by_name(cluster, name); if (node) return NULL; node = g_new(O2CBNode, 1); node->n_name = g_strdup(name); node->n_addr = NULL; node->n_port = 0; node->n_number = cluster->c_num_nodes; cluster->c_num_nodes++; cluster->c_nodes = g_list_append(cluster->c_nodes, node); return node; } /* o2cb_cluster_add_node() */ gint o2cb_cluster_delete_node(O2CBCluster *cluster, const gchar *name) { O2CBNode *node; g_return_val_if_fail(cluster != NULL, -1); g_return_val_if_fail(name != NULL, -1); node = o2cb_cluster_get_node_by_name(cluster, name); if (!node) return -ENOENT; cluster->c_nodes = g_list_remove(cluster->c_nodes, node); cluster->c_num_nodes--; g_free(node->n_name); g_free(node->n_addr); g_free(node); return 0; } /* o2cb_cluster_delete_node() */ gint o2cb_node_get_number(O2CBNode *node) { g_return_val_if_fail(node != NULL, -1); return node->n_number; } /* o2cb_node_get_number() */ gchar *o2cb_node_get_name(O2CBNode *node) { g_return_val_if_fail(node != NULL, NULL); return g_strdup(node->n_name); } /* o2cb_node_get_name() */ gchar *o2cb_node_get_ip_string(O2CBNode *node) { g_return_val_if_fail(node != NULL, NULL); return g_strdup(node->n_addr); } /* o2cb_node_get_ip_string() */ gint o2cb_node_get_ipv4(O2CBNode *node, struct in_addr *addr) { int rc; g_return_val_if_fail(node != NULL, -EINVAL); g_return_val_if_fail(node->n_addr != NULL, -ENOENT); rc = inet_pton(AF_INET, node->n_addr, addr); if (rc < 0) return -errno; if (!rc) return -ENOENT; return 0; } /* o2cb_node_get_ipv4() */ guint o2cb_node_get_port(O2CBNode *node) { g_return_val_if_fail(node != NULL, 0); return node->n_port; } /* o2cb_node_get_port() */ gint o2cb_node_set_name(O2CBNode *node, const gchar *name) { gchar *new_name; new_name = g_strdup(name); if (!new_name) return -ENOMEM; g_free(node->n_name); node->n_name = new_name; return 0; } /* o2cb_node_set_name() */ gint o2cb_node_set_ip_string(O2CBNode *node, const gchar *addr) { gint rc; gchar *new_addr; struct in_addr ip; /* Let's validate the address, shall we? */ rc = inet_pton(AF_INET, addr, &ip); if (rc < 0) return -errno; if (!rc) return -EINVAL; new_addr = g_strdup(addr); if (!new_addr) return -ENOMEM; g_free(node->n_addr); node->n_addr = new_addr; return 0; } /* o2cb_node_set_ip_string() */ gint o2cb_node_set_ipv4(O2CBNode *node, struct in_addr *addr) { gint rc; gchar *new_addr; g_return_val_if_fail(node != NULL, -EINVAL); new_addr = g_malloc0(sizeof(gchar) * INET_ADDRSTRLEN); if (!inet_ntop(AF_INET, addr, new_addr, INET_ADDRSTRLEN)) { rc = -errno; g_free(new_addr); return rc; } node->n_addr = new_addr; return 0; } /* o2cb_node_set_ipv4() */ void o2cb_node_set_port(O2CBNode *node, guint port) { g_return_if_fail(node != NULL); node->n_port = port; } /* o2cb_node_set_port() */ void o2cb_node_set_number(O2CBNode *node, guint num) { g_return_if_fail(node != NULL); node->n_number = num; } /* o2cb_node_set_number() */ ocfs2-tools-ocfs2-tools-1.8.6/o2cb_ctl/o2cb_config.h000066400000000000000000000064601347147137200221010ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 4; -*- * * o2cb_config.h * * Configuration management routines for the o2cb_ctl utility. * * Copyright (C) 2004 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License, version 2, as published by the Free Software Foundation. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. */ #ifndef _O2CB_CONFIG_H #define _O2CB_CONFIG_H typedef struct _O2CBConfig O2CBConfig; typedef struct _O2CBCluster O2CBCluster; typedef struct _O2CBNode O2CBNode; typedef struct _O2CBHeartbeat O2CBHeartbeat; O2CBConfig *o2cb_config_initialize(void); gint o2cb_config_load(const gchar *filename, O2CBConfig **config); gint o2cb_config_store(O2CBConfig *config, const gchar *filename); void o2cb_config_free(O2CBConfig *config); O2CBCluster *o2cb_config_add_cluster(O2CBConfig *config, const gchar *name); gint o2cb_config_remove_cluster(O2CBConfig *config, const gchar *name); JIterator *o2cb_config_get_clusters(O2CBConfig *config); O2CBCluster *o2cb_config_get_cluster_by_name(O2CBConfig *config, const gchar *name); gchar *o2cb_cluster_get_name(O2CBCluster *cluster); gint o2cb_cluster_set_name(O2CBCluster *cluster, const gchar *name); gchar *o2cb_heartbeat_get_region(O2CBHeartbeat *heartbeat); JIterator *o2cb_cluster_get_heartbeat_regions(O2CBCluster *cluster); gchar *o2cb_cluster_get_heartbeat_mode(O2CBCluster *cluster); gint o2cb_cluster_set_heartbeat_mode(O2CBCluster *cluster, const gchar *hb_mode); O2CBHeartbeat *o2cb_cluster_get_heartbeat_by_region(O2CBCluster *cluster, const gchar *region); gint o2cb_cluster_remove_heartbeat(O2CBCluster *cluster, const gchar *region); O2CBHeartbeat *o2cb_cluster_add_heartbeat(O2CBCluster *cluster, const gchar *region); guint o2cb_cluster_get_node_count(O2CBCluster *cluster); JIterator *o2cb_cluster_get_nodes(O2CBCluster *cluster); O2CBNode *o2cb_cluster_get_node(O2CBCluster *cluster, guint n); O2CBNode *o2cb_cluster_get_node_by_name(O2CBCluster *cluster, const gchar *name); O2CBNode *o2cb_cluster_add_node(O2CBCluster *cluster, const gchar *name); gint o2cb_cluster_delete_node(O2CBCluster *cluster, const gchar *name); gint o2cb_node_get_number(O2CBNode *node); gchar *o2cb_node_get_name(O2CBNode *node); gchar *o2cb_node_get_ip_string(O2CBNode *node); gint o2cb_node_get_ipv4(O2CBNode *node, struct in_addr *addr); guint o2cb_node_get_port(O2CBNode *node); gint o2cb_node_set_name(O2CBNode *node, const gchar *name); gint o2cb_node_set_ip_string(O2CBNode *node, const gchar *addr); gint o2cb_node_set_ipv4(O2CBNode *node, struct in_addr *addr); void o2cb_node_set_port(O2CBNode *node, guint port); void o2cb_node_set_number(O2CBNode *node, guint num); #endif /* _O2CB_CONFIG_H */ ocfs2-tools-ocfs2-tools-1.8.6/o2cb_ctl/o2cb_ctl.8.in000066400000000000000000000066251347147137200217460ustar00rootroot00000000000000.TH "o2cb_ctl" "8" "September 2010" "Version @VERSION@" "OCFS2 Manual Pages" .SH "NAME" o2cb_ctl \- Control program for the \fIO2CB\fR cluster service. .SH "SYNOPSIS" .B o2cb_ctl \fB\-C\fR \fB\-n\fR \fIobject\fR \fB\-t\fR type\fR [\fB\-i\fR] [\fB\-a\fR \fIattribute\fR ] .br .B o2cb_ctl \fB\-D\fR \fB\-n\fR \fIobject\fR [\fB\-u\fR] .br .B o2cb_ctl \fB\-I\fR [\fB\-o\|\-z\fR] \fB\-l\fR \fImanager\fR> [\fB\-n\fR \fIobject\fR>] [\fB\-t\fR \fItype\fR] [\fB\-a\fR \fIattribute\fR] .br .B o2cb_ctl \fB\-H\fR [\fB\-n\fR \fIobject\fR] [\fB\-t\fR \fItype\fR>] [\fB\-a\fR \fIattribute\fR>] .br .B o2cb_ctl \fB\-h\fR .br .B o2cb_ctl \fB\-V\fR .SH "DESCRIPTION" .PP \fBo2cb_ctl\fR is the control program for the \fIO2CB\fR cluster service. Users are not advised to use this program directly but instead use the \fIO2CB\fR init service and/or \fIocfs2console\fR. .SH "OPTIONS" .TP \fB\-C\fR Create an object in the OCFS2 Cluster Configuration. .TP \fB\-D\fR Delete an object from the existing OCFS2 Cluster Configuration. .TP \fB\-I\fR Print information about the OCFS2 Cluster Configuration. .TP \fB\-H\fR Change an object or objects in the existing OCFS2 Cluster Configuration. .TP \fB\-h\fR Displays help and exit. .TP \fB\-V\fR Print version and exit. .SH "OTHER OPTIONS" .TP \fB\-a\fR \fI\fR With \fB\-C\fR, \fI\fR is in format "parameter=value", where the parameter is a valid parameter that can be set in the file \fI/etc/ocfs2/cluster.conf\fR. With \fB\-I\fR, \fI\fR may be "parameter", indicating an attribute to be listed in the output, or it may be "parameter==value", indicating that only objects matching "parameter=value" are to be displayed. .TP \fB\-i\fR Valid only with \fB\-C\fR. When creating something (\fInode\fR or \fIcluster\fR), it will also install it in the live cluster. If the parameter is not specified, then only update the \fI/etc/ocfs2/cluster.conf\fR. .TP \fB\-n\fR \fIobject\fR \fIobject\fR is usually the node name or cluster name. In the \fI/etc/ocfs2/cluster.conf\fR file, it would be the value of the name parameter for any of the sections (\fIcluster\fR or \fInode\fR). .TP \fB\-o\fR Valid only with \fB\-I\fR. Using this parameter, if one asks o2cb_ctl to list all nodes, it will output it in a format suitable for shell parsing. .TP \fB\-t\fR \fItype\fR \fItype\fR can be cluster, node or heartbeat. .TP \fB\-u\fR Valid only with \fB\-D\fR. When deleting something (\fInode\fR or \fIcluster\fR), it will also remove it from the live cluster. If the parameter is not specified, then only update the \fI/etc/ocfs2/cluster.conf\fR. .TP \fB\-z\fR Valid only with \fB\-I\fR. This is the default. If one asks o2cb_ctl to list all nodes, it will give a verbose listing. .SH "EXAMPLES" .HP Add node5 to an offline cluster: $ o2cb_ctl -C -n node5 -t node -a number=5 .br -a ip_address=192.168.0.5 -a ip_port=7777 .br -a cluster=mycluster .TP Add node10 to an online cluster: $ o2cb_ctl -C -i -n node10 -t node -a number=10 .br -a ip_address=192.168.1.10 -a ip_port=7777 .br -a cluster=mycluster Note the -i argument. .TP Query the IP address of node5: $ o2cb_ctl -I -n node5 -a ip_address .TP Change the IP address of node5: $ o2cb_ctl -H -n node5 -a ip_address=192.168.1.5 .SH "SEE ALSO" .BR mkfs.ocfs2(8) .BR fsck.ocfs2(8) .BR tunefs.ocfs2(8) .BR mounted.ocfs2(8) .BR o2cb(7) .SH "AUTHORS" Oracle Corporation .SH "COPYRIGHT" Copyright \(co 2004, 2010 Oracle. All rights reserved. ocfs2-tools-ocfs2-tools-1.8.6/o2cb_ctl/o2cb_ctl.c000066400000000000000000001046611347147137200214130ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 4; -*- * vim:expandtab:shiftwidth=4:tabstop=4: * * * o2cb_ctl.c * * Control program for O2CB. * * Copyright (C) 2004 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License, version 2, as published by the Free Software Foundation. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. */ #include #include #include #include #include #include #include #include #include #include #include "jiterator.h" #include "o2cb_config.h" #include "o2cb/o2cb.h" #define PROGNAME "o2cb_ctl" #define O2CB_CONFIG_FILE "/etc/ocfs2/cluster.conf" typedef enum { O2CB_OP_NONE = 0, O2CB_OP_INFO, O2CB_OP_CREATE, O2CB_OP_DELETE, O2CB_OP_CHANGE, } O2CBOperation; typedef enum { O2CB_TYPE_NONE = 0, O2CB_TYPE_CLUSTER, O2CB_TYPE_NODE, } O2CBType; typedef struct _O2CBContext O2CBContext; typedef struct _OptionAttr OptionAttr; struct _O2CBContext { O2CBOperation oc_op; O2CBType oc_type; GList *oc_objects; GList *oc_attrs; gboolean oc_compact_info; gboolean oc_changed; gboolean oc_modify_running; O2CBConfig *oc_config; }; struct _OptionAttr { int oa_set; gchar *oa_name; gchar *oa_value; }; static void print_version(void) { fprintf(stdout, PROGNAME " version %s\n", VERSION); exit(0); } /* print_version() */ static void print_usage(gint rc) { FILE *output = rc ? stderr : stdout; fprintf(output, "Usage: " PROGNAME " -C -n -t [-i] [-a ] ...\n" " " PROGNAME " -D -n [-u]\n" " " PROGNAME " -I [-o|-z] [-n ] [-t ] [-a ] ...\n" " " PROGNAME " -H [-n ] [-t ] [-a ] ...\n" " " PROGNAME " -h\n" " " PROGNAME " -V\n"); exit(rc); } /* print_usage() */ static gint add_object(O2CBContext *ctxt, const gchar *object) { ctxt->oc_objects = g_list_append(ctxt->oc_objects, g_strdup(object)); return 0; } static gboolean valid_attr(O2CBContext *ctxt, OptionAttr *attr) { int i; struct va_table { O2CBType va_type; gchar *va_name; } vat[] = { {O2CB_TYPE_CLUSTER, "name"}, {O2CB_TYPE_CLUSTER, "online"}, {O2CB_TYPE_NODE, "name"}, {O2CB_TYPE_NODE, "cluster"}, {O2CB_TYPE_NODE, "number"}, {O2CB_TYPE_NODE, "ip_address"}, {O2CB_TYPE_NODE, "ip_port"}, }; for (i = 0; i < (sizeof(vat) / sizeof(*vat)); i++) { if ((vat[i].va_type == ctxt->oc_type) && !strcmp(vat[i].va_name, attr->oa_name)) return TRUE; } return FALSE; } /* valid_attr() */ /* Must be called after oc_type is set */ static gint validate_attrs(O2CBContext *ctxt) { GList *list; OptionAttr *attr; list = ctxt->oc_attrs; while (list) { attr = (OptionAttr *)(list->data); if (!valid_attr(ctxt, attr)) { fprintf(stderr, PROGNAME ": Invalid attribute: \"%s\"\n", attr->oa_name); return -EINVAL; } list = list->next; } return 0; } /* validate_attrs() */ static void clear_attrs(O2CBContext *ctxt) { GList *list; OptionAttr *attr; list = ctxt->oc_attrs; if (!list) return; while (list) { attr = (OptionAttr *)list->data; if (attr->oa_name) g_free(attr->oa_name); if (attr->oa_value) g_free(attr->oa_value); g_free(attr); list->data = NULL; list = list->next; } g_list_free(ctxt->oc_attrs); } /* clear_attrs() */ static void clear_objects(O2CBContext *ctxt) { GList *list; list = ctxt->oc_objects; if (!list) return; while (list) { g_free(list->data); list->data = NULL; list = list->next; } g_list_free(ctxt->oc_objects); } static gboolean attr_set(O2CBContext *ctxt, const gchar *attr_name) { OptionAttr *attr; GList *list; list = ctxt->oc_attrs; while (list) { attr = (OptionAttr *)list->data; if (!strcmp(attr->oa_name, attr_name)) return attr->oa_set; list = list->next; } return FALSE; } /* attr_set() */ static const gchar *attr_string(O2CBContext *ctxt, const gchar *attr_name, const gchar *def_value) { OptionAttr *attr; GList *list; list = ctxt->oc_attrs; while (list) { attr = (OptionAttr *)list->data; if (!strcmp(attr->oa_name, attr_name)) { if (!attr->oa_set) return def_value; return attr->oa_value; } list = list->next; } return def_value; } /* attr_string() */ static gboolean attr_boolean(O2CBContext *ctxt, const gchar *attr_name, gboolean def_value) { int i; GList *list; OptionAttr *attr; struct b_table { gchar *match; gboolean value; } bt[] = { {"0", FALSE}, {"1", TRUE}, {"f", FALSE}, {"t", TRUE}, {"false", FALSE}, {"true", TRUE}, {"n", FALSE}, {"y", TRUE}, {"no", FALSE}, {"yes", TRUE}, {"off", FALSE}, {"on", TRUE} }; list = ctxt->oc_attrs; while (list) { attr = (OptionAttr *)list->data; if (!strcmp(attr->oa_name, attr_name)) { if (!attr->oa_set || !attr->oa_value || !*(attr->oa_value)) break; for (i = 0; i < (sizeof(bt) / sizeof(*bt)); i++) { if (!strcmp(bt[i].match, attr->oa_value)) return bt[i].value; } fprintf(stderr, PROGNAME ": Invalid value for attribute \"%s\": %s\n", attr_name, attr->oa_value); return -EINVAL; } list = list->next; } return def_value; } /* attr_boolean() */ static gint append_attr(O2CBContext *ctxt, const gchar *attr_string) { char **p; OptionAttr *attr; p = g_strsplit(attr_string, "=", 2); if (!p) return -ENOMEM; if (!p[0] || !*p[0]) return -EINVAL; attr = g_new(OptionAttr, 1); attr->oa_name = g_strdup(p[0]); attr->oa_value = g_strdup(p[1]); attr->oa_set = 1; g_strfreev(p); ctxt->oc_attrs = g_list_append(ctxt->oc_attrs, attr); return 0; } /* append_attr() */ extern char *optarg; extern int optopt; extern int opterr; extern int optind; static gint parse_options(gint argc, gchar *argv[], O2CBContext *ctxt) { int c, rc; gboolean mi, mu, mo, mz; mi = mu = mo = mz = FALSE; opterr = 0; while ((c = getopt(argc, argv, ":hVCDIHiuozn:t:a:-:")) != EOF) { switch (c) { case 'h': print_usage(0); break; case 'V': print_version(); break; case '-': if (!strcmp(optarg, "version")) print_version(); else if (!strcmp(optarg, "help")) print_usage(0); else { fprintf(stderr, PROGNAME ": Invalid option: \'--%s\'\n", optarg); return -EINVAL; } break; case 'C': if (ctxt->oc_op != O2CB_OP_NONE) return -EINVAL; ctxt->oc_op = O2CB_OP_CREATE; break; case 'D': if (ctxt->oc_op != O2CB_OP_NONE) return -EINVAL; ctxt->oc_op = O2CB_OP_DELETE; break; case 'I': if (ctxt->oc_op != O2CB_OP_NONE) return -EINVAL; ctxt->oc_op = O2CB_OP_INFO; break; case 'H': if (ctxt->oc_op != O2CB_OP_NONE) return -EINVAL; ctxt->oc_op = O2CB_OP_CHANGE; break; case 'i': mi = TRUE; break; case 'u': mu = TRUE; break; case 'z': mz = TRUE; break; case 'o': mo = TRUE; break; case 'n': if (!optarg || !*optarg) { fprintf(stderr, PROGNAME ": Argument to \'-n\' cannot be \"\"\n"); return -EINVAL; } add_object(ctxt, optarg); break; case 't': if (!optarg || !*optarg) { fprintf(stderr, PROGNAME ": Argument to \'-t\' cannot be \"\"\n"); return -EINVAL; } if (!strcmp(optarg, "cluster")) ctxt->oc_type = O2CB_TYPE_CLUSTER; else if (!strcmp(optarg, "node")) ctxt->oc_type = O2CB_TYPE_NODE; else { fprintf(stderr, PROGNAME ": Object type \"%s\" is invalid\n", optarg); return -EINVAL; } break; case 'a': if (!optarg || !*optarg) { fprintf(stderr, PROGNAME ": Argument to \'-a\' cannot be \"\"\n"); return -EINVAL; } rc = append_attr(ctxt, optarg); if (rc) return rc; break; case '?': fprintf(stderr, PROGNAME ": Invalid option: \'-%c\'\n", optopt); return -EINVAL; break; case ':': fprintf(stderr, PROGNAME ": Option \'-%c\' requires an argument\n", optopt); return -EINVAL; break; default: fprintf(stderr, PROGNAME ":Shouldn't get here %c %c\n", optopt, c); return -EINVAL; break; } } if (optind < argc) { fprintf(stderr, PROGNAME ": Extraneous arguments: \""); for (; optind < argc; optind++) { fprintf(stderr, "%s", argv[optind]); if ((optind + 1) < argc) fprintf(stderr, " "); } fprintf(stderr, "\"\n"); return -E2BIG; } if (mu && (ctxt->oc_op != O2CB_OP_DELETE)) c = 'u'; else if (mi && (ctxt->oc_op != O2CB_OP_CREATE)) c = 'i'; else if (mz && (ctxt->oc_op != O2CB_OP_INFO)) c = 'z'; else if (mo && (ctxt->oc_op != O2CB_OP_INFO)) c = 'o'; if (c != EOF) { fprintf(stderr, PROGNAME ": Argument \'-%c\' is not valid for this operation\n", c); return -EINVAL; } if (mz && mo) { fprintf(stderr, PROGNAME ": Cannot specify \'-z\' and \'-o\' at the same time\n"); return -EINVAL; } if ((ctxt->oc_op == O2CB_OP_INFO) && mo) ctxt->oc_compact_info = TRUE; if ((ctxt->oc_op == O2CB_OP_CREATE) && mi) ctxt->oc_modify_running = TRUE; if ((ctxt->oc_op == O2CB_OP_DELETE) && mu) ctxt->oc_modify_running = TRUE; return 0; } /* parse_options() */ static gint load_config(O2CBContext *ctxt) { gint rc; rc = o2cb_config_load(O2CB_CONFIG_FILE, &ctxt->oc_config); if (rc) { fprintf(stderr, PROGNAME ": Unable to load cluster configuration file \"%s\"\n", O2CB_CONFIG_FILE); return -EIO; } return 0; } /* load_config() */ static gint write_config(O2CBContext *ctxt) { gint rc; g_return_val_if_fail(ctxt->oc_config != NULL, -EINVAL); rc = o2cb_config_store(ctxt->oc_config, O2CB_CONFIG_FILE); if (rc) { fprintf(stderr, PROGNAME ": Unable to store cluster configuration file \"%s\": %s\n", O2CB_CONFIG_FILE, g_strerror(-rc)); } return rc; } /* write_config() */ static gint find_objects_for_type(O2CBContext *ctxt) { int rc = 0; JIterator *c_iter, *n_iter; O2CBCluster *cluster; O2CBNode *node; gchar *object = NULL; c_iter = o2cb_config_get_clusters(ctxt->oc_config); if (!c_iter) { rc = -ENOMEM; goto out; } while (j_iterator_has_more(c_iter)) { cluster = j_iterator_get_next(c_iter); if (ctxt->oc_type == O2CB_TYPE_CLUSTER) { object = o2cb_cluster_get_name(cluster); add_object(ctxt, object); g_free(object); } else if (ctxt->oc_type == O2CB_TYPE_NODE) { n_iter = o2cb_cluster_get_nodes(cluster); if (!n_iter) { rc = -ENOMEM; break; } while (j_iterator_has_more(n_iter)) { node = j_iterator_get_next(n_iter); object = o2cb_node_get_name(node); add_object(ctxt, object); g_free(object); } j_iterator_free(n_iter); } else abort(); } j_iterator_free(c_iter); out: return rc; } /* find_objects_for_type() */ static gint find_type_for_objects(O2CBContext *ctxt) { int rc; gchar *object, *name; gulong num; JIterator *c_iter; O2CBNode *node; O2CBCluster *cluster; object = (gchar *)ctxt->oc_objects->data; cluster = o2cb_config_get_cluster_by_name(ctxt->oc_config, object); if (cluster) { rc = 0; ctxt->oc_type = O2CB_TYPE_CLUSTER; goto out; } num = strtoul(object, &name, 10); if (!name || *name || (num == UINT_MAX)) num = UINT_MAX; rc = -ENOMEM; c_iter = o2cb_config_get_clusters(ctxt->oc_config); if (!c_iter) goto out; rc = -ENOENT; while (j_iterator_has_more(c_iter)) { cluster = (O2CBCluster *)j_iterator_get_next(c_iter); node = o2cb_cluster_get_node_by_name(cluster, object); if (!node && (num < UINT_MAX)) node = o2cb_cluster_get_node(cluster, num); if (node) { ctxt->oc_type = O2CB_TYPE_NODE; rc = 0; break; } } j_iterator_free(c_iter); out: return rc; } /* find_type_for_objects() */ static gint run_info_clusters(O2CBContext *ctxt) { GList *list; O2CBCluster *cluster; gchar *name; gchar *format; if (ctxt->oc_compact_info) { format = "%s:%u:%s\n"; fprintf(stdout, "#name:count:status\n"); } else { format = "cluster:\n" "\tname = %s\n" "\tnode_count = %u\n" "\tstatus = %s\n" "\n"; } list = ctxt->oc_objects; while (list) { name = list->data; cluster = o2cb_config_get_cluster_by_name(ctxt->oc_config, name); if (!cluster) { fprintf(stderr, "Cluster \"%s\" does not exist\n", name); return -ENOENT; } fprintf(stdout, format, name, o2cb_cluster_get_node_count(cluster), "configured"); list = list->next; } return 0; } static gint run_info_nodes(O2CBContext *ctxt) { GList *list; JIterator *iter; O2CBCluster *cluster; O2CBNode *node; gchar *name; gchar *format; gchar *cluster_name = NULL; if (ctxt->oc_compact_info) { format = "%s:%s:%u:%s:%d:%s\n"; fprintf(stdout, "#name:cluster:number:ip_address:ip_port:status\n"); } else { format = "node:\n" "\tname = %s\n" "\tcluster = %s\n" "\tnumber = %u\n" "\tip_address = %s\n" "\tip_port = %d\n" "\tstatus = %s\n" "\n"; } list = ctxt->oc_objects; while (list) { name = list->data; iter = o2cb_config_get_clusters(ctxt->oc_config); if (!iter) return -ENOMEM; cluster = NULL; /* For gcc */ node = NULL; while (j_iterator_has_more(iter)) { cluster = j_iterator_get_next(iter); node = o2cb_cluster_get_node_by_name(cluster, name); if (node) break; } j_iterator_free(iter); if (!node) { fprintf(stderr, "Node \"%s\" does not exist\n", name); return -ENOENT; } cluster_name = o2cb_cluster_get_name(cluster); fprintf(stdout, format, name, cluster_name, o2cb_node_get_number(node), o2cb_node_get_ip_string(node), o2cb_node_get_port(node), "configured"); g_free(cluster_name); list = list->next; } return 0; } static gint run_info(O2CBContext *ctxt) { gint rc; if (!ctxt->oc_type && !ctxt->oc_objects) { fprintf(stderr, PROGNAME ": Operation \'-I\' requires an object or object type\n"); return -EINVAL; } rc = validate_attrs(ctxt); if (rc) return rc; rc = load_config(ctxt); if (rc) return rc; if (ctxt->oc_type && !ctxt->oc_objects) { rc = find_objects_for_type(ctxt); if (rc) goto out_error; } else if (ctxt->oc_objects && !ctxt->oc_type) { rc = find_type_for_objects(ctxt); if (rc) goto out_error; } if (ctxt->oc_type == O2CB_TYPE_NODE) { rc = run_info_nodes(ctxt); if (rc) goto out_error; } else if (ctxt->oc_type == O2CB_TYPE_CLUSTER) { rc = run_info_clusters(ctxt); if (rc) goto out_error; } else { rc = -EINVAL; fprintf(stderr, PROGNAME ": Invalid object type!\n"); goto out_error; } out_error: return rc; } /* run_info() */ static errcode_t o2cb_node_is_local(gchar *node_name, gboolean *is_local) { char hostname[PATH_MAX]; size_t host_len, node_len = strlen(node_name); gboolean local = 0; errcode_t ret = 0; ret = gethostname(hostname, sizeof(hostname)); if (ret) { fprintf(stderr, "gethostname() failed: %s", strerror(errno)); ret = O2CB_ET_HOSTNAME_UNKNOWN; goto out; } host_len = strlen(hostname); if (host_len < node_len) goto out; /* nodes are only considered local if they match the hostname. we want * to be sure to catch the node name being "localhost" and the hostname * being "localhost.localdomain". we consider them equal if the * configured node name matches the start of the hostname up to a '.' */ if (!strncasecmp(node_name, hostname, node_len) && (hostname[node_len] == '\0' || hostname[node_len] == '.')) local = 1; out: *is_local = local; return ret; } static gint online_cluster(O2CBContext *ctxt, O2CBCluster *cluster) { errcode_t ret; gint rc; gchar *name, *node_name, *node_num, *ip_address, *ip_port, *local; JIterator *iter; O2CBNode *node; gboolean seen_local = 0, is_local; rc = -ENOMEM; name = o2cb_cluster_get_name(cluster); if (!name) goto out_error; rc = -EIO; ret = o2cb_create_cluster(name); if (ret) { if (ret != O2CB_ET_CLUSTER_EXISTS) { com_err(PROGNAME, ret, "while setting cluster name"); goto out_error; } } else fprintf(stdout, "Cluster %s created\n", name); rc = -ENOMEM; iter = o2cb_cluster_get_nodes(cluster); if (!iter) goto out_error; rc = 0; while (j_iterator_has_more(iter)) { node = (O2CBNode *)j_iterator_get_next(iter); node_num = g_strdup_printf("%d", o2cb_node_get_number(node)); node_name = o2cb_node_get_name(node); ip_port = g_strdup_printf("%d", o2cb_node_get_port(node)); ip_address = o2cb_node_get_ip_string(node); ret = o2cb_node_is_local(node_name, &is_local); if (ret) { com_err(PROGNAME, ret, "while determining if node %s is local", node_name); rc = -EINVAL; goto out_error; } if (is_local) { if (seen_local) { ret = O2CB_ET_CONFIGURATION_ERROR; com_err(PROGNAME, ret, "while adding node %s. It is " "considered local but another node was already marked " "as local. Do multiple node names in the config " "match this machine's host name?", node_name); rc = -EINVAL; goto out_error; } local = g_strdup("1"); seen_local = 1; } else local = g_strdup("0"); ret = o2cb_add_node(name, node_name, node_num, ip_address, ip_port, local); if (ret) { if (ret != O2CB_ET_NODE_EXISTS) { com_err(PROGNAME, ret, "while adding node %s\n", node_name); rc = -EIO; } ret = 0; } else fprintf(stdout, "Node %s added\n", node_name); g_free(node_num); g_free(node_name); g_free(ip_port); g_free(ip_address); g_free(local); if (rc) break; } j_iterator_free(iter); if (rc) goto out_error; if (!seen_local) { ret = O2CB_ET_CONFIGURATION_ERROR; com_err(PROGNAME, ret, "while populating cluster %s. None of its " "nodes were considered local. A node is considered local " "when its node name in the configuration matches this " "machine's host name.", name); rc = -EINVAL; goto out_error; } out_error: g_free(name); return rc; } /* online_cluster() */ static gint offline_cluster(O2CBContext *ctxt, O2CBCluster *cluster) { errcode_t ret; gint rc; gchar *cluster_name = NULL; char **node_name = NULL; int i = 0; rc = -ENOMEM; cluster_name = o2cb_cluster_get_name(cluster); if (!cluster_name) goto out_error; ret = o2cb_list_nodes(cluster_name, &node_name); if (ret && ret != O2CB_ET_SERVICE_UNAVAILABLE) { com_err(PROGNAME, ret, "while listing nodes in cluster '%s'", cluster_name); goto out_error; } rc = -EIO; while(node_name && node_name[i] && *(node_name[i])) { ret = o2cb_del_node(cluster_name, node_name[i]); if (ret) { com_err(PROGNAME, ret, "while deleting node '%s' in cluster '%s'", node_name[i], cluster_name); goto out_error; } i++; } ret = o2cb_remove_cluster(cluster_name); if (ret && ret != O2CB_ET_SERVICE_UNAVAILABLE) { com_err(PROGNAME, ret, "while removing cluster '%s'", cluster_name); goto out_error; } rc = 0; out_error: if (node_name) o2cb_free_nodes_list(node_name); if (cluster_name) g_free(cluster_name); return rc; } /* offline_cluster() */ static gint run_change_cluster_one(O2CBContext *ctxt, O2CBCluster *cluster) { gint rc = 0; const gchar *val; if (attr_set(ctxt, "name")) { if (ctxt->oc_modify_running) { fprintf(stderr, PROGNAME ": Cannot change name of a running cluster\n"); return -EINVAL; } val = attr_string(ctxt, "name", NULL); if (!val || !*val) { fprintf(stderr, PROGNAME ": Empty name for cluster\n"); return -EINVAL; } rc = o2cb_cluster_set_name(cluster, val); if (rc) return rc; ctxt->oc_changed = TRUE; } /* FIXME: Should perhaps store config before changing online info */ if (attr_set(ctxt, "online")) { if (attr_boolean(ctxt, "online", FALSE)) rc = online_cluster(ctxt, cluster); else rc = offline_cluster(ctxt, cluster); } return rc; } /* run_change_cluster_one() */ static gint run_change_clusters(O2CBContext *ctxt) { gint rc = 0; O2CBCluster *cluster; GList *list; list = ctxt->oc_objects; while (list) { cluster = o2cb_config_get_cluster_by_name(ctxt->oc_config, (gchar *)list->data); if (!cluster) { rc = -ENOENT; fprintf(stderr, PROGNAME ": Cluster \"%s\" does not exist\n", (gchar *)list->data); break; } rc = run_change_cluster_one(ctxt, cluster); if (rc) break; list = list->next; } return rc; } static gint run_change(O2CBContext *ctxt) { gint rc; if (!ctxt->oc_type && !ctxt->oc_objects) { fprintf(stderr, PROGNAME ": Operation \'-H\' requires an object or object type\n"); return -EINVAL; } rc = validate_attrs(ctxt); if (rc) return rc; rc = load_config(ctxt); if (rc) return rc; if (ctxt->oc_type && !ctxt->oc_objects) { rc = find_objects_for_type(ctxt); if (rc) goto out_error; } else if (ctxt->oc_objects && !ctxt->oc_type) { rc = find_type_for_objects(ctxt); if (rc) goto out_error; } if (ctxt->oc_type == O2CB_TYPE_NODE) { rc = -ENOTSUP; fprintf(stderr, PROGNAME ": Node changes not yet supported\n"); goto out_error; } else if (ctxt->oc_type == O2CB_TYPE_CLUSTER) { rc = run_change_clusters(ctxt); if (rc) goto out_error; } else { rc = -EINVAL; fprintf(stderr, PROGNAME ": Invalid object type!\n"); goto out_error; } if (ctxt->oc_changed) rc = write_config(ctxt); out_error: return rc; } /* run_change() */ static gint run_create_clusters(O2CBContext *ctxt) { gint rc = 0; O2CBCluster *cluster; errcode_t err; gchar *name; GList *list; list = ctxt->oc_objects; while (list) { name = list->data; cluster = o2cb_config_get_cluster_by_name(ctxt->oc_config, name); if (cluster) { rc = -EEXIST; fprintf(stderr, PROGNAME ": Cluster \"%s\" already exists\n", name); break; } cluster = o2cb_config_add_cluster(ctxt->oc_config, name); if (!cluster) { rc = -ENOMEM; fprintf(stderr, PROGNAME ": Unable to add cluster \"%s\"\n", name); break; } if (ctxt->oc_modify_running) { err = o2cb_create_cluster(name); if (err) { if (err != O2CB_ET_CLUSTER_EXISTS) { rc = -EIO; com_err(PROGNAME, err, "while creating cluster"); break; } } else fprintf(stdout, "Cluster %s created\n", name); } list = list->next; } return rc; } static gint run_create_nodes(O2CBContext *ctxt) { gint rc; O2CBCluster *cluster; O2CBNode *node, *tmpnode; errcode_t err; long num; gchar *ptr; gchar *name, *number, *local; const gchar *cluster_name, *ip_address, *ip_port; gboolean is_local; rc = -EINVAL; if (ctxt->oc_objects->next) { fprintf(stderr, PROGNAME ": Cannot create more than one node at a time\n"); goto out; } cluster_name = attr_string(ctxt, "cluster", NULL); if (!cluster_name || !*cluster_name) { fprintf(stderr, PROGNAME ": \"cluster\" attribute required to create a node\n"); goto out; } ip_address = attr_string(ctxt, "ip_address", NULL); if (!ip_address || !*ip_address) { fprintf(stderr, PROGNAME ": \"ip_address\" attribute required to create a node\n"); goto out; } ip_port = attr_string(ctxt, "ip_port", NULL); if (!ip_port || !*ip_port) { fprintf(stderr, PROGNAME ": \"ip_port\" attribute required to create a node\n"); goto out; } rc = 0; name = ctxt->oc_objects->data; cluster = o2cb_config_get_cluster_by_name(ctxt->oc_config, cluster_name); if (!cluster) { rc = -ENOENT; fprintf(stderr, PROGNAME ": Cluster \"%s\" does not exist\n", cluster_name); goto out; } node = o2cb_cluster_get_node_by_name(cluster, name); if (node) { rc = -EEXIST; fprintf(stderr, PROGNAME ": Node \"%s\" already exists\n", name); goto out; } node = o2cb_cluster_add_node(cluster, name); if (!node) { rc = -ENOMEM; fprintf(stderr, PROGNAME ": Unable to add node \"%s\"\n", name); goto out; } rc = o2cb_node_set_ip_string(node, ip_address); if (rc) { fprintf(stderr, PROGNAME ": IP address \"%s\" is invalid\n", ip_address); goto out; } num = strtol(ip_port, &ptr, 10); if (!ptr || *ptr || (num < 0) || (num > (guint16)-1)) { rc = -ERANGE; fprintf(stderr, PROGNAME ": Port number \"%s\" is invalid\n", ip_port); goto out; } o2cb_node_set_port(node, num); number = g_strdup(attr_string(ctxt, "number", NULL)); if (number) { num = strtol(number, &ptr, 10); if (!ptr || *ptr || (num < 0) || (num > INT_MAX)) { rc = -ERANGE; fprintf(stderr, PROGNAME ": Node number \"%s\" is invalid\n", number); g_free(number); goto out; } tmpnode = o2cb_cluster_get_node(cluster, num); if (tmpnode && (tmpnode != node)) { rc = -EEXIST; fprintf(stderr, PROGNAME ": Node number \"%ld\" already exists\n", num); g_free(number); goto out; } o2cb_node_set_number(node, num); } else number = g_strdup_printf("%d", o2cb_node_get_number(node)); if (ctxt->oc_modify_running) { err = o2cb_node_is_local(name, &is_local); if (err) { com_err(PROGNAME, err, "while determining if node %s is local", name); rc = -EINVAL; g_free(number); goto out; } if (is_local) local = g_strdup("1"); else local = g_strdup("0"); err = o2cb_add_node(cluster_name, name, number, ip_address, ip_port, local); if (err) { if (err != O2CB_ET_NODE_EXISTS) { rc = -EIO; com_err(PROGNAME, err, "while creating node"); } } else fprintf(stdout, "Node %s created\n", name); g_free(local); } g_free(number); out: return rc; } /* run_create_nodes() */ static gint run_create(O2CBContext *ctxt) { gint rc; if (!ctxt->oc_type || !ctxt->oc_objects) { fprintf(stderr, PROGNAME ": Operation \'-C\' requires an object and an object type\n"); return -EINVAL; } rc = validate_attrs(ctxt); if (rc) return rc; rc = load_config(ctxt); if (rc) return rc; if (ctxt->oc_type == O2CB_TYPE_NODE) { rc = run_create_nodes(ctxt); if (rc) goto out_error; } else if (ctxt->oc_type == O2CB_TYPE_CLUSTER) { rc = run_create_clusters(ctxt); if (rc) goto out_error; } else { rc = -EINVAL; fprintf(stderr, PROGNAME ": Invalid object type!\n"); goto out_error; } rc = write_config(ctxt); out_error: return rc; } /* run_create() */ gint main(gint argc, gchar *argv[]) { int rc; errcode_t ret; O2CBContext ctxt = {0, }; setbuf(stdout, NULL); setbuf(stderr, NULL); initialize_o2cb_error_table(); rc = parse_options(argc, argv, &ctxt); if (rc) print_usage(rc); ret = o2cb_init(); if (ret) { com_err(PROGNAME, ret, "Cannot initialize cluster\n"); rc = -EINVAL; goto out_error; } switch (ctxt.oc_op) { case O2CB_OP_NONE: fprintf(stderr, PROGNAME ": You must specify an operation\n"); print_usage(-EINVAL); break; case O2CB_OP_CREATE: rc = run_create(&ctxt); break; case O2CB_OP_DELETE: rc = -ENOTSUP; fprintf(stderr, PROGNAME ": Not yet supported\n"); break; case O2CB_OP_INFO: rc = run_info(&ctxt); break; case O2CB_OP_CHANGE: rc = run_change(&ctxt); break; default: rc = -EINVAL; fprintf(stderr, PROGNAME ": Eeek! Invalid operation!\n"); break; } if (ctxt.oc_config) o2cb_config_free(ctxt.oc_config); clear_attrs(&ctxt); clear_objects(&ctxt); out_error: return rc; } /* main() */ ocfs2-tools-ocfs2-tools-1.8.6/o2cb_ctl/o2cb_scandisk.c000066400000000000000000000111201347147137200224130ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * o2cb_scandisk.c * * Reads all the partitions and get the ocfs2 uuids * * Copyright (C) 2010 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License, version 2, as published by the Free Software Foundation. * * 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. */ #include "o2cb_scandisk.h" #include "ocfs2/ocfs2.h" #include "tools-internal/verbose.h" struct hb_devices { struct list_head hb_list; char *hb_path; }; struct scan_context { struct list_head sc_devlist; int sc_rescan; }; static int fill_desc(char *device, struct o2cb_region_desc *reg, struct o2cb_cluster_desc *cluster) { ocfs2_filesys *fs; errcode_t ret; ret = ocfs2_open(device, OCFS2_FLAG_RO | OCFS2_FLAG_HEARTBEAT_DEV_OK, 0, 0, &fs); if (ret) return ret; ret = ocfs2_fill_heartbeat_desc(fs, reg); if (!ret) ret = ocfs2_fill_cluster_desc(fs, cluster); if (!ret) { /* TODO free this alloc... or not */ reg->r_name = strdup(reg->r_name); reg->r_device_name = strdup(reg->r_device_name); } ocfs2_close(fs); return ret; } static int get_device_uuids(struct scan_context *ctxt, struct list_head *hbdevs) { struct o2cb_device *od; struct list_head *pos, *pos1; struct hb_devices *hb; struct o2cb_region_desc rd; struct o2cb_cluster_desc cd; int numhbdevs = 0; list_for_each(pos, hbdevs) { ++numhbdevs; } if (!numhbdevs) return 0; list_for_each(pos, &ctxt->sc_devlist) { hb = list_entry(pos, struct hb_devices, hb_list); if (fill_desc(hb->hb_path, &rd, &cd)) continue; list_for_each(pos1, hbdevs) { od = list_entry(pos1, struct o2cb_device, od_list); if (od->od_flags & O2CB_DEVICE_FOUND) continue; if (strcmp(rd.r_name, od->od_uuid)) continue; od->od_flags |= O2CB_DEVICE_FOUND; memcpy(&od->od_region, &rd, sizeof(od->od_region)); memcpy(&od->od_cluster, &cd, sizeof(od->od_cluster)); verbosef(VL_DEBUG, "Region %s matched to device %s\n", rd.r_name, rd.r_device_name); --numhbdevs; break; } if (!numhbdevs) break; } return numhbdevs; } static void free_scan_context(struct scan_context *ctxt) { struct list_head *pos, *pos1; struct hb_devices *hb; if (!ctxt) return ; list_for_each_safe(pos, pos1, &ctxt->sc_devlist) { hb = list_entry(pos, struct hb_devices, hb_list); list_del(pos); free(hb); } } static void add_to_list(struct list_head *device_list, struct devnode *node) { struct devpath *path; struct hb_devices *hb; int add = 0; path = node->devpath; while (path) { if (node->mapper) add = !strncmp(path->path, "/dev/mapper/", 12); else if (node->power) add = !strncmp(path->path, "/dev/emcpower", 13); else { add = !strncmp(path->path, "/dev/sd", 7); if (!add) add = !strncmp(path->path, "/dev/loop", 9); if (!add) add = !strncmp(path->path, "/dev/xvd", 8); if (!add) add = !strncmp(path->path, "/dev/vd", 7); if (!add) add = !strncmp(path->path, "/dev/rbd", 8); if (!add) add = !strncmp(path->path, "/dev/drbd", 9); } if (add) { hb = malloc(sizeof(struct hb_devices)); if (hb) { hb->hb_path = strdup(path->path); list_add_tail(&hb->hb_list, device_list); break; } } path = path->next; } } static void filter_devices(struct devnode *node, void *user_data) { struct scan_context *ctxt = user_data; /* No information in sysfs? Ignore it! */ if (!node->sysfsattrs.sysfs) return; /* Not a disk? Ignore it! */ if (!node->sysfsattrs.disk) return; /* It's part of some other device? Ignore it! */ if (node->sysfsattrs.holders) return; /* * No path in /dev? Well, udev probably hasn't gotten there. Trigger * a rescan */ if (!node->devpath) { ctxt->sc_rescan = 1; return; } add_to_list(&ctxt->sc_devlist, node); } void o2cb_scandisk(struct list_head *hbdevs) { struct devlisthead *dev = NULL; int delay = 1; struct scan_context scan_ctxt, *ctxt = &scan_ctxt; INIT_LIST_HEAD(&ctxt->sc_devlist); do { ctxt->sc_rescan = 0; if (delay > 5) break; if (dev) { free_scan_context(ctxt); free_dev_list(dev); sleep(delay); delay += 2; } dev = scan_for_dev(NULL, 5, filter_devices, ctxt); if (!dev) goto bail; if (!get_device_uuids(ctxt, hbdevs)) break; } while (ctxt->sc_rescan); bail: free_scan_context(ctxt); free_dev_list(dev); } ocfs2-tools-ocfs2-tools-1.8.6/o2cb_ctl/o2cb_scandisk.h000066400000000000000000000022121347147137200224220ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * o2cb_scandisk.h * * Copyright (C) 2010 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License, version 2, as published by the Free Software Foundation. * * 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. */ #include #include #include #include #include #include #include #include #include #include "tools-internal/scandisk.h" #include "o2cb/o2cb.h" #include "ocfs2-kernel/kernel-list.h" struct o2cb_device { struct list_head od_list; char *od_uuid; #define O2CB_DEVICE_FOUND 0x01 #define O2CB_DEVICE_HB_STARTED 0x02 int od_flags; struct o2cb_region_desc od_region; struct o2cb_cluster_desc od_cluster; }; void o2cb_scandisk(struct list_head *hbdevs); ocfs2-tools-ocfs2-tools-1.8.6/o2cb_ctl/o2cbtool.c000066400000000000000000000162211347147137200214410ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * o2cbtool.c * * Manipulates o2cb cluster configuration * * Copyright (C) 2010, 2011 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License, version 2, as published by the Free Software Foundation. * * 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. */ #include "o2cbtool.h" char *progname = "o2cbtool"; const char *stackname = "o2cb"; struct o2cb_command o2cbtool_cmds[] = { { .o_name = "add-cluster", .o_action = o2cbtool_add_cluster, .o_usage = "", .o_help = "Add cluster to the config file.", }, { .o_name = "remove-cluster", .o_action = o2cbtool_remove_cluster, .o_usage = "", .o_help = "Removes cluster from the config file.", }, { .o_name = "add-node", .o_action = o2cbtool_add_node, .o_usage = "[--ip ] [--port ] [--number ] " " ", .o_help = "Adds a node to the cluster in the config file.", }, { .o_name = "remove-node", .o_action = o2cbtool_remove_node, .o_usage = " ", .o_help = "Removes a node from the cluster in the config file.", }, { .o_name = "add-heartbeat", .o_action = o2cbtool_add_heartbeat, .o_usage = " [|]", .o_help = "Adds a heartbeat region to the cluster in the " "config file.", }, { .o_name = "remove-heartbeat", .o_action = o2cbtool_remove_heartbeat, .o_usage = " [|]", .o_help = "Removes a heartbeat region from the cluster in " "the config file.", }, { .o_name = "heartbeat-mode", .o_action = o2cbtool_heartbeat_mode, .o_usage = " {global|local}", .o_help = "Toggles the heartbeat mode between global and local.", }, { .o_name = "list-clusters", .o_action = o2cbtool_list_clusters, .o_usage = "", .o_help = "Lists all the cluster names in the config file.", }, { .o_name = "list-cluster", .o_action = o2cbtool_list_objects, .o_usage = "[--oneline] ", .o_help = "Lists all the nodes and heartbeat regions " "associated with the cluster in the config file.", }, { .o_name = "list-nodes", .o_action = o2cbtool_list_objects, .o_usage = "[--oneline] ", .o_help = "Lists all the nodes associated with the cluster in " "the config file. ", }, { .o_name = "list-heartbeats", .o_action = o2cbtool_list_objects, .o_usage = "[--oneline] ", .o_help = "Lists all the heartbeat regions associated with " "the cluster in the config file.", }, { .o_name = "register-cluster", .o_action = o2cbtool_register_cluster, .o_usage = "", .o_help = "Registers the cluster with configfs.", }, { .o_name = "unregister-cluster", .o_action = o2cbtool_unregister_cluster, .o_usage = "", .o_help = "Unregisters the cluster from configfs.", }, { .o_name = "start-heartbeat", .o_action = o2cbtool_start_heartbeat, .o_usage = "", .o_help = "Starts global heartbeat." }, { .o_name = "stop-heartbeat", .o_action = o2cbtool_stop_heartbeat, .o_usage = "", .o_help = "Stops global heartbeat." }, { .o_name = "cluster-status", .o_action = o2cbtool_cluster_status, .o_usage = "[]", .o_help = "Returns 0 if cluster online, 1 otherwise.", }, { .o_name = NULL, .o_action = NULL, }, }; #define USAGE_STR \ "[--config-file=path] [-h|--help] [-v|--verbose] [-V|--version] " \ "COMMAND [ARGS]" static void usage(void) { struct o2cb_command *p = o2cbtool_cmds; fprintf(stderr, "usage: %s %s\n", progname, USAGE_STR); fprintf(stderr, "\n"); fprintf(stderr, "The commands are:\n"); while(p->o_name) { fprintf(stderr, " %-18s %s\n", p->o_name, p->o_help); ++p; } fprintf(stderr, "\n"); exit(1); } static void parse_options(int argc, char *argv[], struct o2cb_command **cmd) { int c, show_version = 0, show_help = 0; char *config_file = NULL; static struct option long_options[] = { { "config-file", 1, 0, CONFIG_FILE_OPTION }, { "help", 0, 0, 'h' }, { "verbose", 0, 0, 'v' }, { "version", 0, 0, 'V' }, { 0, 0, 0, 0 }, }; if (argc && *argv) progname = basename(argv[0]); while (1) { c = getopt_long(argc, argv, "+hvV", long_options, NULL); if (c == -1) break; switch (c) { case 'h': show_help = 1; break; case 'v': tools_verbose(); break; case 'V': show_version = 1; break; case CONFIG_FILE_OPTION: config_file = optarg; break; default: usage(); break; } } if (!config_file) config_file = O2CB_DEFAULT_CONFIG_FILE; if (show_version) { tools_version(); exit(1); } if (show_help) usage(); if (optind == argc) usage(); verbosef(VL_APP, "Using config file '%s'\n", config_file); for (*cmd = &o2cbtool_cmds[0]; ((*cmd)->o_name); ++(*cmd)) { if (!strcmp(argv[optind], (*cmd)->o_name)) { (*cmd)->o_argc = argc - optind; (*cmd)->o_argv = &(argv[optind]); (*cmd)->o_config_file = config_file; optind = 0; /* restart opt processing */ return; } } errorf("Unknown command '%s'\n", argv[optind]); usage(); exit(1); } /* Call this with SIG_BLOCK to block and SIG_UNBLOCK to unblock */ void o2cbtool_block_signals(int how) { sigset_t sigs; sigfillset(&sigs); sigdelset(&sigs, SIGTRAP); sigdelset(&sigs, SIGSEGV); sigprocmask(how, &sigs, NULL); } errcode_t o2cbtool_init_cluster_stack(void) { errcode_t ret; const char *stack = NULL; verbosef(VL_DEBUG, "Initializing cluster stack\n"); ret = o2cb_init(); if (ret) { tcom_err(ret, "while initializing the cluster"); goto bail; } ret = o2cb_get_stack_name(&stack); if (ret) { tcom_err(ret, "while determining the current cluster stack"); goto bail; } if (strcmp(stack, stackname)) { ret = -1; errorf("This tool supports the '%s' stack, but the '%s' " "stack is in use.\n", stackname, stack); } bail: return ret; } int main(int argc, char **argv) { errcode_t ret; struct o2cb_command *cmd; O2CBConfig *oc_config = NULL; setbuf(stdout, NULL); setbuf(stderr, NULL); initialize_o2cb_error_table(); tools_setup_argv0(argv[0]); parse_options(argc, argv, &cmd); ret = o2cb_config_load(cmd->o_config_file, &oc_config); if (ret) { tcom_err(ret, "while loading cluster configuration " "file '%s'", cmd->o_config_file); goto bail; } cmd->o_config = oc_config; cmd->o_modified = 0; cmd->o_print_usage = 1; ret = -1; if (!cmd->o_action) { errorf("Command '%s' has not been implemented\n", cmd->o_name); goto bail; } ret = cmd->o_action(cmd); if (cmd->o_print_usage) errorf("usage: %s %s\n", cmd->o_name, cmd->o_usage); if (ret || !cmd->o_modified) goto bail; ret = o2cb_config_store(oc_config, cmd->o_config_file); if (ret) tcom_err(ret, "while storing the cluster configuration in " "file '%s'", cmd->o_config_file); bail: if (oc_config) o2cb_config_free(oc_config); ret = ret ? 1 : 0; return ret; } ocfs2-tools-ocfs2-tools-1.8.6/o2cb_ctl/o2cbtool.h000066400000000000000000000051031347147137200214430ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * o2cbtool.h * * Manipulates o2cb cluster configuration * * Copyright (C) 2010, 2011 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License, version 2, as published by the Free Software Foundation. * * 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. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "jiterator.h" #include "o2cb_config.h" #include "o2cb/o2cb.h" #include "ocfs2/ocfs2.h" #include "tools-internal/verbose.h" #include "tools-internal/utils.h" #define O2CB_DEFAULT_CONFIG_FILE "/etc/ocfs2/cluster.conf" #define O2CB_DEFAULT_IP_PORT 7777 struct o2cb_command { int o_modified; int o_print_usage; int o_argc; char **o_argv; char *o_name; char *o_help; char *o_usage; char *o_config_file; O2CBConfig *o_config; errcode_t (*o_action)(struct o2cb_command *); }; enum { CONFIG_FILE_OPTION = CHAR_MAX + 1, IP_OPTION, PORT_OPTION, NODENUM_OPTION, ONELINE_OPTION, }; errcode_t o2cbtool_validate_clustername(char *clustername); errcode_t o2cbtool_add_cluster(struct o2cb_command *cmd); errcode_t o2cbtool_remove_cluster(struct o2cb_command *cmd); errcode_t o2cbtool_add_node(struct o2cb_command *cmd); errcode_t o2cbtool_remove_node(struct o2cb_command *cmd); errcode_t o2cbtool_add_heartbeat(struct o2cb_command *cmd); errcode_t o2cbtool_remove_heartbeat(struct o2cb_command *cmd); errcode_t o2cbtool_heartbeat_mode(struct o2cb_command *cmd); errcode_t o2cbtool_list_clusters(struct o2cb_command *cmd); errcode_t o2cbtool_list_objects(struct o2cb_command *cmd); errcode_t o2cbtool_init_cluster_stack(void); errcode_t o2cbtool_register_cluster(struct o2cb_command *cmd); errcode_t o2cbtool_unregister_cluster(struct o2cb_command *cmd); errcode_t o2cbtool_start_heartbeat(struct o2cb_command *cmd); errcode_t o2cbtool_stop_heartbeat(struct o2cb_command *cmd); errcode_t o2cbtool_cluster_status(struct o2cb_command *cmd); void o2cbtool_block_signals(int how); /* utility functions */ int is_cluster_registered(char *clustername); int is_heartbeat_active(char *clustername); ocfs2-tools-ocfs2-tools-1.8.6/o2cb_ctl/o2cbutils.c000066400000000000000000000031731347147137200216260ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * o2cbutils.c * * utility functions * * Copyright (C) 2011 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License, version 2, as published by the Free Software Foundation. * * 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. */ #include "o2cbtool.h" /* * Only after the local node is added to the cluster is the cluster * considered registered * * Returns 1 if registered, 0 otherwise. */ int is_cluster_registered(char *clustername) { char **nodename = NULL; uint32_t local; int ret = 0, i = 0; if (o2cb_list_nodes(clustername, &nodename)) goto bail; while(nodename && nodename[i] && *(nodename[i])) { if (o2cb_get_node_local(clustername, nodename[i], &local)) break; if (local) { ret = 1; break; } ++i; } if (nodename) o2cb_free_nodes_list(nodename); bail: return ret; } /* * Returns 1 if atleast one heartbeat region is active. * 0 otherwise. */ int is_heartbeat_active(char *clustername) { gchar **regions = NULL; errcode_t err; int active = 0; /* lookup active heartbeats */ err = o2cb_list_hb_regions(clustername, ®ions); if (err) goto bail; /* if found, heartbeat is active */ if (regions[0] && *(regions[0])) active = 1; if (regions) o2cb_free_hb_regions_list(regions); bail: return active; } ocfs2-tools-ocfs2-tools-1.8.6/o2cb_ctl/ocfs2.cluster.conf.5.in000066400000000000000000000111631347147137200236650ustar00rootroot00000000000000.TH "/etc/ocfs2/cluster.conf" "5" "January 2012" "Version @VERSION@" "OCFS2 Manual Pages" .SH "NAME" /etc/ocfs2/cluster.conf \- Cluster configuration file for the \fBo2cb\fR cluster stack. .SH "SYNOPSIS" .PP The cluster layout of the \fBo2cb\fR cluster stack is specified in \fI/etc/ocfs2/cluster.conf\fR. It lists the name of the cluster, the nodes comprising that cluster and its heartbeat regions. The cluster stack expects this file to be the same on all nodes in that cluster. This file should be populated using the \fBo2cb(8)\fR cluster configuration utility. A sample of the same is shown in the example section. .SH "DESCRIPTION" The configuration file is divided into three types of stanzas, each with a list of parameters and values. The three stanza types are \fIcluster\fR, \fInode\fR and \fIheartbeat\fR. While a configuration file can store definitions of multiple clusters, the \fBo2cb\fR cluster stack allows only one cluster to be active at any one time. The name of this active cluster is stored in /etc/sysconfig/o2cb [\fBo2cb.sysconfig(5)\fR]. The \fBcluster stanza\fR specifies the name of the cluster, number of nodes and the heartbeat mode. The cluster name can include up to 16 alphanumeric characters [0-9A-Za-z]. No special characters are allowed. .TS CENTER ALLBOX; LB LB LB L. Parameters Description node_count Number of nodes in the cluster heartbeat_mode \fBlocal\fR or \fBglobal\fR heartbeat name Cluster name (up to 16 alphanumeric chars [0-9A-Za-z]) .TE .BR The \fBnode stanza\fR specifies the node name that is part of the cluster alongwith its IPv4 address, port and node number. The node name must match the hostname. The domain name is not required. For example, appserver1.company.com can be appserver1. The IPv4 address need not be the one associated with that hostname. As in, any valid IPv4 address on that node can be used. The \fBo2cb\fR cluster stack will not attempt to match the node name (hostname) with the specified IPv4 address. A low-latency private interconnect address is recommended for best performance. .TS CENTER ALLBOX; LB LB LB L. Parameters Description ip_port IPv4 port ip_address IPv4 address (private interconnect recommended) number Node number (0 - 254) name Node name (\fIhostname\fR without the domain name) cluster Cluster name (should match the name in the cluster stanza) .TE .BR The \fBheartbeat stanza\fR specifies the global heartbeat region UUIDs. A cluster can have up to 32 heartbeat regions. This is an optional stanza and is only required if the global heartbeat mode is enabled. In other words, the regions are only used if \fBheartbeat_mode = global\fR is in the \fIcluster\fR stanza. If not, this stanza is ignored. .TS CENTER ALLBOX; LB LB LB L. Parameters Description region Heartbeat region UUID cluster Cluster name (should match the name in the cluster stanza) .TE .BR While manual editing is not recommended, users doing so must follow the format strictly. The stanza should start at the first column and end with a colon. The parameters must start after a tab. A blank line must demarcate each stanza. Care should be taken to avoid stray white-spaces. .SH "EXAMPLE" The example below illustrates populating a cluster.conf with a cluster called webcluster, having 3 nodes and 3 global heartbeat regions, using the \fBo2cb(8)\fR utility. .nf .ps 10 .ft 6 $ o2cb add-cluster webcluster $ o2cb add-node webcluster node7 --ip 192.168.0.107 --number 7 $ o2cb add-node webcluster node6 --ip 192.168.0.106 --number 6 $ o2cb add-node webcluster node10 --ip 192.168.0.110 --number 10 $ o2cb add-heartbeat webcluster /dev/sdg1 $ o2cb add-heartbeat webcluster /dev/sdk1 $ o2cb add-heartbeat webcluster /dev/sdh1 $ o2cb heartbeat-mode webcluster global $ o2cb list-cluster webcluster heartbeat: region = 77D95EF51C0149D2823674FCC162CF8B cluster = webcluster heartbeat: region = DCDA2845177F4D59A0F2DCD8DE507CC3 cluster = webcluster heartbeat: region = BBA1DBD0F73F449384CE75197D9B7098 cluster = webcluster node: ip_port = 7777 ip_address = 192.168.0.107 number = 7 name = node7 cluster = webcluster node: ip_port = 7777 ip_address = 192.168.0.106 number = 6 name = node6 cluster = webcluster node: ip_port = 7777 ip_address = 192.168.0.110 number = 10 name = node10 cluster = webcluster cluster: node_count = 3 heartbeat_mode = global name = webcluster .ft .ps .fi .SH "SEE ALSO" .BR o2cb(7) .BR o2cb(8) .BR o2cb.sysconfig(5) .SH "AUTHORS" Oracle Corporation .SH "COPYRIGHT" Copyright \(co 2004, 2012 Oracle. All rights reserved. ocfs2-tools-ocfs2-tools-1.8.6/o2cb_ctl/op_cluster.c000066400000000000000000000044211347147137200220740ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * op_cluster.c * * Manipulates o2cb cluster configuration * * Copyright (C) 2010 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License, version 2, as published by the Free Software Foundation. * * 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. */ #include "o2cbtool.h" errcode_t o2cbtool_validate_clustername(char *clustername) { char *p; int len; errcode_t ret = O2CB_ET_INVALID_CLUSTER_NAME; p = tools_strstrip(clustername); len = strlen(p); if (!len) { tcom_err(O2CB_ET_INVALID_CLUSTER_NAME, "; zero length"); return ret; } if (len > OCFS2_CLUSTER_NAME_LEN) { tcom_err(ret, "; max %d characters", OCFS2_CLUSTER_NAME_LEN); return ret; } while(isalnum(*p++) && len--); if (len) { tcom_err(ret, "; only alpha-numeric characters allowed"); return ret; } return 0; } /* * add-cluster */ errcode_t o2cbtool_add_cluster(struct o2cb_command *cmd) { O2CBCluster *cluster; errcode_t ret = -1; gchar *clustername; if (cmd->o_argc < 2) goto bail; cmd->o_print_usage = 0; clustername = cmd->o_argv[1]; ret = o2cbtool_validate_clustername(clustername); if (ret) goto bail; cluster = o2cb_config_add_cluster(cmd->o_config, clustername); if (!cluster) { errorf("Cluster '%s' already exists\n", clustername); goto bail; } cmd->o_modified = 1; ret = 0; verbosef(VL_APP, "Added cluster '%s'\n", clustername); bail: return ret; } /* * remove-cluster */ errcode_t o2cbtool_remove_cluster(struct o2cb_command *cmd) { errcode_t ret = -1; gchar *clustername; if (cmd->o_argc < 2 || !strlen(tools_strstrip(cmd->o_argv[1]))) goto bail; cmd->o_print_usage = 0; clustername = cmd->o_argv[1]; ret = o2cb_config_remove_cluster(cmd->o_config, clustername); if (ret) { errorf("Unknown cluster '%s'\n", clustername); goto bail; } cmd->o_modified = 1; ret = 0; verbosef(VL_APP, "Removed cluster '%s'\n", clustername); bail: return ret; } ocfs2-tools-ocfs2-tools-1.8.6/o2cb_ctl/op_heartbeat.c000066400000000000000000000103421347147137200223510ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * op_heartbeat.c * * Manipulates heartbeat info in the o2cb cluster configuration * * Copyright (C) 2010 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License, version 2, as published by the Free Software Foundation. * * 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. */ #include "o2cbtool.h" #include #include #include static int is_block_device(char *name) { struct stat statbuf; if (!stat(name, &statbuf)) if (S_ISBLK(statbuf.st_mode)) return 1; return 0; } static int get_region(char *device, char **region) { ocfs2_filesys *fs; errcode_t ret = 0; *region = '\0'; if (!is_block_device(device)) { verbosef(VL_DEBUG, "'%s' is not a block device; assuming " "region\n", device); *region = strdup(device); goto bail; } verbosef(VL_DEBUG, "Reading region of block device '%s'\n", device); ret = ocfs2_open(device, OCFS2_FLAG_RO | OCFS2_FLAG_HEARTBEAT_DEV_OK, 0, 0, &fs); if (ret) { tcom_err(ret, "while reading region on device '%s'", device); goto bail; } *region = strdup(fs->uuid_str); ocfs2_close(fs); bail: if (!ret && !*region) { ret = O2CB_ET_NO_MEMORY; tcom_err(ret, "while copying region"); } if (!ret) verbosef(VL_DEBUG, "Heartbeat region '%s'\n", *region); return ret; } /* * o2cb add-heartbeat */ errcode_t o2cbtool_add_heartbeat(struct o2cb_command *cmd) { O2CBCluster *cluster; O2CBHeartbeat *hb; int ret = -1; gchar *clustername, *tmp, *region = '\0'; if (cmd->o_argc < 3) goto bail; cmd->o_print_usage = 0; clustername = cmd->o_argv[1]; tmp = tools_strstrip(cmd->o_argv[2]); ret = get_region(tmp, ®ion); if (ret) goto bail; ret = -1; cluster = o2cb_config_get_cluster_by_name(cmd->o_config, clustername); if (!cluster) { errorf("Unknown cluster '%s'\n", clustername); goto bail; } hb = o2cb_cluster_add_heartbeat(cluster, region); if (!hb) { errorf("Heartbeat region '%s' already exists\n", region); goto bail; } cmd->o_modified = 1; ret = 0; verbosef(VL_APP, "Added heartbeat region '%s' to cluster '%s'\n", region, clustername); bail: if (region) free(region); return ret; } /* * o2cb remove-heartbeat */ errcode_t o2cbtool_remove_heartbeat(struct o2cb_command *cmd) { O2CBCluster *cluster; int ret = -1; gchar *clustername, *tmp, *region = '\0'; if (cmd->o_argc < 3) goto bail; cmd->o_print_usage = 0; clustername = cmd->o_argv[1]; tmp = cmd->o_argv[2]; ret = get_region(tmp, ®ion); if (ret) goto bail; ret = -1; cluster = o2cb_config_get_cluster_by_name(cmd->o_config, clustername); if (!cluster) { errorf("Unknown cluster '%s'\n", clustername); goto bail; } ret = o2cb_cluster_remove_heartbeat(cluster, region); if (ret) { errorf("Unknown heartbeat region '%s'\n", region); goto bail; } cmd->o_modified = 1; ret = 0; verbosef(VL_APP, "Removed heartbeat region '%s' from cluster '%s'\n", region, clustername); bail: if (region) free(region); return ret; } /* * o2cb heartbeat-mode */ errcode_t o2cbtool_heartbeat_mode(struct o2cb_command *cmd) { O2CBCluster *cluster; int ret = -1; gchar *clustername, *hbmode; if (cmd->o_argc < 3) goto bail; clustername = cmd->o_argv[1]; hbmode = cmd->o_argv[2]; if (strcmp(hbmode, O2CB_GLOBAL_HEARTBEAT_TAG) && strcmp(hbmode, O2CB_LOCAL_HEARTBEAT_TAG)) goto bail; cmd->o_print_usage = 0; cluster = o2cb_config_get_cluster_by_name(cmd->o_config, clustername); if (!cluster) { errorf("Unknown cluster '%s'\n", clustername); goto bail; } ret = o2cb_cluster_set_heartbeat_mode(cluster, hbmode); if (ret) { errorf("Could not change heartbeat mode to '%s', ret=%d,\n", hbmode, ret); goto bail; } cmd->o_modified = 1; ret = 0; verbosef(VL_APP, "Changed heartbeat mode in cluster '%s' to '%s'\n", clustername, hbmode); bail: return ret; } ocfs2-tools-ocfs2-tools-1.8.6/o2cb_ctl/op_lists.c000066400000000000000000000105631347147137200215550ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * op_lists.c * * Lists various entities in the o2cb cluster config * * Copyright (C) 2010 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License, version 2, as published by the Free Software Foundation. * * 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. */ #include "o2cbtool.h" static int list_parse_options(int argc, char *argv[], int *oneline, char **clustername) { int c, ret = -1, show_usage = 0; static struct option long_options[] = { { "oneline", 0, 0, ONELINE_OPTION }, { 0, 0, 0, 0 }, }; while (1) { c = getopt_long(argc, argv, "", long_options, NULL); if (c == -1) break; switch (c) { case ONELINE_OPTION: *oneline = 1; break; default: ++show_usage; break; } } if (optind + 1 > argc || show_usage) goto bail; *clustername = argv[optind]; ret = 0; bail: return ret; } static void show_heartbeats(O2CBCluster *cluster, gchar *clustername, int oneline) { O2CBHeartbeat *hb; JIterator *iter; gchar *region; gchar *format; if (oneline) { format = "heartbeat: %s %s\n"; } else format = "heartbeat:\n\tregion = %s\n\tcluster = %s\n\n"; iter = o2cb_cluster_get_heartbeat_regions(cluster); while (j_iterator_has_more(iter)) { hb = j_iterator_get_next(iter); region = o2cb_heartbeat_get_region(hb); verbosef(VL_OUT, format, region, clustername); g_free(region); } j_iterator_free(iter); } static void show_nodes(O2CBCluster *cluster, gchar *clustername, int oneline) { O2CBNode *node; JIterator *iter; gchar *nodename, *ip; gint port, nodenum; gchar *format; if (oneline) format = "node: %d %s %s:%d %s\n"; else format = "node:\n\tnumber = %d\n\tname = %s\n\t" "ip_address = %s\n\tip_port = %d\n\tcluster = %s\n\n"; iter = o2cb_cluster_get_nodes(cluster); while (j_iterator_has_more(iter)) { node = j_iterator_get_next(iter); nodename = o2cb_node_get_name(node); ip = o2cb_node_get_ip_string(node); nodenum = o2cb_node_get_number(node); port = o2cb_node_get_port(node); verbosef(VL_OUT, format, nodenum, nodename, ip, port, clustername); g_free(nodename); g_free(ip); } j_iterator_free(iter); } static void show_cluster(O2CBCluster *cluster, gchar *clustername, int oneline) { guint nodecount; gchar *hbmode; gchar *format; if (oneline) format = "cluster: %d %s %s\n"; else format = "cluster:\n\tnode_count = %d\n\theartbeat_mode = %s\n" "\tname = %s\n\n"; nodecount = o2cb_cluster_get_node_count(cluster); hbmode = o2cb_cluster_get_heartbeat_mode(cluster); if (!hbmode) hbmode = strdup(O2CB_LOCAL_HEARTBEAT_TAG); verbosef(VL_OUT, format, nodecount, hbmode, clustername); g_free(hbmode); } /* * list-cluster [--oneline] * list-nodes [--oneline] * list-heartbeats [--oneline] */ errcode_t o2cbtool_list_objects(struct o2cb_command *cmd) { O2CBCluster *cluster; gchar *clustername; int ret = -1; int oneline = 0; ret = list_parse_options(cmd->o_argc, cmd->o_argv, &oneline, &clustername); if (ret) goto bail; cmd->o_print_usage = 0; cluster = o2cb_config_get_cluster_by_name(cmd->o_config, clustername); if (!cluster) { errorf("Unknown cluster '%s'\n", clustername); goto bail; } if (!strcmp(cmd->o_argv[0], "list-heartbeats")) show_heartbeats(cluster, clustername, oneline); else if (!strcmp(cmd->o_argv[0], "list-nodes")) show_nodes(cluster, clustername, oneline); else { show_heartbeats(cluster, clustername, oneline); show_nodes(cluster, clustername, oneline); show_cluster(cluster, clustername, oneline); } ret = 0; bail: return ret; } /* * list-clusters */ errcode_t o2cbtool_list_clusters(struct o2cb_command *cmd) { O2CBCluster *cluster; gchar *clustername; JIterator *iter; iter = o2cb_config_get_clusters(cmd->o_config); if (!iter) return -1; cmd->o_print_usage = 0; while (j_iterator_has_more(iter)) { cluster = j_iterator_get_next(iter); clustername = o2cb_cluster_get_name(cluster); verbosef(VL_OUT, "%s\n", clustername); g_free(clustername); } j_iterator_free(iter); return 0; } ocfs2-tools-ocfs2-tools-1.8.6/o2cb_ctl/op_node.c000066400000000000000000000137571347147137200213540ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * op_node.c * * Manipulates nodes in the o2cb cluster configuration * * Copyright (C) 2010 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License, version 2, as published by the Free Software Foundation. * * 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. */ #include "o2cbtool.h" #include #include #include static int add_node_parse_options(int argc, char *argv[], char **ip, int *port, int *nodenum, char **nodename, char **clustername) { int c, ret = -1, show_usage = 0; char *p; static struct option long_options[] = { { "ip", 1, 0, IP_OPTION }, { "port", 1, 0, PORT_OPTION }, { "number", 1, 0, NODENUM_OPTION }, { 0, 0, 0, 0 }, }; while (1) { c = getopt_long(argc, argv, "", long_options, NULL); if (c == -1) break; switch (c) { case IP_OPTION: *ip = strdup(optarg); if (!*ip) { errorf("Out-off-memory while copying IP\n"); goto bail; } break; case PORT_OPTION: *port = strtol(optarg, &p, 0); if (*p) { errorf("Invalid port number\n"); goto bail; } break; case NODENUM_OPTION: *nodenum = strtol(optarg, &p, 0); if (*p) { errorf("Invalid node number\n"); goto bail; } break; default: ++show_usage; break; } } if (optind + 2 > argc || show_usage) goto bail; *clustername = argv[optind]; *nodename = tools_strstrip(argv[optind + 1]); if (!strlen(*nodename)) { errorf("Node name cannot be zero length\n"); goto bail; } ret = 0; verbosef(VL_DEBUG, "Add node '%s' in cluster '%s' having ip '%s', " "port '%d' and number '%d'\n", *nodename, *clustername, (*ip ? *ip : "auto"), *port, *nodenum); bail: return ret; } static int validate_ip_address(char *nodename, char **ip) { struct addrinfo hints, *ai = NULL; struct in_addr addr; int ret; /* if given, validate format */ if (*ip) { ret = inet_pton(AF_INET, *ip, &addr); if (ret <= 0) { tcom_err(ret, "Bad IP Address '%s'", *ip); ret = -1; } ret = (ret > 0) ? 0 : ret; goto bail; } /* if not provided, discover it */ memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_INET; hints.ai_protocol = IPPROTO_TCP; hints.ai_socktype = SOCK_STREAM; ret = getaddrinfo(nodename, NULL, &hints, &ai); if (ret) { errorf("%s, while looking up the IP address for '%s'\n", gai_strerror(ret), nodename); goto bail; } addr.s_addr = ((struct sockaddr_in *)(ai->ai_addr))->sin_addr.s_addr; *ip = strdup(inet_ntoa(addr)); if (!*ip) { tcom_err(O2CB_ET_NO_MEMORY, "while setting IP for node '%s'", nodename); goto bail; } ret = 0; bail: if (ai) freeaddrinfo(ai); if (!ret) verbosef(VL_DEBUG, "Validated IP address '%s'\n", *ip); return ret; } static int validate_nodenum(O2CBCluster *cluster, int *nodenum) { O2CBNode *node; int i, ret = -1; /* if none, then get the first unused nodenum */ if (*nodenum == -1) { for(i = 0; i < O2NM_MAX_NODES; ++i) { node = o2cb_cluster_get_node(cluster, i); if (!node) { *nodenum = i; ret = 0; goto bail; } } errorf("Cluster is full - No more nodes can be added to it\n"); goto bail; } /* if provided, validate range... */ if (!(*nodenum >= 0 && *nodenum < O2NM_MAX_NODES)) { errorf("Nodenum should be >=0 and < %d but is %d\n", O2NM_MAX_NODES, *nodenum); goto bail; } /* ... and ensure it is not inuse */ node = o2cb_cluster_get_node(cluster, *nodenum); if (node) { errorf("Choose another node number as '%d' is in use\n", *nodenum); goto bail; } ret = 0; bail: if (!ret) verbosef(VL_DEBUG, "Validated node number '%d'\n", *nodenum); return ret; } /* * o2cb add-node [--ip ] [--port ] [--number ] * */ errcode_t o2cbtool_add_node(struct o2cb_command *cmd) { O2CBCluster *cluster; O2CBNode *node; errcode_t ret; gchar *clustername = '\0', *nodename = '\0', *ip = '\0'; gint port = -1, nodenum = -1; ret = add_node_parse_options(cmd->o_argc, cmd->o_argv, &ip, &port, &nodenum, &nodename, &clustername); if (ret) goto bail; cmd->o_print_usage = 0; ret = -1; cluster = o2cb_config_get_cluster_by_name(cmd->o_config, clustername); if (!cluster) { errorf("Unknown cluster '%s'\n", clustername); goto bail; } /* validate */ ret = validate_ip_address(nodename, &ip); if (ret) goto bail; ret = validate_nodenum(cluster, &nodenum); if (ret) goto bail; if (port == -1) port = O2CB_DEFAULT_IP_PORT; ret = -1; node = o2cb_cluster_add_node(cluster, nodename); if (!node) { errorf("Node '%s' already exists\n", nodename); goto bail; } ret = o2cb_node_set_ip_string(node, ip); if (ret) { tcom_err(ret, "while setting ip '%s'", ip); goto bail; } o2cb_node_set_port(node, port); o2cb_node_set_number(node, nodenum); cmd->o_modified = 1; ret = 0; verbosef(VL_APP, "Added node '%s' in cluster '%s' having ip '%s', " "port '%d' and number '%d'\n", nodename, clustername, ip, port, nodenum); bail: return ret; } /* * o2cb remove-node */ errcode_t o2cbtool_remove_node(struct o2cb_command *cmd) { O2CBCluster *cluster; errcode_t ret = -1; gchar *clustername, *nodename; if (cmd->o_argc < 3) goto bail; cmd->o_print_usage = 0; clustername = cmd->o_argv[1]; nodename = cmd->o_argv[2]; cluster = o2cb_config_get_cluster_by_name(cmd->o_config, clustername); if (!cluster) { errorf("Unknown cluster '%s'\n", clustername); goto bail; } ret = o2cb_cluster_delete_node(cluster, nodename); if (ret) { errorf("Unknown node '%s'\n", nodename); goto bail; } cmd->o_modified = 1; ret = 0; verbosef(VL_APP, "Removed node '%s' from cluster '%s'\n", nodename, clustername); bail: return ret; } ocfs2-tools-ocfs2-tools-1.8.6/o2cb_ctl/op_register.c000066400000000000000000000224301347147137200222370ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * op_register.c * * Registers and unregisters the configured cluster with configfs * * Copyright (C) 2010 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License, version 2, as published by the Free Software Foundation. * * 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. */ #include "o2cbtool.h" extern const char *stackname; static errcode_t node_is_local(gchar *nodename, gboolean *is_local) { char hostname[PATH_MAX]; size_t host_len, node_len = strlen(nodename); gboolean local = 0; errcode_t ret = 0; ret = gethostname(hostname, sizeof(hostname)); if (ret) { errorf("Unable to determine hostname, %s\n", strerror(errno)); ret = O2CB_ET_HOSTNAME_UNKNOWN; goto bail; } host_len = strlen(hostname); if (host_len < node_len) goto bail; /* * nodes are only considered local if they match the hostname. we want * to be sure to catch the node name being "localhost" and the hostname * being "localhost.localdomain". we consider them equal if the * configured node name matches the start of the hostname up to a '.' */ if (!strncasecmp(nodename, hostname, node_len) && (hostname[node_len] == '\0' || hostname[node_len] == '.')) local = 1; bail: *is_local = local; return ret; } /* * Compares the attributes of the registered node with that of the * configured node. Sets different if the comparison fails. */ static errcode_t compare_node_attributes(O2CBCluster *cluster, gchar *clustername, gchar *nodename, int *different) { errcode_t ret = 0; O2CBNode *node; char r_ip[30]; gchar *c_ip = NULL; uint32_t r_port; guint c_port; uint16_t r_nodenum; gint c_nodenum; *different = 0; /* lookup nodename, ip, etc in config file */ node = o2cb_cluster_get_node_by_name(cluster, nodename); if (!node) { *different = 1; verbosef(VL_DEBUG, "Registered node %s not found in config\n", nodename); goto bail; } c_ip = o2cb_node_get_ip_string(node); c_port = o2cb_node_get_port(node); c_nodenum = o2cb_node_get_number(node); /* lookup the registered ip, port, nodenum */ ret = o2cb_get_node_ip_string(clustername, nodename, r_ip, sizeof(r_ip)); if (!ret) ret = o2cb_get_node_port(clustername, nodename, &r_port); if (!ret) ret = o2cb_get_node_num(clustername, nodename, &r_nodenum); if (ret) goto bail; /* compare */ if (strncmp(c_ip, r_ip, sizeof(r_ip)) || (c_port != r_port) || (c_nodenum != r_nodenum)) { *different = 1; verbosef(VL_DEBUG, "Registered node %s has changed. " "%d, %s:%d => %d, %s:%d\n", nodename, r_nodenum, r_ip, r_port, c_nodenum, c_ip, c_port); } bail: g_free(c_ip); return ret; } /* * unregister_nodes() * If cluster != NULL, unregister nodes that are _no_ longer in the config file, * or, are in it with different attributes. * If cluster == NULL, unregister all nodes. */ static errcode_t unregister_nodes(O2CBCluster *cluster, gchar *clustername) { errcode_t ret; char **nodenames = NULL; int i = 0, different; ret = o2cb_list_nodes(clustername, &nodenames); if (ret) goto bail; if (!nodenames) goto bail; while (nodenames[i] && *(nodenames[i])) { if (cluster) { ret = compare_node_attributes(cluster, clustername, nodenames[i], &different); if (ret) { tcom_err(ret, "while comparing node attributes " "for node %s", nodenames[i]); break; } if (!different) { i++; continue; } } verbosef(VL_DEBUG, "Unregistering node %s\n", nodenames[i]); ret = o2cb_del_node(clustername, nodenames[i]); if (!ret) { i++; continue; } tcom_err(ret, "while unregistering node '%s'", nodenames[i]); break; } bail: if (nodenames) o2cb_free_nodes_list(nodenames); return ret; } static errcode_t register_nodes(O2CBCluster *cluster, gchar *clustername) { errcode_t ret = 0; O2CBNode *node; JIterator *iter; gchar *nodename, *ip; gint port, nodenum; gboolean local; gchar s_port[16], s_nodenum[16], s_local[16]; /* Unregister nodes that have been removed/changed in config */ ret = unregister_nodes(cluster, clustername); if (ret) goto bail; /* Register nodes... silently skip if node already registered */ iter = o2cb_cluster_get_nodes(cluster); while (!ret && j_iterator_has_more(iter)) { node = j_iterator_get_next(iter); nodename = o2cb_node_get_name(node); ip = o2cb_node_get_ip_string(node); nodenum = o2cb_node_get_number(node); port = o2cb_node_get_port(node); ret = node_is_local(nodename, &local); if (ret) goto free; snprintf(s_port, sizeof(s_port), "%d", port); snprintf(s_nodenum, sizeof(s_nodenum), "%d", nodenum); snprintf(s_local, sizeof(s_local), "%d", local); verbosef(VL_DEBUG, "Registering node %d, %s, %s:%d, %d\n", nodenum, nodename, ip, port, local); ret = o2cb_add_node(clustername, nodename, s_nodenum, ip, s_port, s_local); if (ret && ret != O2CB_ET_NODE_EXISTS) { tcom_err(ret, "while registering node '%s'", nodename); goto free; } verbosef(VL_DEBUG, "Node %s %s\n", nodename, ((ret == O2CB_ET_NODE_EXISTS) ? "skipped" : "added")); ret = 0; free: g_free(nodename); g_free(ip); } j_iterator_free(iter); bail: return ret; } static errcode_t register_heartbeat_mode(O2CBCluster *cluster, gchar *clustername) { errcode_t ret; gchar *hbmode = NULL; int localhb; hbmode = o2cb_cluster_get_heartbeat_mode(cluster); localhb = !strcmp(hbmode, O2CB_LOCAL_HEARTBEAT_TAG); ret = o2cb_set_heartbeat_mode(clustername, hbmode); if (ret && localhb) ret = 0; if (ret) tcom_err(ret, "while registering heartbeat mode '%s'", hbmode); if (hbmode) g_free(hbmode); return ret; } static errcode_t unregister_cluster(gchar *clustername) { errcode_t ret; ret = o2cb_remove_cluster(clustername); if (ret) tcom_err(ret, "while unregistering cluster '%s'", clustername); return ret; } static errcode_t register_cluster(gchar *clustername) { errcode_t ret; ret = o2cb_create_cluster(clustername); if (!ret) goto bail; if (ret == O2CB_ET_CLUSTER_EXISTS) { ret = 0; goto bail; } tcom_err(ret, "while registering cluster '%s'", clustername); bail: return ret; } /* * register-cluster */ errcode_t o2cbtool_register_cluster(struct o2cb_command *cmd) { O2CBCluster *cluster; errcode_t ret = -1; gchar *clustername; o2cbtool_block_signals(SIG_BLOCK); if (cmd->o_argc < 2) goto bail; cmd->o_print_usage = 0; clustername = cmd->o_argv[1]; cluster = o2cb_config_get_cluster_by_name(cmd->o_config, clustername); if (!cluster) { errorf("Unknown cluster '%s'\n", clustername); goto bail; } ret = o2cbtool_validate_clustername(clustername); if (ret) goto bail; ret = o2cbtool_init_cluster_stack(); if (ret) goto bail; verbosef(VL_DEBUG, "Registering cluster '%s'\n", clustername); ret = register_cluster(clustername); if (ret) goto bail; verbosef(VL_DEBUG, "Registering heartbeat mode in cluster '%s'\n", clustername); ret = register_heartbeat_mode(cluster, clustername); if (ret) goto bail; verbosef(VL_DEBUG, "Registering nodes in cluster '%s'\n", clustername); ret = register_nodes(cluster, clustername); if (ret) goto bail; verbosef(VL_APP, "Cluster '%s' registered\n", clustername); bail: o2cbtool_block_signals(SIG_UNBLOCK); return ret; } static errcode_t proceed_unregister(char *name) { gchar **clusternames = NULL, **regions = NULL; errcode_t ret; /* lookup the registered cluster */ ret = o2cb_list_clusters(&clusternames); if (ret) { tcom_err(ret, "while looking up the registered cluster"); goto bail; } if (!clusternames) goto bail; /* check if name matches to the registered cluster */ if (!clusternames[0] || strcmp(clusternames[0], name)) { errorf("Cluster '%s' is not active\n", name); ret = -1; goto bail; } /* lookup active heartbeats */ ret = o2cb_list_hb_regions(name, ®ions); if (ret) { tcom_err(ret, "while looking up the active heartbeat regions"); goto bail; } /* error, if found */ if (regions[0] && *(regions[0])) { ret = -1; errorf("Atleast one heartbeat region is still active\n"); goto bail; } bail: if (regions) o2cb_free_hb_regions_list(regions); if (clusternames) o2cb_free_cluster_list(clusternames); return ret; } /* * unregister-cluster */ errcode_t o2cbtool_unregister_cluster(struct o2cb_command *cmd) { errcode_t ret = -1; gchar *clustername; o2cbtool_block_signals(SIG_BLOCK); if (cmd->o_argc < 2) goto bail; cmd->o_print_usage = 0; clustername = cmd->o_argv[1]; ret = o2cbtool_init_cluster_stack(); if (ret) goto bail; verbosef(VL_DEBUG, "Looking up cluster '%s'\n", clustername); ret = proceed_unregister(clustername); if (ret) goto bail; verbosef(VL_DEBUG, "Unregistering nodes in cluster '%s'\n", clustername); ret = unregister_nodes(NULL, clustername); if (ret) goto bail; verbosef(VL_DEBUG, "Unregistering cluster '%s'\n", clustername); ret = unregister_cluster(clustername); if (ret) goto bail; verbosef(VL_APP, "Cluster '%s' unregistered\n", clustername); bail: o2cbtool_block_signals(SIG_UNBLOCK); return ret; } ocfs2-tools-ocfs2-tools-1.8.6/o2cb_ctl/op_start.c000066400000000000000000000204251347147137200215520ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * op_start.c * * Starts and stops the global heartbeat * * Copyright (C) 2010, 2011 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License, version 2, as published by the Free Software Foundation. * * 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. */ #include "o2cbtool.h" #include "o2cb_scandisk.h" extern const char *stackname; static errcode_t stop_global_heartbeat(O2CBCluster *cluster, char *clustername); static void stop_heartbeat(struct o2cb_device *od) { errcode_t ret; if (!(od->od_flags & O2CB_DEVICE_FOUND) || !(od->od_flags & O2CB_DEVICE_HB_STARTED)) return; verbosef(VL_DEBUG, "Stopping heartbeat on region %s, device %s\n", od->od_region.r_name, od->od_region.r_device_name); ret = o2cb_stop_heartbeat(&od->od_cluster, &od->od_region); if (ret) tcom_err(ret, "while stopping heartbeat on region " "'%s'", od->od_uuid); else od->od_flags &= ~(O2CB_DEVICE_HB_STARTED); } static void free_region_descs(struct list_head *hbdevs, int stop_hb) { struct list_head *pos, *pos1; struct o2cb_device *od; list_for_each_safe(pos, pos1, hbdevs) { od = list_entry(pos, struct o2cb_device, od_list); if (stop_hb) stop_heartbeat(od); list_del(pos); free(od->od_uuid); free(od); } } static errcode_t get_region_descs(O2CBCluster *cluster, struct list_head *hbdevs) { errcode_t ret = O2CB_ET_NO_MEMORY; O2CBHeartbeat *heartbeat; JIterator *iter; gchar *region; struct o2cb_device *od; iter = o2cb_cluster_get_heartbeat_regions(cluster); while (j_iterator_has_more(iter)) { heartbeat = (O2CBHeartbeat *)j_iterator_get_next(iter); region = o2cb_heartbeat_get_region(heartbeat); verbosef(VL_DEBUG, "Heartbeat region %s\n", region); od = calloc(1, sizeof(struct o2cb_device)); if (od) { list_add_tail(&od->od_list, hbdevs); od->od_uuid = strdup(region); } g_free(region); if (!od || !od->od_uuid) { tcom_err(ret, "while reading regions"); goto bail; } } verbosef(VL_DEBUG, "Scanning devices\n"); o2cb_scandisk(hbdevs); ret = 0; bail: if (iter) j_iterator_free(iter); return ret; } static errcode_t start_heartbeat(struct o2cb_device *od) { errcode_t ret = O2CB_ET_UNKNOWN_REGION; if (!(od->od_flags & O2CB_DEVICE_FOUND)) { tcom_err(ret, "%s", od->od_uuid); goto bail; } verbosef(VL_DEBUG, "Starting heartbeat on region %s, device %s\n", od->od_region.r_name, od->od_region.r_device_name); ret = o2cb_start_heartbeat(&od->od_cluster, &od->od_region); if (ret) { tcom_err(ret, "while starting heartbeat on region '%s'", od->od_uuid); goto bail; } od->od_flags |= O2CB_DEVICE_HB_STARTED; bail: return ret; } static errcode_t start_global_heartbeat(O2CBCluster *cluster, char *clustername) { struct list_head hbdevs, *pos; struct o2cb_device *od; errcode_t ret; o2cbtool_block_signals(SIG_BLOCK); INIT_LIST_HEAD(&hbdevs); ret = get_region_descs(cluster, &hbdevs); if (ret) goto bail; verbosef(VL_DEBUG, "About to start heartbeat\n"); list_for_each(pos, &hbdevs) { od = list_entry(pos, struct o2cb_device, od_list); ret = start_heartbeat(od); if (ret) goto bail; } verbosef(VL_DEBUG, "Stop heartbeat on devices removed from config\n"); ret = stop_global_heartbeat(cluster, clustername); if (ret) goto bail; bail: o2cbtool_block_signals(SIG_UNBLOCK); free_region_descs(&hbdevs, !!ret); return ret; } /* * o2cb start-heartbeat */ errcode_t o2cbtool_start_heartbeat(struct o2cb_command *cmd) { O2CBCluster *cluster; int ret = -1; gchar *clustername; int global = 0; if (cmd->o_argc < 2) goto bail; cmd->o_print_usage = 0; clustername = cmd->o_argv[1]; cluster = o2cb_config_get_cluster_by_name(cmd->o_config, clustername); if (!cluster) { errorf("Unknown cluster '%s'\n", clustername); goto bail; } ret = o2cbtool_init_cluster_stack(); if (ret) goto bail; if (!is_cluster_registered(clustername)) { errorf("Cluster '%s' not registered\n", clustername); goto bail; } verbosef(VL_DEBUG, "Checking heartbeat mode\n"); ret = o2cb_global_heartbeat_mode(clustername, &global); if (ret) { tcom_err(ret, "while starting heartbeat"); goto bail; } if (!global) goto bail; verbosef(VL_DEBUG, "Global heartbeat enabled\n"); ret = start_global_heartbeat(cluster, clustername); if (ret) goto bail; verbosef(VL_OUT, "Global heartbeat started\n"); bail: return ret; } static errcode_t _fake_default_cluster_desc( struct o2cb_cluster_desc *cluster_desc) { errcode_t ret; char **clusters; memset(cluster_desc, 0, sizeof(struct o2cb_cluster_desc)); ret = o2cb_list_clusters(&clusters); if (ret) return ret; cluster_desc->c_stack = strdup(stackname); cluster_desc->c_cluster = strdup(clusters[0]); if (!cluster_desc->c_stack || !cluster_desc->c_cluster) { ret = O2CB_ET_NO_MEMORY; free(cluster_desc->c_stack); free(cluster_desc->c_cluster); } o2cb_free_cluster_list(clusters); return ret; } /* * Only the elements needed by o2cb_stop_heartbeat() are initialized in * _fake_region_desc(). */ static errcode_t _fake_region_desc(struct o2cb_region_desc *region_desc, char *region_name) { memset(region_desc, 0, sizeof(struct o2cb_region_desc)); region_desc->r_name = strdup(region_name); if (!region_desc->r_name) return O2CB_ET_NO_MEMORY; region_desc->r_persist = 1; return 0; } /* * stop_global_heartbeat() * If cluster != NULL, stop heartbeat on regions that are _no_ longer * in the configuration. * If cluster == NULL, stop heartbeat on all regions. */ static errcode_t stop_global_heartbeat(O2CBCluster *cluster, char *clustername) { struct o2cb_cluster_desc cluster_desc = { NULL, NULL }; struct o2cb_region_desc region_desc = { NULL, }; O2CBHeartbeat *hb; errcode_t ret; gchar **regions = NULL; int global = 0; int i; o2cbtool_block_signals(SIG_BLOCK); ret = _fake_default_cluster_desc(&cluster_desc); if (ret) { tcom_err(ret, "while looking up the active cluster"); goto bail; } if (strcmp(cluster_desc.c_cluster, clustername)) { errorf("Cluster '%s' is not active\n", clustername); ret = -1; goto bail; } verbosef(VL_DEBUG, "Checking heartbeat mode\n"); ret = o2cb_global_heartbeat_mode(clustername, &global); if (ret) { tcom_err(ret, "while stopping heartbeat"); goto bail; } if (!global) { verbosef(VL_DEBUG, "Global heartbeat not enabled\n"); goto bail; } verbosef(VL_DEBUG, "Global heartbeat enabled\n"); verbosef(VL_DEBUG, "Looking up active heartbeat regions\n"); ret = o2cb_list_hb_regions(clustername, ®ions); if (ret) { tcom_err(ret, "while looking up the active heartbeat regions"); goto bail; } /* error, if found */ for (i = 0; regions[i]; ++i) { if (!regions[i] || !(*(regions[i]))) continue; if (cluster) { hb = o2cb_cluster_get_heartbeat_by_region(cluster, regions[i]); if (hb) continue; verbosef(VL_DEBUG, "Registered heartbeat region '%s' " "not found in config\n", regions[i]); } ret = _fake_region_desc(®ion_desc, regions[i]); if (ret) { tcom_err(ret, "while filling region description"); goto bail; } verbosef(VL_DEBUG, "Stopping heartbeat on region %s\n", regions[i]); ret = o2cb_stop_heartbeat(&cluster_desc, ®ion_desc); if (ret) { tcom_err(ret, "while stopping heartbeat on region " "'%s'", regions[i]); goto bail; } free(region_desc.r_name); region_desc.r_name = NULL; } ret = 0; bail: o2cbtool_block_signals(SIG_UNBLOCK); if (regions) o2cb_free_hb_regions_list(regions); free(cluster_desc.c_stack); free(cluster_desc.c_cluster); free(region_desc.r_name); return ret; } /* * o2cb stop-heartbeat */ errcode_t o2cbtool_stop_heartbeat(struct o2cb_command *cmd) { int ret = -1; gchar *clustername; if (cmd->o_argc < 2) goto bail; cmd->o_print_usage = 0; clustername = cmd->o_argv[1]; ret = o2cbtool_init_cluster_stack(); if (ret) goto bail; ret = stop_global_heartbeat(NULL, clustername); if (ret) goto bail; verbosef(VL_OUT, "Global heartbeat stopped\n"); bail: return ret; } ocfs2-tools-ocfs2-tools-1.8.6/o2cb_ctl/op_status.c000066400000000000000000000054671347147137200217510ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * op_status.c * * Returns status of the cluster * * Copyright (C) 2011 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License, version 2, as published by the Free Software Foundation. * * 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. */ #include "o2cbtool.h" extern const char *stackname; static errcode_t get_active_clustername(char *name, int namelen) { gchar **clusternames = NULL; errcode_t ret; /* Lookup the registered cluster */ ret = o2cb_list_clusters(&clusternames); if (ret) { tcom_err(ret, "while looking up the registered cluster"); goto bail; } if (!*clusternames) { ret = O2CB_ET_SERVICE_UNAVAILABLE; goto bail; } strncpy(name, clusternames[0], namelen); name[namelen] = '\0'; bail: if (clusternames) o2cb_free_cluster_list(clusternames); return ret; } /* * cluster-status [] * * Return 0 if online and 1 otherwise. */ errcode_t o2cbtool_cluster_status(struct o2cb_command *cmd) { errcode_t err; char clustername[OCFS2_CLUSTER_NAME_LEN + 1]; int global = 0, status = 1; *clustername = '\0'; cmd->o_print_usage = 0; err = o2cbtool_init_cluster_stack(); if (err) goto bail; /* Get active clustername */ err = get_active_clustername(clustername, OCFS2_CLUSTER_NAME_LEN); if (err) goto bail; /* If none, found exit */ if (strlen(clustername) == 0) goto bail; verbosef(VL_DEBUG, "Active cluster '%s'\n", clustername); /* If found but name != arg, then exit */ if (cmd->o_argc > 1 && strncmp(clustername, cmd->o_argv[1], OCFS2_CLUSTER_NAME_LEN)) { strncpy(clustername, cmd->o_argv[1], OCFS2_CLUSTER_NAME_LEN); clustername[OCFS2_CLUSTER_NAME_LEN] = '\0'; goto bail; } /* Check if cluster is registered */ if (!is_cluster_registered(clustername)) goto bail; verbosef(VL_DEBUG, "Cluster '%s' is registered\n", clustername); /* Get heartbeat mode */ err = o2cb_global_heartbeat_mode(clustername, &global); if (err) goto bail; /* If local heartbeat, then cluster is online */ if (!global) { status = 0; goto bail; } verbosef(VL_DEBUG, "Global heartbeat is enabled\n"); /* * In global heartbeat mode, atleast one region should be active * for the cluster to be called online */ if (!is_heartbeat_active(clustername)) goto bail; status = 0; bail: if (strlen(clustername)) verbosef(VL_OUT, "Cluster '%s' is %s\n", clustername, (!status ? "online" : "offline")); else verbosef(VL_OUT, "%s\n", (!status ? "online" : "offline")); return status; } ocfs2-tools-ocfs2-tools-1.8.6/o2dlm.pc.in000066400000000000000000000003611347147137200200270ustar00rootroot00000000000000prefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=@includedir@ Name: o2dlm Description: Library for accessing the ocfs2 DLM Version: @VERSION@ Requires: com_err Libs: -L${libdir} -lo2dlm @DL_LIBS@ Cflags: -I${includedir} ocfs2-tools-ocfs2-tools-1.8.6/o2image/000077500000000000000000000000001347147137200174045ustar00rootroot00000000000000ocfs2-tools-ocfs2-tools-1.8.6/o2image/.gitignore000066400000000000000000000000341347147137200213710ustar00rootroot00000000000000*.sw? *.d o2image o2image.8 ocfs2-tools-ocfs2-tools-1.8.6/o2image/Makefile000066400000000000000000000016561347147137200210540ustar00rootroot00000000000000TOPDIR = .. include $(TOPDIR)/Preamble.make WARNINGS = -Wall -Wstrict-prototypes -Wno-format -Wmissing-prototypes \ -Wmissing-declarations CFLAGS += $(WARNINGS) LIBOCFS2_LIBS = -L$(TOPDIR)/libocfs2 -locfs2 LIBOCFS2_DEPS = $(TOPDIR)/libocfs2/libocfs2.a LIBO2DLM_LIBS = -L$(TOPDIR)/libo2dlm -lo2dlm LIBO2DLM_DEPS = $(TOPDIR)/libo2dlm/libo2dlm.a ifneq ($(BUILD_FSDLM_SUPPORT),) LIBO2CB_LIBS = -L$(TOPDIR)/libo2cb -lo2cb -ldlm_lt else LIBO2CB_LIBS = -L$(TOPDIR)/libo2cb -lo2cb endif LIBO2CB_DEPS = $(TOPDIR)/libo2cb/libo2cb.a sbindir = $(root_sbindir) SBIN_PROGRAMS = o2image INCLUDES = -I$(TOPDIR)/include -I. INCLUDES += $(GLIB_CFLAGS) DEFINES = -DVERSION=\"$(VERSION)\" MANS = o2image.8 CFILES = o2image.c OBJS = $(subst .c,.o,$(CFILES)) DIST_FILES = $(CFILES) $(HFILES) o2image.8.in o2image: $(OBJS) $(LIBOCFS2_DEPS) $(LINK) $(GLIB_LIBS) $(LIBOCFS2_LIBS) $(COM_ERR_LIBS) $(AIO_LIBS) include $(TOPDIR)/Postamble.make ocfs2-tools-ocfs2-tools-1.8.6/o2image/o2image.8.in000066400000000000000000000052231347147137200214270ustar00rootroot00000000000000.TH "o2image" "8" "January 2012" "Version @VERSION@" "OCFS2 Manual Pages" .SH "NAME" o2image \- Copy or restore \fIOCFS2\fR file system meta-data .SH "SYNOPSIS" \fBo2image\fR [\fB\-r\fR] [\fB\-I\fR] \fIdevice\fR \fIimage-file\fR .SH "DESCRIPTION" .PP \fBo2image\fR copies the \fIOCFS2\fR file system meta-data from the device to the specified image-file. This image file contains the file system skeleton that includes the inodes, directory names and file names. It does \fBnot\fR include any file data. This image file can be useful to debug certain problems that are not reproducible otherwise. Like on-disk corruptions. It could also be used to analyse the file system layout in an aging file system with an eye towards improving performance. As the image-file contains a copy of all the meta-data blocks, it can be a large file. By default, it is created in a packed format, in which all meta-data blocks are written back-to-back. With the \fB\-r\fR option, the user could choose to have the file in the raw (or sparse) format, in which the blocks are written to the same offset as they are on the device. \fIdebugfs.ocfs2\fR understands both formats. \fBo2image\fR also has the option, \fI\-I\fR, to restore the meta-data from the image file onto the device. This option will rarely be useful to end-users and has been written specifically for developers and testers. .SH "OPTIONS" .TP \fB\-r\fR Copies the meta-data to the image-file in the raw format. Use this option only if the destination file system supports sparse files. If unsure, do not use this option and let the tool create the image-file in the packed format. .TP \fB\-I\fR Restores meta-data from the image-file onto the device. \fBCAUTION: This option could corrupt the file system.\fR .TP \fB\-i\fR Interactive mode - before writing out the image file print it's size and ask whether to proceed. This setting only applies when '-I' is not specified. It can be useful when the file system holding the image is low on disk space and the user might need to free up space once the target image size is calculated. .SH "EXAMPLES" Copies metadata blocks from /dev/sda1 device to sda1.out file. .nf .ft 6 # o2image /dev/sda1 sda1.out .ft .fi Copies meta-data blocks from sda1.out onto the /dev/sda1 device. \fBAs this command over-writes an existing volume, please use with CAUTION\fR. .nf .ft 6 # o2image -I /dev/sda1 sda1.out .ft .fi .SH "SEE ALSO" .BR debugfs.ocfs2(8) .BR fsck.ocfs2(8) .BR fsck.ocfs2.checks(8) .BR mkfs.ocfs2(8) .BR mount.ocfs2(8) .BR mounted.ocfs2(8) .BR o2cluster(8) .BR o2info(1) .BR tunefs.ocfs2(8) .SH "AUTHORS" Oracle Corporation .SH "COPYRIGHT" Copyright \(co 2007, 2012 Oracle. All rights reserved. ocfs2-tools-ocfs2-tools-1.8.6/o2image/o2image.c000066400000000000000000000474241347147137200211060ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * o2image.c * * o2image utility to backup/restore OCFS2 metadata structures * * Copyright (C) 2008 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License, version 2, as published by the Free Software Foundation. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. */ #define _XOPEN_SOURCE 600 /* Triggers magic in features.h */ #define _LARGEFILE64_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ocfs2/ocfs2.h" #include "ocfs2/image.h" static errcode_t traverse_inode(ocfs2_filesys *ofs, uint64_t inode); char *program_name = NULL; static void usage(void) { fprintf(stderr, ("Usage: %s [-frI] device image_file\n"), program_name); exit(1); } static errcode_t mark_localalloc_bits(ocfs2_filesys *ofs, struct ocfs2_local_alloc *loc) { /* no need to dump space reserved in local alloc inode */ return 0; } static errcode_t traverse_group_desc(ocfs2_filesys *ofs, struct ocfs2_group_desc *grp, int dump_type, int bpc) { errcode_t ret = 0; uint64_t blkno; int i; blkno = grp->bg_blkno; for (i = 1; i < grp->bg_bits; i++) { blkno = ocfs2_get_block_from_group(ofs, grp, bpc, i); if ((dump_type == OCFS2_IMAGE_READ_INODE_YES) && ocfs2_test_bit(i, grp->bg_bitmap)) ret = traverse_inode(ofs, blkno); else ocfs2_image_mark_bitmap(ofs, blkno); } return ret; } static errcode_t mark_dealloc_bits(ocfs2_filesys *ofs, struct ocfs2_truncate_log *tl) { /* no need to dump deleted space */ return 0; } static errcode_t traverse_extents(ocfs2_filesys *ofs, struct ocfs2_extent_list *el) { struct ocfs2_extent_block *eb; struct ocfs2_extent_rec *rec; struct ocfs2_image_state *ost = ofs->ost; errcode_t ret = 0; char *buf = NULL; int i, j; ret = ocfs2_malloc_block(ofs->fs_io, &buf); if (ret) goto out; for (i = 0; i < el->l_next_free_rec; ++i) { rec = &(el->l_recs[i]); ocfs2_image_mark_bitmap(ofs, rec->e_blkno); if (el->l_tree_depth) { ret = ocfs2_read_extent_block(ofs, rec->e_blkno, buf); if (ret) goto out; eb = (struct ocfs2_extent_block *)buf; ret = traverse_extents(ofs, &(eb->h_list)); if (ret) goto out; } else { for (j = 0; j < (rec->e_int_clusters*ost->ost_bpc); j++) ocfs2_image_mark_bitmap(ofs, (rec->e_blkno + j)); } } out: if (buf) ocfs2_free(&buf); return ret; } static errcode_t traverse_chains(ocfs2_filesys *ofs, struct ocfs2_chain_list *cl, int dump_type) { struct ocfs2_group_desc *grp; struct ocfs2_chain_rec *rec; errcode_t ret = 0; char *buf = NULL; uint64_t blkno; int i; ret = ocfs2_malloc_block(ofs->fs_io, &buf); if (ret) { com_err(program_name, ret, "while allocating block buffer " "to group descriptor"); goto out; } for (i = 0; i < cl->cl_next_free_rec; i++) { rec = &(cl->cl_recs[i]); blkno = rec->c_blkno; while (blkno) { ocfs2_image_mark_bitmap(ofs, blkno); ret = ocfs2_read_group_desc(ofs, blkno, buf); if (ret) goto out; grp = (struct ocfs2_group_desc *)buf; if (dump_type) { ret = traverse_group_desc(ofs, grp, dump_type, cl->cl_bpc); if (ret) goto out; } blkno = grp->bg_next_group; } } out: if (buf) ocfs2_free(&buf); return ret; } static errcode_t traverse_dx_root(ocfs2_filesys *ofs, uint64_t blkno) { errcode_t ret; char *buf = NULL; struct ocfs2_dx_root_block *dx_root; ocfs2_image_mark_bitmap(ofs, blkno); ret = ocfs2_malloc_block(ofs->fs_io, &buf); if (ret) goto out; ret = ocfs2_read_dx_root(ofs, blkno, buf); if (ret) goto out; dx_root = (struct ocfs2_dx_root_block *) buf; if (!(dx_root->dr_flags & OCFS2_DX_FLAG_INLINE)) traverse_extents(ofs, &dx_root->dr_list); out: if (buf) ocfs2_free(&buf); return ret; } static errcode_t traverse_xb(ocfs2_filesys *ofs, uint64_t blkno) { errcode_t ret = 0; char *buf = NULL; struct ocfs2_xattr_block *xb; ret = ocfs2_malloc_block(ofs->fs_io, &buf); if (ret) goto out; ret = ocfs2_read_xattr_block(ofs, blkno, buf); if (ret) goto out; xb = (struct ocfs2_xattr_block *)buf; if (xb->xb_flags & OCFS2_XATTR_INDEXED) traverse_extents(ofs, &(xb->xb_attrs.xb_root.xt_list)); else /* Direct xattr block should be handled by * extent_alloc scans */ goto out; out: if (buf) ocfs2_free(&buf); return ret; } static errcode_t traverse_inode(ocfs2_filesys *ofs, uint64_t inode) { struct ocfs2_super_block *super; struct ocfs2_image_state *ost = ofs->ost; struct ocfs2_dinode *di; errcode_t ret = 0; int dump_type = 0; char *buf = NULL; int slot; super = OCFS2_RAW_SB(ofs->fs_super); ocfs2_image_mark_bitmap(ofs, inode); ret = ocfs2_malloc_block(ofs->fs_io, &buf); if (ret) { com_err(program_name, ret, "while allocating block buffer " "to bitmap inode"); goto out; } di = (struct ocfs2_dinode *)buf; ret = ocfs2_read_inode(ofs, inode, (char *)di); if (ret) { com_err(program_name, ret, "while reading global bitmap inode" " %"PRIu64"", inode); goto out; } /* * Do not scan inode if it's regular file. Extent blocks of regular * files get backedup when scanning extent_alloc system files * * NOTE: we do need to handle its xattr btree if exists. */ if (!S_ISDIR(di->i_mode) && !(di->i_flags & OCFS2_SYSTEM_FL) && !(di->i_dyn_features & OCFS2_HAS_XATTR_FL)) goto out; /* Read and traverse group descriptors */ if (di->i_flags & OCFS2_SYSTEM_FL) dump_type = OCFS2_IMAGE_READ_INODE_NO; /* Do not traverse chains of a global bitmap inode */ if (inode == ost->ost_glbl_bitmap_inode) dump_type = OCFS2_IMAGE_READ_CHAIN_NO; /* * If inode is an alloc inode, read the inodes(files/directories) and * traverse inode if it's a directory */ for (slot = 0; slot < super->s_max_slots; slot++) if (inode == ost->ost_inode_allocs[slot]) dump_type = OCFS2_IMAGE_READ_INODE_YES; if (inode == ost->ost_glbl_inode_alloc) { if (ost->ost_glbl_inode_traversed) { goto out; } else { dump_type = OCFS2_IMAGE_READ_INODE_YES; ost->ost_glbl_inode_traversed = 1; } } if ((di->i_flags & OCFS2_LOCAL_ALLOC_FL)) ret = mark_localalloc_bits(ofs, &(di->id2.i_lab)); else if (di->i_flags & OCFS2_CHAIN_FL) ret = traverse_chains(ofs, &(di->id2.i_chain), dump_type); else if (di->i_flags & OCFS2_DEALLOC_FL) ret = mark_dealloc_bits(ofs, &(di->id2.i_dealloc)); else if ((di->i_dyn_features & OCFS2_HAS_XATTR_FL) && di->i_xattr_loc) /* Do need to traverse xattr btree to map bucket leaves */ ret = traverse_xb(ofs, di->i_xattr_loc); else { /* * Don't check superblock flag for the dir indexing * feature in case it (or the directory) is corrupted * we want to try to pick up as much of the supposed * index as possible. * * Error reporting is a bit different though. If the * directory indexing feature is set on the super * block, we should fail here to indicate an * incomplete inode. Otherwise it is safe to ignore * errors from traverse_dx_root. */ if (S_ISDIR(di->i_mode) && (di->i_dyn_features & OCFS2_INDEXED_DIR_FL)) { ret = traverse_dx_root(ofs, di->i_dx_root); if (ret && ocfs2_supports_indexed_dirs(super)) goto out_error; } /* traverse extents for system files */ ret = traverse_extents(ofs, &(di->id2.i_list)); } out_error: if (ret) { com_err(program_name, ret, "while scanning inode %"PRIu64"", inode); goto out; } out: if (buf) ocfs2_free(&buf); return ret; } static errcode_t o2image_initialize(ocfs2_filesys *ofs, int raw_flag) { uint64_t blocks[OCFS2_MAX_BACKUP_SUPERBLOCKS]; struct ocfs2_super_block *super; struct ocfs2_image_state *ost = ofs->ost; errcode_t ret = 0; uint64_t blkno; int i, len; ost->ost_fsblkcnt = ofs->fs_blocks; ret = ocfs2_image_alloc_bitmap(ofs); if (ret) { com_err(program_name, ret, "while allocating bitmap"); goto out; } super = OCFS2_RAW_SB(ofs->fs_super); ost->ost_bpc = ofs->fs_clustersize/ofs->fs_blocksize; /* traverse and mark super blocks for backup */ if (super->s_feature_compat & OCFS2_FEATURE_COMPAT_BACKUP_SB) { len = ocfs2_get_backup_super_offsets(ofs, blocks, ARRAY_SIZE(blocks)); for (i = 0; i < len; i++) ocfs2_image_mark_bitmap(ofs, blocks[i]); } /* mark blocks before first cluster group for backup */ blkno = 0; while(blkno <= super->s_first_cluster_group) ocfs2_image_mark_bitmap(ofs, blkno++); /* get global bitmap system inode number */ ret = ocfs2_lookup_system_inode(ofs, GLOBAL_BITMAP_SYSTEM_INODE, 0, &ost->ost_glbl_bitmap_inode); if (ret) { com_err(program_name, ret, "while looking for bitmap inode"); goto out; } /* track global_inode_alloc inode number */ ret = ocfs2_lookup_system_inode(ofs, GLOBAL_INODE_ALLOC_SYSTEM_INODE, 0, &ost->ost_glbl_inode_alloc); if (ret) { com_err(program_name, ret, "while looking for global inode"); goto out; } ost->ost_glbl_inode_traversed = 0; /* track local alloc inode numbers for all slots */ ret = ocfs2_malloc(((super->s_max_slots) * sizeof(blkno)), &ost->ost_inode_allocs); if (ret) { com_err(program_name, ret, "while allocating mem for " "local_allocs"); goto out; } for (i = 0; i < super->s_max_slots; i++) { ret = ocfs2_lookup_system_inode(ofs, INODE_ALLOC_SYSTEM_INODE, i, &ost->ost_inode_allocs[i]); if (ret) { com_err(program_name, ret, "while reading inode for " "slot %d", i); goto out; } } out: return ret; } /* * This write function takes a file descriptor and pretends to be * pwrite64(). If the descriptor is seekable, it will just call * pwrite64(). Otherwise it will send zeros down to fill any holes. The * caller can't go backwards in the file, because seeking may not be * possible. * * It returns -1 if it fails to finish up at offset+count. It will * print its error, so the caller does not need to. */ #define ZERO_BUF_SIZE (1<<20) static ssize_t raw_write(ocfs2_filesys *fs, int fd, const void *buf, size_t count, loff_t offset) { errcode_t ret; ssize_t written; static char *zero_buf = NULL; static int can_seek = -1; static loff_t fpos = 0; loff_t to_write; if (can_seek == -1) { /* Test if we can seek/pwrite */ fpos = lseek64(fd, 0, SEEK_CUR); if (fpos < 0) { fpos = 0; can_seek = 0; } else can_seek = 1; } if (!can_seek && !zero_buf) { ret = ocfs2_malloc_blocks(fs->fs_io, ZERO_BUF_SIZE / fs->fs_blocksize, &zero_buf); if (ret) { com_err(program_name, ret, "while allocating zero buffer"); written = -1; goto out; } memset(zero_buf, 0, ZERO_BUF_SIZE); } if (can_seek) { written = pwrite64(fd, buf, count, offset); goto out; } /* Ok, let's fake pwrite64() for the caller */ if (fpos > offset) { com_err(program_name, OCFS2_ET_INTERNAL_FAILURE, ": file position went backwards while writing " "image file"); written = -1; goto out; } while (fpos < offset) { to_write = ocfs2_min((loff_t)ZERO_BUF_SIZE, offset - fpos); written = write(fd, zero_buf, to_write); if (written < 0) { com_err(program_name, OCFS2_ET_IO, "while writing zero blocks: %s", strerror(errno)); goto out; } fpos += written; } to_write = count; while (to_write) { written = write(fd, buf, to_write); if (written < 0) { com_err(program_name, OCFS2_ET_IO, "while writing data blocks: %s", strerror(errno)); goto out; } fpos += written; to_write -= written; } /* Ok, we did it */ written = count; out: return written; } static errcode_t write_raw_image_file(ocfs2_filesys *ofs, int fd) { char *blk_buf = NULL; uint64_t blk = -1; ssize_t count; errcode_t ret; ret = ocfs2_malloc_block(ofs->fs_io, &blk_buf); if (ret) { com_err(program_name, ret, "while allocating I/O buffer"); goto out; } while (++blk < ofs->fs_blocks) { if (ocfs2_image_test_bit(ofs, blk)) { ret = ocfs2_read_blocks(ofs, blk, 1, blk_buf); if (ret) { com_err(program_name, ret, "while reading block %"PRIu64, blk); break; } count = raw_write(ofs, fd, blk_buf, ofs->fs_blocksize, (loff_t)(blk * ofs->fs_blocksize)); if (count < 0) { ret = OCFS2_ET_IO; break; } } } out: if (blk_buf) ocfs2_free(&blk_buf); return ret; } static errcode_t write_image_file(ocfs2_filesys *ofs, int fd) { uint64_t supers[OCFS2_MAX_BACKUP_SUPERBLOCKS]; struct ocfs2_image_state *ost = ofs->ost; struct ocfs2_image_hdr *hdr; uint64_t i, blk; errcode_t ret; int bytes; char *buf; ret = ocfs2_malloc_block(ofs->fs_io, &buf); if (ret) { com_err(program_name, ret, "allocating %lu bytes ", ofs->fs_blocksize); return ret; } hdr = (struct ocfs2_image_hdr *)buf; hdr->hdr_magic = OCFS2_IMAGE_MAGIC; memcpy(hdr->hdr_magic_desc, OCFS2_IMAGE_DESC, sizeof(OCFS2_IMAGE_DESC)); /* count metadata blocks that will be backedup */ blk = 0; for (i = 0; ifs_blocks; i++) if (ocfs2_image_test_bit(ofs, i)) blk++; hdr->hdr_timestamp = time(0); hdr->hdr_version = OCFS2_IMAGE_VERSION; hdr->hdr_fsblkcnt = ofs->fs_blocks; hdr->hdr_fsblksz = ofs->fs_blocksize; hdr->hdr_imgblkcnt = blk; hdr->hdr_bmpblksz = ost->ost_bmpblksz; hdr->hdr_superblkcnt = ocfs2_get_backup_super_offsets(ofs, supers, ARRAY_SIZE(supers)); for (i = 0; i < hdr->hdr_superblkcnt; i++) hdr->hdr_superblocks[i] = ocfs2_image_get_blockno(ofs, supers[i]); ocfs2_image_swap_header(hdr); /* o2image header size is smaller than ofs->fs_blocksize */ bytes = write(fd, hdr, ofs->fs_blocksize); if (bytes < 0) { perror("writing header"); ret = bytes; goto out; } if (bytes != ofs->fs_blocksize) { fprintf(stderr, "write_image: short write %d bytes", bytes); goto out; } /* copy metadata blocks to image files */ for (blk = 0; blk < ofs->fs_blocks; blk++) { if (ocfs2_image_test_bit(ofs, blk)) { ret = ocfs2_read_blocks(ofs, blk, 1, buf); if (ret) { com_err(program_name, ret, "error occurred " "during read block %"PRIu64"", blk); goto out; } bytes = write(fd, buf, ofs->fs_blocksize); if ((bytes == -1) || (bytes < ofs->fs_blocksize)) { com_err(program_name, errno, "error writing " "blk %"PRIu64"", blk); goto out; } } } /* write bitmap blocks at the end */ for(blk = 0; blk < ost->ost_bmpblks; blk++) { bytes = write(fd, ost->ost_bmparr[blk].arr_map, ost->ost_bmpblksz); if ((bytes == -1) || (bytes < ost->ost_bmpblksz)) { com_err(program_name, errno, "error writing bitmap " "blk %"PRIu64" bytes %u", blk, bytes); goto out; } } out: if (buf) ocfs2_free(&buf); if (bytes < 0) ret = bytes; return ret; } static errcode_t scan_raw_disk(ocfs2_filesys *ofs) { struct ocfs2_image_state *ost = ofs->ost; uint64_t bits_set = 0; errcode_t ret; int i, j; /* * global inode alloc has list of all metadata inodes blocks. * traverse_inode recursively traverses each inode */ ret = traverse_inode(ofs, ofs->ost->ost_glbl_inode_alloc); if (ret) goto out; /* update set_bit_cnt for future use */ for (i = 0; i < ost->ost_bmpblks; i++) { ost->ost_bmparr[i].arr_set_bit_cnt = bits_set; for (j = 0; j < (ost->ost_bmpblksz * 8); j++) if (ocfs2_test_bit(j, ost->ost_bmparr[i].arr_map)) bits_set++; } out: return ret; } static int prompt_image_creation(ocfs2_filesys *ofs, int rawflg, char *filename) { int i, n; uint64_t free_spc; struct statfs stat; uint64_t img_size = 0; char *filepath; filepath = strdup(filename); statfs(dirname(filepath), &stat); free_spc = stat.f_bsize * stat.f_bavail; n = ofs->ost->ost_bmpblks - 1; if (!rawflg) img_size = ofs->ost->ost_bmpblks * ofs->ost->ost_bmpblksz; img_size += ofs->ost->ost_bmparr[n].arr_set_bit_cnt * ofs->fs_blocksize; for (i = 0; i < (ofs->ost->ost_bmpblksz * 8); i++) if (ocfs2_test_bit(i, ofs->ost->ost_bmparr[n].arr_map)) img_size += ofs->fs_blocksize; fprintf(stdout, "Image file expected to be %luK, " "Available free space %luK. Continue ? (y/N): ", img_size/1024, free_spc/1024); if (toupper(getchar()) != 'Y') { fprintf(stdout, "Aborting image creation.\n"); ocfs2_free(&filepath); return 0; } ocfs2_free(&filepath); return 1; } int main(int argc, char **argv) { ocfs2_filesys *ofs; errcode_t ret; char *src_file = NULL; char *dest_file = NULL; int open_flags = 0; int raw_flag = 0; int install_flag = 0; int interactive = 0; int fd = STDOUT_FILENO; int c; if (argc && *argv) program_name = *argv; initialize_ocfs_error_table(); optind = 0; while((c = getopt(argc, argv, "irI")) != EOF) { switch (c) { case 'r': raw_flag++; break; case 'I': install_flag++; break; case 'i': interactive = 1; break; default: usage(); } } if (optind != argc -2) usage(); /* We interchange src_file and image file if installing */ if (install_flag) { dest_file = argv[optind]; src_file = argv[optind + 1]; if ((strcmp(src_file, "-") == 0) || (strcmp(dest_file, "-") == 0)) { com_err(program_name, 1, "cannot install to/from " "file - "); exit(1); } fprintf(stdout, "Install %s image to %s. Continue? (y/N): ", src_file, dest_file); if (toupper(getchar()) != 'Y') { fprintf(stderr, "Aborting operation.\n"); return 1; } /* if raw is not specified then we are opening image file */ if (!raw_flag) open_flags = OCFS2_FLAG_IMAGE_FILE; } else { src_file = argv[optind]; dest_file = argv[optind + 1]; } /* * ocfs2_open is modified to be aware of OCFS2_FLAG_IMAGE_FILE. * open routine allocates ocfs2_image_state and loads the bitmap if * OCFS2_FLAG_IMAGE_FILE flag is passed in */ ret = ocfs2_open(src_file, OCFS2_FLAG_RO|OCFS2_FLAG_NO_ECC_CHECKS|open_flags, 0, 0, &ofs); if (ret) { com_err(program_name, ret, "while trying to open \"%s\"", src_file); exit(1); } /* * If src_file is opened with OCFS2_FLAG_IMAGE_FILE, then no need to * allocate and initialize ocfs2_image_state. ocfs2_open would have * allocated one already. */ if (!(open_flags & OCFS2_FLAG_IMAGE_FILE)) { ret = ocfs2_malloc0(sizeof(struct ocfs2_image_state),&ofs->ost); if (ret) { com_err(program_name, ret, "allocating memory for " "ocfs2_image_state"); goto out; } ret = o2image_initialize(ofs, raw_flag); if (ret) { com_err(program_name, ret, "during o2image initialize"); goto out; } ret = scan_raw_disk(ofs); if (ret) { com_err(program_name, ret, "while scanning disk \"%s\"", src_file); goto out; } } if (strcmp(dest_file, "-") == 0) fd = STDOUT_FILENO; else { /* prompt user for image creation */ if (interactive && !install_flag && !prompt_image_creation(ofs, raw_flag, dest_file)) goto out; fd = open64(dest_file, O_CREAT|O_TRUNC|O_WRONLY, 0600); if (fd < 0) { com_err(program_name, errno, "while trying to open \"%s\"", argv[optind + 1]); goto out; } } /* Installs always are done in raw format */ if (raw_flag || install_flag) ret = write_raw_image_file(ofs, fd); else ret = write_image_file(ofs, fd); if (ret) { com_err(program_name, ret, "while writing to image \"%s\"", dest_file); goto out; } out: ocfs2_image_free_bitmap(ofs); if (ofs->ost->ost_inode_allocs) ocfs2_free(&ofs->ost->ost_inode_allocs); ret = ocfs2_close(ofs); if (ret) com_err(program_name, ret, "while closing file \"%s\"", src_file); if (fd && (fd != STDOUT_FILENO)) close(fd); return ret; } ocfs2-tools-ocfs2-tools-1.8.6/o2info/000077500000000000000000000000001347147137200172555ustar00rootroot00000000000000ocfs2-tools-ocfs2-tools-1.8.6/o2info/.gitignore000066400000000000000000000000621347147137200212430ustar00rootroot00000000000000*.sw? *.d *.cmd stamp-md5 cscope* o2info o2info.1 ocfs2-tools-ocfs2-tools-1.8.6/o2info/Makefile000066400000000000000000000020351347147137200207150ustar00rootroot00000000000000TOPDIR = .. include $(TOPDIR)/Preamble.make WARNINGS = -Wall -Wstrict-prototypes -Wno-format -Wmissing-prototypes \ -Wmissing-declarations CFLAGS += $(WARNINGS) LIBTOOLS_INTERNAL_LIBS = -L$(TOPDIR)/libtools-internal -ltools-internal LIBTOOLS_INTERNAL_DEPS = $(TOPDIR)/libtools-internal/libtools-internal.a LIBOCFS2_LIBS = -L$(TOPDIR)/libocfs2 -locfs2 LIBOCFS2_DEPS = $(TOPDIR)/libocfs2/libocfs2.a BIN_PROGRAMS = o2info INCLUDES = -I$(TOPDIR)/include -I. DEFINES = -DVERSION=\"$(VERSION)\" MANS = o2info.1 HFILES = o2info.h \ utils.h \ libo2info.h CFILES = \ o2info.c \ operations.c \ utils.c LIBO2INFO_CFILES = libo2info.c OBJS = $(subst .c,.o,$(CFILES)) LIBO2INFO_OBJS = $(subst .c,.o,$(LIBO2INFO_CFILES)) DIST_FILES = $(CFILES) $(LIBO2INFO_CFILES) $(HFILES) o2info.1.in libo2info.a: $(LIBO2INFO_OBJS) rm -f $@ $(AR) r $@ $^ $(RANLIB) $@ o2info: $(OBJS) $(LIBOCFS2_DEPS) libo2info.a $(LINK) $(LIBOCFS2_LIBS) $(LIBTOOLS_INTERNAL_LIBS) $(COM_ERR_LIBS) $(AIO_LIBS) libo2info.a include $(TOPDIR)/Postamble.make ocfs2-tools-ocfs2-tools-1.8.6/o2info/libo2info.c000066400000000000000000000257251347147137200213170ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * libo2info.c * * Shared routines for the ocfs2 o2info utility * * Copyright (C) 2010 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License version 2 as published by the Free Software Foundation. * * 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. */ #define _XOPEN_SOURCE 600 #define _LARGEFILE64_SOURCE #define _GNU_SOURCE #include #include #include #include #include "ocfs2/ocfs2.h" #include "ocfs2/bitops.h" #include "ocfs2-kernel/fiemap.h" #include "tools-internal/verbose.h" #include "libo2info.h" int o2info_get_fs_features(ocfs2_filesys *fs, struct o2info_fs_features *ofs) { int rc = 0; struct ocfs2_super_block *sb = NULL; memset(ofs, 0, sizeof(*ofs)); sb = OCFS2_RAW_SB(fs->fs_super); ofs->compat = sb->s_feature_compat; ofs->incompat = sb->s_feature_incompat; ofs->rocompat = sb->s_feature_ro_compat; return rc; } int o2info_get_volinfo(ocfs2_filesys *fs, struct o2info_volinfo *vf) { int rc = 0; struct ocfs2_super_block *sb = NULL; memset(vf, 0, sizeof(*vf)); sb = OCFS2_RAW_SB(fs->fs_super); vf->blocksize = fs->fs_blocksize; vf->clustersize = fs->fs_clustersize; vf->maxslots = sb->s_max_slots; memcpy(vf->label, sb->s_label, OCFS2_MAX_VOL_LABEL_LEN); memcpy(vf->uuid_str, fs->uuid_str, OCFS2_TEXT_UUID_LEN + 1); rc = o2info_get_fs_features(fs, &(vf->ofs)); return rc; } int o2info_get_mkfs(ocfs2_filesys *fs, struct o2info_mkfs *oms) { errcode_t err; uint64_t blkno; char *buf = NULL; struct ocfs2_dinode *di = NULL; memset(oms, 0, sizeof(*oms)); err = ocfs2_malloc_block(fs->fs_io, &buf); if (err) { tcom_err(err, "while allocating buffer"); goto out; } err = ocfs2_lookup_system_inode(fs, JOURNAL_SYSTEM_INODE, 0, &blkno); if (err) { tcom_err(err, "while looking up journal system inode"); goto out; } err = ocfs2_read_inode(fs, blkno, buf); if (err) { tcom_err(err, "while reading journal system inode"); goto out; } di = (struct ocfs2_dinode *)buf; oms->journal_size = di->i_size; err = o2info_get_volinfo(fs, &(oms->ovf)); out: if (buf) ocfs2_free(&buf); return err; } int o2info_get_freeinode(ocfs2_filesys *fs, struct o2info_freeinode *ofi) { int ret = 0, i, j; char *block = NULL; uint64_t inode_alloc; struct ocfs2_dinode *dinode_alloc = NULL; struct ocfs2_chain_list *cl = NULL; struct ocfs2_chain_rec *rec = NULL; struct ocfs2_super_block *sb = OCFS2_RAW_SB(fs->fs_super); ofi->slotnum = sb->s_max_slots; ret = ocfs2_malloc_block(fs->fs_io, &block); if (ret) { tcom_err(ret, "while allocating block buffer"); goto out; } dinode_alloc = (struct ocfs2_dinode *)block; for (i = 0; i < ofi->slotnum; i++) { ofi->fi[i].total = ofi->fi[i].free = 0; ret = ocfs2_lookup_system_inode(fs, INODE_ALLOC_SYSTEM_INODE, i, &inode_alloc); if (ret) { tcom_err(ret, "while looking up the global" " bitmap inode"); goto out; } ret = ocfs2_read_inode(fs, inode_alloc, (char *)dinode_alloc); if (ret) { tcom_err(ret, "reading global_bitmap inode " "%"PRIu64" for stats", inode_alloc); goto out; } cl = &(dinode_alloc->id2.i_chain); for (j = 0; j < cl->cl_next_free_rec; j++) { rec = &(cl->cl_recs[j]); ofi->fi[i].total += rec->c_total; ofi->fi[i].free += rec->c_free; } } out: if (block) ocfs2_free(&block); return ret; } static int ul_log2(unsigned long arg) { unsigned int i = 0; arg >>= 1; while (arg) { i++; arg >>= 1; } return i; } static void o2info_update_freefrag_stats(struct o2info_freefrag *off, unsigned int chunksize) { int index; index = ul_log2(chunksize); if (index >= OCFS2_INFO_MAX_HIST) index = OCFS2_INFO_MAX_HIST - 1; off->histogram.fc_chunks[index]++; off->histogram.fc_clusters[index] += chunksize; if (chunksize > off->max) off->max = chunksize; if (chunksize < off->min) off->min = chunksize; off->avg += chunksize; off->free_chunks_real++; } static int o2info_scan_global_bitmap_chain(ocfs2_filesys *fs, struct ocfs2_chain_rec *rec, struct o2info_freefrag *off) { int ret = 0, used; uint64_t blkno; char *block = NULL; struct ocfs2_group_desc *bg = NULL; unsigned int max_bits, num_clusters; unsigned int offset = 0, cluster, chunk; unsigned int chunk_free, last_chunksize = 0; if (!rec->c_free) goto out; ret = ocfs2_malloc_block(fs->fs_io, &block); if (ret) { tcom_err(ret, "while allocating block buffer"); goto out; } do { if (!bg) blkno = rec->c_blkno; else blkno = bg->bg_next_group; ret = ocfs2_read_blocks(fs, blkno, 1, block); if (ret < 0) { tcom_err(ret, "while reading group descriptor " "%"PRIu64" for stats", blkno); goto out; } bg = (struct ocfs2_group_desc *)block; if (!bg->bg_free_bits_count) continue; max_bits = bg->bg_bits; offset = 0; for (chunk = 0; chunk < off->chunks_in_group; chunk++) { /* * last chunk may be not an entire one. */ if ((offset + off->clusters_in_chunk) > max_bits) num_clusters = max_bits - offset; else num_clusters = off->clusters_in_chunk; chunk_free = 0; for (cluster = 0; cluster < num_clusters; cluster++) { used = ocfs2_test_bit(offset, (unsigned long *)bg->bg_bitmap); if (!used) { last_chunksize++; chunk_free++; } if (used && (last_chunksize)) { o2info_update_freefrag_stats(off, last_chunksize); last_chunksize = 0; } offset++; } if (chunk_free == off->clusters_in_chunk) off->free_chunks++; } /* * need to update the info of last free chunk. */ if (last_chunksize) o2info_update_freefrag_stats(off, last_chunksize); } while (bg->bg_next_group); out: if (block) ocfs2_free(&block); return ret; } static int o2info_scan_global_bitmap(ocfs2_filesys *fs, struct ocfs2_chain_list *cl, struct o2info_freefrag *off) { int ret = 0, i; struct ocfs2_chain_rec *rec = NULL; off->chunks_in_group = (cl->cl_cpg / off->clusters_in_chunk) + 1; for (i = 0; i < cl->cl_next_free_rec; i++) { rec = &(cl->cl_recs[i]); ret = o2info_scan_global_bitmap_chain(fs, rec, off); if (ret) return ret; } return ret; } int o2info_get_freefrag(ocfs2_filesys *fs, struct o2info_freefrag *off) { int ret = 0; char *block = NULL; uint64_t gb_inode; struct ocfs2_dinode *gb_di = NULL; struct ocfs2_chain_list *cl = NULL; ret = ocfs2_malloc_block(fs->fs_io, &block); if (ret) { tcom_err(ret, "while allocating block buffer"); goto out; } gb_di = (struct ocfs2_dinode *)block; ret = ocfs2_lookup_system_inode(fs, GLOBAL_BITMAP_SYSTEM_INODE, 0, &gb_inode); if (ret) { tcom_err(ret, "while looking up the global bitmap inode"); goto out; } ret = ocfs2_read_inode(fs, gb_inode, (char *)gb_di); if (ret) { tcom_err(ret, "reading global_bitmap inode " "%"PRIu64" for stats", gb_inode); goto out; } off->clusters = gb_di->id1.bitmap1.i_total; off->free_clusters = gb_di->id1.bitmap1.i_total - gb_di->id1.bitmap1.i_used; off->total_chunks = (off->clusters + off->clusters_in_chunk) >> (off->chunkbits - off->clustersize_bits); cl = &(gb_di->id2.i_chain); ret = o2info_scan_global_bitmap(fs, cl, off); if (ret) goto out; if (off->free_chunks_real) { off->min <<= (off->clustersize_bits - 10); off->max <<= (off->clustersize_bits - 10); off->avg /= off->free_chunks_real; off->avg <<= (off->clustersize_bits - 10); } out: if (block) ocfs2_free(&block); return ret; } static int figure_extents(int fd, uint32_t *num, int flags) { int ret; static struct fiemap fiemap; fiemap.fm_start = 0ULL; fiemap.fm_length = FIEMAP_MAX_OFFSET; if (flags & FIEMAP_FLAG_XATTR) fiemap.fm_flags = FIEMAP_FLAG_XATTR; fiemap.fm_extent_count = 0; ret = ioctl(fd, FS_IOC_FIEMAP, &fiemap); if (ret < 0) { ret = errno; tcom_err(ret, "fiemap get count error"); return -1; } *num = fiemap.fm_mapped_extents; return 0; } static uint32_t clusters_in_bytes(uint32_t clustersize, uint32_t bytes) { uint64_t ret = bytes + clustersize - 1; if (ret < bytes) ret = UINT64_MAX; ret = ret >> ul_log2(clustersize); if (ret > UINT32_MAX) ret = UINT32_MAX; return (uint32_t)ret; } static int do_fiemap(int fd, int flags, struct o2info_fiemap *ofp) { char buf[4096]; int ret = 0, last = 0; int cluster_shift = 0; int count = (sizeof(buf) - sizeof(struct fiemap)) / sizeof(struct fiemap_extent); struct fiemap *fiemap = (struct fiemap *)buf; struct fiemap_extent *fm_ext = &fiemap->fm_extents[0]; uint32_t num_extents = 0, extents_got = 0, i; uint32_t prev_start = 0, prev_len = 0; uint32_t start = 0, len = 0; if (ofp->clustersize) cluster_shift = ul_log2(ofp->clustersize); memset(fiemap, 0, sizeof(*fiemap)); ret = figure_extents(fd, &num_extents, 0); if (ret) return -1; if (flags & FIEMAP_FLAG_XATTR) fiemap->fm_flags = FIEMAP_FLAG_XATTR; else fiemap->fm_flags = flags; do { fiemap->fm_length = ~0ULL; fiemap->fm_extent_count = count; ret = ioctl(fd, FS_IOC_FIEMAP, (unsigned long)fiemap); if (ret < 0) { ret = errno; if (errno == EBADR) { fprintf(stderr, "fiemap failed with unsupported" " flags %x\n", fiemap->fm_flags); } else fprintf(stderr, "fiemap error: %d, %s\n", ret, strerror(ret)); return -1; } if (!fiemap->fm_mapped_extents) break; for (i = 0; i < fiemap->fm_mapped_extents; i++) { start = fm_ext[i].fe_logical >> cluster_shift; len = fm_ext[i].fe_length >> cluster_shift; if (fiemap->fm_flags & FIEMAP_FLAG_XATTR) { ofp->xattr += len; } else { if (fm_ext[i].fe_flags & FIEMAP_EXTENT_UNWRITTEN) ofp->unwrittens += len; if (fm_ext[i].fe_flags & FIEMAP_EXTENT_SHARED) ofp->shared += len; if ((prev_start + prev_len) < start) ofp->holes += start - prev_start - prev_len; } if (fm_ext[i].fe_flags & FIEMAP_EXTENT_LAST) last = 1; prev_start = start; prev_len = len; extents_got++; ofp->clusters += len; } fiemap->fm_start = (fm_ext[i-1].fe_logical + fm_ext[i-1].fe_length); } while (!last); if (extents_got != num_extents) { fprintf(stderr, "Got wrong extents number, expected:%lu, " "got:%lu\n", num_extents, extents_got); return -1; } if (flags & FIEMAP_FLAG_XATTR) ofp->num_extents_xattr = num_extents; else ofp->num_extents = num_extents; return ret; } int o2info_get_fiemap(int fd, int flags, struct o2info_fiemap *ofp) { int ret = 0; ret = do_fiemap(fd, flags, ofp); if (ret) return ret; if ((ofp->clusters > 1) && ofp->num_extents) { float e = ofp->num_extents, c = ofp->clusters; int clusters_per_mb = clusters_in_bytes(ofp->clustersize, OCFS2_MAX_CLUSTERSIZE); ofp->frag = 100 * (e / c); ofp->score = ofp->frag * clusters_per_mb; } return ret; } ocfs2-tools-ocfs2-tools-1.8.6/o2info/libo2info.h000066400000000000000000000046721347147137200213220ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * libo2info.h * * o2info helper library prototypes. * * Copyright (C) 2010 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License version 2 as published by the Free Software Foundation. * * 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. */ #ifndef __LIBO2INFO_H__ #define __LIBO2INFO_H__ struct o2info_fs_features { uint32_t compat; uint32_t incompat; uint32_t rocompat; }; struct o2info_volinfo { uint32_t blocksize; uint32_t clustersize; uint32_t maxslots; uint8_t label[OCFS2_MAX_VOL_LABEL_LEN]; uint8_t uuid_str[OCFS2_TEXT_UUID_LEN + 1]; struct o2info_fs_features ofs; }; struct o2info_mkfs { struct o2info_volinfo ovf; uint64_t journal_size; }; struct o2info_local_freeinode { unsigned long total; unsigned long free; }; struct o2info_freeinode { int slotnum; struct o2info_local_freeinode fi[OCFS2_MAX_SLOTS]; }; #define DEFAULT_CHUNKSIZE (1024*1024) struct free_chunk_histogram { uint32_t fc_chunks[OCFS2_INFO_MAX_HIST]; uint32_t fc_clusters[OCFS2_INFO_MAX_HIST]; }; struct o2info_freefrag { unsigned long chunkbytes; uint32_t clusters; uint32_t free_clusters; uint32_t total_chunks; uint32_t free_chunks; uint32_t free_chunks_real; int clustersize_bits; int blksize_bits; int chunkbits; uint32_t clusters_in_chunk; uint32_t chunks_in_group; uint32_t min, max, avg; /* chunksize in clusters */ struct free_chunk_histogram histogram; }; struct o2info_fiemap { uint32_t blocksize; uint32_t clustersize; uint32_t num_extents; uint32_t num_extents_xattr; uint32_t clusters; uint32_t shared; uint32_t holes; uint32_t unwrittens; uint32_t xattr; float frag; /* extents / clusters ratio */ float score; }; int o2info_get_fs_features(ocfs2_filesys *fs, struct o2info_fs_features *ofs); int o2info_get_volinfo(ocfs2_filesys *fs, struct o2info_volinfo *vf); int o2info_get_mkfs(ocfs2_filesys *fs, struct o2info_mkfs *oms); int o2info_get_freeinode(ocfs2_filesys *fs, struct o2info_freeinode *ofi); int o2info_get_freefrag(ocfs2_filesys *fs, struct o2info_freefrag *off); int o2info_get_fiemap(int fd, int flags, struct o2info_fiemap *ofp); #endif ocfs2-tools-ocfs2-tools-1.8.6/o2info/o2info.1.in000066400000000000000000000101371347147137200211420ustar00rootroot00000000000000.TH "o2info" "1" "January 2012" "Version @VERSION@" "OCFS2 Manual Pages" .SH "NAME" o2info \- Show \fIOCFS2\fR file system information. .SH "SYNOPSIS" \fBo2info\fR [\fB\-C|\-\-cluster\-coherent\fR] [\fB\-\-fs\-features\fR] [\fB\-\-volinfo\fR] [\fB\-\-mkfs\fR] [\fB\-\-freeinode\fR] [\fB\-\-freefrag\fR \fIchunksize\fR] [\fB\-\-space\-usage\fR] [\fB\-\-filestat\fR] <\fBdevice or file\fR> .SH "DESCRIPTION" .PP \fBo2info\fR shows information about \fIOCFS2\fR file systems. It differs from \fBdebugfs.ocfs2(8)\fR in that it allows users with limited read privileges to query similar information. Users do not have to have the read privilege on the device as is expected by \fBdebugfs.ocfs2(8)\fR. This utility allows users to provide a path to an object on a mounted file system. The user needs to have the read privilege on that object. .SH "OPTIONS" .TP \fB\-C, \-\-cluster\-coherent\fR Force cluster coherency when querying a mounted file systems. The is disabled by default. Enable this only if accurate information is required as it involves taking cluster locks. .TP \fB\-\-fs\-features\fR Show all the file system features (compat, incompat, ro compat) enabled on the file system. .TP \fB\-\-volinfo\fR Show file system information such as label, uuid, block and cluster sizes, node slots, etc. .TP \fB\-\-mkfs\fR Show file system information in \fBmkfs.ocfs2(8)\fR format. .TP \fB\-\-freeinode\fR Show the inode (allocated/free) count for all slots in the file system. .TP \fB\-\-freefrag\fR \fIchunksize\fR Show the free space fragmentation of the file system. The chunksize should be equal to or greater than the cluster size. .TP \fB\-\-space\-usage\fR Show the disk space used by a file in block sized units. It also provides the block count of the holes, shared extents and unwritten extents. .TP \fB\-\-filestat\fR Show the extended \fBstat(1)\fR information that includes the number of clusters consumed by extended attributes, unwritten extents, shared extents and holes, along with the file fragmentation score. .TP \fB\-V, \-\-version\fR Show version and exit. .TP \fB\-h, \-\-help\fR Show help and exit. .SH "NOTE" .PP The utility uses custom info ioctls to query information from a mounted file system. As these info ioctls were added starting in Linux kernel 2.6.37, this utility will not work on mounted \fIOCFS2\fR file systems running on systems having older kernels. .SH "EXAMPLES" .PP Non-privileged users can query the volume information by providing the path to a file on a mounted file system. Priviledged users can provide the path to the device. .in +4n .nf # \fBo2info --volinfo /ocfs2/testfile\fR Label: webhome UUID: 8AB016CD59FC4327A2CDAB69F08518E3 Block Size: 4096 Cluster Size: 131072 Node Slots: 8 Features: backup-super strict-journal-super sparse extended-slotmap Features: inline-data xattr indexed-dirs refcount discontig-bg clusterinfo Features: unwritten .fi .in The same goes for querying file system fragmentation. .in +4n .nf # \fBo2info --freefrag 128 /ocfs2/testfile\fR Blocksize: 4096 bytes Clustersize: 131072 bytes Total clusters: 409599 Free clusters: 376537 (91.92%) Min. free extent: 256 KB Max. free extent: 4091648 KB Avg. free extent: 172672 KB Chunksize: 131072 bytes (1 clusters) Total chunks: 409600 Free chunks: 376537 (91.9%) HISTOGRAM OF FREE EXTENT SIZES: Extent Size Range : Free extents Free Clusters Percent 256K... 512K- : 4 10 0.00% 512K... 1024K- : 251 1179 0.31% 1M... 2M- : 5 72 0.02% 8M... 16M- : 3 288 0.08% 32M... 64M- : 1 447 0.12% 128M... 256M- : 2 3371 0.90% 1G... 2G- : 1 13823 3.67% 2G... 4G- : 12 357347 94.90% .fi .in .SH "SEE ALSO" .BR debugfs.ocfs2(8) .BR fsck.ocfs2(8) .BR fsck.ocfs2.checks(8) .BR mkfs.ocfs2(8) .BR mount.ocfs2(8) .BR mounted.ocfs2(8) .BR o2cluster(8) .BR o2image(8) .BR tunefs.ocfs2(8) .SH "AUTHORS" Oracle Corporation .SH "COPYRIGHT" Copyright \(co 2010, 2012 Oracle. All rights reserved. ocfs2-tools-ocfs2-tools-1.8.6/o2info/o2info.c000066400000000000000000000275671347147137200206360ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * o2info.c * * Ocfs2 utility to gather and report fs information * * Copyright (C) 2010 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License version 2 as published by the Free Software Foundation. * * 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. */ #define _XOPEN_SOURCE 600 #define _LARGEFILE64_SOURCE #define _GNU_SOURCE /* Because libc really doesn't want us using O_DIRECT? */ #include #include #include #include #include #include "ocfs2/ocfs2.h" #include "ocfs2-kernel/ocfs2_ioctl.h" #include "ocfs2-kernel/kernel-list.h" #include "tools-internal/verbose.h" #include "utils.h" extern struct o2info_operation fs_features_op; extern struct o2info_operation volinfo_op; extern struct o2info_operation mkfs_op; extern struct o2info_operation freeinode_op; extern struct o2info_operation freefrag_op; extern struct o2info_operation space_usage_op; extern struct o2info_operation filestat_op; static LIST_HEAD(o2info_op_task_list); static int o2info_op_task_count; int cluster_coherent; void print_usage(int rc); static int help_handler(struct o2info_option *opt, char *arg) { print_usage(0); exit(0); } static int version_handler(struct o2info_option *opt, char *arg) { tools_version(); exit(0); } static int coherency_handler(struct o2info_option *opt, char *arg) { cluster_coherent = 1; return 0; } static struct o2info_option help_option = { .opt_option = { .name = "help", .val = 'h', .has_arg = 0, .flag = NULL, }, .opt_help = NULL, .opt_handler = help_handler, .opt_op = NULL, .opt_private = NULL, }; static struct o2info_option version_option = { .opt_option = { .name = "version", .val = 'V', .has_arg = 0, .flag = NULL, }, .opt_help = NULL, .opt_handler = version_handler, .opt_op = NULL, .opt_private = NULL, }; static struct o2info_option coherency_option = { .opt_option = { .name = "cluster-coherent", .val = 'C', .has_arg = 0, .flag = NULL, }, .opt_help = "-C|--cluster-coherent", .opt_handler = coherency_handler, .opt_op = NULL, .opt_private = NULL, }; static struct o2info_option fs_features_option = { .opt_option = { .name = "fs-features", .val = CHAR_MAX, .has_arg = 0, .flag = NULL, }, .opt_help = " --fs-features", .opt_handler = NULL, .opt_op = &fs_features_op, .opt_private = NULL, }; static struct o2info_option volinfo_option = { .opt_option = { .name = "volinfo", .val = CHAR_MAX, .has_arg = 0, .flag = NULL, }, .opt_help = " --volinfo", .opt_handler = NULL, .opt_op = &volinfo_op, .opt_private = NULL, }; static struct o2info_option mkfs_option = { .opt_option = { .name = "mkfs", .val = CHAR_MAX, .has_arg = 0, .flag = NULL, }, .opt_help = " --mkfs", .opt_handler = NULL, .opt_op = &mkfs_op, .opt_private = NULL, }; static struct o2info_option freeinode_option = { .opt_option = { .name = "freeinode", .val = CHAR_MAX, .has_arg = 0, .flag = NULL, }, .opt_help = " --freeinode", .opt_handler = NULL, .opt_op = &freeinode_op, .opt_private = NULL, }; static struct o2info_option freefrag_option = { .opt_option = { .name = "freefrag", .val = CHAR_MAX, .has_arg = 1, .flag = NULL, }, .opt_help = " --freefrag ", .opt_handler = NULL, .opt_op = &freefrag_op, .opt_private = NULL, }; static struct o2info_option space_usage_option = { .opt_option = { .name = "space-usage", .val = CHAR_MAX, .has_arg = 0, .flag = NULL, }, .opt_help = " --space-usage", .opt_handler = NULL, .opt_op = &space_usage_op, .opt_private = NULL, }; static struct o2info_option filestat_option = { .opt_option = { .name = "filestat", .val = CHAR_MAX, .has_arg = 0, .flag = NULL, }, .opt_help = " --filestat", .opt_handler = NULL, .opt_op = &filestat_op, .opt_private = NULL, }; static struct o2info_option *options[] = { &help_option, &version_option, &coherency_option, &fs_features_option, &volinfo_option, &mkfs_option, &freeinode_option, &freefrag_option, &space_usage_option, &filestat_option, NULL, }; void print_usage(int rc) { int i; enum tools_verbosity_level level = VL_ERR; if (!rc) level = VL_OUT; verbosef(level, "Usage: %s [options] \n", tools_progname()); verbosef(level, " %s -h|--help\n", tools_progname()); verbosef(level, " %s -V|--version\n", tools_progname()); verbosef(level, "[options] can be followings:\n"); for (i = 0; options[i]; i++) { if (options[i]->opt_help) verbosef(level, "\t%s\n", options[i]->opt_help); } exit(rc); } static int build_options(char **optstring, struct option **longopts) { errcode_t err; int i, num_opts, rc = 0; int unprintable_counter; size_t optstring_len; char *p, *str = NULL; struct option *lopts = NULL; struct o2info_option *opt; unprintable_counter = 1; /* Start unique at CHAR_MAX + 1*/ optstring_len = 1; /* For the leading ':' */ for (i = 0; options[i]; i++) { opt = options[i]; /* * Any option with a val of CHAR_MAX wants an unique but * unreadable ->val. Only readable characters go into * optstring. */ if (opt->opt_option.val == CHAR_MAX) { opt->opt_option.val = CHAR_MAX + unprintable_counter; unprintable_counter++; continue; } /* * A given option has a single character in optstring. * If it takes a mandatory argument, has_arg==1 and you add * a ":" to optstring. If it takes an optional argument, * has_arg==2 and you add "::" to optstring. Thus, * 1 + has_arg is the total space needed in opstring. */ optstring_len += 1 + opt->opt_option.has_arg; } num_opts = i; err = ocfs2_malloc0(sizeof(char) * (optstring_len + 1), &str); if (!err) err = ocfs2_malloc(sizeof(struct option) * (num_opts + 1), &lopts); if (err) { rc = -ENOMEM; goto out; } p = str; *p++ = ':'; for (i = 0; options[i]; i++) { assert(p < (str + optstring_len + 1)); opt = options[i]; memcpy(&lopts[i], &opt->opt_option, sizeof(struct option)); if (opt->opt_option.val >= CHAR_MAX) continue; *p = opt->opt_option.val; p++; if (opt->opt_option.has_arg > 0) { *p = ':'; p++; } if (opt->opt_option.has_arg > 1) { *p = ':'; p++; } } /* * Fill last entry of options with zeros. */ memset(&lopts[i], 0, sizeof(struct option)); out: if (!rc) { *optstring = str; *longopts = lopts; } else { if (str) free(str); if (lopts) free(lopts); } return rc; } static struct o2info_option *find_option_by_val(int val) { int i; struct o2info_option *opt = NULL; for (i = 0; options[i]; i++) { if (options[i]->opt_option.val == val) { opt = options[i]; break; } } return opt; } static errcode_t o2info_append_task(struct o2info_operation *o2p) { errcode_t err; struct o2info_op_task *task; err = ocfs2_malloc0(sizeof(struct o2info_op_task), &task); if (!err) { task->o2p_task = o2p; list_add_tail(&task->o2p_list, &o2info_op_task_list); o2info_op_task_count++; } return err; } static void o2info_free_op_task_list(void) { struct o2info_op_task *task; struct list_head *pos, *next; if (list_empty(&o2info_op_task_list)) return; list_for_each_safe(pos, next, &o2info_op_task_list) { task = list_entry(pos, struct o2info_op_task, o2p_list); list_del(pos); ocfs2_free(&task); } } extern int optind, opterr, optopt; extern char *optarg; static errcode_t parse_options(int argc, char *argv[], char **device_or_file) { int c, lopt_idx = 0; errcode_t err; struct option *long_options = NULL; char *optstring = NULL; struct o2info_option *opt; err = build_options(&optstring, &long_options); if (err) goto out; opterr = 0; while ((c = getopt_long(argc, argv, optstring, long_options, &lopt_idx)) != EOF) { opt = NULL; switch (c) { case '?': if (optopt) errorf("Invalid option: '-%c'\n", optopt); else errorf("Invalid option: '%s'\n", argv[optind - 1]); print_usage(1); break; case ':': if (optopt < CHAR_MAX) errorf("Option '-%c' requires an argument\n", optopt); else errorf("Option '%s' requires an argument\n", argv[optind - 1]); print_usage(1); break; default: opt = find_option_by_val(c); if (!opt) { errorf("Shouldn't have gotten here: " "option '-%c'\n", c); print_usage(1); } if (optarg) opt->opt_private = (void *)optarg; break; } if (opt->opt_set) { errorf("Option '-%c' specified more than once\n", c); print_usage(1); } opt->opt_set = 1; /* * Handlers for simple options such as showing version, * printing the usage, or specify the coherency etc. */ if (opt->opt_handler) { if (opt->opt_handler(opt, optarg)) print_usage(1); } /* * Real operation will be added to a list to run later. */ if (opt->opt_op) { opt->opt_op->to_private = opt->opt_private; err = o2info_append_task(opt->opt_op); if (err) goto out; } } if (optind == 1) print_usage(1); if (optind >= argc) { errorf("No device or file specified\n"); print_usage(1); } *device_or_file = strdup(argv[optind]); if (!*device_or_file) { errorf("No memory for allocation\n"); goto out; } optind++; if (optind < argc) { errorf("Too many arguments\n"); print_usage(1); } out: if (optstring) ocfs2_free(&optstring); if (long_options) ocfs2_free(&long_options); return err; } static errcode_t o2info_run_task(struct o2info_method *om) { struct list_head *p, *n; struct o2info_op_task *task; list_for_each_safe(p, n, &o2info_op_task_list) { task = list_entry(p, struct o2info_op_task, o2p_list); task->o2p_task->to_run(task->o2p_task, om, task->o2p_task->to_private); } return 0; } static void handle_signal(int caught_sig) { int exitp = 0, abortp = 0; static int segv_already; switch (caught_sig) { case SIGQUIT: abortp = 1; /* FALL THROUGH */ case SIGTERM: case SIGINT: case SIGHUP: errorf("Caught signal %d, exiting\n", caught_sig); exitp = 1; break; case SIGSEGV: errorf("Segmentation fault, exiting\n"); exitp = 1; if (segv_already) { errorf("Segmentation fault loop detected\n"); abortp = 1; } else segv_already = 1; break; default: errorf("Caught signal %d, ignoring\n", caught_sig); break; } if (!exitp) return; if (abortp) abort(); exit(1); } static int setup_signals(void) { int rc = 0; struct sigaction act; act.sa_sigaction = NULL; sigemptyset(&act.sa_mask); act.sa_handler = handle_signal; #ifdef SA_INTERRUPT act.sa_flags = SA_INTERRUPT; #endif rc += sigaction(SIGTERM, &act, NULL); rc += sigaction(SIGINT, &act, NULL); rc += sigaction(SIGHUP, &act, NULL); rc += sigaction(SIGQUIT, &act, NULL); rc += sigaction(SIGSEGV, &act, NULL); act.sa_handler = SIG_IGN; rc += sigaction(SIGPIPE, &act, NULL); /* Get EPIPE instead */ return rc; } static void o2info_init(const char *argv0) { initialize_ocfs_error_table(); tools_setup_argv0(argv0); setbuf(stdout, NULL); setbuf(stderr, NULL); if (setup_signals()) { errorf("Unable to setup signal handling \n"); exit(1); } cluster_coherent = 0; } int main(int argc, char *argv[]) { int rc = 0; char *device_or_file = NULL; static struct o2info_method om; o2info_init(argv[0]); parse_options(argc, argv, &device_or_file); rc = o2info_method(device_or_file); if (rc < 0) goto out; else om.om_method = rc; strncpy(om.om_path, device_or_file, PATH_MAX); rc = o2info_open(&om, 0); if (rc) goto out; rc = o2info_run_task(&om); if (rc) goto out; o2info_free_op_task_list(); rc = o2info_close(&om); out: if (device_or_file) ocfs2_free(&device_or_file); return rc; } ocfs2-tools-ocfs2-tools-1.8.6/o2info/o2info.h000066400000000000000000000037771347147137200206400ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * o2info.h * * o2info operation prototypes. * * Copyright (C) 2010 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License version 2 as published by the Free Software Foundation. * * 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. */ #ifndef __O2INFO_H__ #define __O2INFO_H__ #include #include "ocfs2/ocfs2.h" #include "ocfs2-kernel/kernel-list.h" enum o2info_method_type { O2INFO_USE_LIBOCFS2 = 1, O2INFO_USE_IOCTL, O2INFO_USE_NUMTYPES }; struct o2info_method { enum o2info_method_type om_method; char om_path[PATH_MAX]; union { ocfs2_filesys *om_fs; /* Use libocfs2 for device */ int om_fd; /* Use ioctl for file */ }; }; struct o2info_operation { char *to_name; int (*to_run)(struct o2info_operation *op, struct o2info_method *om, void *arg); void *to_private; }; struct o2info_option { struct option opt_option; /* For getopt_long(). If there is no short option, set .val to CHAR_MAX. A unique value will be inserted by the code. */ struct o2info_operation *opt_op; char *opt_help; /* Help string */ int opt_set; /* Was this option seen */ int (*opt_handler)(struct o2info_option *opt, char *arg); void *opt_private; }; struct o2info_op_task { struct list_head o2p_list; struct o2info_operation *o2p_task; }; #define __O2INFO_OP(_name, _run, _private) \ { \ .to_name = #_name, \ .to_run = _run, \ .to_private = _private \ } #define DEFINE_O2INFO_OP(_name, _run, _private) \ struct o2info_operation _name##_op = \ __O2INFO_OP(_name, _run, _private) #endif /* __O2INFO_H__ */ ocfs2-tools-ocfs2-tools-1.8.6/o2info/operations.c000066400000000000000000000554571347147137200216240ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * operations.c * * Implementations for all o2info's operation. * * Copyright (C) 2010 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License version 2 as published by the Free Software Foundation. * * 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. */ #define _XOPEN_SOURCE 600 #define _LARGEFILE64_SOURCE #define _GNU_SOURCE /* Because libc really doesn't want us using O_DIRECT? */ #include #include #include #include "ocfs2/ocfs2.h" #include "ocfs2/bitops.h" #include "ocfs2-kernel/ocfs2_ioctl.h" #include "ocfs2-kernel/kernel-list.h" #include "tools-internal/verbose.h" #include "utils.h" #include "libo2info.h" extern void print_usage(int rc); extern int cluster_coherent; static inline void o2info_fill_request(struct ocfs2_info_request *req, size_t size, enum ocfs2_info_type code, int flags) { memset(req, 0, size); req->ir_magic = OCFS2_INFO_MAGIC; req->ir_size = size; req->ir_code = code, req->ir_flags = flags; } static void o2i_info(struct o2info_operation *op, const char *fmt, ...) { va_list ap; fprintf(stdout, "%s Info: ", op->to_name); va_start(ap, fmt); vfprintf(stdout, fmt, ap); va_end(ap); return; } static void o2i_error(struct o2info_operation *op, const char *fmt, ...) { va_list ap; fprintf(stderr, "%s Error: ", op->to_name); va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); return; } /* * Helper to scan all requests: * * - Print all errors and unknown requests. * - Return number of unknown requests. * - Return number of errors. * - Return number of handled requesets. * - Return first and last error code. */ static void o2i_scan_requests(struct o2info_operation *op, struct ocfs2_info info, uint32_t *unknowns, uint32_t *errors, uint32_t *fills) { uint32_t i, num_unknown = 0, num_error = 0, num_filled = 0; uint64_t *reqs; struct ocfs2_info_request *req; for (i = 0; i < info.oi_count; i++) { reqs = (uint64_t *)info.oi_requests; req = (struct ocfs2_info_request *)reqs[i]; if (req->ir_flags & OCFS2_INFO_FL_ERROR) { o2i_error(op, "o2info request(%d) failed.\n", req->ir_code); num_error++; continue; } if (!(req->ir_flags & OCFS2_INFO_FL_FILLED)) { o2i_info(op, "o2info request(%d) is unsupported.\n", req->ir_code); num_unknown++; continue; } num_filled++; } *unknowns = num_unknown; *errors = num_error; *fills = num_filled; } static int get_fs_features_ioctl(struct o2info_operation *op, int fd, struct o2info_fs_features *ofs) { int rc = 0, flags = 0; uint32_t unknowns = 0, errors = 0, fills = 0; uint64_t reqs[1]; struct ocfs2_info_fs_features oif; struct ocfs2_info info; memset(ofs, 0, sizeof(*ofs)); if (!cluster_coherent) flags |= OCFS2_INFO_FL_NON_COHERENT; o2info_fill_request((struct ocfs2_info_request *)&oif, sizeof(oif), OCFS2_INFO_FS_FEATURES, flags); reqs[0] = (unsigned long)&oif; info.oi_requests = (uint64_t)reqs; info.oi_count = 1; rc = ioctl(fd, OCFS2_IOC_INFO, &info); if (rc) { rc = errno; o2i_error(op, "ioctl failed: %s\n", strerror(rc)); o2i_scan_requests(op, info, &unknowns, &errors, &fills); goto out; } if (oif.if_req.ir_flags & OCFS2_INFO_FL_FILLED) { ofs->compat = oif.if_compat_features; ofs->incompat = oif.if_incompat_features; ofs->rocompat = oif.if_ro_compat_features; } out: return rc; } static void o2info_print_line(char const *qualifier, char *content, char splitter) { char *ptr = NULL, *token = NULL, *tmp = NULL; uint32_t max_len = 80, len = 0; tmp = malloc(max_len); ptr = content; snprintf(tmp, max_len, "%s", qualifier); fprintf(stdout, "%s", tmp); len += strlen(tmp); while (ptr) { token = ptr; ptr = strchr(ptr, splitter); if (ptr) *ptr = 0; if (strcmp(token, "") != 0) { snprintf(tmp, max_len, "%s ", token); len += strlen(tmp); if (len > max_len) { fprintf(stdout, "\n"); len = 0; snprintf(tmp, max_len, "%s", qualifier); fprintf(stdout, "%s", tmp); len += strlen(tmp); snprintf(tmp, max_len, "%s ", token); fprintf(stdout, "%s", tmp); len += strlen(tmp); } else fprintf(stdout, "%s", tmp); } if (!ptr) break; ptr++; } fprintf(stdout, "\n"); if (tmp) ocfs2_free(&tmp); } static int fs_features_run(struct o2info_operation *op, struct o2info_method *om, void *arg) { int rc = 0; static struct o2info_fs_features ofs; char *compat = NULL; char *incompat = NULL; char *rocompat = NULL; char *features = NULL; if (om->om_method == O2INFO_USE_IOCTL) rc = get_fs_features_ioctl(op, om->om_fd, &ofs); else rc = o2info_get_fs_features(om->om_fs, &ofs); if (rc) goto out; rc = o2info_get_compat_flag(ofs.compat, &compat); if (rc) goto out; rc = o2info_get_incompat_flag(ofs.incompat, &incompat); if (rc) goto out; rc = o2info_get_rocompat_flag(ofs.rocompat, &rocompat); if (rc) goto out; features = malloc(strlen(compat) + strlen(incompat) + strlen(rocompat) + 3); sprintf(features, "%s %s %s", compat, incompat, rocompat); o2info_print_line("", features, ' '); out: if (compat) ocfs2_free(&compat); if (incompat) ocfs2_free(&incompat); if (rocompat) ocfs2_free(&rocompat); if (features) ocfs2_free(&features); return rc; } DEFINE_O2INFO_OP(fs_features, fs_features_run, NULL); static int get_volinfo_ioctl(struct o2info_operation *op, int fd, struct o2info_volinfo *vf) { int rc = 0, flags = 0; uint32_t unknowns = 0, errors = 0, fills = 0; struct ocfs2_info_blocksize oib; struct ocfs2_info_clustersize oic; struct ocfs2_info_maxslots oim; struct ocfs2_info_label oil; struct ocfs2_info_uuid oiu; uint64_t reqs[5]; struct ocfs2_info info; memset(vf, 0, sizeof(*vf)); if (!cluster_coherent) flags |= OCFS2_INFO_FL_NON_COHERENT; o2info_fill_request((struct ocfs2_info_request *)&oib, sizeof(oib), OCFS2_INFO_BLOCKSIZE, flags); o2info_fill_request((struct ocfs2_info_request *)&oic, sizeof(oic), OCFS2_INFO_CLUSTERSIZE, flags); o2info_fill_request((struct ocfs2_info_request *)&oim, sizeof(oim), OCFS2_INFO_MAXSLOTS, flags); o2info_fill_request((struct ocfs2_info_request *)&oil, sizeof(oil), OCFS2_INFO_LABEL, flags); o2info_fill_request((struct ocfs2_info_request *)&oiu, sizeof(oiu), OCFS2_INFO_UUID, flags); reqs[0] = (unsigned long)&oib; reqs[1] = (unsigned long)&oic; reqs[2] = (unsigned long)&oim; reqs[3] = (unsigned long)&oil; reqs[4] = (unsigned long)&oiu; info.oi_requests = (uint64_t)reqs; info.oi_count = 5; rc = ioctl(fd, OCFS2_IOC_INFO, &info); if (rc) { rc = errno; o2i_error(op, "ioctl failed: %s\n", strerror(rc)); o2i_scan_requests(op, info, &unknowns, &errors, &fills); goto out; } if (oib.ib_req.ir_flags & OCFS2_INFO_FL_FILLED) vf->blocksize = oib.ib_blocksize; if (oic.ic_req.ir_flags & OCFS2_INFO_FL_FILLED) vf->clustersize = oic.ic_clustersize; if (oim.im_req.ir_flags & OCFS2_INFO_FL_FILLED) vf->maxslots = oim.im_max_slots; if (oil.il_req.ir_flags & OCFS2_INFO_FL_FILLED) memcpy(vf->label, oil.il_label, OCFS2_MAX_VOL_LABEL_LEN); if (oiu.iu_req.ir_flags & OCFS2_INFO_FL_FILLED) memcpy(vf->uuid_str, oiu.iu_uuid_str, OCFS2_TEXT_UUID_LEN + 1); rc = get_fs_features_ioctl(op, fd, &(vf->ofs)); out: return rc; } static int volinfo_run(struct o2info_operation *op, struct o2info_method *om, void *arg) { int rc = 0; static struct o2info_volinfo vf; char *compat = NULL; char *incompat = NULL; char *rocompat = NULL; char *features = NULL; #define VOLINFO " Label: %s\n" \ " UUID: %s\n" \ " Block Size: %u\n" \ "Cluster Size: %u\n" \ " Node Slots: %u\n" if (om->om_method == O2INFO_USE_IOCTL) rc = get_volinfo_ioctl(op, om->om_fd, &vf); else rc = o2info_get_volinfo(om->om_fs, &vf); if (rc) goto out; rc = o2info_get_compat_flag(vf.ofs.compat, &compat); if (rc) goto out; rc = o2info_get_incompat_flag(vf.ofs.incompat, &incompat); if (rc) goto out; rc = o2info_get_rocompat_flag(vf.ofs.rocompat, &rocompat); if (rc) goto out; features = malloc(strlen(compat) + strlen(incompat) + strlen(rocompat) + 3); sprintf(features, "%s %s %s", compat, incompat, rocompat); fprintf(stdout, VOLINFO, vf.label, vf.uuid_str, vf.blocksize, vf.clustersize, vf.maxslots); o2info_print_line(" Features: ", features, ' '); out: if (compat) ocfs2_free(&compat); if (incompat) ocfs2_free(&incompat); if (rocompat) ocfs2_free(&rocompat); if (features) ocfs2_free(&features); return rc; } DEFINE_O2INFO_OP(volinfo, volinfo_run, NULL); static int get_mkfs_ioctl(struct o2info_operation *op, int fd, struct o2info_mkfs *oms) { int rc = 0, flags = 0; uint32_t unknowns = 0, errors = 0, fills = 0; struct ocfs2_info_journal_size oij; uint64_t reqs[1]; struct ocfs2_info info; memset(oms, 0, sizeof(*oms)); if (!cluster_coherent) flags |= OCFS2_INFO_FL_NON_COHERENT; o2info_fill_request((struct ocfs2_info_request *)&oij, sizeof(oij), OCFS2_INFO_JOURNAL_SIZE, flags); reqs[0] = (unsigned long)&oij; info.oi_requests = (uint64_t)reqs; info.oi_count = 1; rc = ioctl(fd, OCFS2_IOC_INFO, &info); if (rc) { rc = errno; o2i_error(op, "ioctl failed: %s\n", strerror(rc)); o2i_scan_requests(op, info, &unknowns, &errors, &fills); goto out; } if (oij.ij_req.ir_flags & OCFS2_INFO_FL_FILLED) oms->journal_size = oij.ij_journal_size; rc = get_volinfo_ioctl(op, fd, &(oms->ovf)); out: return rc; } static int o2info_gen_mkfs_string(struct o2info_mkfs oms, char **mkfs) { int rc = 0; char *compat = NULL; char *incompat = NULL; char *rocompat = NULL; char *features = NULL; char *ptr = NULL; char op_fs_features[PATH_MAX]; char op_label[PATH_MAX]; char buf[4096]; #define MKFS "-N %u " \ "-J size=%llu " \ "-b %u " \ "-C %u " \ "%s " \ "%s " rc = o2info_get_compat_flag(oms.ovf.ofs.compat, &compat); if (rc) goto out; rc = o2info_get_incompat_flag(oms.ovf.ofs.incompat, &incompat); if (rc) goto out; rc = o2info_get_rocompat_flag(oms.ovf.ofs.rocompat, &rocompat); if (rc) goto out; features = malloc(strlen(compat) + strlen(incompat) + strlen(rocompat) + 3); sprintf(features, "%s %s %s", compat, incompat, rocompat); ptr = features; while ((ptr = strchr(ptr, ' '))) *ptr = ','; if (strcmp("", features)) snprintf(op_fs_features, PATH_MAX, "--fs-features %s", features); else strcpy(op_fs_features, ""); if (strcmp("", (char *)oms.ovf.label)) snprintf(op_label, PATH_MAX, "-L %s", (char *)(oms.ovf.label)); else strcpy(op_label, ""); snprintf(buf, 4096, MKFS, oms.ovf.maxslots, oms.journal_size, oms.ovf.blocksize, oms.ovf.clustersize, op_fs_features, op_label); *mkfs = strdup(buf); out: if (compat) ocfs2_free(&compat); if (incompat) ocfs2_free(&incompat); if (rocompat) ocfs2_free(&rocompat); if (features) ocfs2_free(&features); return rc; } static int mkfs_run(struct o2info_operation *op, struct o2info_method *om, void *arg) { int rc = 0; static struct o2info_mkfs oms; char *mkfs = NULL; if (om->om_method == O2INFO_USE_IOCTL) rc = get_mkfs_ioctl(op, om->om_fd, &oms); else rc = o2info_get_mkfs(om->om_fs, &oms); if (rc) goto out; o2info_gen_mkfs_string(oms, &mkfs); fprintf(stdout, "%s\n", mkfs); out: if (mkfs) ocfs2_free(&mkfs); return rc; } DEFINE_O2INFO_OP(mkfs, mkfs_run, NULL); static int get_freeinode_ioctl(struct o2info_operation *op, int fd, struct o2info_freeinode *ofi) { uint64_t reqs[1]; int ret = 0, flags = 0; struct ocfs2_info info; struct ocfs2_info_freeinode oifi; uint32_t unknowns = 0, errors = 0, fills = 0; memset(ofi, 0, sizeof(*ofi)); if (!cluster_coherent) flags |= OCFS2_INFO_FL_NON_COHERENT; o2info_fill_request((struct ocfs2_info_request *)&oifi, sizeof(oifi), OCFS2_INFO_FREEINODE, flags); reqs[0] = (unsigned long)&oifi; info.oi_requests = (uint64_t)reqs; info.oi_count = 1; ret = ioctl(fd, OCFS2_IOC_INFO, &info); if (ret) { ret = errno; o2i_error(op, "ioctl failed: %s\n", strerror(ret)); o2i_scan_requests(op, info, &unknowns, &errors, &fills); goto out; } if (oifi.ifi_req.ir_flags & OCFS2_INFO_FL_FILLED) { ofi->slotnum = oifi.ifi_slotnum; memcpy(ofi->fi, oifi.ifi_stat, sizeof(struct o2info_local_freeinode) * OCFS2_MAX_SLOTS); } out: return ret; } static int freeinode_run(struct o2info_operation *op, struct o2info_method *om, void *arg) { int ret = 0, i; struct o2info_freeinode ofi; unsigned long total = 0, free = 0; if (om->om_method == O2INFO_USE_IOCTL) ret = get_freeinode_ioctl(op, om->om_fd, &ofi); else ret = o2info_get_freeinode(om->om_fs, &ofi); if (ret) return ret; fprintf(stdout, "Slot\t\tSpace\t\tFree\n"); for (i = 0; i < ofi.slotnum ; i++) { fprintf(stdout, "%3d\t%13lu\t%12lu\n", i, ofi.fi[i].total, ofi.fi[i].free); total += ofi.fi[i].total; free += ofi.fi[i].free; } fprintf(stdout, "Total\t%13lu\t%12lu\n", total, free); return ret; } DEFINE_O2INFO_OP(freeinode, freeinode_run, NULL); static int ul_log2(unsigned long arg) { unsigned int i = 0; arg >>= 1; while (arg) { i++; arg >>= 1; } return i; } static int o2info_init_freefrag(struct o2info_freefrag *off, struct o2info_volinfo *ovf) { int ret = 0, i; off->clustersize_bits = ul_log2((unsigned long)ovf->clustersize); off->blksize_bits = ul_log2((unsigned long)ovf->blocksize); if (off->chunkbytes) { off->chunkbits = ul_log2(off->chunkbytes); off->clusters_in_chunk = off->chunkbytes >> off->clustersize_bits; } else { off->chunkbits = ul_log2(DEFAULT_CHUNKSIZE); off->clusters_in_chunk = DEFAULT_CHUNKSIZE >> off->clustersize_bits; } off->min = ~0U; off->max = off->avg = 0; off->free_chunks_real = 0; off->free_chunks = 0; for (i = 0; i < OCFS2_INFO_MAX_HIST; i++) { off->histogram.fc_chunks[i] = 0; off->histogram.fc_clusters[i] = 0; } return ret; } static int get_freefrag_ioctl(struct o2info_operation *op, int fd, struct o2info_freefrag *off) { uint64_t reqs[1]; int ret = 0, flags = 0; struct ocfs2_info info; struct ocfs2_info_freefrag oiff; uint32_t unknowns = 0, errors = 0, fills = 0; if (!cluster_coherent) flags |= OCFS2_INFO_FL_NON_COHERENT; o2info_fill_request((struct ocfs2_info_request *)&oiff, sizeof(oiff), OCFS2_INFO_FREEFRAG, flags); oiff.iff_chunksize = off->clusters_in_chunk; reqs[0] = (unsigned long)&oiff; info.oi_requests = (uint64_t)reqs; info.oi_count = 1; ret = ioctl(fd, OCFS2_IOC_INFO, &info); if (ret) { ret = errno; o2i_error(op, "ioctl failed: %s\n", strerror(ret)); o2i_scan_requests(op, info, &unknowns, &errors, &fills); goto out; } if (oiff.iff_req.ir_flags & OCFS2_INFO_FL_FILLED) { off->clusters = oiff.iff_ffs.ffs_clusters; off->free_clusters = oiff.iff_ffs.ffs_free_clusters; off->free_chunks = oiff.iff_ffs.ffs_free_chunks; off->free_chunks_real = oiff.iff_ffs.ffs_free_chunks_real; if (off->free_chunks_real) { off->min = oiff.iff_ffs.ffs_min << (off->clustersize_bits - 10); off->max = oiff.iff_ffs.ffs_max << (off->clustersize_bits - 10); off->avg = oiff.iff_ffs.ffs_avg << (off->clustersize_bits - 10); } else off->min = 0; memcpy(&(off->histogram), &(oiff.iff_ffs.ffs_fc_hist), sizeof(struct free_chunk_histogram)); } off->total_chunks = (off->clusters + off->clusters_in_chunk) >> (off->chunkbits - off->clustersize_bits); out: return ret; } static void o2info_report_freefrag(struct o2info_freefrag *off) { char *unitp = "KMGTPEZY"; char end_str[32]; int i, unit = 10; /* Begin from KB in terms of 10 bits */ unsigned int start = 0, end; fprintf(stdout, "Blocksize: %u bytes\n", 1 << off->blksize_bits); fprintf(stdout, "Clustersize: %u bytes\n", 1 << off->clustersize_bits); fprintf(stdout, "Total clusters: %llu\nFree clusters: %u (%0.1f%%)\n", off->clusters, off->free_clusters, (double)off->free_clusters * 100 / off->clusters); fprintf(stdout, "\nMin. free extent: %u KB \nMax. free extent: %u KB\n" "Avg. free extent: %u KB\n", off->min, off->max, off->avg); if (off->chunkbytes) { fprintf(stdout, "\nChunksize: %lu bytes (%u clusters)\n", off->chunkbytes, off->clusters_in_chunk); fprintf(stdout, "Total chunks: %u\nFree chunks: %u (%0.1f%%)\n", off->total_chunks, off->free_chunks, (double)off->free_chunks * 100 / off->total_chunks); } fprintf(stdout, "\nHISTOGRAM OF FREE EXTENT SIZES:\n"); fprintf(stdout, "%s : %12s %12s %7s\n", "Extent Size Range", "Free extents", "Free Clusters", "Percent"); /* * We probably need to start with 'M' when clustersize = 1M. */ start = 1 << (off->clustersize_bits - unit); if (start == (1 << 10)) { unit += 10; unitp++; } for (i = 0; i < OCFS2_INFO_MAX_HIST; i++) { start = 1 << (i + off->clustersize_bits - unit); end = start << 1; if (off->histogram.fc_chunks[i] != 0) { snprintf(end_str, 32, "%5lu%c-", end, *unitp); if (i == (OCFS2_INFO_MAX_HIST - 1)) strcpy(end_str, "max "); fprintf(stdout, "%5u%c...%7s : " "%12u %12u %6.2f%%\n", start, *unitp, end_str, off->histogram.fc_chunks[i], off->histogram.fc_clusters[i], (double)off->histogram.fc_clusters[i] * 100 / off->free_clusters); } start = end; if (start == (1 << 10)) { unit += 10; unitp++; if (!(*unitp)) break; } } } static int freefrag_run(struct o2info_operation *op, struct o2info_method *om, void *arg) { int ret = 0; static struct o2info_freefrag off; static struct o2info_volinfo ovf; char *end; off.chunkbytes = strtoull((char *)arg, &end, 0); if (*end != '\0') { o2i_error(op, "bad chunk size '%s'\n", (char *)arg); ret = -1; print_usage(ret); } if (off.chunkbytes & (off.chunkbytes - 1)) { o2i_error(op, "chunksize needs to be power of 2\n"); ret = -1; print_usage(ret); } off.chunkbytes *= 1024; if (om->om_method == O2INFO_USE_IOCTL) ret = get_volinfo_ioctl(op, om->om_fd, &ovf); else ret = o2info_get_volinfo(om->om_fs, &ovf); if (ret) return -1; if (off.chunkbytes && (off.chunkbytes < ovf.clustersize)) { o2i_error(op, "chunksize should be greater than or equal to " "filesystem cluster size\n"); ret = -1; print_usage(ret); } ret = o2info_init_freefrag(&off, &ovf); if (ret) return -1; if (om->om_method == O2INFO_USE_IOCTL) ret = get_freefrag_ioctl(op, om->om_fd, &off); else ret = o2info_get_freefrag(om->om_fs, &off); if (ret) return ret; o2info_report_freefrag(&off); return ret; } DEFINE_O2INFO_OP(freefrag, freefrag_run, NULL); static int space_usage_run(struct o2info_operation *op, struct o2info_method *om, void *arg) { int ret = 0, flags = 0; struct stat st; struct o2info_volinfo ovf; struct o2info_fiemap ofp; if (om->om_method == O2INFO_USE_LIBOCFS2) { o2i_error(op, "specify a none-device file to stat\n"); ret = -1; goto out; } ret = lstat(om->om_path, &st); if (ret < 0) { ret = errno; o2i_error(op, "lstat error: %s\n", strerror(ret)); ret = -1; goto out; } memset(&ofp, 0, sizeof(ofp)); ret = get_volinfo_ioctl(op, om->om_fd, &ovf); if (ret) return -1; ofp.blocksize = ovf.blocksize; ofp.clustersize = ovf.clustersize; ret = o2info_get_fiemap(om->om_fd, flags, &ofp); if (ret) goto out; fprintf(stdout, "Blocks: %-10u Shared: %-10u\tUnwritten: %-7u " "Holes: %-6u\n", st.st_blocks, ofp.shared, ofp.unwrittens, ofp.holes); out: return ret; } DEFINE_O2INFO_OP(space_usage, space_usage_run, NULL); static int o2info_report_filestat(struct o2info_method *om, struct stat *st, struct o2info_fiemap *ofp) { int ret = 0; uint16_t perm; char *path = NULL; char *filetype = NULL, *h_perm = NULL; char *uname = NULL, *gname = NULL; char *ah_time = NULL, *ch_time = NULL, *mh_time = NULL; ret = o2info_get_human_path(st->st_mode, om->om_path, &path); if (ret) goto out; ret = o2info_get_filetype(*st, &filetype); if (ret) goto out; ret = o2info_uid2name(st->st_uid, &uname); if (ret) goto out; ret = o2info_gid2name(st->st_gid, &gname); if (ret) goto out; ret = o2info_get_human_permission(st->st_mode, &perm, &h_perm); if (ret) goto out; if (!ofp->blocksize) ofp->blocksize = st->st_blksize; fprintf(stdout, " File: %s\n", path); fprintf(stdout, " Size: %-10lu\tBlocks: %-10u IO Block: %-6u %s\n", st->st_size, st->st_blocks, ofp->blocksize, filetype); if (S_ISBLK(st->st_mode) || S_ISCHR(st->st_mode)) fprintf(stdout, "Device: %xh/%dd\tInode: %-10i Links: %-5u" " Device type: %u,%u\n", st->st_dev, st->st_dev, st->st_ino, st->st_nlink, st->st_dev >> 16UL, st->st_dev & 0x0000FFFF); else fprintf(stdout, "Device: %xh/%dd\tInode: %-10i Links: %u\n", st->st_dev, st->st_dev, st->st_ino, st->st_nlink); fprintf(stdout, " Frag%: %-10.2f\tClusters: %-8u Extents: " "%-6lu Score: %.0f\n", ofp->frag, ofp->clusters, ofp->num_extents, ofp->score); fprintf(stdout, "Shared: %-10u\tUnwritten: %-7u Holes: %-8u " "Xattr: %u\n", ofp->shared, ofp->unwrittens, ofp->holes, ofp->xattr); fprintf(stdout, "Access: (%04o/%10s) Uid: (%5u/%8s) " "Gid: (%5u/%8s)\n", perm, h_perm, st->st_uid, uname, st->st_gid, gname); ret = o2info_get_human_time(&ah_time, o2info_get_stat_atime(st)); if (ret) goto out; ret = o2info_get_human_time(&mh_time, o2info_get_stat_mtime(st)); if (ret) goto out; ret = o2info_get_human_time(&ch_time, o2info_get_stat_ctime(st)); if (ret) goto out; fprintf(stdout, "Access: %s\n", ah_time); fprintf(stdout, "Modify: %s\n", mh_time); fprintf(stdout, "Change: %s\n", ch_time); out: if (path) ocfs2_free(&path); if (filetype) ocfs2_free(&filetype); if (uname) ocfs2_free(&uname); if (gname) ocfs2_free(&gname); if (h_perm) ocfs2_free(&h_perm); if (ah_time) ocfs2_free(&ah_time); if (mh_time) ocfs2_free(&mh_time); if (ch_time) ocfs2_free(&ch_time); return ret; } static int filestat_run(struct o2info_operation *op, struct o2info_method *om, void *arg) { int ret = 0, flags = 0; struct stat st; struct o2info_volinfo ovf; struct o2info_fiemap ofp; if (om->om_method == O2INFO_USE_LIBOCFS2) { o2i_error(op, "specify a none-device file to stat\n"); ret = -1; goto out; } ret = lstat(om->om_path, &st); if (ret < 0) { ret = errno; o2i_error(op, "lstat error: %s\n", strerror(ret)); ret = -1; goto out; } memset(&ofp, 0, sizeof(ofp)); ret = get_volinfo_ioctl(op, om->om_fd, &ovf); if (ret) return -1; ofp.blocksize = ovf.blocksize; ofp.clustersize = ovf.clustersize; ret = o2info_get_fiemap(om->om_fd, flags, &ofp); if (ret) goto out; ret = o2info_report_filestat(om, &st, &ofp); out: return ret; } DEFINE_O2INFO_OP(filestat, filestat_run, NULL); ocfs2-tools-ocfs2-tools-1.8.6/o2info/utils.c000066400000000000000000000217251347147137200205700ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * utils.c * * utility functions for o2info * * Copyright (C) 2010 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License version 2 as published by the Free Software Foundation. * * 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. */ #define _XOPEN_SOURCE 600 #define _LARGEFILE64_SOURCE #define _GNU_SOURCE /* Because libc really doesn't want us using O_DIRECT? */ #include #include #include #include #include "ocfs2/ocfs2.h" #include "tools-internal/verbose.h" #include "utils.h" int o2info_get_compat_flag(uint32_t flag, char **compat) { errcode_t err; char buf[PATH_MAX]; ocfs2_fs_options flags = { .opt_compat = flag, }; *buf = '\0'; err = ocfs2_snprint_feature_flags(buf, PATH_MAX, &flags); if (err) { tcom_err(err, "while processing feature flags"); goto bail; } *compat = strdup(buf); if (!*compat) { errorf("No memory for allocation\n"); err = -1; } bail: return err; } int o2info_get_incompat_flag(uint32_t flag, char **incompat) { errcode_t err; char buf[PATH_MAX]; ocfs2_fs_options flags = { .opt_incompat = flag, }; *buf = '\0'; err = ocfs2_snprint_feature_flags(buf, PATH_MAX, &flags); if (err) { tcom_err(err, "while processing feature flags"); goto bail; } *incompat = strdup(buf); if (!*incompat) { errorf("No memory for allocation\n"); err = -1; } bail: return err; } int o2info_get_rocompat_flag(uint32_t flag, char **rocompat) { errcode_t err; char buf[PATH_MAX]; ocfs2_fs_options flags = { .opt_ro_compat = flag, }; *buf = '\0'; err = ocfs2_snprint_feature_flags(buf, PATH_MAX, &flags); if (err) { tcom_err(err, "while processing feature flags"); goto bail; } *rocompat = strdup(buf); if (!*rocompat) { errorf("No memory for allocation\n"); err = -1; } bail: return err; } errcode_t o2info_open(struct o2info_method *om, int flags) { errcode_t err = 0; int fd, open_flags; ocfs2_filesys *fs = NULL; if (om->om_method == O2INFO_USE_LIBOCFS2) { open_flags = flags|OCFS2_FLAG_HEARTBEAT_DEV_OK|OCFS2_FLAG_RO; err = ocfs2_open(om->om_path, open_flags, 0, 0, &fs); if (err) { tcom_err(err, "while opening device %s", om->om_path); goto out; } om->om_fs = fs; } else { open_flags = flags | O_RDONLY; fd = open(om->om_path, open_flags); if (fd < 0) { err = errno; tcom_err(err, "while opening file %s", om->om_path); goto out; } om->om_fd = fd; } out: return err; } errcode_t o2info_close(struct o2info_method *om) { errcode_t err = 0; int rc = 0; if (om->om_method == O2INFO_USE_LIBOCFS2) { if (om->om_fs) { err = ocfs2_close(om->om_fs); if (err) { tcom_err(err, "while closing device"); goto out; } } } else { if (om->om_fd >= 0) { rc = close(om->om_fd); if (rc < 0) { rc = errno; tcom_err(rc, "while closing fd: %d.\n", om->om_fd); err = rc; } } } out: return err; } int o2info_method(const char *path) { int rc; struct stat st; rc = stat(path, &st); if (rc < 0) { tcom_err(errno, "while stating %s", path); goto out; } rc = O2INFO_USE_IOCTL; if ((S_ISBLK(st.st_mode)) || (S_ISCHR(st.st_mode))) rc = O2INFO_USE_LIBOCFS2; out: return rc; } int o2info_get_filetype(struct stat st, char **filetype) { int rc = 0; if (S_ISREG(st.st_mode)) if (st.st_size != 0) *filetype = strdup("regular file"); else *filetype = strdup("regular empty file"); else if (S_ISDIR(st.st_mode)) *filetype = strdup("directory"); else if (S_ISCHR(st.st_mode)) *filetype = strdup("character special file"); else if (S_ISBLK(st.st_mode)) *filetype = strdup("block special file"); else if (S_ISFIFO(st.st_mode)) *filetype = strdup("FIFO"); else if (S_ISLNK(st.st_mode)) if (st.st_blocks == 0) *filetype = strdup("fast symbolic link"); else *filetype = strdup("symbolic link"); else if (S_ISSOCK(st.st_mode)) *filetype = strdup("socket"); else { *filetype = strdup("unknown file type"); rc = -1; } if (!*filetype) { errorf("No memory for allocation\n"); rc = -1; } return rc; } int o2info_get_human_permission(mode_t st_mode, uint16_t *perm, char **h_perm) { int rc = 0; char tmp[11] = "----------"; *perm = (uint16_t)(st_mode & 0x00000FFF); tmp[10] = '\0'; tmp[9] = (*perm & 0x0001) ? 'x' : '-'; tmp[8] = (*perm & 0x0002) ? 'w' : '-'; tmp[7] = (*perm & 0x0004) ? 'r' : '-'; tmp[6] = (*perm & 0x0008) ? 'x' : '-'; tmp[5] = (*perm & 0x0010) ? 'w' : '-'; tmp[4] = (*perm & 0x0020) ? 'r' : '-'; tmp[3] = (*perm & 0x0040) ? 'x' : '-'; tmp[2] = (*perm & 0x0080) ? 'w' : '-'; tmp[1] = (*perm & 0x0100) ? 'r' : '-'; /* * Handling the setuid/setgid/sticky bits, * by following the convention stat obeys. */ if (*perm & 0x0200) { if (*perm & 0x0001) tmp[9] = 't'; else tmp[9] = 'T'; } if (*perm & 0x0400) { if (*perm & 0x0008) tmp[6] = 's'; else tmp[6] = 'S'; } if (*perm & 0x0800) { if (*perm & 0x0040) tmp[3] = 's'; else tmp[3] = 'S'; } if (S_ISCHR(st_mode)) tmp[0] = 'c'; else if (S_ISBLK(st_mode)) tmp[0] = 'b'; else if (S_ISFIFO(st_mode)) tmp[0] = 'p'; else if (S_ISLNK(st_mode)) tmp[0] = 'l'; else if (S_ISSOCK(st_mode)) tmp[0] = 's'; else if (S_ISDIR(st_mode)) tmp[0] = 'd'; *h_perm = strdup(tmp); if (!*h_perm) { errorf("No memory for allocation\n"); rc = -1; } return rc; } int o2info_uid2name(uid_t uid, char **uname) { struct passwd *entry; int ret = 0; entry = getpwuid(uid); if (!entry) { errorf("user %d does not exist!\n", uid); ret = -1; } else { *uname = strdup(entry->pw_name); if (*uname == NULL) { errorf("No memory for allocation\n"); ret = -1; } } return ret; } int o2info_gid2name(gid_t gid, char **gname) { struct group *group; int ret = 0; group = getgrgid(gid); if (!group) { errorf("group %d does not exist!\n", gid); ret = -1; } else { *gname = strdup(group->gr_name); if (*gname == NULL) { errorf("No memory for allocation\n"); ret = -1; } } return ret; } struct timespec o2info_get_stat_atime(struct stat *st) { #ifdef __USE_MISC return st->st_atim; #else struct timespec t; t.tv_sec = st->st_atime; t.tv_nsec = st->st_atimensec; return t; #endif } struct timespec o2info_get_stat_mtime(struct stat *st) { #ifdef __USE_MISC return st->st_mtim; #else struct timespec t; t.tv_sec = st->st_mtime; t.tv_nsec = st->st_mtimensec; return t; #endif } struct timespec o2info_get_stat_ctime(struct stat *st) { #ifdef __USE_MISC return st->st_ctim; #else struct timespec t; t.tv_sec = st->st_ctime; t.tv_nsec = st->st_ctimensec; return t; #endif } static int *get_prefix(char *str_pattern, int pattern_len) { int i = 1, j = 0; int *prefix = (int *)malloc(pattern_len * sizeof(int)); prefix[0] = 0; while (i < pattern_len) { if (str_pattern[i] == str_pattern[j]) prefix[i] = ++j; else { j = 0; prefix[i] = j; } i++; } return prefix; } static int kmp(const char *str_pattern, int pattern_len, const char *str_target, int target_len, int *prefix) { int i = 0; int j = 0; if (!prefix) return -1; while ((i < pattern_len) && (j < target_len)) { if ((j == 0) || (str_pattern[i] == str_target[j])) { i++; j++; } else j = prefix[j]; } if (prefix) { free(prefix); prefix = NULL; } if (j == target_len) return i - j + 1; else return -1; } static int print_nsec_to_htime(char *htime, unsigned long nsec, const char *stuff) { char *s_nsec; int index = 0, ret = 0; int *prefix = get_prefix(htime, strlen(htime)); s_nsec = (char *)malloc(strlen(stuff) + 1); snprintf(s_nsec, strlen(stuff) + 1, "%lu", nsec); index = kmp(htime, strlen(htime), stuff, strlen(stuff), prefix); if (index < 0) { ret = -1; goto bail; } strncpy(&htime[index], s_nsec, strlen(stuff)); bail: if (s_nsec) free(s_nsec); return ret; } int o2info_get_human_time(char **htime, struct timespec t) { struct tm const *tm = localtime(&t.tv_sec); int ret, size; size = strlen("YYYY-MM-DD HH:MM:SS.NNNNNNNNN +ZZZZ") + 1; *htime = (char *)malloc(size); strftime(*htime, size, "%Y-%m-%d %H:%M:%S.NNNNNNNNN %z", tm); ret = print_nsec_to_htime(*htime, t.tv_nsec, "NNNNNNNNN"); if (ret < 0) { errorf("print n_seconds failed."); return -1; } return 0; } int o2info_get_human_path(mode_t st_mode, const char *path, char **h_path) { int rc; char link[PATH_MAX]; char tmp_path[PATH_MAX * 2 + 4]; if (!S_ISLNK(st_mode)) *h_path = strdup(path); else { rc = readlink(path, link, PATH_MAX); if (rc < 0) { rc = errno; tcom_err(rc, "while readlink %s", path); return -1; } else link[rc] = '\0'; strncpy(tmp_path, path, PATH_MAX); strcat(tmp_path, " -> "); strcat(tmp_path, link); *h_path = strdup(tmp_path); } return 0; } ocfs2-tools-ocfs2-tools-1.8.6/o2info/utils.h000066400000000000000000000030571347147137200205730ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * utils.h * * Common utility function prototypes * * Copyright (C) 2010 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License version 2 as published by the Free Software Foundation. * * 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. */ #ifndef __UTILS_H__ #define __UTILS_H__ #include "o2info.h" int o2info_get_compat_flag(uint32_t flag, char **compat); int o2info_get_incompat_flag(uint32_t flag, char **incompat); int o2info_get_rocompat_flag(uint32_t flag, char **rocompat); int o2info_get_filetype(struct stat st, char **filetype); int o2info_get_human_permission(mode_t st_mode, uint16_t *perm, char **h_perm); int o2info_uid2name(uid_t uid, char **uname); int o2info_gid2name(gid_t gid, char **name); struct timespec o2info_get_stat_atime(struct stat *st); struct timespec o2info_get_stat_ctime(struct stat *st); struct timespec o2info_get_stat_mtime(struct stat *st); int o2info_get_human_time(char **htime, struct timespec t); int o2info_get_human_path(mode_t st_mode, const char *path, char **h_path); int o2info_method(const char *path); errcode_t o2info_open(struct o2info_method *om, int flags); errcode_t o2info_close(struct o2info_method *om); #endif /* __UTILS_H__ */ ocfs2-tools-ocfs2-tools-1.8.6/o2monitor/000077500000000000000000000000001347147137200200115ustar00rootroot00000000000000ocfs2-tools-ocfs2-tools-1.8.6/o2monitor/.gitignore000066400000000000000000000000441347147137200217770ustar00rootroot00000000000000*.sw? *.d o2hbmonitor o2hbmonitor.8 ocfs2-tools-ocfs2-tools-1.8.6/o2monitor/Makefile000066400000000000000000000007241347147137200214540ustar00rootroot00000000000000TOPDIR = .. include $(TOPDIR)/Preamble.make SBIN_EXTRA = o2hbmonitor WARNINGS = -Wall -Wstrict-prototypes -Wno-format -Wmissing-prototypes \ -Wmissing-declarations CFLAGS += $(WARNINGS) INCLUDES = -I$(TOPDIR)/include -I. DEFINES = -DVERSION=\"$(VERSION)\" CFILES = o2hbmonitor.c OBJS = $(subst .c,.o,$(CFILES)) MANS = o2hbmonitor.8 DIST_FILES = $(CFILES) $(HFILES) o2hbmonitor.8.in o2hbmonitor: $(OBJS) $(LINK) include $(TOPDIR)/Postamble.make ocfs2-tools-ocfs2-tools-1.8.6/o2monitor/o2hbmonitor.8.in000066400000000000000000000027021347147137200227520ustar00rootroot00000000000000.TH "o2hbmonitor" "8" "January 2012" "Version @VERSION@" "OCFS2 Manual Pages" .SH "NAME" o2hbmonitor \- Monitors disk heartbeat in the O2CB cluster stack .SH "SYNOPSIS" \fBo2hbmonitor\fR [\fB\-w\fR percent] [\fB\-ivV\fR] .SH "DESCRIPTION" .PP \fBo2hbmonitor\fR is a utility to monitor the disk heartbeat in the \fBo2cb\fR cluster stack. It tracks the time elapsed since the last heartbeat and logs messages once it exceeds the warn threshold. By default, it runs as a daemon and logs messages to the system logger. It can be started at any time and stopped using \fBkill(1)\fR. It does not affect the functioning of the heartbeat thread. It is typically automatically started during cluster online and stopped during cluster offline by the \fIo2cb\fR init script. This utility expects the \fBdebugfs\fR file system to be mounted at \fB/sys/kernel/debug\fR. .SH "OPTIONS" .TP \fB\-w\fR percent Warn threshold percent. It is the percentage of the idle threshold. It defaults to 50%. .TP \fB\-i\fR Interactive mode. It works as a daemon by default. This mode is typically only used for debugging. .TP \fB\-v\fR Verbose mode. It logs messages only to the system logger by default. In this mode it also logs the messages to stdout. .TP \fB\-V\fR Displays version. .SH "NOTES" This utility works with Linux kernel \fB2.6.37\fR and later. .SH "SEE ALSO" .BR o2cb(7) .SH "AUTHORS" Oracle Corporation .SH "COPYRIGHT" Copyright \(co 2010, 2012 Oracle. All rights reserved. ocfs2-tools-ocfs2-tools-1.8.6/o2monitor/o2hbmonitor.c000066400000000000000000000206041347147137200224210ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * o2hbmonitor.c * * Monitors o2hb * * Copyright (C) 2010 Oracle. All rights reserved. * * 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. */ /* * This utility requires the o2hb debugfs file elapsed_time_in_ms which shows * the time since the o2hb heartbeat timer was last armed. This file was added * in the mainline kernel via commit 43695d095dfaf266a8a940d9b07eed7f46076b49. * * This utility scans the configfs to see if the cluster is up. If not up, it * checks again after CONFIG_POLL_IN_SECS. * * If up, it loads the dead threshold and then scans the debugfs file, * elapsed_time_in_ms, of each heartbeat region. If the elapsed time is * greater than the warn threshold, it logs a message in syslog. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define SYS_CONFIG_DIR "/sys/kernel/config" #define O2HB_CLUSTER_DIR SYS_CONFIG_DIR"/cluster" #define O2HB_HEARTBEAT_DIR O2HB_CLUSTER_DIR"/%s/heartbeat" #define O2HB_DEAD_THRESHOLD O2HB_HEARTBEAT_DIR"/dead_threshold" #define O2HB_DEVICE O2HB_HEARTBEAT_DIR"/%s/dev" #define SYS_DEBUG_DIR "/sys/kernel/debug" #define O2HB_DEBUG_DIR SYS_DEBUG_DIR"/o2hb" #define O2HB_ELAPSED_TIME O2HB_DEBUG_DIR"/%s/elapsed_time_in_ms" #define DEAD_THRESHOLD_IN_MSECS(a) (((a) - 1) * 2000) #define WARN_THRESHOLD_PERCENT 50 #define CONFIG_POLL_IN_SECS 60 #define SLOW_POLL_IN_SECS 10 #define FAST_POLL_IN_SECS 2 #define O2HB_SEM_MAGIC_KEY 0x6F326862 char *progname; int interactive; int warn_threshold_percent; int verbose; char *cluster_name; unsigned long dead_threshold_in_ms; unsigned long warn_threshold_in_ms; unsigned long poll_in_secs; static void show_version(void) { fprintf(stderr, "%s %s\n", progname, VERSION); } static char *do_strchomp(char *str) { int len = strlen(str); char *p; if (!len) return str; p = str + len - 1; while ((len--) && (isspace(*p) || (*p == '\n'))) *p-- = '\0'; return str; } static int get_value(char *path, char *value, int count) { int fd = -1, ret = -1; char *p = value; fd = open(path, O_RDONLY); if (fd > 0) ret = read(fd, value, count); if (ret > 0) { p += ret; *p = '\0'; ret = 0; } if (!ret) do_strchomp(value); if (fd > -1) close(fd); return ret; } static void get_device_name(char *region, char **device) { int ret; char val[255]; char path[PATH_MAX]; sprintf(path, O2HB_DEVICE, cluster_name, region); ret = get_value(path, val, sizeof(val)); if (ret) goto bail; *device = strdup(val); bail: return ; } static void process_elapsed_time(char *region, unsigned long elapsed) { int warn = 0; char *device = NULL; if (elapsed >= warn_threshold_in_ms) warn++; if (!verbose && !warn) return; get_device_name(region, &device); if (verbose) fprintf(stdout, "Last ping %lu msecs ago on /dev/%s, %s\n", elapsed, device, region); if (warn) { poll_in_secs = FAST_POLL_IN_SECS; syslog(LOG_WARNING, "Last ping %lu msecs ago on /dev/%s, %s\n", elapsed, device, region); } if (device) free(device); } static int read_elapsed_time(char *region, unsigned long *elapsed) { int ret; char val[32]; char path[PATH_MAX]; *elapsed = 0; sprintf(path, O2HB_ELAPSED_TIME, region); ret = get_value(path, val, sizeof(val)); if (ret) goto bail; *elapsed = strtoul(val, NULL, 0); ret = 0; bail: return ret; } static void scan_heartbeat_regions(void) { int ret = -1; DIR *dir = NULL; struct dirent *ent; char path[PATH_MAX]; unsigned long elapsed; sprintf(path, O2HB_DEBUG_DIR); dir = opendir(path); if (!dir) return; do { ent = readdir(dir); if (ent && ent->d_type == DT_DIR && strcmp(ent->d_name, ".") && strcmp(ent->d_name, "..")) { ret = read_elapsed_time(ent->d_name, &elapsed); if (!ret) process_elapsed_time(ent->d_name, elapsed); } } while (ent); if (dir) closedir(dir); } static int populate_thresholds(void) { int ret; char val[32]; char path[PATH_MAX]; sprintf(path, O2HB_DEAD_THRESHOLD, cluster_name); ret = get_value(path, val, sizeof(val)); if (!ret) { dead_threshold_in_ms = DEAD_THRESHOLD_IN_MSECS(strtoul(val, NULL, 0)); warn_threshold_in_ms = (dead_threshold_in_ms * warn_threshold_percent / 100); } return ret; } static int populate_cluster(void) { DIR *dir; struct dirent *ent; if (cluster_name) { free(cluster_name); cluster_name = NULL; } dir = opendir(O2HB_CLUSTER_DIR); if (!dir) return -1; do { ent = readdir(dir); if (ent && ent->d_type == 4 && strcmp(ent->d_name, ".") && strcmp(ent->d_name, "..")) { cluster_name = strdup(ent->d_name); break; } } while (ent); closedir(dir); if (cluster_name) return 0; return -1; } static int is_cluster_up(void) { struct stat buf; int status; static int warn_count = 0; status = stat(O2HB_CLUSTER_DIR, &buf); if (status) return 0; status = stat(O2HB_DEBUG_DIR, &buf); if (status) { if (!(warn_count++ % 10)) syslog(LOG_WARNING, "mount debugfs at /sys/kernel/debug"); return 0; } return 1; } static void monitor(void) { int ret; while (1) { if (!is_cluster_up()) { sleep(CONFIG_POLL_IN_SECS); continue; } ret = populate_cluster(); if (!ret) ret = populate_thresholds(); if (ret) { sleep(CONFIG_POLL_IN_SECS); continue; } poll_in_secs = SLOW_POLL_IN_SECS; scan_heartbeat_regions(); sleep(poll_in_secs); } } static int islocked(void) { int semid; struct sembuf trylock[1] = { {.sem_num = 0, .sem_op = 0, .sem_flg = SEM_UNDO|IPC_NOWAIT}, }; semid = semget(O2HB_SEM_MAGIC_KEY, 1, 0); if (semid < 0) return 0; if (semop(semid, trylock, 1) < 0) return 1; return 0; } static int getlock(void) { int semid, vals[1] = { 0 }; struct sembuf trylock[2] = { {.sem_num = 0, .sem_op = 0, .sem_flg = SEM_UNDO|IPC_NOWAIT}, {.sem_num = 0, .sem_op = 1, .sem_flg = SEM_UNDO|IPC_NOWAIT}, }; semid = semget(O2HB_SEM_MAGIC_KEY, 1, 0); if (semid < 0) { semid = semget(O2HB_SEM_MAGIC_KEY, 1, IPC_CREAT|IPC_EXCL|S_IRUSR); if (semid < 0) goto out; semctl(semid, 0, SETALL, vals); if (semop(semid, trylock, 2) < 0) goto out; else return 0; } if (semop(semid, trylock, 2) < 0) goto out; return 0; out: if (errno == EAGAIN) { syslog(LOG_WARNING, "Another instance of %s is already running." " Aborting.\n", progname); return 1; } return 0; } static void usage(void) { fprintf(stderr, "usage: %s [-w percent] -[ivV]\n", progname); fprintf(stderr, "\t -w, Warn threshold percent (default 50%%)\n"); fprintf(stderr, "\t -i, Interactive\n"); fprintf(stderr, "\t -v, Verbose\n"); fprintf(stderr, "\t -V, Version\n"); exit(1); } int main(int argc, char **argv) { int c, ret, version = 0; /* init globals */ progname = basename(argv[0]); interactive = 0; warn_threshold_percent = WARN_THRESHOLD_PERCENT; verbose = 0; cluster_name = NULL; while (1) { c = getopt(argc, argv, "w:i?hvV"); if (c == -1) break; switch (c) { case 'i': interactive = 1; break; case 'v': ++verbose; break; case 'w': warn_threshold_percent = strtoul(optarg, NULL, 0); if (warn_threshold_percent < 1 || warn_threshold_percent > 99) warn_threshold_percent = WARN_THRESHOLD_PERCENT; break; case 'V': version = 1; break; case '?': case 'h': default: usage(); break; } } if (version) show_version(); if (islocked()) { fprintf(stderr, "Another instance of %s is already running. " "Aborting.\n", progname); return 1; } if (!interactive) { ret = daemon(0, verbose); if (ret) fprintf(stderr, "Unable to daemonize, %s\n", strerror(errno)); } openlog(progname, LOG_CONS|LOG_NDELAY, LOG_DAEMON); ret = getlock(); if (ret) { closelog(); return ret; } syslog(LOG_INFO, "Starting\n"); monitor(); closelog(); return 0; } ocfs2-tools-ocfs2-tools-1.8.6/ocfs2.pc.in000066400000000000000000000003541347147137200200300ustar00rootroot00000000000000prefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=@includedir@ Name: ocfs2 Description: Userspace ocfs2 library Version: @VERSION@ Requires: o2dlm o2cb com_err Libs: -L${libdir} -locfs2 -laio Cflags: -I${includedir} ocfs2-tools-ocfs2-tools-1.8.6/ocfs2_controld/000077500000000000000000000000001347147137200210015ustar00rootroot00000000000000ocfs2-tools-ocfs2-tools-1.8.6/ocfs2_controld/.gitignore000066400000000000000000000000761347147137200227740ustar00rootroot00000000000000*.sw? *.d test_client ocfs2_controld.cman ocfs2_controld.pcmk ocfs2-tools-ocfs2-tools-1.8.6/ocfs2_controld/Makefile000066400000000000000000000043061347147137200224440ustar00rootroot00000000000000TOPDIR = .. include $(TOPDIR)/Preamble.make sbindir = $(root_sbindir) ifneq ($(BUILD_OCFS2_CONTROLD),) ifneq ($(BUILD_CMAN_SUPPORT),) SBIN_PROGRAMS += ocfs2_controld.cman endif UNINST_PROGRAMS = test_client endif ifneq ($(BUILD_PCMK_SUPPORT),) SBIN_PROGRAMS += ocfs2_controld.pcmk # Some pacemaker headers which pacemaker.c includes want this. PCMK_INCLUDES = -I/usr/include/pacemaker -I/usr/include/heartbeat/ $(GLIB_CFLAGS) endif INCLUDES = -I$(TOPDIR)/include -I. $(PCMK_INCLUDES) LIBO2CB_LIBS = -L$(TOPDIR)/libo2cb -lo2cb -ldlm_lt LIBO2CB_DEPS = $(TOPDIR)/libo2cb/libo2cb.a LIBOCFS2_LIBS = -L$(TOPDIR)/libocfs2 -locfs2 LIBOCFS2_DEPS = $(TOPDIR)/libocfs2/libocfs2.a COROSYNC_LIBS = $(CPG_LDFLAGS) -lcpg OPENAIS_LIBS = $(AIS_LDFLAGS) -lSaCkpt DLMCONTROL_LIBS = -ldlmcontrol CRM_LIBS = -lcrmcluster -lstonithd -lcrmcommon ifdef OCFS2_DEBUG OPTS += -ggdb else OPTS += -O2 endif CFLAGS := $(OPTS) -Wall -Wstrict-prototypes -Wmissing-prototypes \ -Wmissing-declarations DEFINES = -DOCFS2_FLAT_INCLUDES -DO2DLM_FLAT_INCLUDES \ -DO2CB_FLAT_INCLUDES -DVERSION=\"$(VERSION)\" UNINST_HFILES = ocfs2_controld.h ifneq ($(HAVE_COROSYNC),) DEFINES += -DHAVE_COROSYNC=1 endif DAEMON_CFILES = main.c cpg.c mount.c ckpt.c dlmcontrol.c CMAN_CFILES = cman.c CMAN_DAEMON_CFILES = $(DAEMON_CFILES) $(CMAN_CFILES) CMAN_DAEMON_OBJS = $(subst .c,.o,$(CMAN_DAEMON_CFILES)) PCMK_CFILES = pacemaker.c PCMK_DAEMON_CFILES = $(DAEMON_CFILES) $(PCMK_CFILES) PCMK_DAEMON_OBJS = $(subst .c,.o,$(PCMK_DAEMON_CFILES)) TEST_CFILES = test_client.c TEST_OBJS = $(subst .c,.o,$(TEST_CFILES) $(PROTO_CFILES)) MANS = DIST_FILES = \ $(DAEMON_CFILES) \ $(PCMK_CFILES) \ $(CMAN_CFILES) \ $(TEST_CFILES) \ $(UNINST_HFILES) \ $(addsuffix .in,$(MANS)) ocfs2_controld.pcmk: $(PCMK_DAEMON_OBJS) $(LIBO2CB_DEPS) $(LINK) $(GLIB_LIBS) $(LIBO2CB_LIBS) $(COM_ERR_LIBS) \ $(OPENAIS_LIBS) $(COROSYNC_LIBS) \ $(DLMCONTROL_LIBS) $(CRM_LIBS) ocfs2_controld.cman: $(CMAN_DAEMON_OBJS) $(LIBO2CB_DEPS) $(LINK) $(LIBO2CB_LIBS) $(COM_ERR_LIBS) $(OPENAIS_LIBS) \ $(COROSYNC_LIBS) $(DLMCONTROL_LIBS) -lcman test_client: $(TEST_OBJS) $(LIBO2CB_DEPS) $(LIBOCFS2_DEPS) $(LINK) $(LIBOCFS2_LIBS) $(LIBO2CB_LIBS) $(COM_ERR_LIBS) include $(TOPDIR)/Postamble.make ocfs2-tools-ocfs2-tools-1.8.6/ocfs2_controld/ckpt.c000066400000000000000000000425401347147137200221130ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * Copyright (C) 2008 Oracle. All rights reserved. * * This copyrighted material is made available to anyone wishing to use, * modify, copy, or redistribute it subject to the terms and conditions * of the GNU General Public License v.2. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ocfs2_controld.h" struct ckpt_handle { SaNameT ch_name; SaCkptCheckpointHandleT ch_handle; }; static SaCkptHandleT daemon_handle; struct ckpt_handle *global_handle; /* This is the version OpenAIS supports */ static SaVersionT version = { 'B', 1, 1 }; static SaCkptCallbacksT callbacks = { NULL, NULL, }; /* * All of our checkpoints store 4K of data in 32 sections of 128bytes. We * probably won't actually use more than one section of each checkpoint, * but we spec them larger so that we can use space later compatibly. Note * that data space is only allocated when needed, so if we store one section * of 10 bytes, the checkpoint uses 10 bytes, not 4K. * * Retention time is 0 - when a daemon exits, it should disappear. * * Max section ID size is basically big enough to hold a uuid (32 * characters) plus something extra. We don't use uuids in section names * yet, but just in case. */ #define CKPT_MAX_SECTION_SIZE 128 #define CKPT_MAX_SECTIONS 32 #define CKPT_MAX_SECTION_ID 40 static SaCkptCheckpointCreationAttributesT ckpt_attributes = { .creationFlags = SA_CKPT_WR_ALL_REPLICAS, .checkpointSize = 4096, .retentionDuration = 0LL, .maxSections = CKPT_MAX_SECTIONS, .maxSectionSize = CKPT_MAX_SECTION_SIZE, .maxSectionIdSize = CKPT_MAX_SECTION_ID, }; static void ais_err_to_errno(SaAisErrorT error, int *rc, char **reason) { switch (error) { case SA_AIS_OK: *rc = 0; *reason = "Success"; break; case SA_AIS_ERR_LIBRARY: *rc = -ENXIO; *reason = "Internal library error"; break; case SA_AIS_ERR_TIMEOUT: *rc = -ETIMEDOUT; *reason = "Timed out"; break; case SA_AIS_ERR_TRY_AGAIN: *rc = -EAGAIN; *reason = "Try again"; break; case SA_AIS_ERR_INVALID_PARAM: *rc = -EINVAL; *reason = "Invalid parameter"; break; case SA_AIS_ERR_NO_MEMORY: *rc = -ENOMEM; *reason = "Out of memory"; break; case SA_AIS_ERR_NO_RESOURCES: *rc = -EBUSY; *reason = "Insufficient resources"; break; case SA_AIS_ERR_VERSION: *rc = -EPROTOTYPE; *reason = "Protocol not compatible"; break; case SA_AIS_ERR_BAD_HANDLE: *rc = -EINVAL; *reason = "Bad Ckpt handle"; break; case SA_AIS_ERR_INIT: *rc = -ENODEV; *reason = "Initialization not complete"; break; case SA_AIS_ERR_NOT_EXIST: *rc = -ENOENT; *reason = "Object does not exist"; break; case SA_AIS_ERR_EXIST: *rc = -EEXIST; *reason = "Object already exists"; break; case SA_AIS_ERR_BAD_FLAGS: *rc = -EINVAL; *reason = "Invalid flags"; break; case SA_AIS_ERR_ACCESS: *rc = -EACCES; *reason = "Permission denied"; break; default: *rc = -ENOSYS; *reason = "Unknown error"; log_error("Unknown error seen! (%d)", error); break; } } /* * Our retention-time scheme of 0 means that we need to create any * checkpoint we want to update. Nobody is writing to the same checkpoint * at the same time. */ static int call_ckpt_open(struct ckpt_handle *handle, int write) { int rc, existcount = 0, againcount = 0; char *reason; SaAisErrorT error; int flags = SA_CKPT_CHECKPOINT_READ; if (write) flags |= (SA_CKPT_CHECKPOINT_WRITE | SA_CKPT_CHECKPOINT_CREATE); while (1) { log_debug("Opening checkpoint \"%.*s\" (try %d)", handle->ch_name.length, handle->ch_name.value, existcount + againcount + 1); error = saCkptCheckpointOpen(daemon_handle, &handle->ch_name, write ? &ckpt_attributes : NULL, flags, 0, &handle->ch_handle); ais_err_to_errno(error, &rc, &reason); if (!rc) { log_debug("Opened checkpoint \"%.*s\" with handle 0x%llx", handle->ch_name.length, handle->ch_name.value, (unsigned long long)handle->ch_handle); break; } if ((rc != -EAGAIN) && (!write || (rc != -EEXIST))){ log_error("Unable to open checkpoint \"%.*s\": %s", handle->ch_name.length, handle->ch_name.value, reason); break; } if (write && (rc == -EEXIST)) { /* * EEXIST means one of two things: * * 1) Another daemon is up and running. This * one is just going to sit here printing to * the log until it's killed or the other one * dies. This will confuse people; they'll * stop the running daemon, but not be able to * unload the stack. We have to do this because * of reason (2). * * 2) The daemon was stopped and then immediately * restarted. AIS cleans up the checkpoint * in a lazy fashion, so there is no guarantee * the checkpoint is gone by the time the new * daemon starts up. So we can get an EEXIST * for a little while until AIS gets around to * the cleanup. Because scheduling, etc, can * take a while, we don't know how long that * will be. So we keep retrying. Eventually, * AIS will clean up the checkpoint from the * daemon that exited and let us create our new * one. */ retry_warning(existcount, "Checkpoint exists seen %d times " "while opening checkpoint \"%.*s\", " "still trying", existcount, handle->ch_name.length, handle->ch_name.value); } else if (rc == -EAGAIN) { /* TRY_AGAIN is Ckpt saying it's just busy. */ retry_warning(againcount, "TRY_AGAIN seen %d times while " "opening checkpoint \"%.*s\", " "still trying", againcount, handle->ch_name.length, handle->ch_name.value); } else assert(0); sleep_ms(10); } return rc; } static void call_ckpt_close(struct ckpt_handle *handle) { int rc, againcount = 0; char *reason; SaAisErrorT error; while (1) { log_debug("Closing checkpoint \"%.*s\" (try %d)", handle->ch_name.length, handle->ch_name.value, againcount + 1); error = saCkptCheckpointClose(handle->ch_handle); ais_err_to_errno(error, &rc, &reason); if (!rc) { log_debug("Closed checkpoint \"%.*s\"", handle->ch_name.length, handle->ch_name.value); break; } if (rc != -EAGAIN) { log_error("Unable to close checkpoint \"%.*s\": %s", handle->ch_name.length, handle->ch_name.value, reason); break; } retry_warning(againcount, "TRY_AGAIN seen %d times while " "closing checkpoint \"%.*s\", " "still trying", againcount, handle->ch_name.length, handle->ch_name.value); sleep_ms(10); } } /* * All of our sections live for the life of the checkpoint. We don't need * to delete them. */ static int call_section_create(struct ckpt_handle *handle, const char *name, const char *data, size_t data_len) { int rc, againcount = 0; char *reason; SaAisErrorT error; SaCkptSectionIdT id = { .idLen = strlen(name), .id = (SaUint8T *)name, }; SaCkptSectionCreationAttributesT attrs = { .sectionId = &id, .expirationTime = SA_TIME_END, }; while (1) { log_debug("Creating section \"%s\" on checkpoint " "\"%.*s\" (try %d)", name, handle->ch_name.length, handle->ch_name.value, againcount + 1); error = saCkptSectionCreate(handle->ch_handle, &attrs, data, data_len); ais_err_to_errno(error, &rc, &reason); if (!rc) { log_debug("Created section \"%s\" on checkpoint " "\"%.*s\"", name, handle->ch_name.length, handle->ch_name.value); break; } if (rc != -EAGAIN) { log_error("Unable to create section \"%s\" on " "checkpoint \"%.*s\": %s", name, handle->ch_name.length, handle->ch_name.value, reason); break; } retry_warning(againcount, "TRY_AGAIN seen %d times while " "creating section \"%s\" on checkpoint " "\"%.*s\", still trying", againcount, name, handle->ch_name.length, handle->ch_name.value); sleep_ms(10); } return rc; } static int call_section_write(struct ckpt_handle *handle, const char *name, const char *data, size_t data_len) { int rc, againcount = 0; char *reason; SaAisErrorT error; SaCkptSectionIdT id = { .idLen = strlen(name), .id = (SaUint8T *)name, }; while (1) { log_debug("Writing to section \"%s\" on checkpoint " "\"%.*s\" (try %d)", name, handle->ch_name.length, handle->ch_name.value, againcount + 1); error = saCkptSectionOverwrite(handle->ch_handle, &id, data, data_len); ais_err_to_errno(error, &rc, &reason); if (!rc) { log_debug("Stored section \"%s\" on checkpoint " "\"%.*s\"", name, handle->ch_name.length, handle->ch_name.value); break; } /* If it doesn't exist, create it. */ if (rc == -ENOENT) { rc = call_section_create(handle, name, data, data_len); break; } if (rc != -EAGAIN) { log_error("Unable to write section \"%s\" on " "checkpoint \"%.*s\": %s", name, handle->ch_name.length, handle->ch_name.value, reason); break; } retry_warning(againcount, "TRY_AGAIN seen %d times while " "writing section \"%s\" on checkpoint " "\"%.*s\", still trying", againcount, name, handle->ch_name.length, handle->ch_name.value); sleep_ms(10); } return rc; } static int call_section_read(struct ckpt_handle *handle, const char *name, char **data, size_t *data_len) { int rc, againcount = 0; char *reason, *p; char readbuf[CKPT_MAX_SECTION_SIZE]; SaAisErrorT error; SaCkptIOVectorElementT readvec[] = { { .sectionId = { .idLen = strlen(name), .id = (SaUint8T *)name, }, .dataBuffer = readbuf, .dataSize = CKPT_MAX_SECTION_SIZE, } }; while (1) { log_debug("Reading from section \"%s\" on checkpoint " "\"%.*s\" (try %d)", name, handle->ch_name.length, handle->ch_name.value, againcount + 1); error = saCkptCheckpointRead(handle->ch_handle, readvec, 1, NULL); ais_err_to_errno(error, &rc, &reason); if (!rc) { log_debug("Read section \"%s\" from checkpoint " "\"%.*s\"", name, handle->ch_name.length, handle->ch_name.value); break; } /* -ENOENT is a clean error for the caller to handle */ if (rc == -ENOENT) { log_debug("Checkpoint \"%.*s\" does not have a " "section named \"%s\"", handle->ch_name.length, handle->ch_name.value, name); break; } if (rc != -EAGAIN) { log_error("Unable to read section \"%s\" from " "checkpoint \"%.*s\": %s", name, handle->ch_name.length, handle->ch_name.value, reason); break; } retry_warning(againcount, "TRY_AGAIN seen %d times while " "reading section \"%s\" on checkpoint " "\"%.*s\", still trying", againcount, name, handle->ch_name.length, handle->ch_name.value); sleep_ms(10); } if (rc) goto out; p = malloc(sizeof(char) * readvec[0].readSize); if (p) { memcpy(p, readbuf, readvec[0].readSize); *data = p; *data_len = readvec[0].readSize; } else { log_error("Unable to allocate memory while reading section " "\"%s\" from checkpoint \"%.*s\"", name, handle->ch_name.length, handle->ch_name.value); rc = -ENOMEM; goto out; } out: return rc; } int ckpt_section_store(struct ckpt_handle *handle, const char *section, const char *data, size_t data_len) { if (strlen(section) > CKPT_MAX_SECTION_ID) { log_error("Error: section id \"%s\" is too long " "(max is %d)", section, CKPT_MAX_SECTION_ID); return -EINVAL; } if (data_len > CKPT_MAX_SECTION_SIZE) { log_error("Error: attempt to store %lu bytes in a section " "(max is %d)", data_len, CKPT_MAX_SECTION_SIZE); return -EINVAL; } return call_section_write(handle, section, data, data_len); } int ckpt_global_store(const char *section, const char *data, size_t data_len) { if (!global_handle) { log_error("Error: The global checkpoint is not initialized"); return -EINVAL; } return ckpt_section_store(global_handle, section, data, data_len); } int ckpt_section_get(struct ckpt_handle *handle, const char *section, char **data, size_t *data_len) { if (strlen(section) > CKPT_MAX_SECTION_ID) { log_error("Error: section id \"%s\" is too long " "(max is %d)", section, CKPT_MAX_SECTION_ID); return -EINVAL; } return call_section_read(handle, section, data, data_len); } int ckpt_global_get(const char *section, char **data, size_t *data_len) { if (!global_handle) { log_error("Error: The global checkpoint is not initialized"); return -EINVAL; } return call_section_read(global_handle, section, data, data_len); } /* * We name our ckeckpoints in one of three ways, all prefixed with 'ocfs2:'. * * The global checkpoint is named 'ocfs2:controld'. * The node info checkpoint is named 'ocfs2:controld:<8-hex-char-nodeid>' * A mount checkpoint is named 'ocfs2::<8-hex-char-nodeid>' */ #define CKPT_PREFIX "ocfs2:" static int ckpt_new(const char *name, int write, struct ckpt_handle **handle) { int rc; size_t namelen = strlen(name) + strlen(CKPT_PREFIX); struct ckpt_handle *h; if (namelen > SA_MAX_NAME_LENGTH) { log_error("Checkpoint name \"%s\" too long", name); return -EINVAL; } h = malloc(sizeof(struct ckpt_handle)); if (!h) { log_error("Unable to allocate checkpoint handle"); return -ENOMEM; } memset(h, 0, sizeof(struct ckpt_handle)); h->ch_name.length = snprintf((char *)(h->ch_name.value), SA_MAX_NAME_LENGTH, "%s%s", CKPT_PREFIX, name); rc = call_ckpt_open(h, write); if (!rc) *handle = h; else free(h); return rc; } static void ckpt_free(struct ckpt_handle *handle) { if (handle->ch_handle) call_ckpt_close(handle); free(handle); } int ckpt_open_global(int write) { if (global_handle) return 0; return ckpt_new("controld", write, &global_handle); } void ckpt_close_global(void) { if (global_handle) { ckpt_free(global_handle); global_handle = NULL; } } int ckpt_open_node(int nodeid, struct ckpt_handle **handle) { char name[SA_MAX_NAME_LENGTH]; snprintf(name, SA_MAX_NAME_LENGTH, "controld:%08x", nodeid); return ckpt_new(name, 0, handle); } int ckpt_open_this_node(struct ckpt_handle **handle) { char name[SA_MAX_NAME_LENGTH]; snprintf(name, SA_MAX_NAME_LENGTH, "controld:%08x", our_nodeid); return ckpt_new(name, 1, handle); } void ckpt_close(struct ckpt_handle *handle) { ckpt_free(handle); } int setup_ckpt(void) { int rc, againcount = 0; char *reason; SaAisErrorT error; while (1) { log_debug("Initializing CKPT service (try %d)", againcount + 1); error = saCkptInitialize(&daemon_handle, &callbacks, &version); ais_err_to_errno(error, &rc, &reason); if (!rc) { log_debug("Connected to CKPT service with handle 0x%llx", (unsigned long long)daemon_handle); break; } if (rc != -EAGAIN) { log_error("Unable to connect to CKPT: %s", reason); break; } retry_warning(againcount, "TRY_AGAIN seen %d times while " "connectiong to CKPT, still trying", againcount); sleep_ms(10); } return rc; } void exit_ckpt(void) { int rc, againcount = 0; char *reason; SaAisErrorT error; if (!daemon_handle) return; while (1) { log_debug("Disconnecting from CKPT service (try %d)", againcount + 1); error = saCkptFinalize(daemon_handle); ais_err_to_errno(error, &rc, &reason); if (!rc) { log_debug("Disconnected from CKPT service"); break; } if (rc != -EAGAIN) { log_error("Unable to disconnect from CKPT: %s", reason); break; } retry_warning(againcount, "TRY_AGAIN seen %d times while " "disconnecting to CKPT, still trying", againcount); sleep_ms(10); } } #ifdef DEBUG_EXE int dump_point, dump_wrap, daemon_debug_opt = 1; char daemon_debug_buf[1024]; char dump_buf[DUMP_SIZE]; void daemon_dump_save(void) { int len, i; len = strlen(daemon_debug_buf); for (i = 0; i < len; i++) { dump_buf[dump_point++] = daemon_debug_buf[i]; if (dump_point == DUMP_SIZE) { dump_point = 0; dump_wrap = 1; } } } int our_nodeid = 2; int main(int argc, char *argv[]) { int rc; char *buf; size_t buflen; struct ckpt_handle *h; rc = setup_ckpt(); if (rc) goto out; rc = ckpt_open_global(1); if (rc) goto out_exit; rc = ckpt_global_store("version", "1.0", strlen("1.0")); if (!rc) { rc = ckpt_global_get("foo", &buf, &buflen); if (rc != -ENOENT) { log_error("read should not have found anything"); rc = -EIO; } else rc = 0; } ckpt_close_global(); if (rc) goto out_exit; rc = ckpt_open_this_node(&h); if (rc) goto out_exit; rc = ckpt_section_store(h, "foo", "bar", strlen("bar")); if (!rc) { rc = ckpt_section_get(h, "foo", &buf, &buflen); if (!rc) { if ((buflen != strlen("bar")) || memcmp(buf, "bar", strlen("bar"))) { log_error("read returned bad value"); rc = -EIO; } free(buf); } } ckpt_close(h); if (rc) goto out_exit; rc = ckpt_open_node(4, &h); if (rc) goto out_exit; ckpt_close(h); out_exit: exit_ckpt(); out: return rc; } #endif /* DEBUG_EXE */ ocfs2-tools-ocfs2-tools-1.8.6/ocfs2_controld/cman.c000066400000000000000000000141371347147137200220710ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: */ /****************************************************************************** ******************************************************************************* ** ** Copyright (C) 2005 Red Hat, Inc. All rights reserved. ** ** This copyrighted material is made available to anyone wishing to use, ** modify, copy, or redistribute it subject to the terms and conditions ** of the GNU General Public License v.2. ** ******************************************************************************* ******************************************************************************/ /* * Copyright (C) 2007 Oracle. All rights reserved. * * This copyrighted material is made available to anyone wishing to use, * modify, copy, or redistribute it subject to the terms and conditions * of the GNU General Public License v.2. */ #include #include #include #include #include #include #include #include #include "ocfs2-kernel/kernel-list.h" #include "o2cb/o2cb.h" #include "ocfs2_controld.h" int our_nodeid; static int cman_ci; static char * clustername; static cman_cluster_t cluster; static cman_handle_t ch; static cman_handle_t ch_admin; extern struct list_head mounts; static cman_node_t old_nodes[O2NM_MAX_NODES]; static int old_node_count; static cman_node_t cman_nodes[O2NM_MAX_NODES]; static int cman_node_count; const char *stackname = "cman"; int kill_stack_node(int nodeid) { int error; log_debug("killing node %d", nodeid); error = cman_kill_node(ch_admin, nodeid); if (error) log_debug("Unable to kill node %d, %d %d", nodeid, error, errno); return error; } static int is_member(cman_node_t *node_list, int count, int nodeid) { int i; for (i = 0; i < count; i++) { if (node_list[i].cn_nodeid == nodeid) return node_list[i].cn_member; } return 0; } static int is_old_member(int nodeid) { return is_member(old_nodes, old_node_count, nodeid); } static int is_cman_member(int nodeid) { return is_member(cman_nodes, cman_node_count, nodeid); } static cman_node_t *find_cman_node(int nodeid) { int i; for (i = 0; i < cman_node_count; i++) { if (cman_nodes[i].cn_nodeid == nodeid) return &cman_nodes[i]; } return NULL; } char *nodeid2name(int nodeid) { cman_node_t *cn; cn = find_cman_node(nodeid); if (!cn) return NULL; return cn->cn_name; } int validate_cluster(const char *cluster) { if (!clustername) { log_error("Trying to validate before cman is alive"); return 0; } if (!cluster) return 0; return !strcmp(cluster, clustername); } int get_clustername(const char **cluster) { if (!clustername) { log_error("Trying to validate before cman is alive"); return -EIO; } if (!cluster) { log_error("NULL passed!"); return -EINVAL; } *cluster = clustername; return 0; } /* keep track of the nodes */ static void statechange(void) { int i, rv; old_node_count = cman_node_count; memcpy(&old_nodes, &cman_nodes, sizeof(old_nodes)); cman_node_count = 0; memset(&cman_nodes, 0, sizeof(cman_nodes)); rv = cman_get_nodes(ch, O2NM_MAX_NODES, &cman_node_count, cman_nodes); if (rv < 0) { log_debug("cman_get_nodes error %d %d", rv, errno); return; } for (i = 0; i < old_node_count; i++) { if (old_nodes[i].cn_member && !is_cman_member(old_nodes[i].cn_nodeid)) { log_debug("cman: node %d removed", old_nodes[i].cn_nodeid); } } for (i = 0; i < cman_node_count; i++) { if (cman_nodes[i].cn_member && !is_old_member(cman_nodes[i].cn_nodeid)) { log_debug("cman: node %d added", cman_nodes[i].cn_nodeid); } } } static void cman_callback(cman_handle_t h, void *private, int reason, int arg) { switch (reason) { case CMAN_REASON_TRY_SHUTDOWN: if (!have_mounts()) cman_replyto_shutdown(ch, 1); else { log_debug("no to cman shutdown"); cman_replyto_shutdown(ch, 0); } break; case CMAN_REASON_STATECHANGE: statechange(); break; } } static void dead_cman(int ci) { if (ci != cman_ci) { log_error("Unknown connection %d", ci); return; } log_error("cman connection died"); shutdown_daemon(); connection_dead(ci); } static void process_cman(int ci) { int rv; if (ci != cman_ci) { log_error("Unknown connection %d", ci); return; } rv = cman_dispatch(ch, CMAN_DISPATCH_ALL); if (rv == -1 && errno == EHOSTDOWN) { log_error("cman connection died"); shutdown_daemon(); } } int setup_stack(void) { cman_node_t node; int rv, fd; ch = cman_init(NULL); if (!ch) { log_error("cman_init error %d", errno); rv = -ENOTCONN; goto fail_finish; } ch_admin = cman_admin_init(NULL); if (!ch_admin) { log_error("cman_admin_init error %d", errno); rv = -ENOTCONN; goto fail_finish; } rv = cman_start_notification(ch, cman_callback); if (rv < 0) { log_error("cman_start_notification error %d %d", rv, errno); goto fail_finish; } /* FIXME: wait here for us to be a member of the cluster */ memset(&cluster, 0, sizeof(cluster)); rv = cman_get_cluster(ch, &cluster); if (rv < 0) { log_error("cman_get_cluster error %d %d", rv, errno); goto fail_stop; } clustername = cluster.ci_name; memset(&node, 0, sizeof(node)); rv = cman_get_node(ch, CMAN_NODEID_US, &node); if (rv < 0) { log_error("cman_get_node error %d %d", rv, errno); goto fail_stop; } our_nodeid = node.cn_nodeid; fd = cman_get_fd(ch); old_node_count = 0; memset(&old_nodes, 0, sizeof(old_nodes)); cman_node_count = 0; memset(&cman_nodes, 0, sizeof(cman_nodes)); /* Fill the node list */ statechange(); cman_ci = connection_add(fd, process_cman, dead_cman); if (cman_ci < 0) { rv = cman_ci; log_error("Unable to add cman client: %s", strerror(-cman_ci)); goto fail_stop; } return 0; fail_stop: cman_stop_notification(ch); fail_finish: if (ch_admin) cman_finish(ch_admin); if (ch) cman_finish(ch); ch = ch_admin = NULL; return rv; } void exit_stack(void) { if (ch_admin) cman_finish(ch_admin); if (ch) { log_debug("closing cman connection"); cman_stop_notification(ch); cman_finish(ch); } } ocfs2-tools-ocfs2-tools-1.8.6/ocfs2_controld/cpg.c000066400000000000000000000425301347147137200217220ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * Copyright (C) 2007 Oracle. All rights reserved. * * This copyrighted material is made available to anyone wishing to use, * modify, copy, or redistribute it subject to the terms and conditions * of the GNU General Public License v.2. */ /* Portions of this file are: */ /****************************************************************************** ******************************************************************************* ** ** Copyright (C) 2005 Red Hat, Inc. All rights reserved. ** ** This copyrighted material is made available to anyone wishing to use, ** modify, copy, or redistribute it subject to the terms and conditions ** of the GNU General Public License v.2. ** ******************************************************************************* ******************************************************************************/ #include #include #include #include #include #include #include #ifdef HAVE_COROSYNC # include #else # include #endif #include "ocfs2-kernel/kernel-list.h" #include "ocfs2_controld.h" struct cnode { struct list_head cn_list; int cn_nodeid; }; struct cgroup { struct list_head cg_list; /* List of all CPG groups */ cpg_handle_t cg_handle; int cg_joined; int cg_fd; int cg_ci; /* CPG's idea of the group */ struct cpg_name cg_name; struct cpg_address cg_members[CPG_MEMBERS_MAX]; int cg_member_count; /* * Our idea of the group. * This lags cg_members until join/leave processing is complete. */ struct list_head cg_nodes; int cg_node_count; /* Hooks for mounters */ void (*cg_set_cgroup)(struct cgroup *cg, void *user_data); void (*cg_node_down)(int nodeid, void *user_data); void *cg_user_data; /* Callback state */ int cg_got_confchg; struct cpg_address cg_cb_members[CPG_MEMBERS_MAX]; struct cpg_address cg_cb_joined[CPG_MEMBERS_MAX]; struct cpg_address cg_cb_left[CPG_MEMBERS_MAX]; int cg_cb_member_count; int cg_cb_joined_count; int cg_cb_left_count; }; /* Note, we never store cnode structures on daemon_group. Thus, * ->node_down() (which is NULL) is never called */ struct cgroup daemon_group; struct list_head group_list; static int message_flow_control_on; static void for_each_node_list(struct cpg_address list[], int count, void (*func)(struct cpg_address *addr, void *user_data), void *user_data) { int i; for (i = 0; i < count; i++) func(&list[i], user_data); } struct fen_proxy { void (*fp_func)(int nodeid, void *user_data); void *fp_data; }; static void for_each_node_proxy(struct cpg_address *addr, void *user_data) { struct fen_proxy *fen = user_data; fen->fp_func(addr->nodeid, fen->fp_data); } void for_each_node(struct cgroup *cg, void (*func)(int nodeid, void *user_data), void *user_data) { struct fen_proxy fen = { .fp_func = func, .fp_data = user_data, }; for_each_node_list(cg->cg_members, cg->cg_member_count, for_each_node_proxy, &fen); } static struct cnode *find_node(struct cgroup *cg, int nodeid) { struct list_head *p; struct cnode *cn = NULL; list_for_each(p, &cg->cg_nodes) { cn = list_entry(p, struct cnode, cn_list); if (cn->cn_nodeid == nodeid) break; cn = NULL; } return cn; } static void push_node(struct cgroup *cg, int nodeid) { struct cnode *cn; if (find_node(cg, nodeid)) { log_error("Node %d is already part of group %.*s", nodeid, cg->cg_name.length, cg->cg_name.value); /* * If we got lost in our group members, we can't interact * safely. */ shutdown_daemon(); return; } cn = malloc(sizeof(struct cnode)); if (!cn) { log_error("Unable to allocate node structure, exiting"); /* * If we can't keep track of the group, we can't * interact safely. */ shutdown_daemon(); return; } cn->cn_nodeid = nodeid; list_add(&cn->cn_list, &cg->cg_nodes); cg->cg_node_count++; } static void pop_node(struct cgroup *cg, int nodeid) { struct cnode *cn = find_node(cg, nodeid); if (cn) { list_del(&cn->cn_list); cg->cg_node_count--; } else { log_error("Unable to find node %d in group %.*s", nodeid, cg->cg_name.length, cg->cg_name.value); } if (cg->cg_node_count < 0) { log_error("cg_node_count went negative for group %.*s", cg->cg_name.length, cg->cg_name.value); cg->cg_node_count = 0; } } static void push_node_on_join(struct cpg_address *addr, void *user_data) { struct cgroup *cg = user_data; log_debug("Filling node %d to group %.*s", addr->nodeid, cg->cg_name.length, cg->cg_name.value); push_node(cg, addr->nodeid); } static void handle_node_join(struct cpg_address *addr, void *user_data) { struct cgroup *cg = user_data; log_debug("Node %d joins group %.*s", addr->nodeid, cg->cg_name.length, cg->cg_name.value); /* * If I read group/daemon/cpg.c correctly, you cannot have more than * one entry in the join_list when you yourself join. Thus, it is * safe to add all members of cg_cb_members. There will not be * a duplicate in cg_cb_joined. */ if (addr->nodeid == our_nodeid) { if (cg->cg_joined) { log_error("This node has joined group %.*s more than once", cg->cg_name.length, cg->cg_name.value); } else { log_debug("This node joins group %.*s", cg->cg_name.length, cg->cg_name.value); for_each_node_list(cg->cg_cb_members, cg->cg_cb_member_count, push_node_on_join, cg); cg->cg_joined = 1; cg->cg_set_cgroup(cg, cg->cg_user_data); } } else push_node(cg, addr->nodeid); } static void pop_nodes_on_leave(struct cpg_address *addr, void *user_data) { struct cgroup *cg = user_data; pop_node(cg, addr->nodeid); } static void finalize_group(struct cgroup *cg) { /* First, tell our mounter */ cg->cg_set_cgroup(NULL, cg->cg_user_data); for_each_node_list(cg->cg_members, cg->cg_member_count, pop_nodes_on_leave, cg); /* We're not in members anymore */ pop_node(cg, our_nodeid); if (cg->cg_node_count) log_error("node count is not zero on group %.*s!", cg->cg_name.length, cg->cg_name.value); if (!list_empty(&cg->cg_nodes)) log_error("node list is not empty on group %.*s!", cg->cg_name.length, cg->cg_name.value); cpg_finalize(cg->cg_handle); connection_dead(cg->cg_ci); list_del(&cg->cg_list); free(cg); } static void handle_node_leave(struct cpg_address *addr, void *user_data) { struct cgroup *cg = user_data; switch (addr->reason) { case CPG_REASON_LEAVE: log_debug("Node %d leaves group %.*s", addr->nodeid, cg->cg_name.length, cg->cg_name.value); if (addr->nodeid == our_nodeid) finalize_group(cg); else pop_node(cg, addr->nodeid); break; case CPG_REASON_NODEDOWN: case CPG_REASON_PROCDOWN: /* * These are ignored here. They will be handled * at the daemon group level in daemon_change(). */ break; case CPG_REASON_NODEUP: case CPG_REASON_JOIN: log_error("Unexpected reason %d while looking at group leave event for node %d", addr->reason, addr->nodeid); break; default: log_error("Invalid reason %d while looking at group leave event for node %d", addr->reason, addr->nodeid); break; } } static void group_change(struct cgroup *cg) { log_debug("group \"%.*s\" confchg: members %d, left %d, joined %d", cg->cg_name.length, cg->cg_name.value, cg->cg_cb_member_count, cg->cg_cb_left_count, cg->cg_cb_joined_count); for_each_node_list(cg->cg_cb_joined, cg->cg_cb_joined_count, handle_node_join, cg); for_each_node_list(cg->cg_cb_left, cg->cg_cb_left_count, handle_node_leave, cg); } static void handle_daemon_left(struct cpg_address *addr, void *user_data) { log_debug("node daemon left %d", addr->nodeid); switch (addr->reason) { case CPG_REASON_LEAVE: break; case CPG_REASON_PROCDOWN: /* * ocfs2_controld failed but the node is * still up. If the node was part of any * mounts, we need to kick it out of the cluster * to force fencing. */ /* XXX actually check for mounts */ if (1) { log_error("kill node %d - ocfs2_controld PROCDOWN", addr->nodeid); kill_stack_node(addr->nodeid); } /* FALL THROUGH */ case CPG_REASON_NODEDOWN: /* A nice clean failure */ /* XXX process node leaving ocfs2_controld * group. This is not the same as * processing a node leaving its mount * groups. We handle that below. */ break; case CPG_REASON_NODEUP: case CPG_REASON_JOIN: log_error("Unexpected reason %d while looking at node leave event for node %d", addr->reason, addr->nodeid); break; default: log_error("Invalid reason %d while looking at node leave event for node %d", addr->reason, addr->nodeid); break; } } static void handle_node_down(struct cpg_address *addr, void *user_data) { struct list_head *p; struct cgroup *cg; if ((addr->reason != CPG_REASON_NODEDOWN) && (addr->reason != CPG_REASON_PROCDOWN)) return; log_debug("node down %d", addr->nodeid); list_for_each(p, &group_list) { cg = list_entry(p, struct cgroup, cg_list); if (find_node(cg, addr->nodeid)) { cg->cg_node_down(addr->nodeid, cg->cg_user_data); pop_node(cg, addr->nodeid); } } } static void daemon_change(struct cgroup *cg) { int i, found = 0; log_debug("ocfs2_controld (group \"%.*s\") confchg: members %d, left %d, joined %d", cg->cg_name.length, cg->cg_name.value, cg->cg_cb_member_count, cg->cg_cb_left_count, cg->cg_cb_joined_count); for (i = 0; i < cg->cg_cb_member_count; i++) { if ((cg->cg_cb_members[i].nodeid == our_nodeid) && (cg->cg_cb_members[i].pid == (uint32_t)getpid())) { found = 1; } } if (found) { if (!cg->cg_joined) cg->cg_set_cgroup(cg, cg->cg_user_data); cg->cg_joined = 1; } else log_error("this node is not in the ocfs2_controld confchg: %u %u", our_nodeid, (uint32_t)getpid()); /* Here we do any internal work for the daemon group changing */ for_each_node_list(cg->cg_cb_left, cg->cg_cb_left_count, handle_daemon_left, NULL); /* * This is the pass to notify mountgroups. It is important we do * it here rather than in group_change(); we ensure the same order * on all nodes. */ for_each_node_list(cg->cg_cb_left, cg->cg_cb_left_count, handle_node_down, NULL); } static void process_configuration_change(struct cgroup *cg) { memcpy(&cg->cg_members, &cg->cg_cb_members, sizeof(cg->cg_cb_members)); cg->cg_member_count = cg->cg_cb_member_count; if (cg == &daemon_group) daemon_change(cg); else group_change(cg); } static struct cgroup *client_to_group(int ci) { struct list_head *p; struct cgroup *cg; if (ci == daemon_group.cg_ci) return &daemon_group; list_for_each(p, &group_list) { cg = list_entry(p, struct cgroup, cg_list); if (cg->cg_ci == ci) return cg; } log_error("unknown client %d", ci); return NULL; } static struct cgroup *handle_to_group(cpg_handle_t handle) { struct list_head *p; struct cgroup *cg; if (handle == daemon_group.cg_handle) return &daemon_group; list_for_each(p, &group_list) { cg = list_entry(p, struct cgroup, cg_list); if (cg->cg_handle == handle) return cg; } log_error("unknown handle %llu", (unsigned long long)handle); return NULL; } static void deliver_cb(cpg_handle_t handle, struct cpg_name *group_name, uint32_t nodeid, uint32_t pid, void *data, int data_len) { log_debug("deliver called"); } static void confchg_cb(cpg_handle_t handle, struct cpg_name *group_name, struct cpg_address *member_list, int member_list_entries, struct cpg_address *left_list, int left_list_entries, struct cpg_address *joined_list, int joined_list_entries) { int i; struct cgroup *cg; log_debug("confchg called"); cg = handle_to_group(handle); if (!cg) return; if (left_list_entries > CPG_MEMBERS_MAX) { log_debug("left_list_entries %d", left_list_entries); left_list_entries = CPG_MEMBERS_MAX; } else if (joined_list_entries > CPG_MEMBERS_MAX) { log_debug("joined_list_entries %d", joined_list_entries); joined_list_entries = CPG_MEMBERS_MAX; } else if (member_list_entries > CPG_MEMBERS_MAX) { log_debug("member_list_entries %d", member_list_entries); member_list_entries = CPG_MEMBERS_MAX; } cg->cg_cb_left_count = left_list_entries; cg->cg_cb_joined_count = joined_list_entries; cg->cg_cb_member_count = member_list_entries; for (i = 0; i < left_list_entries; i++) cg->cg_cb_left[i] = left_list[i]; for (i = 0; i < joined_list_entries; i++) cg->cg_cb_joined[i] = joined_list[i]; for (i = 0; i < member_list_entries; i++) cg->cg_cb_members[i] = member_list[i]; cg->cg_got_confchg = 1; } static cpg_callbacks_t callbacks = { .cpg_deliver_fn = deliver_cb, .cpg_confchg_fn = confchg_cb, }; static void process_cpg(int ci) { cpg_error_t error; cpg_flow_control_state_t flow_control_state; struct cgroup *cg; cg = client_to_group(ci); if (!cg) return; cg->cg_got_confchg = 0; error = cpg_dispatch(cg->cg_handle, CPG_DISPATCH_ONE); if (error != CPG_OK) { log_error("cpg_dispatch error %d", error); return; } error = cpg_flow_control_state_get(cg->cg_handle, &flow_control_state); if (error != CPG_OK) log_error("cpg_flow_control_state_get %d", error); else if (flow_control_state == CPG_FLOW_CONTROL_ENABLED) { message_flow_control_on = 1; log_debug("flow control on"); } else { if (message_flow_control_on) log_debug("flow control off"); message_flow_control_on = 0; } if (cg->cg_got_confchg) process_configuration_change(cg); } static void dead_cpg(int ci) { struct cgroup *cg; if (ci == daemon_group.cg_ci) { log_error("cpg connection died"); shutdown_daemon(); /* We can't talk to cpg anymore */ daemon_group.cg_handle = 0; connection_dead(ci); } else { cg = client_to_group(ci); if (cg) finalize_group(cg); } } static int start_join(struct cgroup *cg) { cpg_error_t error; log_debug("Starting join for group \"%.*s\"", cg->cg_name.length, cg->cg_name.value); do { error = cpg_join(cg->cg_handle, &cg->cg_name); if (error == CPG_OK) { log_debug("cpg_join succeeded"); error = 0; } else if (error == CPG_ERR_TRY_AGAIN) { log_debug("cpg_join retry"); sleep(1); } else log_error("cpg_join error %d", error); } while (error == CPG_ERR_TRY_AGAIN); return error; } static int start_leave(struct cgroup *cg) { int i; cpg_error_t error; if (!cg->cg_handle) return -EINVAL; log_debug("leaving group \"%.*s\"", cg->cg_name.length, cg->cg_name.value); for (i = 0; i < 10; i++) { error = cpg_leave(cg->cg_handle, &cg->cg_name); if (error == CPG_ERR_TRY_AGAIN) { if (!i) log_debug("cpg_leave retry"); sleep(1); continue; } if (error == CPG_OK) log_debug("cpg_leave succeeded"); else log_error("cpg_leave error %d", error); break; } if (error == CPG_OK) return 0; else if (error == CPG_ERR_TRY_AGAIN) return -EAGAIN; else return -EIO; } static int init_group(struct cgroup *cg, const char *name) { cpg_error_t error; cg->cg_name.length = snprintf(cg->cg_name.value, CPG_MAX_NAME_LENGTH, "ocfs2:%s", name); if (cg->cg_name.length >= CPG_MAX_NAME_LENGTH) { log_error("Group name \"%s\" is too long", name); error = -ENAMETOOLONG; goto out; } error = cpg_initialize(&cg->cg_handle, &callbacks); if (error != CPG_OK) { log_error("cpg_initialize error %d", error); goto out; } cpg_fd_get(cg->cg_handle, &cg->cg_fd); cg->cg_ci = connection_add(cg->cg_fd, process_cpg, dead_cpg); if (cg->cg_ci < 0) { error = cg->cg_ci; log_error("Unable to add cpg client: %s", strerror(-error)); goto out_finalize; } error = start_join(cg); if (error) goto out_close; return 0; out_close: connection_dead(cg->cg_fd); out_finalize: cpg_finalize(cg->cg_handle); cg->cg_handle = 0; out: return error; } int group_leave(struct cgroup *cg) { if (!cg->cg_joined) { log_error("Unable to leave unjoined group %.*s", cg->cg_name.length, cg->cg_name.value); return -EINVAL; } return start_leave(cg); } int group_join(const char *name, void (*set_cgroup)(struct cgroup *cg, void *user_data), void (*node_down)(int nodeid, void *user_data), void *user_data) { int rc; struct cgroup *cg; cg = malloc(sizeof(struct cgroup)); if (!cg) { log_error("Unable to allocate cgroup structure"); rc = -ENOMEM; goto out; } memset(cg, 0, sizeof(struct cgroup)); INIT_LIST_HEAD(&cg->cg_nodes); cg->cg_set_cgroup = set_cgroup; cg->cg_node_down = node_down; cg->cg_user_data = user_data; rc = init_group(cg, name); if (rc) free(cg); else list_add(&cg->cg_list, &group_list); out: return rc; } static void daemon_set_cgroup(struct cgroup *cg, void *user_data) { void (*daemon_joined)(int first) = user_data; if (cg != &daemon_group) { log_error("Somehow, daemon_set_cgroup is called on a different group!"); return; } daemon_joined(daemon_group.cg_member_count == 1); } int setup_cpg(void (*daemon_joined)(int first)) { cpg_error_t error; INIT_LIST_HEAD(&group_list); daemon_group.cg_set_cgroup = daemon_set_cgroup; daemon_group.cg_user_data = daemon_joined; error = init_group(&daemon_group, "controld"); return error; } void exit_cpg(void) { if (!daemon_group.cg_handle) return; start_leave(&daemon_group); log_debug("closing cpg connection"); cpg_finalize(daemon_group.cg_handle); } ocfs2-tools-ocfs2-tools-1.8.6/ocfs2_controld/dlmcontrol.c000066400000000000000000000175131347147137200233310ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * Copyright (C) 2008 Oracle. All rights reserved. * * This copyrighted material is made available to anyone wishing to use, * modify, copy, or redistribute it subject to the terms and conditions * of the GNU General Public License v.2. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "ocfs2-kernel/kernel-list.h" #include "ocfs2_controld.h" /* * Structure to keep track of filesystems we've registered with * dlm_controld. */ struct dlmcontrol_fs { struct list_head df_list; char df_name[DLM_LOCKSPACE_LEN+1]; struct list_head df_notifications; void (*df_result)(int status, void *user_data); void *df_user_data; }; /* Keeps track of pending node-down notifications */ struct dlmcontrol_notification { struct list_head dn_list; int dn_nodeid; }; static int dlmcontrol_ci; /* Client number in the main loop */ static int dlmcontrol_fd = -1; /* fd for dlm_controld connection */ /* List of all filesystems we have registered */ static LIST_HEAD(register_list); int dlmcontrol_register(const char *name, void (*result_func)(int status, void *user_data), void *user_data) { int rc; struct dlmcontrol_fs *df; df = malloc(sizeof(struct dlmcontrol_fs)); if (!df) { log_error("Unable to allocate memory to register \"%s\" " "with dlm_controld", name); rc = -ENOMEM; goto out; } snprintf(df->df_name, DLM_LOCKSPACE_LEN+1, "%s", name); INIT_LIST_HEAD(&df->df_notifications); df->df_result = result_func; df->df_user_data = user_data; /* * We register *before* allowing the filesystem to mount. * Otherwise we have a window between mount(2) and registration * where notification is unordered. */ log_debug("Registering \"%s\" with dlm_controld", name); rc = dlmc_fs_register(dlmcontrol_fd, df->df_name); if (rc) { rc = -errno; log_error("Unable to register \"%s\" with dlm_controld: %s", df->df_name, strerror(-rc)); goto out; } list_add(&df->df_list, ®ister_list); out: if (rc && df) free(df); return rc; } static struct dlmcontrol_fs *find_fs(const char *name) { struct list_head *pos; struct dlmcontrol_fs *df; list_for_each(pos, ®ister_list) { df = list_entry(pos, struct dlmcontrol_fs, df_list); if (!strcmp(df->df_name, name)) return df; } return NULL; } static struct dlmcontrol_notification *find_notification(struct dlmcontrol_fs *df, int nodeid) { struct list_head *pos; struct dlmcontrol_notification *dn; list_for_each(pos, &df->df_notifications) { dn = list_entry(pos, struct dlmcontrol_notification, dn_list); if (dn->dn_nodeid == nodeid) return dn; } return NULL; } static void complete_notification(struct dlmcontrol_fs *df, int nodeid) { struct dlmcontrol_notification *dn; dn = find_notification(df, nodeid); if (dn) { log_debug("Completing notification on \"%s\" for node %d\n", df->df_name, nodeid); list_del(&dn->dn_list); free(dn); } } static void complete_all_notifications(struct dlmcontrol_fs *df) { struct list_head *pos, *n; struct dlmcontrol_notification *dn; list_for_each_safe(pos, n, &df->df_notifications) { dn = list_entry(pos, struct dlmcontrol_notification, dn_list); complete_notification(df, dn->dn_nodeid); } } int dlmcontrol_unregister(const char *name) { int rc; struct dlmcontrol_fs *df; df = find_fs(name); if (!df) { log_error("Name \"%s\" is unknown", name); rc = -ENOENT; goto out; } list_del(&df->df_list); /* * From here out, we're going to try to drop everything, even in * the face of errors. */ log_debug("Unregistering \"%s\" from dlm_controld", name); complete_all_notifications(df); rc = dlmc_fs_unregister(dlmcontrol_fd, df->df_name); if (rc) { rc = -errno; log_error("Unable to unregister \"%s\" from dlm_controld: " "%s", name, strerror(-rc)); } free(df); out: return rc; } static void dlmcontrol_unregister_all(void) { struct list_head *pos, *n; struct dlmcontrol_fs *df; list_for_each_safe(pos, n, ®ister_list) { df = list_entry(pos, struct dlmcontrol_fs, df_list); /* This is exit-time, don't care about errors */ dlmcontrol_unregister(df->df_name); } } /* * This is a fire and forget function. It will queue the notification and * send it to dlm_controld. If dlm_controld responds in the negative, this * function is called again to send. This goes on until dlm_controld sees * the node go down. * * We don't sleep because the process switching should be good enough. * * If there is any error, malloc/network/whatever, we kill the daemon. We * can't continue safely if we're not interacting with dlm_controld. */ void dlmcontrol_node_down(const char *name, int nodeid) { int rc; struct dlmcontrol_fs *df; struct dlmcontrol_notification *dn; df = find_fs(name); if (!df) { log_error("Name \"%s\" is unknown", name); return; } dn = find_notification(df, nodeid); if (!dn) { dn = malloc(sizeof(struct dlmcontrol_notification)); if (!dn) { log_error("Unable to allocate memory"); goto fail; } dn->dn_nodeid = nodeid; list_add(&dn->dn_list, &df->df_notifications); } log_debug("Sending notification of node %d for \"%s\"", dn->dn_nodeid, df->df_name); rc = dlmc_fs_notified(dlmcontrol_fd, df->df_name, dn->dn_nodeid); if (rc) { log_error("Unable to send notification for node %d on " "\"%s\": %s", dn->dn_nodeid, df->df_name, strerror(errno)); goto fail; } return; fail: shutdown_daemon(); } static void notify_result(struct dlmcontrol_fs *df, int nodeid, int status) { if (!find_notification(df, nodeid)) { log_error("Notified for nodeid %d on \"%s\", but we never asked for it!", nodeid, df->df_name); return; } if (!status) { complete_notification(df, nodeid); return; } dlmcontrol_node_down(df->df_name, nodeid); } static void dead_dlmcontrol(int ci) { if (ci != dlmcontrol_ci) { log_error("Unknown connection %d", ci); return; } log_error("dlmcontrol connection died"); shutdown_daemon(); connection_dead(ci); } static void process_dlmcontrol(int ci) { int rc, result_type, nodeid, status; char name[DLM_LOCKSPACE_LEN + 1]; struct dlmcontrol_fs *df; memset(name, 0, sizeof(name)); if (ci != dlmcontrol_ci) { log_error("Unknown connection %d", ci); return; } log_debug("message from dlmcontrol\n"); rc = dlmc_fs_result(dlmcontrol_fd, name, &result_type, &nodeid, &status); if (rc) { rc = -errno; log_error("Error from dlmc_fs_result: %s", strerror(-rc)); return; } df = find_fs(name); if (!df) { log_error("Name \"%s\" is unknown", name); return; } switch (result_type) { case DLMC_RESULT_REGISTER: log_debug("Registration of \"%s\" complete", name); df->df_result(status, df->df_user_data); break; case DLMC_RESULT_NOTIFIED: log_debug("Notified for \"%s\", node %d, status %d", name, nodeid, status); notify_result(df, nodeid, status); break; default: log_error("Unknown message from dlm_controld: %d", result_type); break; } } int setup_dlmcontrol(void) { int rc; rc = dlmc_fs_connect(); if (rc < 0) { rc = -errno; log_error("Unable to connect to dlm_controld: %s", strerror(-rc)); goto out; } dlmcontrol_fd = rc; dlmcontrol_ci = connection_add(dlmcontrol_fd, process_dlmcontrol, dead_dlmcontrol); if (dlmcontrol_ci < 0) { rc = dlmcontrol_ci; log_error("Unable to add dlmcontrol client: %s", strerror(-dlmcontrol_ci)); dlmc_fs_disconnect(dlmcontrol_fd); goto out; } rc = 0; out: return rc; } void exit_dlmcontrol(void) { if (dlmcontrol_fd < 0) return; dlmcontrol_unregister_all(); log_debug("Closing dlm_controld connection"); dlmc_fs_disconnect(dlmcontrol_fd); } ocfs2-tools-ocfs2-tools-1.8.6/ocfs2_controld/main.c000066400000000000000000000632631347147137200221030ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: */ /****************************************************************************** ******************************************************************************* ** ** Copyright (C) 2005 Red Hat, Inc. All rights reserved. ** ** This copyrighted material is made available to anyone wishing to use, ** modify, copy, or redistribute it subject to the terms and conditions ** of the GNU General Public License v.2. ** ******************************************************************************* ******************************************************************************/ /* * Copyright (C) 2007 Oracle. All rights reserved. * * This copyrighted material is made available to anyone wishing to use, * modify, copy, or redistribute it subject to the terms and conditions * of the GNU General Public License v.2. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ocfs2-kernel/kernel-list.h" #include "o2cb/o2cb.h" #include "o2cb/o2cb_client_proto.h" #include "ocfs2_controld.h" #define OPTION_STRING "DhVw" #define LOCKFILE_NAME "/var/run/ocfs2_controld.pid" #define NALLOC 8 #define CONTROLD_PROTOCOL_MAJOR 1 #define CONTROLD_PROTOCOL_MINOR 0 #define DAEMON_MAX_PROTOCOL_SECTION "daemon_max_protocol" #define DAEMON_PROTOCOL_SECTION "daemon_protocol" #define FS_MAX_PROTOCOL_SECTION "ocfs2_max_protocol" #define FS_PROTOCOL_SECTION "ocfs2_protocol" struct client { int fd; char type[32]; void (*work)(int ci); void (*dead)(int ci); #if 0 struct mountgroup *mg; int another_mount; #endif }; static int client_maxi; static int client_size = 0; static struct client *client = NULL; static struct pollfd *pollfd = NULL; static int time_to_die = 0; static int sigpipe_write_fd; char *prog_name; int daemon_debug_opt; char daemon_debug_buf[1024]; char dump_buf[DUMP_SIZE]; int dump_point; int dump_wrap; /* * Protocol negotiation. * * This is the maximum protocol supported by the daemon for inter-daemon * communication. The negotiated value daemon_running_proto is what the * daemon uses at runtime. * * All daemons must support the initial protocol, which works as follows: * Prior to starting CPG, daemons store two values in the local node * checkpoint. The maximum daemon protocol is stored in the * "daemon_max_protocol" section, and the ocfs2 maximum protocol is stored * in the "ocfs2_max_protocol" section. The protocols are stored in the * format: * * <2-char-hex-major><2-char-hex-minor> * * These sections MUST be created before CPG is started. Other sections * MUST NOT be created at this time. * * Once CPG is started, the daemon reads the "daemon_protocol" and * "ocfs2_protocol" sections from the daemon's global checkpoint. The * values are stored as the running versions. All interaction takes place * based on the running versions. At this point, the daemon may add * other sections to the local node checkpoint that are part of the * running protocol. * * If the daemon is the first node to join the group, it sets the * "daemon_protocol" and "ocfs2_protocol" sections of the global checkpoint * to the maximum values this daemon supports. */ static struct ocfs2_protocol_version daemon_max_proto = { .pv_major = CONTROLD_PROTOCOL_MAJOR, .pv_minor = CONTROLD_PROTOCOL_MINOR, }; static struct ocfs2_protocol_version fs_max_proto; struct ocfs2_protocol_version daemon_running_proto; struct ocfs2_protocol_version fs_running_proto; struct ckpt_handle *node_handle; void shutdown_daemon(void) { time_to_die = 1; } static void handler(int signum) { log_debug("Caught signal %d", signum); if (write(sigpipe_write_fd, &signum, sizeof(signum)) < sizeof(signum)) log_error("Problem writing signal: %s", strerror(-errno)); } static void dead_sigpipe(int ci) { log_error("Error on the signal pipe"); connection_dead(ci); shutdown_daemon(); } static void handle_signal(int ci) { int rc, caught_sig, abortp = 0; static int segv_already = 0; rc = read(client[ci].fd, (char *)&caught_sig, sizeof(caught_sig)); if (rc < 0) { rc = -errno; log_error("Error reading from signal pipe: %s", strerror(-rc)); goto out; } if (rc != sizeof(caught_sig)) { rc = -EIO; log_error("Error reading from signal pipe: %s", strerror(-rc)); goto out; } switch (caught_sig) { case SIGQUIT: abortp = 1; /* FALL THROUGH */ case SIGTERM: case SIGINT: case SIGHUP: if (have_mounts()) { log_error("Caught signal %d, but mounts exist. Ignoring.", caught_sig); rc = 0; } else { log_error("Caught signal %d, exiting", caught_sig); rc = 1; } break; case SIGSEGV: log_error("Segmentation fault, exiting"); rc = 1; if (segv_already) { log_error("Segmentation fault loop detected"); abortp = 1; } else segv_already = 1; break; default: log_error("Caught signal %d, ignoring", caught_sig); rc = 0; break; } if (rc && abortp) abort(); out: if (rc) shutdown_daemon(); } static int setup_sigpipe(void) { int rc; int signal_pipe[2]; struct sigaction act = { .sa_handler = handler, }; rc = pipe(signal_pipe); if (rc) { rc = -errno; log_error("Unable to set up signal pipe: %s", strerror(-rc)); goto out; } sigpipe_write_fd = signal_pipe[1]; sigemptyset(&act.sa_mask); #ifdef SA_INTERRUPT act.sa_flags = SA_INTERRUPT; #endif rc += sigaction(SIGTERM, &act, NULL); rc += sigaction(SIGINT, &act, NULL); rc += sigaction(SIGHUP, &act, NULL); rc += sigaction(SIGQUIT, &act, NULL); rc += sigaction(SIGSEGV, &act, NULL); act.sa_handler = SIG_IGN; rc += sigaction(SIGPIPE, &act, NULL); /* Get EPIPE instead */ if (rc) { log_error("Unable to set up signal handlers"); goto out; } rc = connection_add(signal_pipe[0], handle_signal, dead_sigpipe); if (rc < 0) log_error("Unable to add signal pipe: %s", strerror(-rc)); out: return rc; } static int do_mount(int ci, int fd, const char *fstype, const char *uuid, const char *cluster, const char *device, const char *service) { char *error_msg; if (!fstype || strcmp(fstype, OCFS2_FS_NAME)) { error_msg = "Invalid filesystem type"; goto fail; } if (!validate_cluster(cluster)) { error_msg = "Invalid cluster name"; goto fail; } return start_mount(ci, fd, uuid, device, service); fail: return send_message(fd, CM_STATUS, EINVAL, error_msg); } static int do_mount_result(int ci, int fd, const char *fstype, const char *uuid, const char *errcode, const char *service) { if (!fstype || strcmp(fstype, OCFS2_FS_NAME)) return send_message(fd, CM_STATUS, EINVAL, "Invalid filesystem type"); return complete_mount(ci, fd, uuid, errcode, service); } static int do_unmount(int ci, int fd, const char *fstype, const char *uuid, const char *service) { if (!fstype || strcmp(fstype, OCFS2_FS_NAME)) return send_message(fd, CM_STATUS, EINVAL, "Invalid filesystem type"); return remove_mount(ci, fd, uuid, service); } void connection_dead(int ci) { log_debug("client %d fd %d dead", ci, client[ci].fd); close(client[ci].fd); client[ci].work = NULL; client[ci].fd = -1; pollfd[ci].fd = -1; #if 0 client[ci].mg = NULL; #endif } static int client_alloc(void) { int i; struct client *new_client; struct pollfd *new_pollfd; if (!client) { new_client = malloc(NALLOC * sizeof(struct client)); new_pollfd = malloc(NALLOC * sizeof(struct pollfd)); } else { new_client = realloc(client, (client_size + NALLOC) * sizeof(struct client)); new_pollfd = realloc(pollfd, (client_size + NALLOC) * sizeof(struct pollfd)); } if (!new_client || !new_pollfd) { log_error("Can't allocate client memory."); return -ENOMEM; } client = new_client; pollfd = new_pollfd; for (i = client_size; i < (client_size + NALLOC); i++) { client[i].work = NULL; client[i].dead = NULL; client[i].fd = -1; pollfd[i].fd = -1; pollfd[i].revents = 0; } client_size += NALLOC; return 0; } int connection_add(int fd, void (*work)(int ci), void (*dead)(int ci)) { int i; if (!client) { i = client_alloc(); if (i) return i; } while (1) { for (i = 0; i < client_size; i++) { if (client[i].fd == -1) { client[i].fd = fd; client[i].work = work; client[i].dead = dead ? dead : connection_dead; pollfd[i].fd = fd; pollfd[i].events = POLLIN; if (i > client_maxi) client_maxi = i; return i; } } i = client_alloc(); if (i) return i; } return -ELOOP; } /* 4 characters for "ITEM", 1 for the space, 1 for the '\0' */ #define DEBUG_BYTES_PER_ITEM (OCFS2_CONTROLD_MAXLINE - 6) static size_t debug_bytes_to_count(size_t bytes) { return (bytes + DEBUG_BYTES_PER_ITEM - 1) / DEBUG_BYTES_PER_ITEM; } static int send_debug(int fd, const char *ptr, size_t bytes) { int rc = 0; size_t remain = bytes; size_t itemlen = DEBUG_BYTES_PER_ITEM; char itembuf[DEBUG_BYTES_PER_ITEM + 1]; while (remain) { if (itemlen > remain) itemlen = remain; memcpy(itembuf, ptr, itemlen); itembuf[itemlen] = '\0'; rc = send_message(fd, CM_ITEM, itembuf); if (rc) break; ptr += itemlen; remain -= itemlen; } return rc; } static int dump_debug(int ci, int fd) { int rc, rctmp; char error_msg[100]; /* Arbitrary size smaller than a message */ int count = 0; if (dump_wrap) count += debug_bytes_to_count(DUMP_SIZE - dump_point); count += debug_bytes_to_count(dump_point); rc = send_message(fd, CM_ITEMCOUNT, count); if (rc) { snprintf(error_msg, sizeof(error_msg), "Unable to send ITEMCOUNT: %s", strerror(-rc)); goto out_status; } if (dump_wrap) { rc = send_debug(fd, dump_buf + dump_point, DUMP_SIZE - dump_point); if (rc) { snprintf(error_msg, sizeof(error_msg), "Unable to send ITEM: %s", strerror(-rc)); goto out_status; } } rc = send_debug(fd, dump_buf, dump_point); if (rc) { snprintf(error_msg, sizeof(error_msg), "Unable to send ITEM: %s", strerror(-rc)); goto out_status; } strcpy(error_msg, "OK"); out_status: rctmp = send_message(fd, CM_STATUS, -rc, error_msg); if (rctmp) { log_error("Error sending STATUS message: %s", strerror(-rc)); if (!rc) rc = rctmp; } return rc; } static int send_filesystems(int ci, int fd, const char *fstype, const char *cluster) { char *error_msg; if (!fstype || strcmp(fstype, OCFS2_FS_NAME)) { error_msg = "Invalid filesystem type"; goto fail; } if (!validate_cluster(cluster)) { error_msg = "Invalid cluster name"; goto fail; } return send_mountgroups(ci, fd); fail: return send_message(fd, CM_STATUS, EINVAL, error_msg); } static int send_clustername(int ci, int fd) { int rc = 0, rctmp; char error_msg[100]; /* Arbitrary size smaller than a message */ const char *cluster; rc = get_clustername(&cluster); if (rc) { snprintf(error_msg, sizeof(error_msg), "Unable to query cluster name: %s", strerror(-rc)); goto out_status; } /* Cman only supports one cluster */ rc = send_message(fd, CM_ITEMCOUNT, 1); if (rc) { snprintf(error_msg, sizeof(error_msg), "Unable to send ITEMCOUNT: %s", strerror(-rc)); goto out_status; } rc = send_message(fd, CM_ITEM, cluster); if (rc) { snprintf(error_msg, sizeof(error_msg), "Unable to send ITEM: %s", strerror(-rc)); goto out_status; } strcpy(error_msg, "OK"); out_status: rctmp = send_message(fd, CM_STATUS, -rc, error_msg); if (rctmp) { log_error("Error sending STATUS message: %s", strerror(-rc)); if (!rc) rc = rctmp; } return rc; } static void dead_client(int ci) { dead_mounter(ci, client[ci].fd); connection_dead(ci); } static void process_client(int ci) { client_message message; char *argv[OCFS2_CONTROLD_MAXARGS + 1]; char buf[OCFS2_CONTROLD_MAXLINE]; int rv, fd = client[ci].fd; log_debug("client msg"); /* receive_message ensures we have the proper number of arguments */ rv = receive_message(fd, buf, &message, argv); if (rv == -EPIPE) { dead_client(ci); return; } if (rv < 0) { /* XXX: Should print better errors matching our returns */ log_debug("client %d fd %d read error %d", ci, fd, -rv); return; } log_debug("client message %d from %d: %s", message, ci, message_to_string(message)); switch (message) { case CM_MOUNT: rv = do_mount(ci, fd, argv[0], argv[1], argv[2], argv[3], argv[4]); fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK); break; case CM_MRESULT: rv = do_mount_result(ci, fd, argv[0], argv[1], argv[2], argv[3]); break; case CM_UNMOUNT: rv = do_unmount(ci, fd, argv[0], argv[1], argv[2]); break; case CM_LISTCLUSTERS: rv = send_clustername(ci, fd); break; case CM_LISTFS: rv = send_filesystems(ci, fd, argv[0], argv[1]); break; case CM_DUMP: rv = dump_debug(ci, fd); break; case CM_STATUS: log_error("Someone sent us cm_status!"); break; default: log_error("Invalid message received"); break; } return; } static void process_listener(int ci) { int fd, i; fd = accept(client[ci].fd, NULL, NULL); if (fd < 0) { log_debug("accept error %d %d", fd, errno); return; } i = connection_add(fd, process_client, dead_client); if (i < 0) { log_error("Error adding client: %s", strerror(-i)); close(fd); } else log_debug("new client connection %d", i); } static void dead_listener(int ci) { log_error("Error on the listening socket"); connection_dead(ci); shutdown_daemon(); } static int setup_listener(void) { int fd, i; fd = ocfs2_client_listen(); if (fd < 0) { log_error("Unable to start listening socket: %s", strerror(-fd)); return fd; } i = connection_add(fd, process_listener, dead_listener); if (i < 0) { log_error("Unable to add listening socket: %s", strerror(-i)); close(fd); return i; } log_debug("new listening connection %d", i); return 0; } static int proto_version_to_checkpoint(struct ocfs2_protocol_version *proto, char **checkpoint_data) { size_t len; char *buf; len = snprintf(NULL, 0, "%02x %02x", proto->pv_major, proto->pv_minor); buf = malloc(sizeof(char) * (len + 1)); if (!buf) { log_error("Unable to allocate memory for checkpoint data"); return -ENOMEM; } snprintf(buf, len + 1, "%02x %02x", proto->pv_major, proto->pv_minor); *checkpoint_data = buf; return 0; } static int checkpoint_to_proto_version(char *data, size_t data_len, struct ocfs2_protocol_version *proto) { long major, minor; char *ptr; struct proto_version_str { char major[2]; char space; char minor[2]; char null; } str; if (data_len != sizeof(struct proto_version_str)) { log_error("Protocol version string \"%.*s\" has incorrect " "length", (int)data_len, data); return -EINVAL; } memcpy((char *)&str, data, data_len); if ((str.space != ' ') || (str.null != '\0')) { log_error("Protocol version string \"%.*s\" has invalid " "separators", (int)data_len, data); return -EINVAL; } str.space = '\0'; major = strtol(str.major, &ptr, 16); if (!ptr || *ptr) { log_error("Protocol request has bad version 0x%s 0x%s", str.major, str.minor); return -EINVAL; } minor = strtol(str.minor, &ptr, 16); if (!ptr || *ptr) { log_error("Protocol version string has bad version 0x%s 0x%s", str.major, str.minor); return -EINVAL; } /* The major and minor must be between 0 and 255, inclusive. */ if ((major == LONG_MIN) || (major == LONG_MAX) || (minor == LONG_MIN) || (minor == LONG_MAX) || (major > (uint8_t)-1) || (major < 0) || (minor > (uint8_t)-1) || (minor < 0)) { log_error("Protocol version string has bad version 0x%s 0x%s", str.major, str.minor); return -ERANGE; } proto->pv_major = major; proto->pv_minor = minor; return 0; } static int install_node_checkpoint(void) { int rc; char *buf; errcode_t err; err = o2cb_get_max_locking_protocol(&fs_max_proto); if (err) { log_error("Error querying maximum filesystem locking " "protocol: %s", error_message(err)); rc = -EIO; goto out; } rc = ckpt_open_this_node(&node_handle); if (rc) goto out; rc = proto_version_to_checkpoint(&daemon_max_proto, &buf); if (rc) goto out; rc = ckpt_section_store(node_handle, DAEMON_MAX_PROTOCOL_SECTION, buf, strlen(buf) + 1); free(buf); if (rc) goto out; rc = proto_version_to_checkpoint(&fs_max_proto, &buf); if (rc) goto out; rc = ckpt_section_store(node_handle, FS_MAX_PROTOCOL_SECTION, buf, strlen(buf) + 1); free(buf); out: return rc; } static void drop_node_checkpoint(void) { if (node_handle) ckpt_close(node_handle); } /* * If we're the only daemon running, install our maximum protocols * as the running values. */ static int install_global_checkpoint(void) { int rc; char *buf; daemon_running_proto = daemon_max_proto; fs_running_proto = fs_max_proto; rc = ckpt_open_global(1); if (rc) goto out; rc = proto_version_to_checkpoint(&daemon_running_proto, &buf); if (rc) goto out; rc = ckpt_global_store(DAEMON_PROTOCOL_SECTION, buf, strlen(buf) + 1); free(buf); if (rc) goto out; rc = proto_version_to_checkpoint(&fs_running_proto, &buf); if (rc) goto out; rc = ckpt_global_store(FS_PROTOCOL_SECTION, buf, strlen(buf) + 1); free(buf); out: return rc; } /* * Compare the cluster's locking protocol version against our maximum. * * If the major numbers are different, they are incompatible. * If the cluster's minor is greater than our maximum minor, they are * incompatible. */ static int protocol_compatible(struct ocfs2_protocol_version *cluster, struct ocfs2_protocol_version *our_max) { if (cluster->pv_major != our_max->pv_major) return 0; if (cluster->pv_minor > our_max->pv_minor) return 0; return 1; } static int read_global_checkpoint(void) { int rc, seen = 0, opened = 0, retrycount = 0; char *buf; size_t len; retry: rc = ckpt_open_global(0); if (rc) goto out; seen = 1; opened = 1; rc = ckpt_global_get(DAEMON_PROTOCOL_SECTION, &buf, &len); if (rc) goto out; rc = checkpoint_to_proto_version(buf, len, &daemon_running_proto); free(buf); if (rc) goto out; if (!protocol_compatible(&daemon_running_proto, &daemon_max_proto)) { log_error("Our maximum daemon protocol (%d.%d) is not " "compatible with the cluster's protocol (%d.%d)", daemon_max_proto.pv_major, daemon_max_proto.pv_minor, daemon_running_proto.pv_major, daemon_running_proto.pv_minor); rc = -EPROTONOSUPPORT; goto out; } rc = ckpt_global_get(FS_PROTOCOL_SECTION, &buf, &len); if (rc) goto out; rc = checkpoint_to_proto_version(buf, len, &fs_running_proto); free(buf); if (rc) goto out; if (!protocol_compatible(&fs_running_proto, &fs_max_proto)) { log_error("Our maximum fs protocol (%d.%d) is not " "compatible with the cluster's protocol (%d.%d)", fs_max_proto.pv_major, fs_max_proto.pv_minor, fs_running_proto.pv_major, fs_running_proto.pv_minor); rc = -EPROTONOSUPPORT; } out: if (rc == -ENOENT) { /* * -ENOENT means the first daemon hasn't gotten the * global checkpoint fully installed yet. Either the * checkpoint or one of its sections is missing. * * If we saw the checkpoint once, but now it's gone, it * means the first daemon died. We don't recover from * that. But if we haven't seen the checkpoint yet, or if * it's just a missing section, we can keep trying. */ if (opened) ckpt_close_global(); if (seen && !opened) { log_error("The global checkpoint disappeared out " "from underneath us. This shouldn't " "happen to a daemon that is not the " "first in the cluster"); } else { opened = 0; retry_warning(retrycount, "Attempted to read the cluster's " "protocol versions %d times, still " "trying", retrycount); sleep_ms(10); goto retry; } } return rc; } static void cpg_joined(int first) { int rv; errcode_t err; log_debug("CPG is live, we are %s first daemon", first ? "the" : "not the"); if (first) rv = install_global_checkpoint(); else rv = read_global_checkpoint(); if (rv) { shutdown_daemon(); return; } log_debug("Daemon protocol is %d.%d", daemon_running_proto.pv_major, daemon_running_proto.pv_minor); log_debug("fs protocol is %d.%d", fs_running_proto.pv_major, fs_running_proto.pv_minor); log_debug("Connecting to dlm_controld"); rv = setup_dlmcontrol(); if (rv) { shutdown_daemon(); return; } log_debug("Opening control device"); err = o2cb_control_open(our_nodeid, &fs_running_proto); if (err) { log_error("Error opening control device: %s", error_message(err)); shutdown_daemon(); return; } log_debug("Starting to listen for mounters"); rv = setup_listener(); if (rv < 0) { shutdown_daemon(); return; } } static int loop(void) { int rv, i, poll_timeout = -1; rv = setup_sigpipe(); if (rv < 0) goto out; rv = setup_stack(); if (rv < 0) goto out; rv = setup_ckpt(); if (rv < 0) goto out; rv = install_node_checkpoint(); if (rv < 0) goto out; rv = setup_cpg(cpg_joined); if (rv < 0) goto out; log_debug("setup done"); for (;;) { rv = poll(pollfd, client_maxi + 1, poll_timeout); if ((rv < 0) && (errno != EINTR)) log_error("poll error %d errno %d", rv, errno); rv = 0; for (i = 0; i <= client_maxi; i++) { if (client[i].fd < 0) continue; /* * We handle POLLIN before POLLHUP so clients can * finish what they were doing */ if (pollfd[i].revents & POLLIN) { client[i].work(i); if (time_to_die) goto stop; } if (pollfd[i].revents & POLLHUP) { client[i].dead(i); if (time_to_die) goto stop; } } } stop: if (!rv && have_mounts()) rv = 1; bail_on_mounts(); o2cb_control_close(); exit_dlmcontrol(); exit_cpg(); drop_node_checkpoint(); exit_ckpt(); exit_stack(); out: return rv; } static void lockfile(void) { int fd, error; struct flock lock; char buf[33]; memset(buf, 0, 33); fd = open(LOCKFILE_NAME, O_CREAT|O_WRONLY, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH); if (fd < 0) { fprintf(stderr, "cannot open/create lock file %s\n", LOCKFILE_NAME); exit(EXIT_FAILURE); } lock.l_type = F_WRLCK; lock.l_start = 0; lock.l_whence = SEEK_SET; lock.l_len = 0; error = fcntl(fd, F_SETLK, &lock); if (error) { fprintf(stderr, "ocfs2_controld is already running\n"); exit(EXIT_FAILURE); } error = ftruncate(fd, 0); if (error) { fprintf(stderr, "cannot clear lock file %s\n", LOCKFILE_NAME); exit(EXIT_FAILURE); } sprintf(buf, "%d\n", getpid()); error = write(fd, buf, strlen(buf)); if (error <= 0) { fprintf(stderr, "cannot write lock file %s\n", LOCKFILE_NAME); exit(EXIT_FAILURE); } } static void daemonize(void) { int fd; pid_t pid = fork(); if (pid < 0) { perror("main: cannot fork"); exit(EXIT_FAILURE); } if (pid) exit(EXIT_SUCCESS); setsid(); chdir("/"); umask(0); close(0); close(1); close(2); fd = open("/dev/null", O_RDWR); if (fd >= 0) { /* dup2 to 0 / 1 / 2 (stdin / stdout / stderr) */ dup2(fd, STDIN_FILENO); /* 0 */ dup2(fd, STDOUT_FILENO); /* 1 */ dup2(fd, STDERR_FILENO); /* 2 */ /* Should be 0, but just in case it isn't... */ if (fd > 2) { close(fd); } } openlog("ocfs2_controld", LOG_PID, LOG_DAEMON); lockfile(); } static void print_usage(void) { printf("Usage:\n"); printf("\n"); printf("%s [options]\n", prog_name); printf("\n"); printf("Options:\n"); printf("\n"); printf(" -D Enable debugging code and don't fork\n"); printf(" -h Print this help, then exit\n"); printf(" -V Print program version information, then exit\n"); } static void decode_arguments(int argc, char **argv) { int cont = 1; int optchar; while (cont) { optchar = getopt(argc, argv, OPTION_STRING); switch (optchar) { case 'D': daemon_debug_opt = 1; break; case 'h': print_usage(); exit(EXIT_SUCCESS); break; case 'V': printf("ocfs2_controld (built %s %s)\n", __DATE__, __TIME__); /* printf("%s\n", REDHAT_COPYRIGHT); */ exit(EXIT_SUCCESS); break; case ':': case '?': fprintf(stderr, "Please use '-h' for usage.\n"); exit(EXIT_FAILURE); break; case EOF: cont = 0; break; default: fprintf(stderr, "unknown option: %c\n", optchar); exit(EXIT_FAILURE); break; }; } } static void set_oom_adj(int val) { FILE *fp; fp = fopen("/proc/self/oom_score_adj", "w"); if (!fp) return; fprintf(fp, "%i", val); fclose(fp); } static void set_scheduler(void) { struct sched_param sched_param; int rv; rv = sched_get_priority_max(SCHED_RR); if (rv != -1) { sched_param.sched_priority = rv; rv = sched_setscheduler(0, SCHED_RR, &sched_param); if (rv == -1) log_error("could not set SCHED_RR priority %d err %d", sched_param.sched_priority, errno); } else { log_error("could not get maximum scheduler priority err %d", errno); } } int main(int argc, char **argv) { errcode_t err; prog_name = argv[0]; const char *stack = NULL; init_mounts(); initialize_o2cb_error_table(); err = o2cb_init(); if (err) { com_err(prog_name, err, "while trying to initialize o2cb"); return 1; } err = o2cb_get_stack_name(&stack); if (err) { com_err(prog_name, err, "while determining the current cluster stack"); return 1; } if (strcmp(stack, stackname)) { fprintf(stderr, "%s: This daemon supports the \"%s\" stack, but the \"%s\" stack is in use\n", prog_name, stackname, stack); return 1; } decode_arguments(argc, argv); if (!daemon_debug_opt) daemonize(); set_scheduler(); set_oom_adj(-16); return loop(); } void daemon_dump_save(void) { int len, i; len = strlen(daemon_debug_buf); for (i = 0; i < len; i++) { dump_buf[dump_point++] = daemon_debug_buf[i]; if (dump_point == DUMP_SIZE) { dump_point = 0; dump_wrap = 1; } } } ocfs2-tools-ocfs2-tools-1.8.6/ocfs2_controld/mount.c000066400000000000000000000577671347147137200223350ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * Copyright (C) 2007 Oracle. All rights reserved. * * This copyrighted material is made available to anyone wishing to use, * modify, copy, or redistribute it subject to the terms and conditions * of the GNU General Public License v.2. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ocfs2-kernel/kernel-list.h" #include "ocfs2-kernel/sparse_endian_types.h" #include "ocfs2-kernel/ocfs2_fs.h" #include "o2cb/o2cb.h" #include "o2cb/o2cb_client_proto.h" #include "ocfs2_controld.h" /* OCFS2_VOL_UUID_LEN is in bytes. The hex string representation uses * two characters per byte */ #define OCFS2_UUID_STR_LEN (OCFS2_VOL_UUID_LEN * 2) struct service { struct list_head ms_list; char ms_service[PATH_MAX + 1]; int ms_additional; /* This is a second mount */ }; struct mountgroup { struct list_head mg_list; struct cgroup *mg_group; int mg_leave_on_join; int mg_registered; char mg_uuid[OCFS2_UUID_STR_LEN + 1]; char mg_device[PATH_MAX + 1]; struct list_head mg_services; struct service *mg_ms_in_progress; /* Communication with mount.ocfs2 */ int mg_mount_ci; int mg_mount_fd; int mg_mount_notified; int mg_error; char mg_error_msg[128]; }; static struct list_head mounts; static void fill_error(struct mountgroup *mg, int error, char *errfmt, ...) { int rc; va_list args; /* Don't overwrite an error */ if (mg->mg_error) return; mg->mg_error = error; va_start(args, errfmt); rc = vsnprintf(mg->mg_error_msg, sizeof(mg->mg_error_msg), errfmt, args); va_end(args); if (rc >= sizeof(mg->mg_error_msg)) { log_debug("Error message truncated"); mg->mg_error_msg[sizeof(mg->mg_error_msg) - 1] = '\0'; } } int have_mounts(void) { return !list_empty(&mounts); } static struct mountgroup *find_mg_by_uuid(const char *uuid) { struct list_head *p; struct mountgroup *mg; list_for_each(p, &mounts) { mg = list_entry(p, struct mountgroup, mg_list); if ((strlen(mg->mg_uuid) == strlen(uuid)) && !strncmp(mg->mg_uuid, uuid, strlen(uuid))) return mg; } return NULL; } static struct mountgroup *find_mg_by_client(int ci) { struct list_head *p; struct mountgroup *mg; if (ci < 0) return NULL; list_for_each(p, &mounts) { mg = list_entry(p, struct mountgroup, mg_list); if (mg->mg_mount_ci == ci) return mg; } return NULL; } static struct mountgroup *create_mg(const char *uuid, const char *device) { struct mountgroup *mg = NULL; if (strlen(uuid) > OCFS2_UUID_STR_LEN) { log_error("uuid too long!"); goto out; } mg = malloc(sizeof(struct mountgroup)); if (!mg) goto out; memset(mg, 0, sizeof(struct mountgroup)); INIT_LIST_HEAD(&mg->mg_services); mg->mg_mount_ci = -1; mg->mg_mount_fd = -1; strncpy(mg->mg_uuid, uuid, sizeof(mg->mg_uuid)); strncpy(mg->mg_device, device, sizeof(mg->mg_device)); list_add(&mg->mg_list, &mounts); out: return mg; } static void notify_mount_client(struct mountgroup *mg) { int error = mg->mg_error; char *error_msg = "OK"; if (error) { if (mg->mg_error_msg[0]) error_msg = mg->mg_error_msg; else error_msg = strerror(error); mg->mg_error = 0; } log_debug("notify_mount_client sending %d \"%s\"", error, error_msg); if (mg->mg_mount_fd < 0) { log_debug("not sending - client went away"); return; } error = send_message(mg->mg_mount_fd, CM_STATUS, error, error_msg); if (error) log_error("Unable to notify client, send_message failed with %d: %s", -error, strerror(-error)); else mg->mg_mount_notified = 1; /* * XXX If we failed to notify the client, what can we do? I'm * guessing that our main loop will get POLLHUP and we'll clean * up. */ } static struct service *find_service(struct mountgroup *mg, const char *service) { struct list_head *p; struct service *ms; list_for_each(p, &mg->mg_services) { ms = list_entry(p, struct service, ms_list); if ((strlen(ms->ms_service) == strlen(service)) && !strcmp(ms->ms_service, service)) return ms; } return NULL; } static void remove_service(struct mountgroup *mg, const char *service) { int rc; struct service *ms; ms = find_service(mg, service); if (!ms) { log_error("service \"%s\" not found for mountgroup \"%s\"", service, mg->mg_uuid); return; } list_del(&ms->ms_list); /* * We must clear the list here so that dead_mounter() * knows we're in the middle of a LEAVE. */ INIT_LIST_HEAD(&ms->ms_list); if (list_empty(&mg->mg_services)) { /* Set in-progress for leave */ mg->mg_ms_in_progress = ms; if (mg->mg_registered) { log_debug("Unregistering mountgroup %s", mg->mg_uuid); rc = dlmcontrol_unregister(mg->mg_uuid); if (rc) log_error("Unable to deregister mountgroup " "%s: %s", mg->mg_uuid, strerror(-rc)); mg->mg_registered = 0; } log_debug("time to leave group %s", mg->mg_uuid); if (mg->mg_group) { log_debug("calling LEAVE for group %s", mg->mg_uuid); if (group_leave(mg->mg_group)) { log_error("Unable to leave group %s", mg->mg_uuid); /* XXX what to do? finalize? Shutdown? */ } } else { /* * Join is in progress, let's leave when we get * there. */ log_debug("Not joined %s, so set leave_on_join", mg->mg_uuid); mg->mg_leave_on_join = 1; } } else free(ms); } static void add_service(struct mountgroup *mg, const char *device, const char *service, int ci, int fd) { struct service *ms; struct stat st1, st2; log_debug("Adding service \"%s\" to device \"%s\" uuid \"%s\"", service, device, mg->mg_uuid); if (stat(mg->mg_device, &st1)) { fill_error(mg, errno, "Failed to stat device \"%s\": %s", mg->mg_device, strerror(errno)); return; } if (stat(device, &st2)) { fill_error(mg, errno, "Failed to stat device \"%s\": %s", device, strerror(errno)); return; } if (st1.st_rdev != st2.st_rdev) { fill_error(mg, EINVAL, "Trying to mount fs \"%s\" on device \"%s\", " "but it is already mounted from device \"%s\"", mg->mg_uuid, device, mg->mg_device); return; } if (mg->mg_ms_in_progress) { fill_error(mg, EBUSY, "Another mount is in progress"); return; } ms = find_service(mg, service); if (ms) { /* * Real mounts use the OCFS2_FS_NAME service. There can * be more than one at a time. All other services may * only have one instance. */ if (strcmp(service, OCFS2_FS_NAME)) { fill_error(mg, EBUSY, "Filesystem %s is already mounted on %s", mg->mg_uuid, service); return; } /* * There can be more than one real mount. However, if an * additional mount fails in sys_mount(2), we can't have * complete_mount() removing the service. We only want that * to happen when it's the first mount. */ log_debug("Additional mount of %s starting for %s", mg->mg_uuid, service); ms->ms_additional = 1; } else { ms = malloc(sizeof(struct service)); if (!ms) { fill_error(mg, ENOMEM, "Unable to allocate service structure"); return; } memset(ms, 0, sizeof(struct service)); strncpy(ms->ms_service, service, sizeof(ms->ms_service)); /* Make sure a new service has the empty list */ INIT_LIST_HEAD(&ms->ms_list); } if ((mg->mg_mount_ci != -1) || (mg->mg_mount_fd != -1)) { log_error("adding a service, but ci/fd are set: %d %d", mg->mg_mount_ci, mg->mg_mount_fd); } mg->mg_mount_ci = ci; mg->mg_mount_fd = fd; mg->mg_ms_in_progress = ms; /* * This special error is returned to mount.ocfs2 when the filesystem * is already mounted elsewhere. The group is already joined, and * no additional work is required from ocfs2_controld. When * mount.ocfs2 sees this error, it will just clal mount(2). */ if (!list_empty(&mg->mg_services)) fill_error(mg, EALREADY, "Already mounted, go ahead"); /* new service */ if (list_empty(&ms->ms_list)) list_add(&ms->ms_list, &mg->mg_services); } static void register_result(int status, void *user_data) { struct mountgroup *mg = user_data; struct service *ms; if (!mg->mg_group) { log_error("No cgroup (mg %s)", mg->mg_uuid); return; } ms = mg->mg_ms_in_progress; if (!ms) { log_error("No service in progress for mountgroup %s", mg->mg_uuid); return; } if (status) { fill_error(mg, -status, "Error registering mg %s with dlm_controld: %s", mg->mg_uuid, strerror(-status)); /* remove_service() will kick off a LEAVE if needed */ remove_service(mg, ms->ms_service); return; } log_debug("Mountgroup %s successfully registered with dlm_controld", mg->mg_uuid); mg->mg_registered = 1; notify_mount_client(mg); } static void finish_join(struct mountgroup *mg, struct cgroup *cg) { int rc; struct service *ms; if (mg->mg_group) { log_error("cgroup passed, but one already exists! (mg %s, existing %p, new %p)", mg->mg_uuid, mg->mg_group, cg); return; } ms = mg->mg_ms_in_progress; if (!ms) { log_error("No service in progress for mountgroup %s", mg->mg_uuid); return; } if (list_empty(&ms->ms_list)) { if (mg->mg_leave_on_join) { if (group_leave(cg)) { log_error("Unable to leave group %s", mg->mg_uuid); /* XXX What to do? */ } } else { log_error("mountgroup %s is in the process of leaving, not joining", mg->mg_uuid); } return; } if (list_empty(&mg->mg_services)) { log_error("No services on mountgroup %s", mg->mg_uuid); return; } /* Ok, we've successfully joined the group */ mg->mg_group = cg; /* Now tell dlm_controld */ log_debug("Registering mountgroup %s with dlm_controld", mg->mg_uuid); rc = dlmcontrol_register(mg->mg_uuid, register_result, mg); if (rc) { fill_error(mg, -rc, "Unable to register mountgroup %s with " "dlm_controld: %s", mg->mg_uuid, strerror(errno)); /* remove_service() will kick off a LEAVE if needed */ remove_service(mg, ms->ms_service); } } static void mount_node_down(int nodeid, void *user_data) { struct mountgroup *mg = user_data; errcode_t err; log_debug("Node %d has left mountgroup %s", nodeid, mg->mg_uuid); err = o2cb_control_node_down(mg->mg_uuid, nodeid); if (err) log_debug("%s while trying to send DOWN message", error_message(err)); dlmcontrol_node_down(mg->mg_uuid, nodeid); } static void finish_leave(struct mountgroup *mg) { struct list_head *p, *n; struct service *ms; struct timespec ts; if (list_empty(&mg->mg_services) && mg->mg_ms_in_progress) { /* We're done */ notify_mount_client(mg); /* This is possible due to leave_on_join */ if (!mg->mg_group) log_debug("mg_group was NULL"); free(mg->mg_ms_in_progress); goto out; } /* * This leave is unexpected. If we weren't part of the group, we * just cleanup our state. However, if we were part of a group, we * cannot safely continue and must die. Fail-fast allows other * nodes to make a decision about us. */ log_error("Unexpected leave of group %s", mg->mg_uuid); if (mg->mg_group) { log_error("Group %s is live, exiting", mg->mg_uuid); /* * The _exit(2) may cause a reboot, and we want the errors * to hit syslogd(8). We can't call sync(2) which might * sleep on an ocfs2 operation. I'd say sleeping for 10ms * is a good compromise. Local syslogd(8) won't have time * to write to disk, but a network syslogd(8) should get * the data. */ ts.tv_sec = 0; ts.tv_nsec = 10000000; nanosleep(&ts, NULL); _exit(1); } log_error("No mg_group for group %s", mg->mg_uuid); list_for_each_safe(p, n, &mg->mg_services) { ms = list_entry(p, struct service, ms_list); list_del(&ms->ms_list); /* The in-progress ms may or may not be on the list */ if (ms != mg->mg_ms_in_progress) free(ms); } /* So free the in-progress ms last */ if (mg->mg_ms_in_progress) free(mg->mg_ms_in_progress); /* If we had a client attached, let it know we died */ if (mg->mg_mount_ci != -1) connection_dead(mg->mg_mount_ci); out: list_del(&mg->mg_list); free(mg); } /* * This is called when we join or leave a group. There are three possible * states. * * 1) We've asked to join a group for a new filesystem. * - mg_ms_in_progress != NULL * - length(mg_services) == 1 * - mg_group == NULL * * cg will be our now-joined group. * * 2) We've asked to leave a group upon the last unmount of a filesystem. * - mg_ms_in_progress != NULL * - mg_services is empty * - mg_group is only NULL if we had to set leave_on_join. * * cg is NULL. We should complete our leave. * * 3) We've dropped out of the group unexpectedly. * - mg_services is not empty. * - mg_group != NULL * * cg is NULL. We should basically crash. This usually is handled by * closing our sysfs fd. */ static void mount_set_group(struct cgroup *cg, void *user_data) { struct mountgroup *mg = user_data; if (cg) finish_join(mg, cg); else finish_leave(mg); } /* * THIS FUNCTION CAUSES PROBLEMS. * * bail_on_mounts() is called when we are forced to exit via a signal * or cluster stack dying on us. As such, it tells ocfs2 that nodes * are down but not communicate with the stack or cpg. This can cause * ocfs2 to self-fence or the stack to go nuts. But hey, if you SIGKILL * the daemon, you get what you pay for. */ void bail_on_mounts(void) { struct list_head *p, *n; struct mountgroup *mg; list_for_each_safe(p, n, &mounts) { mg = list_entry(p, struct mountgroup, mg_list); finish_leave(mg); } } int start_mount(int ci, int fd, const char *uuid, const char *device, const char *service) { int rc = 0; struct mountgroup mg_error = { /* Until we have a real mg */ .mg_error = 0, }; struct mountgroup *mg = &mg_error; log_debug("start_mount: uuid \"%s\", device \"%s\", service \"%s\"", uuid, device, service); if (strlen(uuid) > OCFS2_UUID_STR_LEN) { fill_error(mg, ENAMETOOLONG, "UUID too long: %s", uuid); goto out; } mg = find_mg_by_uuid(uuid); if (mg) { add_service(mg, device, service, ci, fd); goto out; } /* Here we stop using &mg_error and start using the real one */ mg = create_mg(uuid, device); if (!mg) { mg = &mg_error; /* Well, almost did */ fill_error(mg, ENOMEM, "Unable to allocate mountgroup structure"); goto out; } add_service(mg, device, service, ci, fd); if (mg->mg_error) goto out; /* * Fire off a group join. The cpg infrastructure will * let us know when the group is joined, at which point we * notify_mount_client(). If there's a failure, we notify as well. */ rc = group_join(mg->mg_uuid, mount_set_group, mount_node_down, mg); if (rc) { fill_error(mg, -rc, "Unable to start join to group %s", mg->mg_uuid); /* * Because we never started a join, mg->mg_group is NULL. * remove_service() will set up for leave_on_join, but * that actually never happens. Thus, it is safe to * clear ms_in_progress. */ remove_service(mg, service); if (mg->mg_ms_in_progress) { free(mg->mg_ms_in_progress); mg->mg_ms_in_progress = NULL; } else log_error("First mount of %s failed a join, yet ms_in_progress was NULL", mg->mg_uuid); } out: /* * Only reply on error. If we're doing OK, the reply is delayed * until join completes (notify_mount_client()). * * This reply includes -EALREADY, which tells the mount client that * we're doing an additional mount - it can just go ahead. */ if (mg->mg_error) { rc = -mg->mg_error; send_message(fd, CM_STATUS, mg->mg_error, mg->mg_error_msg); mg->mg_error = 0; if (rc == -EALREADY) mg->mg_mount_notified = 1; else { log_error("mount: %s", mg->mg_error_msg); if ((mg != &mg_error) && list_empty(&mg->mg_services)) { log_debug("mount: freeing failed mountgroup"); list_del(&mg->mg_list); free(mg); } } } log_debug("start_mount returns %d", rc); return rc; } int complete_mount(int ci, int fd, const char *uuid, const char *errcode, const char *service) { int rc = 0; int reply = 1; struct mountgroup mg_error = { /* Until we have a real mg */ .mg_error = 0, }; struct mountgroup *mg; struct service *ms; long err; char *ptr = NULL; log_debug("complete_mount: uuid \"%s\", errcode \"%s\", service \"%s\"", uuid, errcode, service); mg = find_mg_by_client(ci); if (!mg) { mg = &mg_error; fill_error(mg, EINVAL, "Client is not attached to a mountgroup"); goto out; } if (mg->mg_mount_fd != fd) { fill_error(mg, EINVAL, "Client file descriptor does not match"); goto out; } if (strlen(uuid) > OCFS2_UUID_STR_LEN) { fill_error(mg, EINVAL, "UUID too long: %s", uuid); goto out; } if (strcmp(uuid, mg->mg_uuid)) { fill_error(mg, EINVAL, "UUID %s does not match mountgroup %s", uuid, mg->mg_uuid); goto out; } if (!mg->mg_ms_in_progress) { fill_error(mg, ENOENT, "No mount in progress for filesystem %s", mg->mg_uuid); goto out; } ms = find_service(mg, service); if (!ms) { fill_error(mg, ENOENT, "Unknown service %s for filesystem %s", service, mg->mg_uuid); goto out; } if (ms != mg->mg_ms_in_progress) { fill_error(mg, EINVAL, "Service %s is not in progress", service); goto out; } err = strtol(errcode, &ptr, 10); if (ptr && *ptr != '\0') { fill_error(mg, EINVAL, "Invalid error code string: %s", errcode); goto out; } if ((err == LONG_MIN) || (err == LONG_MAX) || (err < INT_MIN) || (err > INT_MAX)) { fill_error(mg, ERANGE, "Error code %ld is out of range", err); goto out; } /* * Clear the in-progress pointer and store off the reply fd. If * there was an error, remove_service may reset the in-progress * pointer. */ mg->mg_ms_in_progress = NULL; if (ms->ms_additional) { /* * Our additional real mount is done whether it succeeded * or failed. We only have to clear the additional state * and reply OK. */ log_debug("Completed additional mount of filesystem %s, " "error is %ld", mg->mg_uuid, err); ms->ms_additional = 0; err = 0; } if (!err) { mg->mg_mount_fd = -1; mg->mg_mount_ci = -1; } else { /* * remove_service() will kick off a leave if this was * the last service. As part of the leave, it will set * ms_in_progress. */ remove_service(mg, service); /* * We don't pass err onto mg->mg_error because it came * from mount.ocfs2. We actually respond with 0, as we * successfully processed the MRESULT. Unless * remove_service() set mg_error. */ } if (mg->mg_ms_in_progress) reply = 0; out: if (reply) send_message(fd, CM_STATUS, mg->mg_error, mg->mg_error ? mg->mg_error_msg : "OK"); return rc; } int remove_mount(int ci, int fd, const char *uuid, const char *service) { int rc = 0; int reply = 1; struct mountgroup mg_error = { .mg_error = 0, }; struct mountgroup *mg = NULL; struct service *ms; log_debug("remove_mount: uuid \"%s\", service \"%s\"", uuid, service); if (strlen(uuid) > OCFS2_UUID_STR_LEN) { fill_error(&mg_error, ENAMETOOLONG, "UUID too long: %s", uuid); goto out; } mg = find_mg_by_uuid(uuid); if (!mg) { fill_error(&mg_error, ENOENT, "Filesystem %s is unknown or not mounted anywhere", uuid); goto out; } /* find_mg() should fail if the uuid isn't mounted *somewhere* */ if (list_empty(&mg->mg_services)) log_error("Service list is empty!"); ms = find_service(mg, service); if (!ms) { fill_error(&mg_error, ENOENT, "Service %s is not mounted on %s", uuid, service); goto out; } if (mg->mg_ms_in_progress) { fill_error(&mg_error, EBUSY, "Another mount is in progress"); /* * If the service we're removing has ms_additional set, it * must be the filesystem service. That means the * in_progress service is an additional real mount, but * the kernel is no longer mounted. * * As such, the in-progress service is now a new mount, and * we clear the ms_addditional flag. It will succeed or * fail as a new mount. */ if (ms->ms_additional) { if (ms != mg->mg_ms_in_progress) log_error("Somehow ms_additional was set " "even though the in-progress " "mount isn't the filesystem " "(group %s, removing %s, " "in-progress %s", mg->mg_uuid, ms->ms_service, mg->mg_ms_in_progress->ms_service); ms->ms_additional = 0; } goto out; } if ((mg->mg_mount_ci != -1) || (mg->mg_mount_fd != -1)) { log_error("removing a service, but ci/fd are set: %d %d", mg->mg_mount_ci, mg->mg_mount_fd); } remove_service(mg, service); if (mg->mg_ms_in_progress) { /* * remove_service() kicked off a LEAVE. It needs the * client connection information. It will * handle replying via notify_mount_client(). */ mg->mg_mount_ci = ci; mg->mg_mount_fd = fd; reply = 0; } else if (mg->mg_error) { fill_error(&mg_error, mg->mg_error, "%s", mg->mg_error_msg); } out: if (reply) send_message(fd, CM_STATUS, mg_error.mg_error, mg_error.mg_error ? mg_error.mg_error_msg : "OK"); if (mg_error.mg_error) rc = -mg_error.mg_error; return rc; } void dead_mounter(int ci, int fd) { struct mountgroup *mg; struct service *ms; /* If there's no matching mountgroup, nothing to do. */ mg = find_mg_by_client(ci); if (!mg) return; ms = mg->mg_ms_in_progress; /* If we have nothing in progress, nothing to do. */ if (!ms) return; log_error("Mounter for filesystem %s, service %s died", mg->mg_uuid, ms->ms_service); mg->mg_mount_ci = -1; mg->mg_mount_fd = -1; /* * If ms_list is empty, the daemon is in the process * of leaving the group. We need that to complete whether we * have a client or not. */ if (list_empty(&ms->ms_list)) return; /* * If this was just an additional real mount, we just clear the * state. */ if (ms->ms_additional) { log_debug("Additional mounter of filesystem %s died", mg->mg_uuid); ms->ms_additional = 0; mg->mg_ms_in_progress = NULL; return; } /* * We haven't notified the client yet. Thus, the client can't have * called mount(2). Let's just abort this service. If this was * the last service, we'll plan to leave the group. */ if (!mg->mg_mount_notified) { remove_service(mg, ms->ms_service); return; } /* * XXX * * This is the hard one. If we've notified the client, we're * expecting the client to call mount(2). But the client died. * We don't know if that happened, so we can't leave the group. * * We do know, though, that all the other in-progress operations * (group join, dlmc_fs_register) must have completed, or we * wouldn't have set mg_mount_notified. Thus, we can treat it as * a live mount. Witness the power of a fully armed and * operational mountgroup. * * We can clear the in-progress flag and allow other mounters. If * it really mounted, it can be unmounted. If it didn't mount, the * state can be torn down with ocfs2_hb_ctl. * * Maybe later we'll learn how to detect the mount via the kernel * and tear it down ourselves. But not right now. */ log_error("Kernel mount of filesystem %s already entered, " "assuming it succeeded", mg->mg_uuid); mg->mg_ms_in_progress = NULL; } int send_mountgroups(int ci, int fd) { unsigned int count = 0; int rc = 0, rctmp; char error_msg[100]; /* Arbitrary size smaller than a message */ struct list_head *p; struct mountgroup *mg; list_for_each(p, &mounts) { count++; } rc = send_message(fd, CM_ITEMCOUNT, count); if (rc) { snprintf(error_msg, sizeof(error_msg), "Unable to send ITEMCOUNT: %s", strerror(-rc)); goto out_status; } list_for_each(p, &mounts) { mg = list_entry(p, struct mountgroup, mg_list); rc = send_message(fd, CM_ITEM, mg->mg_uuid); if (rc) { snprintf(error_msg, sizeof(error_msg), "Unable to send ITEM: %s", strerror(-rc)); goto out_status; } } strcpy(error_msg, "OK"); out_status: log_debug("Sending status %d \"%s\"", -rc, error_msg); rctmp = send_message(fd, CM_STATUS, -rc, error_msg); if (rctmp) { log_error("Error sending STATUS message: %s", strerror(-rc)); if (!rc) rc = rctmp; } return rc; } void init_mounts(void) { INIT_LIST_HEAD(&mounts); } ocfs2-tools-ocfs2-tools-1.8.6/ocfs2_controld/ocfs2_controld.h000066400000000000000000000112061347147137200240720ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: */ /****************************************************************************** ******************************************************************************* ** ** Copyright (C) 2005 Red Hat, Inc. All rights reserved. ** ** This copyrighted material is made available to anyone wishing to use, ** modify, copy, or redistribute it subject to the terms and conditions ** of the GNU General Public License v.2. ** ******************************************************************************* ******************************************************************************/ /* * Copyright (C) 2007 Oracle. All rights reserved. * * This copyrighted material is made available to anyone wishing to use, * modify, copy, or redistribute it subject to the terms and conditions * of the GNU General Public License v.2. */ #ifndef __OCFS2_CONTROLD_H #define __OCFS2_CONTROLD_H #define DUMP_SIZE (1024 * 1024) struct cgroup; struct ckpt_handle; extern char *prog_name; extern int daemon_debug_opt; extern char daemon_debug_buf[1024]; extern char dump_buf[DUMP_SIZE]; extern int dump_point; extern int dump_wrap; extern int our_nodeid; extern const char *stackname; extern void daemon_dump_save(void); #define log_debug(fmt, args...) \ do { \ snprintf(daemon_debug_buf, 1023, "%ld %s@%d: " fmt "\n", \ time(NULL), __FUNCTION__, __LINE__, ##args); \ if (daemon_debug_opt) fprintf(stderr, "%s", daemon_debug_buf); \ daemon_dump_save(); \ } while (0) #define log_error(fmt, args...) \ do { \ log_debug(fmt, ##args); \ syslog(LOG_ERR, fmt, ##args); \ } while (0) /* main.c */ int connection_add(int fd, void (*work)(int ci), void (*dead)(int ci)); void connection_dead(int ci); void shutdown_daemon(void); /* ckpt.c */ int setup_ckpt(void); void exit_ckpt(void); int ckpt_open_global(int write); void ckpt_close_global(void); int ckpt_open_node(int nodeid, struct ckpt_handle **handle); int ckpt_open_this_node(struct ckpt_handle **handle); void ckpt_close(struct ckpt_handle *handle); int ckpt_global_store(const char *section, const char *data, size_t data_len); int ckpt_global_get(const char *section, char **data, size_t *data_len); int ckpt_section_store(struct ckpt_handle *handle, const char *section, const char *data, size_t data_len); int ckpt_section_get(struct ckpt_handle *handle, const char *section, char **data, size_t *data_len); /* stack-specific interfaces (cman.c) */ int setup_stack(void); char *nodeid2name(int nodeid); int validate_cluster(const char *cluster); int get_clustername(const char **cluster); int kill_stack_node(int nodeid); void exit_stack(void); /* cpg.c */ int setup_cpg(void (*daemon_joined)(int first)); void exit_cpg(void); void for_each_node(struct cgroup *cg, void (*func)(int nodeid, void *user_data), void *user_data); int group_join(const char *name, void (*set_cgroup)(struct cgroup *cg, void *user_data), void (*node_down)(int nodeid, void *user_data), void *user_data); int group_leave(struct cgroup *cg); /* dlmcontrol.c */ int setup_dlmcontrol(void); void exit_dlmcontrol(void); int dlmcontrol_register(const char *name, void (*result_func)(int status, void *user_data), void *user_data); int dlmcontrol_unregister(const char *name); void dlmcontrol_node_down(const char *name, int nodeid); /* mount.c */ void init_mounts(void); int have_mounts(void); int start_mount(int ci, int fd, const char *uuid, const char *device, const char *mountpoint); int complete_mount(int ci, int fd, const char *uuid, const char *errcode, const char *mountpoint); int remove_mount(int ci, int fd, const char *uuid, const char *mountpoint); void dead_mounter(int ci, int fd); void bail_on_mounts(void); int send_mountgroups(int ci, int fd); /* * We need to do some retries in more than one file. * Here's some code that prints an error as we take a long time to do it. * There is a power-of-two backoff on printing the error. */ static inline void sleep_ms(unsigned int ms) { struct timespec ts = { .tv_sec = ms / 1000, .tv_nsec = (ms % 1000) * 1000, }; nanosleep(&ts, NULL); } /* We use this for our backoff print */ static inline unsigned int hc_hweight32(unsigned int w) { unsigned int res = (w & 0x55555555) + ((w >> 1) & 0x55555555); res = (res & 0x33333333) + ((res >> 2) & 0x33333333); res = (res & 0x0F0F0F0F) + ((res >> 4) & 0x0F0F0F0F); res = (res & 0x00FF00FF) + ((res >> 8) & 0x00FF00FF); return (res & 0x0000FFFF) + ((res >> 16) & 0x0000FFFF); } #define retry_warning(count, fmt, arg...) do { \ if (hc_hweight32(count) == 1) \ log_error(fmt, ##arg); \ count++; \ } while (0) #endif ocfs2-tools-ocfs2-tools-1.8.6/ocfs2_controld/pacemaker.c000066400000000000000000000075001347147137200230770ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: */ /* * Copyright (C) 2008 Novell. * * Some portions Copyright Oracle. * * This copyrighted material is made available to anyone wishing to use, * modify, copy, or redistribute it subject to the terms and conditions * of the GNU General Public License v.2. */ #include #include #include #include #include #include #include #include #include /* heartbeat support is irrelevant here */ #undef SUPPORT_HEARTBEAT #define SUPPORT_HEARTBEAT 0 #include #include #include #include #include #include #include "ocfs2-kernel/kernel-list.h" #include "o2cb/o2cb.h" #include "ocfs2_controld.h" #include int our_nodeid = 0; static int pcmk_ci; static char * clustername = "pacemaker"; extern struct list_head mounts; const char *stackname = "pcmk"; extern int ais_fd_async; char *local_node_uname = NULL; static int pcmk_cluster_fd = 0; static void stonith_callback(int ci) { log_error("%s: Lost connection to the cluster", __FUNCTION__); pcmk_cluster_fd = 0; return; } int kill_stack_node(int nodeid) { int fd = pcmk_cluster_fd; int rc = crm_terminate_member_no_mainloop(nodeid, NULL, &fd); if(fd > 0 && fd != pcmk_cluster_fd) { pcmk_cluster_fd = fd; connection_add(pcmk_cluster_fd, NULL, stonith_callback); } switch(rc) { case 1: log_debug("Requested that node %d be kicked from the cluster", nodeid); break; case -1: log_error("Don't know how to kick node %d from the cluster", nodeid); break; case 0: log_error("Could not kick node %d from the cluster", nodeid); break; default: log_error("Unknown result when kicking node %d from the cluster", nodeid); break; } return rc; } char *nodeid2name(int nodeid) { crm_node_t *node = crm_get_peer(nodeid, NULL); if(node->uname == NULL) return NULL; return strdup(node->uname); } int validate_cluster(const char *cluster) { if (!clustername) { log_error("Trying to validate before pacemaker is alive"); return 0; } if (!cluster) return 0; return !strcmp(cluster, clustername); } int get_clustername(const char **cluster) { if (!clustername) { log_error("Trying to validate before pacemaker is alive"); return -EIO; } if (!cluster) { log_error("NULL passed!"); return -EINVAL; } *cluster = clustername; return 0; } static void dead_pcmk(int ci) { if (ci != pcmk_ci) { log_error("Unknown connection %d", ci); return; } log_error("pacemaker connection died"); shutdown_daemon(); connection_dead(ci); } extern void terminate_ais_connection(void); void exit_stack(void) { log_debug("closing pacemaker connection"); terminate_ais_connection(); } static void process_pcmk(int ci) { ais_dispatch(ais_fd_async, NULL); } int setup_stack(void) { crm_log_init("ocfs2_controld", LOG_INFO, FALSE, TRUE, 0, NULL); if(init_ais_connection(NULL, NULL, NULL, &local_node_uname, &our_nodeid) == FALSE) { log_error("Connection to our AIS plugin (CRM) failed"); return -1; } /* Sign up for membership updates */ send_ais_text(crm_class_notify, "true", TRUE, NULL, crm_msg_ais); /* Requesting the current list of known nodes */ send_ais_text(crm_class_members, __FUNCTION__, TRUE, NULL, crm_msg_ais); log_debug("Cluster connection established. Local node id: %d", our_nodeid); pcmk_ci = connection_add(ais_fd_async, process_pcmk, dead_pcmk); if (pcmk_ci >= 0) { log_debug("Added Pacemaker as client %d with fd %d", pcmk_ci, ais_fd_async); return ais_fd_async; } log_error("Unable to add pacemaker client: %s", strerror(-pcmk_ci)); exit_stack(); return pcmk_ci; } ocfs2-tools-ocfs2-tools-1.8.6/ocfs2_controld/test_client.c000066400000000000000000000166101347147137200234660ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * Copyright (C) 2007 Oracle. All rights reserved. * * This copyrighted material is made available to anyone wishing to use, * modify, copy, or redistribute it subject to the terms and conditions * of the GNU General Public License v.2. */ #include #include #include #include #include #include #include #include #include "ocfs2/ocfs2.h" #include "o2cb/o2cb_client_proto.h" static errcode_t fill_uuid(const char *device, char *uuid) { errcode_t err; ocfs2_filesys *fs; struct o2cb_region_desc desc; err = ocfs2_open(device, OCFS2_FLAG_RO, 0, 0, &fs); if (err) goto out; err = ocfs2_fill_heartbeat_desc(fs, &desc); ocfs2_close(fs); if (!err) strncpy(uuid, desc.r_name, OCFS2_VOL_UUID_LEN + 1); out: return err; } static int call_mount(int fd, const char *uuid, const char *cluster, const char *device, const char *mountpoint) { int rc; int error; char *error_msg; client_message message; char *argv[OCFS2_CONTROLD_MAXARGS + 1]; char buf[OCFS2_CONTROLD_MAXLINE]; rc = send_message(fd, CM_MOUNT, OCFS2_FS_NAME, uuid, cluster, device, mountpoint); if (rc) { fprintf(stderr, "Unable to send MOUNT message: %s\n", strerror(-rc)); goto out; } rc = receive_message(fd, buf, &message, argv); if (rc < 0) { fprintf(stderr, "Error reading from daemon: %s\n", strerror(-rc)); goto out; } switch (message) { case CM_STATUS: rc = parse_status(argv, &error, &error_msg); if (rc) { fprintf(stderr, "Bad status message: %s\n", strerror(-rc)); goto out; } if (error && (error != EALREADY)) { rc = -error; fprintf(stderr, "Error %d from daemon: %s\n", error, error_msg); goto out; } break; default: rc = -EINVAL; fprintf(stderr, "Unexpected message %s from daemon\n", message_to_string(message)); goto out; break; } /* XXX Here we fake mount */ /* rc = mount(...); */ rc = 0; rc = send_message(fd, CM_MRESULT, OCFS2_FS_NAME, uuid, rc, mountpoint); if (rc) { fprintf(stderr, "Unable to send MRESULT message: %s\n", strerror(-rc)); goto out; } rc = receive_message(fd, buf, &message, argv); if (rc < 0) { fprintf(stderr, "Error reading from daemon: %s\n", strerror(-rc)); goto out; } switch (message) { case CM_STATUS: rc = parse_status(argv, &error, &error_msg); if (rc) { fprintf(stderr, "Bad status message: %s\n", strerror(-rc)); goto out; } if (error) { rc = -error; fprintf(stderr, "Error %d from daemon: %s\n", error, error_msg); } break; default: rc = -EINVAL; fprintf(stderr, "Unexpected message %s from daemon\n", message_to_string(message)); break; } out: return rc; } static int call_unmount(int fd, const char *uuid, const char *mountpoint) { int rc = 0; int error; char *error_msg; client_message message; char *argv[OCFS2_CONTROLD_MAXARGS + 1]; char buf[OCFS2_CONTROLD_MAXLINE]; #if 0 errcode_t err; FILE *mntfile; struct mntent *entp; char device[PATH_MAX + 1]; char uuid[OCFS2_VOL_UUID_LEN + 1]; device[0] = '\0'; mntfile = setmntent("/tmp/fakemtab", "r"); if (!mntfile) { rc = -errno; fprintf(stderr, "Unable to open mtab: %s\n", strerror(-rc)); goto out; } while ((entp = getmntent(mntfile)) != NULL) { if (strcmp(entp->mnt_type, OCFS2_FSTYPE)) continue; if (strcmp(entp->mnt_type, mountpoint)) continue; strncpy(device, entp->mnt_fsname, PATH_MAX); } endmntent(mntfile); if (!*device) { rc = -ENOENT; fprintf(stderr, "Unable to find filesystem %s\n", mountpoint); goto out; } err = fill_uuid(device, uuid); if (err) { com_err("test_client", err, "while trying to read uuid from %s", device); rc = -EIO; goto out; } #endif rc = send_message(fd, CM_UNMOUNT, OCFS2_FS_NAME, uuid, mountpoint); if (rc) { fprintf(stderr, "Unable to send UNMOUNT message: %s\n", strerror(-rc)); goto out; } rc = receive_message(fd, buf, &message, argv); if (rc < 0) { fprintf(stderr, "Error reading from daemon: %s\n", strerror(-rc)); goto out; } switch (message) { case CM_STATUS: rc = parse_status(argv, &error, &error_msg); if (rc) { fprintf(stderr, "Bad status message: %s\n", strerror(-rc)); goto out; } if (error) { rc = -error; fprintf(stderr, "Error %d from daemon: %s\n", error, error_msg); goto out; } break; default: rc = -EINVAL; fprintf(stderr, "Unexpected message %s from daemon\n", message_to_string(message)); goto out; break; } out: return rc; } static int call_listclusters(int fd) { int rc, i; char **list; char buf[OCFS2_CONTROLD_MAXLINE]; rc = send_message(fd, CM_LISTCLUSTERS); if (rc) { fprintf(stderr, "Unable to send LISTCLUSTERS message: %s\n", strerror(-rc)); goto out; } rc = receive_list(fd, buf, &list); if (rc < 0) { fprintf(stderr, "Error reading from daemon: %s\n", strerror(-rc)); goto out; } for (i = 0; list[i]; i++) fprintf(stderr, "%s\n", list[i]); free_received_list(list); out: return rc; } static int call_listfs(int fd, const char *cluster) { int rc, i; char **list; char buf[OCFS2_CONTROLD_MAXLINE]; rc = send_message(fd, CM_LISTFS, OCFS2_FS_NAME, cluster); if (rc) { fprintf(stderr, "Unable to send LISTFS message: %s\n", strerror(-rc)); goto out; } rc = receive_list(fd, buf, &list); if (rc < 0) { fprintf(stderr, "Error reading from daemon: %s\n", strerror(-rc)); goto out; } for (i = 0; list[i]; i++) fprintf(stderr, "%s\n", list[i]); free_received_list(list); out: return rc; } enum { OP_MOUNT, OP_UMOUNT, OP_LISTCLUSTERS, OP_LISTFS, }; static int parse_options(int argc, char **argv, int *op, char ***args) { int rc = 0; if (argc < 2) { fprintf(stderr, "Operation required\n"); return -EINVAL; } if (!strcmp(argv[1], "mount")) { if (argc == 6) { *op = OP_MOUNT; *args = argv + 2; } else { fprintf(stderr, "Invalid number of arguments\n"); rc = -EINVAL; } } else if (!strcmp(argv[1], "umount")) { if (argc == 4) { *op = OP_UMOUNT; *args = argv + 2; } else { fprintf(stderr, "Invalid number of arguments\n"); rc = -EINVAL; } } else if (!strcmp(argv[1], "listclusters")) { if (argc == 2) { *op = OP_LISTCLUSTERS; *args = argv + 2; } else { fprintf(stderr, "Invalid number of arguments\n"); rc = -EINVAL; } } else if (!strcmp(argv[1], "listfs")) { if (argc == 3) { *op = OP_LISTFS; *args = argv + 2; } else { fprintf(stderr, "Invalid number of arguments\n"); rc = -EINVAL; } } else { fprintf(stderr, "Invalid operation: %s\n", argv[1]); rc = -EINVAL; } return rc; } int main(int argc, char **argv) { int rc, fd, op; char **args; rc = parse_options(argc, argv, &op, &args); if (rc) goto out; rc = ocfs2_client_connect(); if (rc < 0) { fprintf(stderr, "Unable to connect to ocfs2_controld: %s\n", strerror(-rc)); goto out; } fd = rc; switch (op) { case OP_MOUNT: rc = call_mount(fd, args[0], args[1], args[2], args[3]); break; case OP_UMOUNT: rc = call_unmount(fd, args[0], args[1]); break; case OP_LISTCLUSTERS: rc = call_listclusters(fd); break; case OP_LISTFS: rc = call_listfs(fd, args[0]); break; default: fprintf(stderr, "Can't get here!\n"); rc = -ENOTSUP; break; } close(fd); out: return rc; } ocfs2-tools-ocfs2-tools-1.8.6/ocfs2_hb_ctl/000077500000000000000000000000001347147137200204105ustar00rootroot00000000000000ocfs2-tools-ocfs2-tools-1.8.6/ocfs2_hb_ctl/.gitignore000066400000000000000000000000571347147137200224020ustar00rootroot00000000000000*.sw? ocfs2_hb_ctl ocfs2_hb_ctl.8 *.d cscope.* ocfs2-tools-ocfs2-tools-1.8.6/ocfs2_hb_ctl/Cscope.make000066400000000000000000000004441347147137200224650ustar00rootroot00000000000000.PHONY: cscope cscope: rm -f cscope.* echo "-k" >> cscope.files echo "-I inc" >> cscope.files find . -name '*.[ch]' >>cscope.files find ../libocfs2/ -name '*.[ch]' >>cscope.files find ../libo2cb/ -name '*.[ch]' >>cscope.files find ../libo2dlm/ -name '*.[ch]' >>cscope.files cscope -b ocfs2-tools-ocfs2-tools-1.8.6/ocfs2_hb_ctl/Makefile000066400000000000000000000017211347147137200220510ustar00rootroot00000000000000TOPDIR = .. include $(TOPDIR)/Preamble.make sbindir = $(root_sbindir) SBIN_PROGRAMS = ocfs2_hb_ctl INCLUDES = -I$(TOPDIR)/include LIBOCFS2_LIBS = -L$(TOPDIR)/libocfs2 -locfs2 LIBOCFS2_DEPS = $(TOPDIR)/libocfs2/libocfs2.a LIBO2DLM_LIBS = -L$(TOPDIR)/libo2dlm -lo2dlm $(DL_LIBS) LIBO2DLM_DEPS = $(TOPDIR)/libo2dlm/libo2dlm.a LIBO2CB_LIBS = -L$(TOPDIR)/libo2cb -lo2cb LIBO2CB_DEPS = $(TOPDIR)/libo2cb/libo2cb.a ifneq ($(BUILD_FSDLM_SUPPORT),) LIBO2CB_LIBS += -ldlm_lt endif ifneq ($(BUILD_CMAP_SUPPORT),) LIBO2CB_LIBS += -lcmap endif ifndef OCFS2_DYNAMIC_CTL LDFLAGS += -static endif DEFINES = -DVERSION=\"$(VERSION)\" CFILES = ocfs2_hb_ctl.c HFILES = OBJS = $(subst .c,.o,$(CFILES)) MANS = ocfs2_hb_ctl.8 DIST_FILES = $(CFILES) $(HFILES) ocfs2_hb_ctl.8.in all: ocfs2_hb_ctl ocfs2_hb_ctl: $(OBJS) $(LIBOCFS2_DEPS) $(LIBO2DLM_DEPS) $(LIBO2CB_DEPS) $(LINK) $(LIBOCFS2_LIBS) $(LIBO2DLM_LIBS) $(LIBO2CB_LIBS) $(COM_ERR_LIBS) $(AIO_LIBS) include $(TOPDIR)/Postamble.make ocfs2-tools-ocfs2-tools-1.8.6/ocfs2_hb_ctl/ocfs2_hb_ctl.8.in000066400000000000000000000041161347147137200234370ustar00rootroot00000000000000.TH "ocfs2_hb_ctl" "8" "January 2012" "Version @VERSION@" "OCFS2 Manual Pages" .SH "NAME" ocfs2_hb_ctl \- Starts and stops the \fIO2CB\fR \fBlocal\fR heartbeat on a given device. .SH "SYNOPSIS" .B ocfs2_hb_ctl \fB-S\fR \fB-d\fR \fIdevice\fR \fIservice\fR .br .B ocfs2_hb_ctl \fB-S\fR \fB-u\fR \fIuuid\fR \fIservice\fR .br .B ocfs2_hb_ctl \fB-K\fR \fB-d\fR \fIdevice\fR \fIservice\fR .br .B ocfs2_hb_ctl \fB-K\fR \fB-u\fR \fIuuid\fR \fIservice\fR .br .B ocfs2_hb_ctl \fB-I\fR \fB-d\fR \fIdevice\fR .br .B ocfs2_hb_ctl \fB-I\fR \fB-u\fR \fIuuid\fR .br .B ocfs2_hb_ctl \fB-P\fR \fB-d\fR \fIdevice\fR [\fB-n\fR \fIio_priority\fR] .br .B ocfs2_hb_ctl \fB-P\fR \fB-u\fR \fIuuid\fR [\fB-n\fR \fIio_priority\fR] .br .B ocfs2_hb_ctl \fB-h\fI .br .SH "DESCRIPTION" .PP \fBocfs2_hb_ctl\fR starts and stops \fBlocal\fR heartbeat on a \fIOCFS2\fR device. \fBUsers are strongly urged not to use this tool directly\fR. It is automatically invoked by \fBmount.ocfs2(8)\fR and other tools that require heartbeat notifications. This utility only operates in the \fBlocal\fR heartbeat mode. It fails silently when run in \fBglobal\fR heartbeat mode. More information on the heartbeat modes can be found in \fBo2cb(7)\fR. The tools accepts devices to be specified by its name or its uuid. Service denotes the application that is requesting the heartbeat notification. .SH "OPTIONS" .TP \fB\-S\fR Starts the heartbeat. .TP \fB\-K\fR Stops the heartbeat. .TP \fB\-I\fR Prints the heartbeat reference counts for that heartbeat region. .TP \fB\-d\fR Specify region by device name. .TP \fB\-u\fR Specify region by device uuid. .TP \fB\-n\fR Adjust IO priority for the heartbeat thread. This option calls the \fBionice\fR tool to set its IO scheduling class to realtime with scheduling class data as provided. This option is usable only with the \fBO2CB\fR cluster stack. .TP \fB\-h\fR Displays help and exit. .SH "SEE ALSO" .BR mount.ocfs2(8) .BR o2cb(7) .BR o2cb(8) .BR o2cb.sysconfig(5) .BR ocfs2.cluster.conf(5) .BR o2cluster(8) .SH "AUTHORS" Oracle Corporation .SH "COPYRIGHT" Copyright \(co 2004, 2012 Oracle. All rights reserved. ocfs2-tools-ocfs2-tools-1.8.6/ocfs2_hb_ctl/ocfs2_hb_ctl.c000066400000000000000000000321451347147137200231100ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * ocfs2_hb_ctl.c Utility to start / stop heartbeat on demand * * Copyright (C) 2005 Oracle. All rights reserved. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * * Authors: Mark Fasheh */ #define _XOPEN_SOURCE 600 /* Triggers XOPEN2K in features.h */ #define _LARGEFILE64_SOURCE #define _GNU_SOURCE /* Because libc really doesn't want us using O_DIRECT? */ #include #include #include #include #include #include #include #include #include #include #include "ocfs2/ocfs2.h" #define DEV_PREFIX "/dev/" #define PROC_IDE_FORMAT "/proc/ide/%s/media" #define IONICE_PATH "/usr/bin/ionice" enum hb_ctl_action { HB_ACTION_UNKNOWN, HB_ACTION_USAGE, HB_ACTION_START, HB_ACTION_STOP, HB_ACTION_REFINFO, HB_ACTION_IONICE, }; struct hb_ctl_options { enum hb_ctl_action action; char *dev_str; char *uuid_str; int io_prio; char *service; /* The service accessing the region. Ths is usually the mountpoint, but could be a program name like 'fsck.ocfs2'. Note that the service is now a required argument to this program. This will work even with old kernels, because o2cb.init fills the hb_ctl path with /bin/true. Nothing in ocfs2-tools will call this incorrectly. */ }; static char *progname = "ocfs2_hb_ctl"; static struct o2cb_region_desc *region_desc = NULL; static struct o2cb_cluster_desc *cluster_desc = NULL; static void block_signals(int how) { sigset_t sigs; sigfillset(&sigs); sigdelset(&sigs, SIGTRAP); sigdelset(&sigs, SIGSEGV); sigprocmask(how, &sigs, NULL); } static void free_desc(void) { if (region_desc) { if (region_desc->r_name) ocfs2_free(®ion_desc->r_name); if (region_desc->r_device_name) ocfs2_free(®ion_desc->r_device_name); ocfs2_free(®ion_desc); region_desc = NULL; } if (cluster_desc) { if (cluster_desc->c_stack) ocfs2_free(&cluster_desc->c_stack); if (cluster_desc->c_cluster) ocfs2_free(&cluster_desc->c_cluster); ocfs2_free(&cluster_desc); cluster_desc = NULL; } } static errcode_t get_desc(const char *dev) { errcode_t err = 0; ocfs2_filesys *fs; if (region_desc) goto out; err = ocfs2_malloc0(sizeof(struct o2cb_region_desc), ®ion_desc); if (err) goto out; err = ocfs2_malloc0(sizeof(struct o2cb_cluster_desc), &cluster_desc); if (err) goto out; err = ocfs2_open(dev, OCFS2_FLAG_RO | OCFS2_FLAG_HEARTBEAT_DEV_OK, 0, 0, &fs); if (err) goto out; err = ocfs2_fill_heartbeat_desc(fs, region_desc); if (!err) { region_desc->r_name = strdup(region_desc->r_name); region_desc->r_device_name = strdup(region_desc->r_device_name); if (!region_desc->r_name || !region_desc->r_device_name) err = OCFS2_ET_NO_MEMORY; } else { region_desc->r_name = NULL; region_desc->r_device_name = NULL; goto out_close; } err = ocfs2_fill_cluster_desc(fs, cluster_desc); if (!err) { if (cluster_desc->c_stack) { cluster_desc->c_stack = strdup(cluster_desc->c_stack); if (!cluster_desc->c_stack) err = OCFS2_ET_NO_MEMORY; } if (cluster_desc->c_cluster) { cluster_desc->c_cluster = strdup(cluster_desc->c_cluster); if (!cluster_desc->c_cluster) err = OCFS2_ET_NO_MEMORY; } } else { cluster_desc->c_stack = NULL; cluster_desc->c_cluster = NULL; } out_close: ocfs2_close(fs); out: if (err) free_desc(); return err; } static errcode_t get_uuid(char *dev, char **uuid) { errcode_t ret; ret = get_desc(dev); if (!ret) { *uuid = strdup(region_desc->r_name); if (!*uuid) ret = OCFS2_ET_NO_MEMORY; } return ret; } static errcode_t compare_dev(const char *dev, struct hb_ctl_options *hbo) { errcode_t err; int len; char *device; if (region_desc) { fprintf(stderr, "We have a descriptor already!\n"); free_desc(); } len = strlen(DEV_PREFIX) + strlen(dev) + 1; device = malloc(sizeof(char) * len); if (!device) return OCFS2_ET_NO_MEMORY; snprintf(device, len, DEV_PREFIX "%s", dev); /* Any problem with getting the descriptor is NOT FOUND */ err = OCFS2_ET_FILE_NOT_FOUND; if (get_desc(device)) goto out; if (!strcmp(region_desc->r_name, hbo->uuid_str)) { hbo->dev_str = device; err = 0; } else free_desc(); out: if (err && device) free(device); return err; } static int as_ide_disk(const char *dev_name) { FILE *f; int is_disk = 1; size_t len; char *proc_name; len = strlen(PROC_IDE_FORMAT) + strlen(dev_name); proc_name = (char *)malloc(sizeof(char) * len); if (!proc_name) return 0; snprintf(proc_name, len, PROC_IDE_FORMAT, dev_name); /* If not ide, file won't exist */ f = fopen(proc_name, "r"); if (f) { if (fgets(proc_name, len, f)) { /* IDE devices we don't want to probe */ if (!strncmp(proc_name, "cdrom", strlen("cdrom")) || !strncmp(proc_name, "tape", strlen("tape"))) is_disk = 0; } fclose(f); } free(proc_name); return is_disk; } /* as_ide_disk() */ /* Um, wow, this is, like, one big hardcode */ static errcode_t scan_devices(errcode_t (*func)(const char *, struct hb_ctl_options *), struct hb_ctl_options *hbo) { errcode_t err = 0; int major, minor; FILE *f; char *buffer, *name; buffer = (char *)malloc(sizeof(char) * (PATH_MAX + 1)); if (!buffer) return OCFS2_ET_NO_MEMORY; name = (char *)malloc(sizeof(char) * (PATH_MAX + 1)); if (!name) { free(buffer); return OCFS2_ET_NO_MEMORY; } f = fopen("/proc/partitions", "r"); if (!f) { err = -errno; goto out_free; } err = OCFS2_ET_FILE_NOT_FOUND; while (1) { if ((fgets(buffer, PATH_MAX + 1, f)) == NULL) break; name[0] = '\0'; major = minor = 0; /* FIXME: If this is bad, send patches */ if (sscanf(buffer, "%d %d %*d %99[^ \t\n]", &major, &minor, name) < 3) continue; if (*name && major) { if (!as_ide_disk(name)) continue; err = func(name, hbo); if (!err || (err != OCFS2_ET_FILE_NOT_FOUND)) break; } } fclose(f); out_free: free(buffer); free(name); return err; } /* scan_devices() */ static errcode_t lookup_dev(struct hb_ctl_options *hbo) { return scan_devices(compare_dev, hbo); } static errcode_t start_heartbeat(struct hb_ctl_options *hbo) { errcode_t err = 0; if (!hbo->dev_str) err = lookup_dev(hbo); if (!err) { region_desc->r_persist = 1; /* hb_ctl is for reals */ region_desc->r_service = hbo->service; err = o2cb_begin_group_join(cluster_desc, region_desc); if (!err) { /* * This is a manual start, there is no service * or mountpoint being started by hb_ctl, so * we assume success */ err = o2cb_complete_group_join(cluster_desc, region_desc, 0); } } return err; } static errcode_t adjust_priority(struct hb_ctl_options *hbo) { int ret, child_status; pid_t hb_pid, child_pid; char level_arg[16], pid_arg[16]; if (access (IONICE_PATH, X_OK) != 0) return OCFS2_ET_NO_IONICE; ret = o2cb_get_hb_thread_pid (NULL, hbo->uuid_str, &hb_pid); if (ret != 0) return ret; child_pid = fork (); if (child_pid == 0) { sprintf (level_arg, "-n%d", hbo->io_prio); sprintf (pid_arg, "-p%d", hb_pid); execlp (IONICE_PATH, "ionice", "-c1", level_arg, pid_arg, NULL); ret = errno; exit (ret); } else if (child_pid > 0) { ret = waitpid (child_pid, &child_status, 0); if (ret == 0) ret = WEXITSTATUS(child_status); else ret = errno; } else { ret = errno; } return ret; } static errcode_t stop_heartbeat(struct hb_ctl_options *hbo) { errcode_t err = 0; if (!hbo->dev_str) err = lookup_dev(hbo); if (!err) { region_desc->r_persist = 1; /* hb_ctl is for reals */ region_desc->r_service = hbo->service; err = o2cb_group_leave(cluster_desc, region_desc); } return err; } static errcode_t print_hb_ref_info(struct hb_ctl_options *hbo) { errcode_t err; int num; err = o2cb_num_region_refs(hbo->uuid_str, &num); if (!err) printf("%s: %d refs\n", hbo->uuid_str, num); return err; } static int read_options(int argc, char **argv, struct hb_ctl_options *hbo) { int c, ret; ret = 0; while(1) { c = getopt(argc, argv, "ISKPd:u:n:h"); if (c == -1) break; switch (c) { case 'h': hbo->action = HB_ACTION_USAGE; break; case 'K': hbo->action = HB_ACTION_STOP; break; case 'S': hbo->action = HB_ACTION_START; break; case 'P': hbo->action = HB_ACTION_IONICE; break; case 'd': if (optarg) hbo->dev_str = strdup(optarg); break; case 'u': if (optarg) hbo->uuid_str = strdup(optarg); break; case 'n': if (optarg) hbo->io_prio = atoi(optarg); break; case 'I': hbo->action = HB_ACTION_REFINFO; break; case '?': case ':': default: ret = -1; break; } } if (!ret && (optind < argc)) hbo->service = strdup(argv[optind]); return ret; } static int process_options(struct hb_ctl_options *hbo) { int ret = 0; switch (hbo->action) { case HB_ACTION_START: /* For start must specify exactly one of uuid or device. */ if ((hbo->uuid_str && hbo->dev_str) || (!hbo->uuid_str && !hbo->dev_str) || !hbo->service) ret = -EINVAL; break; case HB_ACTION_STOP: /* For stop must specify exactly one of uuid or device. */ if ((hbo->uuid_str && hbo->dev_str) || (!hbo->uuid_str && !hbo->dev_str)) ret = -EINVAL; else if (!hbo->service) { /* This is a special case - the kernel calls us * with uuid_str and ! service. In that case only * we fill in the service */ if (hbo->uuid_str) hbo->service = strdup(OCFS2_FS_NAME); else ret = -EINVAL; } break; case HB_ACTION_REFINFO: /* Refinfo needs uuid or device */ if ((hbo->uuid_str && hbo->dev_str) || (!hbo->uuid_str && !hbo->dev_str)) ret = -EINVAL; break; case HB_ACTION_IONICE: /* ionice needs uuid and priority */ if ((hbo->uuid_str && hbo->dev_str) || (!hbo->uuid_str && !hbo->dev_str) || hbo->io_prio < 0 || hbo->io_prio > 7) ret = -EINVAL; break; case HB_ACTION_UNKNOWN: ret = -EINVAL; break; case HB_ACTION_USAGE: default: break; } return ret; } static void print_usage(int err) { FILE *output = err ? stderr : stdout; fprintf(output, "Usage: %s -S -d \n", progname); fprintf(output, " %s -S -u \n", progname); fprintf(output, " %s -K -d \n", progname); fprintf(output, " %s -K -u \n", progname); fprintf(output, " %s -I -d \n", progname); fprintf(output, " %s -I -u \n", progname); fprintf(output, " %s -P -d [-n ]\n", progname); fprintf(output, " %s -P -u [-n ]\n", progname); fprintf(output, " %s -h\n", progname); } int main(int argc, char **argv) { errcode_t err = 0; int ret = 0; struct hb_ctl_options hbo = { .action = HB_ACTION_UNKNOWN, }; char *hbuuid = NULL; setbuf(stdout, NULL); setbuf(stderr, NULL); initialize_ocfs_error_table(); initialize_o2dl_error_table(); initialize_o2cb_error_table(); ret = read_options(argc, argv, &hbo); if (ret) { print_usage(1); goto bail; } ret = process_options(&hbo); if (ret) { print_usage(1); goto bail; } if (hbo.action == HB_ACTION_USAGE) { print_usage(0); goto bail; } err = o2cb_init(); if (err) { com_err(progname, err, "Cannot initialize cluster\n"); ret = -EINVAL; goto bail; } if (!hbo.uuid_str) { err = get_uuid(hbo.dev_str, &hbuuid); if (err) { com_err(progname, err, "while reading uuid"); ret = -EINVAL; goto bail; } hbo.uuid_str = hbuuid; } block_signals(SIG_BLOCK); switch(hbo.action) { case HB_ACTION_USAGE: ret = 0; print_usage(0); break; case HB_ACTION_START: err = start_heartbeat(&hbo); if (err) { com_err(progname, err, "while starting heartbeat"); ret = -EINVAL; } break; case HB_ACTION_STOP: err = stop_heartbeat(&hbo); if (err) { com_err(progname, err, "while stopping heartbeat"); ret = -EINVAL; } break; case HB_ACTION_IONICE: err = adjust_priority(&hbo); if (err) ret = err; break; case HB_ACTION_REFINFO: err = print_hb_ref_info(&hbo); if (err) { com_err(progname, err, "while reading reference counts"); ret = -EINVAL; } break; default: abort(); } block_signals(SIG_UNBLOCK); bail: ocfs2_free(&hbo.dev_str); ocfs2_free(&hbo.service); ocfs2_free(&hbo.uuid_str); free_desc(); return ret ? 1 : 0; } ocfs2-tools-ocfs2-tools-1.8.6/ocfs2cdsl/000077500000000000000000000000001347147137200177435ustar00rootroot00000000000000ocfs2-tools-ocfs2-tools-1.8.6/ocfs2cdsl/.gitignore000066400000000000000000000000521347147137200217300ustar00rootroot00000000000000*.sw? ocfs2cdsl stamp-md5 *.d ocfs2cdsl.8 ocfs2-tools-ocfs2-tools-1.8.6/ocfs2cdsl/Makefile000066400000000000000000000014661347147137200214120ustar00rootroot00000000000000TOPDIR = .. include $(TOPDIR)/Preamble.make LIBOCFS2_LIBS = -L$(TOPDIR)/libocfs2 -locfs2 LIBOCFS2_DEPS = $(TOPDIR)/libocfs2/libocfs2.a LIBO2DLM_LIBS = -L$(TOPDIR)/libo2dlm -lo2dlm $(DL_LIBS) LIBO2DLM_DEPS = $(TOPDIR)/libo2dlm/libo2dlm.a LIBO2CB_LIBS = -L$(TOPDIR)/libo2cb -lo2cb ifneq ($(BUILD_FSDLM_SUPPORT),) LIBO2CB_LIBS = -L$(TOPDIR)/libo2cb -lo2cb -ldlm_lt else LIBO2CB_DEPS = $(TOPDIR)/libo2cb/libo2cb.a endif sbindir = $(root_sbindir) SBIN_PROGRAMS = ocfs2cdsl DEFINES = -DVERSION=\"$(VERSION)\" DEFINES += -DG_DISABLE_DEPRECATED INCLUDES = -I$(TOPDIR)/include $(GLIB_CFLAGS) CFILES = ocfs2cdsl.c OBJS = $(subst .c,.o,$(CFILES)) DIST_RULES = dist-incdir MANS = ocfs2cdsl.8 DIST_FILES = $(CFILES) ocfs2cdsl.8.in ocfs2cdsl: $(OBJS) $(LINK) $(GLIB_LIBS) $(COM_ERR_LIBS) include $(TOPDIR)/Postamble.make ocfs2-tools-ocfs2-tools-1.8.6/ocfs2cdsl/ocfs2cdsl.8.in000066400000000000000000000055331347147137200223310ustar00rootroot00000000000000.TH "ocfs2cdsl" "8" "March 2008" "Version @VERSION@" "OCFS2 Manual Pages" .SH "NAME" ocfs2cdsl \- Create context dependent symbolic link (CDSL). .SH "SYNOPSIS" \fBocfs2cdsl\fR [\fB-cfnqvV\fR] [\fB\-t\fR \fIhostname|mach|os|nodenum\fR] \fIfilename\fR .SH "DESCRIPTION" .PP \fBocfs2cdsl\fR is used to create a context dependent symbolic link for \fIfilename\fR (file or directory) for a node. A CDSL \fIfilename\fR will have its own image for a specific node, but a common name in the OCFS2. It is very important that the \fBocfs2ctl\fR command to be issued on all nodes that are part of the cluster and to any subsequent nodes that are added. If the \fBocfs2cdsl\fR command is not executed in some of the nodes, the cdsl directory will not be accessible by them. .SH "OPTIONS" .TP \fB\-t\fR \fIhostname|mach|os|nodenum\fR \fIhostname|mach|os|nodenum\fR - One of these options should be specified in order to create the CDSL. As for information, \fIhostname\fR would be the same as the output of "uname -n", \fImach\fR would be the output of "uname -m", \fIos\fR would the the output of "uname -o" and \fInodenum\fR would be the global node number for the hostname in the \fI/etc/ocfs2/cluster.conf\fR file. .TP \fB\-c\fR During the creation of the CDSL, if the file does exist, the data will be copied to the new location. .TP \fB\-f\fR Force the execution of the command. .TP \fB\-n\fR Will perform a check of all options and motions, but will not perform any change on disk. .TP \fB\-q\fR Execute \fBocfs2cdsl\fR in quiet mode. .TP \fB\-v\fR Execute \fBocfs2cdsl\fR in verbose mode. .TP \fB\-V\fR Print version and exit. .SH "EXAMPLE" If one wants to have a shared ORACLE_HOME installed on an OCFS2 partition, and wants to have node specific configuration \fIfilename\fR in the same shared ORACLE_HOME, one can have the \fIfilename\fR created by ocfs2cdsl. A good example would be the directory $ORACLE_HOME/network/agent. Node1 could have its own view of the directory, which is different from Node2 view, but having the exact same full pathname. .br # ocfs2cdsl -c -t hostname $ORACLE_HOME/network/agent .br In the example above, a CDSL directory will be created and the contents of the \fIagent\fR directory will copied to the new location. If the same is to be executed on all nodes, and assuming that no changes are made to the directory, each one of the nodes would have the exact same view of the \fIagent\fR directory. In case of change in the \fIagent\fR directory after the creation of the CDSL, only the node performing the change would be able to see it. All other nodes would still have the same initial view of the \fIagent\fR directory. .SH "SEE ALSO" .BR mkfs.ocfs2(8) .BR fsck.ocfs2(8) .BR tunefs.ocfs2(8) .BR mounted.ocfs2(8) .BR ocfs2console(8) .BR debugfs.ocfs2(8) .SH "AUTHORS" Oracle Corporation .SH "COPYRIGHT" Copyright \(co 2004, 2008 Oracle. All rights reserved. ocfs2-tools-ocfs2-tools-1.8.6/ocfs2cdsl/ocfs2cdsl.c000066400000000000000000000360451347147137200220010ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * ocfs2cdsl.c * * OCFS2 CDSL utility * * Copyright (C) 2004, 2005 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License, version 2, as published by the Free Software Foundation. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * * Authors: Manish Singh */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ocfs2/ocfs2.h" #define CDSL_BASE ".cluster" #define CDSL_COMMON_DIR "common" #define PROC_OCFS2 "/proc/fs/ocfs2" typedef enum { CDSL_TYPE_HOSTNAME, CDSL_TYPE_MACH, CDSL_TYPE_OS, CDSL_TYPE_NODENUM, CDSL_TYPE_SYS, CDSL_TYPE_UID, CDSL_TYPE_GID, CDSL_TYPE_UNKNOWN } CDSLType; static const char * const cdsl_names[] = { [CDSL_TYPE_HOSTNAME] = "hostname", [CDSL_TYPE_MACH] = "mach", [CDSL_TYPE_OS] = "os", [CDSL_TYPE_NODENUM] = "nodenum", [CDSL_TYPE_SYS] = "sys", [CDSL_TYPE_UID] = "uid", [CDSL_TYPE_GID] = "gid", [CDSL_TYPE_UNKNOWN] = NULL }; typedef struct _State State; struct _State { char *progname; gboolean copy; gboolean local; gboolean no_common; gboolean force; gboolean dry_run; gboolean verbose; gboolean quiet; CDSLType type; char *dirname; char *quoted_dirname; char *filename; char *quoted_filename; char *fullname; char *quoted_fullname; }; static State *get_state (int argc, char **argv); static void usage(const char *progname); static void version(const char *progname); static void run_command(State *s, const char *cmd); static char *verify_ocfs2(State *s); static char *get_ocfs2_root(const char *path); static CDSLType cdsl_type_from_string(const char *str); static char *cdsl_common_path(State *s); static char *cdsl_path_expand(State *s); static char *cdsl_source_directory(State *s, const char *fsroot, const char *path); static char *cdsl_target(State *s, const char *path); static gboolean cdsl_match(State *s, const char *path, const char *cdsl); static void copy(State *s, const char *src, const char *dest); static void delete(State *s, const char *path); static char *get_node_num(State *s); static void create_directory(State *s, const char *path); static void make_common_file(State *s, const char *fsroot, const char *path); static void copy_common_file(State *s, const char *fsroot, const char *path, const char *dir); extern char *optarg; extern int optind, opterr, optopt; int main(int argc, char **argv) { State *s; char *fsroot, *path; char *filename, *dir; gboolean exists; char *target, *quoted_target; s = get_state(argc, argv); fsroot = verify_ocfs2(s); path = s->dirname + strlen(fsroot); if (!(fsroot[0] == '/' && fsroot[1] == '\0')) path += 1; dir = cdsl_target(s, path); target = g_build_filename(dir, s->filename, NULL); g_free(dir); quoted_target = g_shell_quote(target); if (g_file_test(s->fullname, G_FILE_TEST_IS_SYMLINK)) { if (!s->copy && cdsl_match(s, s->fullname, target)) { dir = cdsl_source_directory(s, fsroot, path); filename = g_build_filename(dir, s->filename, NULL); if (!g_file_test(filename, G_FILE_TEST_EXISTS)) copy_common_file(s, fsroot, path, dir); g_free(filename); g_free(dir); exit(1); } else { com_err(s->progname, 0, "%s is already a symbolic link", s->quoted_fullname); exit(1); } } exists = g_file_test(s->fullname, G_FILE_TEST_EXISTS); if (exists) { if (!g_file_test(s->fullname, G_FILE_TEST_IS_REGULAR | G_FILE_TEST_IS_DIR)) { com_err(s->progname, 0, "%s is not a file or a directory", s->quoted_fullname); exit(1); } } else if (s->copy) { com_err(s->progname, 0, "%s does not exist, no file to copy", s->quoted_fullname); exit(1); } if (exists && !s->copy) { if (s->force) { delete(s, s->fullname); exists = FALSE; } else { com_err(s->progname, 0, "%s already exists, but copy not requested or " "force (-f) not given", s->quoted_fullname); exit(1); } } if (exists) make_common_file(s, fsroot, path); else { dir = cdsl_source_directory(s, fsroot, path); if (!s->no_common) copy_common_file(s, fsroot, path, dir); g_free(dir); } if (s->verbose || s->dry_run) printf("ln -s %s %s\n", quoted_target, s->quoted_fullname); if (!s->dry_run) { if (symlink(target, s->fullname) != 0) { com_err(s->progname, errno, "could not symlink %s to %s", quoted_target, s->quoted_fullname); exit(1); } } g_free(quoted_target); g_free(target); return 0; } static State * get_state(int argc, char **argv) { char *progname; gboolean copy = FALSE, local = FALSE, no_common = FALSE; gboolean force = FALSE, dry_run = FALSE; gboolean quiet = FALSE, verbose = FALSE, show_version = FALSE; CDSLType type = CDSL_TYPE_HOSTNAME; char *filename, *dirname, *tmp; State *s; int c; static struct option long_options[] = { { "type", 1, 0, 't' }, { "copy", 0, 0, 'c' }, { "local", 0, 0, 'L' }, { "no-common", 0, 0, 'N' }, { "force", 0, 0, 'f' }, { "dry-run", 0, 0, 'n' }, { "verbose", 0, 0, 'v' }, { "quiet", 0, 0, 'q' }, { "version", 0, 0, 'V' }, { 0, 0, 0, 0 } }; if (argc && *argv) progname = g_path_get_basename(argv[0]); else progname = g_strdup("ocfs2cdsl"); while (1) { c = getopt_long(argc, argv, "t:acLNfnvqV", long_options, NULL); if (c == -1) break; switch (c) { case 't': type = cdsl_type_from_string(optarg); if (type == CDSL_TYPE_UNKNOWN) { com_err(progname, 0, "'%s' not a recognized type\n", optarg); exit(1); } break; case 'c': copy = TRUE; break; case 'L': local = TRUE; break; case 'N': no_common = TRUE; break; case 'f': force = TRUE; break; case 'n': dry_run = TRUE; break; case 'q': quiet = TRUE; break; case 'v': verbose = TRUE; break; case 'V': show_version = TRUE; break; default: usage(progname); break; } } if ((optind == argc) && !show_version) usage(progname); if (show_version) { version(progname); exit(0); } filename = argv[optind++]; tmp = g_path_get_dirname(filename); dirname = canonicalize_file_name(tmp); g_free(tmp); if (dirname == NULL) { com_err(progname, errno, g_path_get_dirname(filename)); exit(1); } if (optind < argc) usage(progname); if (local) copy = TRUE; s = g_new0(State, 1); s->progname = progname; s->type = type; s->copy = copy; s->local = local; s->no_common = no_common; s->force = force; s->dry_run = dry_run; s->verbose = verbose; s->quiet = quiet; s->dirname = g_strdup(dirname); s->filename = g_path_get_basename(filename); s->fullname = g_build_filename(s->dirname, s->filename, NULL); free(dirname); s->quoted_dirname = g_shell_quote(s->dirname); s->quoted_filename = g_shell_quote(s->filename); s->quoted_fullname = g_shell_quote(s->fullname); return s; } static void usage(const char *progname) { const char * const *name; fprintf(stderr, "Usage: %s [-cLfnqvV] [-t", progname); for (name = cdsl_names; *name; name++) fprintf(stderr, " %s", *name); fprintf(stderr, "] filename\n"); exit(1); } static void version(const char *progname) { printf("%s %s\n", progname, VERSION); } static void run_command(State *s, const char *cmd) { char *name, *space, *cmd_err; int len, ret; GError *error = NULL; space = strchr(cmd, ' '); if (space) { len = space - cmd; name = g_new(char, space - cmd); strncpy(name, cmd, len); name[len] = '\0'; } else name = g_strdup(cmd); if (s->verbose || s->dry_run) printf("%s\n", cmd); if (!s->dry_run) { if (!g_spawn_command_line_sync(cmd, NULL, &cmd_err, &ret, &error)) { com_err(s->progname, 0, "could not run %s: %s", name, error->message); exit(1); } if (ret != 0) { com_err(s->progname, 0, "%s error: %s", name, cmd_err); exit(1); } g_free(cmd_err); } } static char * verify_ocfs2(State *s) { struct statfs sbuf; char *fsroot; if (statfs(s->dirname, &sbuf) != 0) { com_err(s->progname, errno, "could not statfs %s", s->quoted_dirname); exit(1); } if (sbuf.f_type != OCFS2_SUPER_MAGIC) { com_err(s->progname, 0, "%s is not on an ocfs2 filesystem", s->quoted_fullname); exit(1); } fsroot = get_ocfs2_root(s->dirname); if (fsroot == NULL) { com_err(s->progname, 0, "%s is not on an ocfs2 filesystem", s->quoted_fullname); exit(1); } return fsroot; } static char * get_ocfs2_root(const char *path) { struct mntent *mnt; FILE *fp; int len, found_len = 0; char *found = NULL, *found_type = NULL; char *ret = NULL; fp = setmntent (_PATH_MOUNTED, "r"); if (fp == NULL) return NULL; while ((mnt = getmntent(fp))) { len = strlen(mnt->mnt_dir); if (strncmp(mnt->mnt_dir, path, len) == 0) { if (len > found_len && (len == 1 || path[len] == '/' || path[len] == '\0')) { found_len = len; g_free(found); found = g_strdup(mnt->mnt_dir); g_free(found_type); found_type = g_strdup(mnt->mnt_type); } } } endmntent(fp); if (found_type && strcmp(found_type, "ocfs2") == 0) ret = g_strdup(found); g_free(found_type); g_free(found); return ret; } static CDSLType cdsl_type_from_string(const char *str) { const char * const *name; CDSLType type; for (name = cdsl_names, type = CDSL_TYPE_HOSTNAME; *name; name++, type++) if (strcmp(str, *name) == 0) break; return type; } static char * cdsl_common_path(State *s) { const char *prefix; g_assert(s->type < CDSL_TYPE_UNKNOWN); prefix = cdsl_names[s->type]; return g_build_filename(CDSL_BASE, CDSL_COMMON_DIR, prefix, NULL); } static char * cdsl_path_expand(State *s) { const char *prefix; char *val, *ret; struct utsname buf; uname(&buf); switch(s->type) { case CDSL_TYPE_HOSTNAME: val = g_strdup(buf.nodename); break; case CDSL_TYPE_MACH: val = g_strdup(buf.machine); break; case CDSL_TYPE_OS: val = g_strdup(buf.sysname); break; case CDSL_TYPE_NODENUM: val = get_node_num(s); break; case CDSL_TYPE_SYS: val = g_strdup_printf("%s_%s", buf.machine, buf.sysname); break; case CDSL_TYPE_UID: val = g_strdup_printf("%lu", (unsigned long)getuid()); break; case CDSL_TYPE_GID: val = g_strdup_printf("%lu", (unsigned long)getgid()); break; case CDSL_TYPE_UNKNOWN: default: g_assert_not_reached(); val = NULL; break; } prefix = cdsl_names[s->type]; ret = g_build_filename(CDSL_BASE, prefix, val, NULL); g_free(val); return ret; } static char * cdsl_source_directory(State *s, const char *fsroot, const char *path) { char *prefix, *dir; prefix = cdsl_path_expand(s); dir = g_build_filename(fsroot, prefix, path, NULL); g_free(prefix); create_directory(s, dir); return dir; } static char * cdsl_target(State *s, const char *path) { const char *type; char *val, *ret; GString *prefix; char **parts; int i; type = cdsl_names[s->type]; val = g_strdup_printf("{%s}", type); prefix = g_string_new(""); parts = g_strsplit(path, "/", -1); for (i = 0; parts[i] != NULL; i++) g_string_append(prefix, "../"); g_strfreev(parts); ret = g_build_filename(prefix->str, CDSL_BASE, type, val, path, NULL); g_string_free(prefix, TRUE); g_free(val); return ret; } static gboolean cdsl_match(State *s, const char *path, const char *cdsl) { char buf[PATH_MAX]; int n; n = readlink(path, buf, sizeof(buf) - 1); if (n < 0) { com_err(s->progname, errno, "readlink"); exit(1); } buf[n] = '\0'; return strcmp(buf, cdsl) == 0; } static void copy(State *s, const char *src, const char *dest) { char *cmd, *quoted_src, *quoted_dest; quoted_src = g_shell_quote(src); quoted_dest = g_shell_quote(dest); cmd = g_strdup_printf("cp -a %s %s", quoted_src, quoted_dest); g_free(quoted_src); g_free(quoted_dest); run_command(s, cmd); g_free(cmd); } static void delete(State *s, const char *path) { char *cmd, *quoted; quoted = g_shell_quote(path); cmd = g_strdup_printf("rm -rf %s", quoted); g_free(quoted); run_command(s, cmd); g_free(cmd); } static char * get_node_num(State *s) { struct stat sbuf; char *dev, *path, buf[20]; FILE *f; int i; if (stat(s->dirname, &sbuf) != 0) { com_err(s->progname, errno, "could not stat %s", s->quoted_dirname); exit(1); } dev = g_strdup_printf("%u_%u", major(sbuf.st_dev), minor(sbuf.st_dev)); path = g_build_filename(PROC_OCFS2, dev, "nodenum", NULL); g_free(dev); f = fopen(path, "r"); if (f == NULL) { com_err(s->progname, errno, "could not open %s", path); exit(1); } if (fgets(buf, sizeof(buf), f) == NULL) { com_err(s->progname, errno, "could not read node number"); exit(1); } fclose(f); g_free(path); for (i = 0; i < sizeof(buf); i++) { if (buf[i] < '0' || buf[i] > '9') { buf[i] = '\0'; break; } } if (buf[0] == '\0') { com_err(s->progname, 0, "could not read node number"); exit(1); } return g_strdup(buf); } static void create_directory(State *s, const char *path) { char *cmd, *quoted; quoted = g_shell_quote(path); cmd = g_strdup_printf("mkdir -p %s", quoted); g_free(quoted); run_command(s, cmd); g_free(cmd); } static void make_common_file(State *s, const char *fsroot, const char *path) { char *prefix, *cdsl_path, *cdsl_full, *quoted, *dir; if (s->local) prefix = cdsl_path_expand(s); else prefix = cdsl_common_path(s); cdsl_path = g_build_filename(fsroot, prefix, path, NULL); g_free(prefix); create_directory(s, cdsl_path); cdsl_full = g_build_filename(cdsl_path, s->filename, NULL); g_free (cdsl_path); if (g_file_test(cdsl_full, G_FILE_TEST_EXISTS)) { if (s->force) delete(s, cdsl_full); else { com_err(s->progname, 0, "CDSL already exists. To replace, use the " "force (-f) option"); exit(1); } } if (s->verbose || s->dry_run) { quoted = g_shell_quote(cdsl_full); printf("mv %s %s\n", s->quoted_fullname, quoted); g_free(quoted); } if (!s->dry_run) { if (rename(s->fullname, cdsl_full) != 0) { com_err(s->progname, errno, "could not rename %s", s->quoted_fullname); exit(1); } } if (!s->local) { dir = cdsl_source_directory(s, fsroot, path); copy(s, cdsl_full, dir); g_free(dir); } g_free (cdsl_full); } static void copy_common_file(State *s, const char *fsroot, const char *path, const char *dir) { char *prefix, *filename; prefix = cdsl_common_path(s); filename = g_build_filename(fsroot, prefix, path, s->filename, NULL); g_free(prefix); if (g_file_test(filename, G_FILE_TEST_EXISTS)) copy(s, filename, dir); g_free(filename); } ocfs2-tools-ocfs2-tools-1.8.6/ocfs2console/000077500000000000000000000000001347147137200204605ustar00rootroot00000000000000ocfs2-tools-ocfs2-tools-1.8.6/ocfs2console/.gitignore000066400000000000000000000001261347147137200224470ustar00rootroot00000000000000*.sw? *.pyc ocfsmarshal.c ocfsmarshal.h cellmap.c *.so ocfs2tool.8 *.d ocfs2console.8 ocfs2-tools-ocfs2-tools-1.8.6/ocfs2console/Makefile000066400000000000000000000003161347147137200221200ustar00rootroot00000000000000TOPDIR = .. include $(TOPDIR)/Preamble.make SUBDIRS = blkid ocfs2interface MANS = ocfs2console.8 SBIN_EXTRA = ocfs2console DIST_FILES = ocfs2console ocfs2console.8.in include $(TOPDIR)/Postamble.make ocfs2-tools-ocfs2-tools-1.8.6/ocfs2console/blkid/000077500000000000000000000000001347147137200215455ustar00rootroot00000000000000ocfs2-tools-ocfs2-tools-1.8.6/ocfs2console/blkid/.gitignore000066400000000000000000000000541347147137200235340ustar00rootroot00000000000000*.sw? *.d blkid_types.h libblkid-internal.a ocfs2-tools-ocfs2-tools-1.8.6/ocfs2console/blkid/ChangeLog000066400000000000000000000361311347147137200233230ustar00rootroot000000000000002005-03-21 Theodore Ts'o * Release of E2fsprogs 1.37 2005-03-21 Theodore Ts'o * cache.c (blkid_get_cache): Ignore the BLKID_FILE environment variable if blkid_get_cache() is called from a setuid program. 2005-03-16 Theodore Ts'o * getsize.c (blkid_get_dev_size): Fix compilation problem on Darwin systems. 2006-02-05 Theodore Ts'o * Release of E2fsprogs 1.36 2005-02-05 Theodore Ts'o * Makefile.in: Remove blkid.pc on a "make distclean" 2005-01-27 Theodore Ts'o * blkid.h, blkidP.h: Rename blkid_verify_devname() to be blkid_verify(), and make it be a publically exported function. 2005-01-26 Theodore Ts'o * version.c: Add functions to query the version of the blkid library. * blkid.pc.in: Add pkg-config files. 2005-01-25 Theodore Ts'o * probe.c: Windows can perform a "quick format" that doesn't clear enough of the partition that the blkid probes can get confused. Do the NTFS test first to deal with this Windows misfeature. (Addresses Debian Bug #291990) 2005-01-21 Theodore Ts'o * probe.c (probe_oracleasm): Add support for recognizing Oracle ASM volumes. Thanks to Manish Singh (manish.singh at oracle.com) for supplying this patch. 2005-01-18 Theodore Ts'o * Makefile.in: Fix the kernel compile-time echo commands to be consistent and portable 2005-01-13 Matthias Andree * getsize.c: Move #include "blkidP.h" before to avoid a clash with the LIST_HEAD in the latter file. 2005-01-10 Theodore Ts'o * probe.c: Integrate and fix up Janos Farkas's patch. Version 0 swap headers won't ever have uuid/labels. Also, if the swap partition is recreated without a label, make sure label in the blkid file gets freed. (get_ext2_info, probe_vfat, probe_msdos, probe_reiserfs): Make sure the label is cleared from the blkid file if the label gets cleared from the filesystem. (probe_romfs): Avoid dereferencing a null pointer of the label is not present. 2005-01-10 Janos Farkas * probe.h: Define linux swap format. * probe.c: Fetch uuid/label from swap headers if present. Mark swap types as needing extra probe. 2005-01-05 Theodore Ts'o * save.c (save_dev): Don't save relative pathnames since they won't be useful to another process. * devname.c (probe_one): Make sure the device is a block device before checking st_rdev. * probe.c (probe_msdos): Mark msdos filesystems as type vfat, with a SEC_TYPE of msdos, so that mount will use vfat to mount msdos filesystems. (Addresses Debian bug #287455) (probe_ext3): For ext3 filesystems, return a type of ext3 and a SEC_TYPE of ext2, for similar reasons as above. (blkid_verify_devname): Allow non-block devices to be verified, for testing purposes. 2004-12-14 Theodore Ts'o * Makefile.in: Use Linux-kernel-style makefile output for "make install" * Makefile.in (installdirs): Use $(MKINSTALLDIRS) macro 2004-11-30 Theodore Ts'o * probe.c, probe.h: Avoid using uint and uchar types, to allow compilation using dietlibc. * Makefile.in: Use Linux-kernel-style makefile output to make it easier to see errors/warnings. 2004-11-19 Theodore Ts'o * probe.c (probe_ocfs): Fix bug where the wrong size for "ocfs1" was being passed to blkid_set_tag(). Thanks to Andrea Dilger for pointing this out. 2004-09-17 Theodore Ts'o * probe.c, probe.h: Add support for ocfs2 detection, courtesy of manish.singh@oracle.com * getsize.c: Clean up header #include's. Include sys/disk.h if present since this is the new place where the DIOCGMEDIASIZE ioctl is defined on FreeBSD systems. (Addresses Debian bug #264630) * llseek.c (blkid_llseek): On non-linux systems, use lseek64() if it is present. (Addresses Debian bug #269044) 2004-05-12 Theodore Ts'o * read.c (blkid_read_cache): Add missing fclose() which was causing a memory and file descriptor leak. Thanks to magnus.fromreide at teligent.se. 2004-04-19 Theodore Ts'o * probe.c (blkid_verify_devname): If the time is earlier than the last modified time of the device, then force a reverify; it means the system time may not be trustworthy. 2004-04-12 Theodore Ts'o * cache.c (blkid_get_cache): If the BLKID_FILE environment variable is set, use it to find the blkid.tab file if the calling application did not supply a filename. 2004-04-03 Theodore Ts'o * Makefile.in: Update the modtime even if subst doesn't need to update the libblkid man page, to avoid always re-running subst, especially since there are no dependencies on the man page. 2004-04-03 Theodore Ts'o * blkid_types.h.in: Remove check for _UUID_TYPES since uuid_types.h is no longer used. 2004-03-21 Theodore Ts'o * getsize.c (blkid_get_dev_size): Don't close the file descriptor when determining the size. This bug was introduced in the previous getsize changes, and was screwing up the blkid library probe functions. (Addresses Debian Bug #239191) 2004-03-08 Theodore Ts'o * getsize.c (blkid_get_dev_size): Only use the BLKGETSIZE64 ioctl on Linux 2.6 since it is unreliable in Linux 2.4.. (Addresses Debian Bug #236528) Fix typo in the ioctl used for Mac OS X. 2004-03-04 Theodore Ts'o * probe.c (probe_ocfs), probe.h: Add support for the Oracle Cluster Filesystem (ocfs). Patches courtesy of Rusty Lynch (rusty@linux.co.intel.com). 2004-03-02 Theodore Ts'o * getsize.c (blkid_get_dev_size): Update getsize functions to use Apple Darwin and Linux 64-bit ioctl's 2004-02-29 Brian Bergstrand * Makefile.in: Use $(BSDLIB_PIC_FLAG) to determine whether to use -fpic or -fPIC 2004-02-28 Theodore Ts'o * Release of E2fsprogs 1.35 2003-12-07 Theodore Ts'o * probe.c, read.c, blkidP.h: Fix gcc -Wall nitpicks. 2003-07-25 Theodore Ts'o * Release of E2fsprogs 1.34 2003-07-22 Theodore Ts'o * probe.c (probe_udf): Add specific UDF probing code, and probe UDF before checking for ISO9660 filesystems. 2003-07-21 Theodore Ts'o * probe.c (blkid_known_fstype): New function which returns true if the filesystem type is one which this blkid library supports. 2003-07-20 Theodore Ts'o * probe.c: When revalidating a filesystem, delete the LABEL tag if the filesystem no longer has a label. 2003-07-18 Theodore Ts'o * tag.c (blkid_find_dev_with_tag): If blkid_probe_all() returns an error, then reflect that error upwards; don't try again (forever). This prevents an infinite loop when /proc and the /etc/blkid.tab file are not present. 2003-07-06 Theodore Ts'o * blkid_types.h.in: Fix gcc -Wall nitpicks (don't use #elsif) * cache.c: Fix gcc -Wall nitpicks (missing #include ) * probe.h: Fix gcc -Wall nitpicks (missing casts) 2003-05-21 Theodore Ts'o * Makefile.in (ELF_OTHER_LIBS): The blkid library depends on the uuid library. (Addresses Debian bug: #194094) 2003-04-21 Theodore Ts'o * Release of E2fsprogs 1.33 2003-04-19 Theodore Ts'o * blkidP.h: Fix gcc -Wall warnings by using __inline__ instead of inline. 2003-04-02 Theodore Ts'o * probe.c, probe.h: Fix XFS superblock definition. Add support to extract UUID and labels for JFS and romfs. (Thanks to Janos Farkas .) 2003-03-30 Theodore Ts'o * getsize.c: #include stat.h for the Apple Darwin port 2003-03-17 Theodore Ts'o * cache.c: Initialize blkid_debug_mask to zero since some operating systems can't deal with variables in the common section in shared libraries. * getsize.c (blkid_get_dev_size): Fix Apple Darwin port. 2003-03-06 * devname.c (probe_one): Fix bug; if a partition has no known type, don't derference a null pointer and crash. 2003-03-06 Theodore Tso * blkid_types.h.in: Don't redefine types if other e2fsprogs *_types.h files have been included already. * list.h, probe.h: Use static inline instead of extern inline to comply with C99 inline support. * devname.c (blkid_probe_all): Avoid GCC extension; don't initialize an array with the address of an automatic variable. * Makefile.in: Eliminate -Wall as a C compiler option by default; it's not portable. 2003-03-02 Theodore Ts'o * Makefile.in: Don't install list.h, since it's not used by the public blkid.h file. * blkid_types.h.in: Change #ifndef protection to use _BLKID_TYPES_H 2003-03-01 Theodore Ts'o * tag.c (blkid_set_tag): Fix bug; don't return an error when deleting a tag by setting the value to NULL. This caused a failed device verification to loop forever in blkid_verify_devname(). * resolve.c (main): Update debugging test program so that it compiles with the latest blkid API changes. * libblkid.3.in: Update manual page to reflect recent API changes. * resolve.c (blkid_get_tag_value): If the passed-in cache is NULL, then get and release a temporary cache as a convenience to the calling application. (blkid_get_devname): If the passed in token does not contain an '=', and value is NULL, then return the passed in token. * read.c (blkid_read_cache): Don't return 0 since blkid_read_cache now returns void. * blkid.h: Add include of sys/types.h, since we use dev_t 2003-02-27 Theodore Ts'o * resolve.c (blkid_get_tag_value): Rename function (used to be blkid_get_tagname_devname) 2003-02-22 Theodore Ts'o * devname.c (blkid_probe_all), tag.c (blkid_find_dev_with_tag): Call blkid_read_cache to make sure the in-core version of the data structure is the latest. After probing all of the devices in blkid_probe_all() force the cache file to be written out, the probe_all represents a lot of effort that shouldn't be lost. * tag.c (blkid_set_tag): Always replace an existing tag with the new value; we no longer suppor multiple tags with the same value attached to a device, as this was never really supported well, and significantly increased the code complexity. * probe.c (probe_ext2): Change handling of ext2/ext3 filesystems. Ext3 filesystems are now always treated as ext2 filesystems, with a special SEC_TYPE tag set to ext3. This was necessary because we now longer support multiple tags with the same name attached to a device. * save.c (save_dev): Don't special case the TYPE tag; just write it out along with all of the normal tags. (blkid_flush_cache): Eliminate special case code for stdout. * cache.c (blkid_new_cache, blkid_get_cache): Eliminate blkid_new_cache and fold into blkid_get_cache (moved to cache.c) * read.c (blkid_read_cache): New function created from blkid_get_cache which used to be in read.c that only updates the in-core cache data structure from the file. Uses the file modification time of the cache file to determine whether the cache file needs to be re-read. * cache.c, dev.c, devname.c, devno.c, probe.c, read.c, resolve.c, save.c, tag.c, blkidP.h: Add dynamic debugging capabilities, controlled by the environment variable BLKID_DEBUG. 2003-02-16 Theodore Ts'o * blkid.h, dev.c, devname.c, probe.c, read.c, resolve.c: Rename blkid_get_devname() to blkid_get_dev(). Also rename blkid_get_token() to blkid_get_devname(). This more accurately describes what these functions do. 2003-02-14 Theodore Ts'o * blkidP.h, devname.c (blkid_get_devname), read.c (parse_tag), save.c (save_dev): Remove bid_id, as it is not used for anything. * Makefile.in (blkid): When building the blkid, don't link against the shared blkid library; link only against the static blkid library. * blkidP.h (struct blkid_struct_dev): Remove bid_size and bid_devsize (since they aren't used any more) and add bid_pri to the device structure. * devname.c (probe_one, lvm_probe_all, evms_probe_all, blkid_probe_all): Set the bid_pri filed in the device structure depending on type of device so that EVMS, LVM, and MD devices get priority over normal devices. * tag.c (blkid_find_dev_with_tag): When looking for a device that matches the search criteria, return the one with the largest priority (bid_pri). * save.c (save_dev): Write out the PRI tag from bid_pri. * read.c (parse_tag): Parse the PRI tag and store its value in bid_pri. * probe.c (blkid_verify_devname): If the device does not exist (open returns ENOENT), treat this as a fatal error and release the device. After verifying the device, set the cache as being modified so the changes are written out. * resolve.c (main): Change the test driver to get a blkid cache and pass it to blkid_get_tagname_devname and blkid_get_token, as the cache is no longer optional. 2003-02-12 Theodore Ts'o * blkid.h, blkidP.h, cache.c, dev.c, devname.c, devno.c, probe.c, probe.h, read.c, resolve.c, save.c, tag.c: Wholesale changes to library to simplify the implementation and shrink its size. Change library version to be 1.0. 2003-01-27 Theodore Ts'o * read.c (parse_tag): Do not return that blkid_tag when parsing the blkid.tag file. * resolve.c (blkid_get_token, blkid_get_tagname_devname): Fold in code from removed functions * tag.c (blkid_create_tag): Don't return the newly tag strcture any more, as it's not needed. (blkid_find_tag_cache, blkid_get_tag_cache, blkid_token_to_tag, blkid_find_tv_tags): Remove these functions, as they are either only used once or have interfaces that should be deprecated. (blkid_find_tag_dev, blkid_find_head_cache): Replace use of blkid_tag with one or two const char * type/value arguments. (blkid_find_dev_with_tag): Fold in code from removed functions * probe.h, probe.c: Use a more sophisticated set of byte-swapping routines which are more compact and efficient. Drop calculation of bid_free, since it's unnecessary. Avoid * save.c (blkid_save_cache): Set the permissions on the blkid cache file to 644. 2003-01-25 Theodore Ts'o * cache.c, dev.c, devname.c, devno.c, getsize.c, llseek.c, probe.c, probe.h, read.c, resolve.c, save.c, tag.c, blkid.h, blkidP.h: Separate public and private interfaces into separate header files. Start separating internal implementation details from the publically exported interface. * devname.c: Add support for EVMS * blkid.h, cache.c, dev.c, devname.c, devno.c, probe.c, probe.h, read.c, resolve.c, save.c, tag.c: Fix gcc -Wall nits. 2003-01-24 Theodore Ts'o * save.c (blkid_save_cache): Use mkstemp() instead mktemp(). 2002-10-04 Jordan Breeding * Forward port to e2fsprogs 1.30 2001-09-20 Andreas Dilger * Initial release of libblkid. ocfs2-tools-ocfs2-tools-1.8.6/ocfs2console/blkid/Makefile000066400000000000000000000013671347147137200232140ustar00rootroot00000000000000TOPDIR = ../.. include $(TOPDIR)/Preamble.make INCLUDES = -I.. CFLAGS += -fPIC CPPFLAGS = -DHAVE_UNISTD_H=1 -DHAVE_SYS_TYPES_H=1 -DHAVE_SYS_STAT_H=1 \ -DHAVE_ERRNO_H=1 -DHAVE_SYS_IOCTL_H=1 -DHAVE_LINUX_FD_H=1 \ -DHAVE_SYS_QUEUE_H=1 -DHAVE_STDLIB_H=1 -DHAVE_LSEEK64=1 \ -DHAVE_LSEEK64_PROTOTYPE=1 CFILES = \ cache.c \ dev.c \ devname.c \ devno.c \ getsize.c \ llseek.c \ probe.c \ read.c \ resolve.c \ save.c \ tag.c \ version.c HFILES = \ blkid.h \ blkidP.h \ list.h \ probe.h ifndef HAVE_BLKID UNINST_LIBRARIES = libblkid-internal.a OBJS = $(subst .c,.o,$(CFILES)) libblkid-internal.a: $(OBJS) rm -f $@ $(AR) r $@ $^ $(RANLIB) $@ endif DIST_FILES = $(CFILES) $(HFILES) ChangeLog include $(TOPDIR)/Postamble.make ocfs2-tools-ocfs2-tools-1.8.6/ocfs2console/blkid/blkid.h000066400000000000000000000057201347147137200230070ustar00rootroot00000000000000/* * blkid.h - Interface for libblkid, a library to identify block devices * * Copyright (C) 2001 Andreas Dilger * Copyright (C) 2003 Theodore Ts'o * * %Begin-Header% * This file may be redistributed under the terms of the * GNU Lesser General Public License. * %End-Header% */ #ifndef _BLKID_BLKID_H #define _BLKID_BLKID_H #include #include #ifdef __cplusplus extern "C" { #endif #define BLKID_VERSION "1.0.0" #define BLKID_DATE "12-Feb-2003" typedef struct blkid_struct_dev *blkid_dev; typedef struct blkid_struct_cache *blkid_cache; typedef __s64 blkid_loff_t; typedef struct blkid_struct_tag_iterate *blkid_tag_iterate; typedef struct blkid_struct_dev_iterate *blkid_dev_iterate; /* * Flags for blkid_get_dev * * BLKID_DEV_CREATE Create an empty device structure if not found * in the cache. * BLKID_DEV_VERIFY Make sure the device structure corresponds * with reality. * BLKID_DEV_FIND Just look up a device entry, and return NULL * if it is not found. * BLKID_DEV_NORMAL Get a valid device structure, either from the * cache or by probing the device. */ #define BLKID_DEV_FIND 0x0000 #define BLKID_DEV_CREATE 0x0001 #define BLKID_DEV_VERIFY 0x0002 #define BLKID_DEV_NORMAL (BLKID_DEV_CREATE | BLKID_DEV_VERIFY) /* cache.c */ extern void blkid_put_cache(blkid_cache cache); extern int blkid_get_cache(blkid_cache *cache, const char *filename); /* dev.c */ extern const char *blkid_dev_devname(blkid_dev dev); extern blkid_dev_iterate blkid_dev_iterate_begin(blkid_cache cache); extern int blkid_dev_next(blkid_dev_iterate iterate, blkid_dev *dev); extern void blkid_dev_iterate_end(blkid_dev_iterate iterate); /* devno.c */ extern char *blkid_devno_to_devname(dev_t devno); /* devname.c */ extern int blkid_probe_all(blkid_cache cache); extern blkid_dev blkid_get_dev(blkid_cache cache, const char *devname, int flags); /* getsize.c */ extern blkid_loff_t blkid_get_dev_size(int fd); /* probe.c */ int blkid_known_fstype(const char *fstype); extern blkid_dev blkid_verify(blkid_cache cache, blkid_dev dev); /* read.c */ /* resolve.c */ extern char *blkid_get_tag_value(blkid_cache cache, const char *tagname, const char *devname); extern char *blkid_get_devname(blkid_cache cache, const char *token, const char *value); /* tag.c */ extern blkid_tag_iterate blkid_tag_iterate_begin(blkid_dev dev); extern int blkid_tag_next(blkid_tag_iterate iterate, const char **type, const char **value); extern void blkid_tag_iterate_end(blkid_tag_iterate iterate); extern blkid_dev blkid_find_dev_with_tag(blkid_cache cache, const char *type, const char *value); extern int blkid_parse_tag_string(const char *token, char **ret_type, char **ret_val); /* version.c */ extern int blkid_parse_version_string(const char *ver_string); extern int blkid_get_library_version(const char **ver_string, const char **date_string); #ifdef __cplusplus } #endif #endif /* _BLKID_BLKID_H */ ocfs2-tools-ocfs2-tools-1.8.6/ocfs2console/blkid/blkidP.h000066400000000000000000000147761347147137200231420ustar00rootroot00000000000000/* * blkidP.h - Internal interfaces for libblkid * * Copyright (C) 2001 Andreas Dilger * Copyright (C) 2003 Theodore Ts'o * * %Begin-Header% * This file may be redistributed under the terms of the * GNU Lesser General Public License. * %End-Header% */ #ifndef _BLKID_BLKIDP_H #define _BLKID_BLKIDP_H #include #include #include #include #ifdef __GNUC__ #define __BLKID_ATTR(x) __attribute__(x) #else #define __BLKID_ATTR(x) #endif /* * This describes the attributes of a specific device. * We can traverse all of the tags by bid_tags (linking to the tag bit_names). * The bid_label and bid_uuid fields are shortcuts to the LABEL and UUID tag * values, if they exist. */ struct blkid_struct_dev { struct list_head bid_devs; /* All devices in the cache */ struct list_head bid_tags; /* All tags for this device */ blkid_cache bid_cache; /* Dev belongs to this cache */ char *bid_name; /* Device inode pathname */ char *bid_type; /* Preferred device TYPE */ int bid_pri; /* Device priority */ dev_t bid_devno; /* Device major/minor number */ time_t bid_time; /* Last update time of device */ unsigned int bid_flags; /* Device status bitflags */ char *bid_label; /* Shortcut to device LABEL */ char *bid_uuid; /* Shortcut to binary UUID */ }; #define BLKID_BID_FL_VERIFIED 0x0001 /* Device data validated from disk */ #define BLKID_BID_FL_INVALID 0x0004 /* Device is invalid */ /* * Each tag defines a NAME=value pair for a particular device. The tags * are linked via bit_names for a single device, so that traversing the * names list will get you a list of all tags associated with a device. * They are also linked via bit_values for all devices, so one can easily * search all tags with a given NAME for a specific value. */ struct blkid_struct_tag { struct list_head bit_tags; /* All tags for this device */ struct list_head bit_names; /* All tags with given NAME */ char *bit_name; /* NAME of tag (shared) */ char *bit_val; /* value of tag */ blkid_dev bit_dev; /* pointer to device */ }; typedef struct blkid_struct_tag *blkid_tag; /* * Minimum number of seconds between device probes, even when reading * from the cache. This is to avoid re-probing all devices which were * just probed by another program that does not share the cache. */ #define BLKID_PROBE_MIN 2 /* * Time in seconds an entry remains verified in the in-memory cache * before being reverified (in case of long-running processes that * keep a cache in memory and continue to use it for a long time). */ #define BLKID_PROBE_INTERVAL 200 /* This describes an entire blkid cache file and probed devices. * We can traverse all of the found devices via bic_list. * We can traverse all of the tag types by bic_tags, which hold empty tags * for each tag type. Those tags can be used as list_heads for iterating * through all devices with a specific tag type (e.g. LABEL). */ struct blkid_struct_cache { struct list_head bic_devs; /* List head of all devices */ struct list_head bic_tags; /* List head of all tag types */ time_t bic_time; /* Last probe time */ time_t bic_ftime; /* Mod time of the cachefile */ unsigned int bic_flags; /* Status flags of the cache */ char *bic_filename; /* filename of cache */ }; #define BLKID_BIC_FL_PROBED 0x0002 /* We probed /proc/partition devices */ #define BLKID_BIC_FL_CHANGED 0x0004 /* Cache has changed from disk */ extern char *blkid_strdup(const char *s); extern char *blkid_strndup(const char *s, const int length); #define BLKID_CACHE_FILE "/etc/blkid.tab" extern const char *blkid_devdirs[]; #define BLKID_ERR_IO 5 #define BLKID_ERR_PROC 9 #define BLKID_ERR_MEM 12 #define BLKID_ERR_CACHE 14 #define BLKID_ERR_DEV 19 #define BLKID_ERR_PARAM 22 #define BLKID_ERR_BIG 27 /* * Priority settings for different types of devices */ #define BLKID_PRI_EVMS 30 #define BLKID_PRI_LVM 20 #define BLKID_PRI_MD 10 #if defined(TEST_PROGRAM) && !defined(CONFIG_BLKID_DEBUG) #define CONFIG_BLKID_DEBUG #endif #define DEBUG_CACHE 0x0001 #define DEBUG_DUMP 0x0002 #define DEBUG_DEV 0x0004 #define DEBUG_DEVNAME 0x0008 #define DEBUG_DEVNO 0x0010 #define DEBUG_PROBE 0x0020 #define DEBUG_READ 0x0040 #define DEBUG_RESOLVE 0x0080 #define DEBUG_SAVE 0x0100 #define DEBUG_TAG 0x0200 #define DEBUG_INIT 0x8000 #define DEBUG_ALL 0xFFFF #ifdef CONFIG_BLKID_DEBUG #include extern int blkid_debug_mask; #define DBG(m,x) if ((m) & blkid_debug_mask) x; #else #define DBG(m,x) #endif #ifdef CONFIG_BLKID_DEBUG static inline void DEB_DUMP_TAG(int mask, blkid_tag tag) { if (!(mask & blkid_debug_mask)) return; if (!tag) { printf(" tag: NULL\n"); return; } printf(" tag: %s=\"%s\"\n", tag->bit_name, tag->bit_val); } static inline void DEB_DUMP_DEV(int mask, blkid_dev dev) { struct list_head *p; if (!(mask & blkid_debug_mask)) return; if (!dev) { printf(" dev: NULL\n"); return; } printf(" dev: name = %s\n", dev->bid_name); printf(" dev: DEVNO=\"0x%0Lx\"\n", dev->bid_devno); printf(" dev: TIME=\"%lu\"\n", dev->bid_time); printf(" dev: PRI=\"%d\"\n", dev->bid_pri); printf(" dev: flags = 0x%08X\n", dev->bid_flags); list_for_each(p, &dev->bid_tags) { blkid_tag tag = list_entry(p, struct blkid_struct_tag, bit_tags); DEB_DUMP_TAG(mask, tag); } printf("\n"); } static inline void DEB_DUMP_CACHE(int mask, blkid_cache cache) { struct list_head *p; if (!cache || !(mask & blkid_debug_mask)) { printf("cache: NULL\n"); return; } printf("cache: time = %lu\n", cache->bic_time); printf("cache: flags = 0x%08X\n", cache->bic_flags); list_for_each(p, &cache->bic_devs) { blkid_dev dev = list_entry(p, struct blkid_struct_dev, bid_devs); DEB_DUMP_DEV(mask, dev); } } #else #define DEB_DUMP_TAG(mask, tag) do {} while (0) #define DEB_DUMP_DEV(mask, dev) do {} while (0) #define DEB_DUMP_CACHE(mask, cache) do {} while (0) #endif /* lseek.c */ extern blkid_loff_t blkid_llseek(int fd, blkid_loff_t offset, int whence); /* read.c */ extern void blkid_read_cache(blkid_cache cache); /* save.c */ extern int blkid_flush_cache(blkid_cache cache); /* * Functions to create and find a specific tag type: tag.c */ extern void blkid_free_tag(blkid_tag tag); extern blkid_tag blkid_find_tag_dev(blkid_dev dev, const char *type); extern int blkid_set_tag(blkid_dev dev, const char *name, const char *value, const int vlength); /* * Functions to create and find a specific tag type: dev.c */ extern blkid_dev blkid_new_dev(void); extern void blkid_free_dev(blkid_dev dev); #ifdef __cplusplus } #endif #endif /* _BLKID_BLKIDP_H */ ocfs2-tools-ocfs2-tools-1.8.6/ocfs2console/blkid/cache.c000066400000000000000000000054601347147137200227610ustar00rootroot00000000000000/* * cache.c - allocation/initialization/free routines for cache * * Copyright (C) 2001 Andreas Dilger * Copyright (C) 2003 Theodore Ts'o * * %Begin-Header% * This file may be redistributed under the terms of the * GNU Lesser General Public License. * %End-Header% */ #include #include #if HAVE_UNISTD_H #include #endif #if HAVE_SYS_TYPES_H #include #endif #include "blkidP.h" int blkid_debug_mask = 0; int blkid_get_cache(blkid_cache *ret_cache, const char *filename) { blkid_cache cache; #ifdef CONFIG_BLKID_DEBUG if (!(blkid_debug_mask & DEBUG_INIT)) { char *dstr = getenv("BLKID_DEBUG"); if (dstr) blkid_debug_mask = strtoul(dstr, 0, 0); blkid_debug_mask |= DEBUG_INIT; } #endif DBG(DEBUG_CACHE, printf("creating blkid cache (using %s)\n", filename ? filename : "default cache")); if (!(cache = (blkid_cache) calloc(1, sizeof(struct blkid_struct_cache)))) return -BLKID_ERR_MEM; INIT_LIST_HEAD(&cache->bic_devs); INIT_LIST_HEAD(&cache->bic_tags); if (filename && !strlen(filename)) filename = 0; if (!filename && (getuid() == geteuid())) filename = getenv("BLKID_FILE"); if (!filename) filename = BLKID_CACHE_FILE; cache->bic_filename = blkid_strdup(filename); blkid_read_cache(cache); *ret_cache = cache; return 0; } void blkid_put_cache(blkid_cache cache) { if (!cache) return; (void) blkid_flush_cache(cache); DBG(DEBUG_CACHE, printf("freeing cache struct\n")); /* DEB_DUMP_CACHE(cache); */ while (!list_empty(&cache->bic_devs)) { blkid_dev dev = list_entry(cache->bic_devs.next, struct blkid_struct_dev, bid_devs); blkid_free_dev(dev); } while (!list_empty(&cache->bic_tags)) { blkid_tag tag = list_entry(cache->bic_tags.next, struct blkid_struct_tag, bit_tags); while (!list_empty(&tag->bit_names)) { blkid_tag bad = list_entry(tag->bit_names.next, struct blkid_struct_tag, bit_names); DBG(DEBUG_CACHE, printf("warning: unfreed tag %s=%s\n", bad->bit_name, bad->bit_val)); blkid_free_tag(bad); } blkid_free_tag(tag); } if (cache->bic_filename) free(cache->bic_filename); free(cache); } #ifdef TEST_PROGRAM int main(int argc, char** argv) { blkid_cache cache = NULL; int ret; blkid_debug_mask = DEBUG_ALL; if ((argc > 2)) { fprintf(stderr, "Usage: %s [filename] \n", argv[0]); exit(1); } if ((ret = blkid_get_cache(&cache, argv[1])) < 0) { fprintf(stderr, "error %d parsing cache file %s\n", ret, argv[1] ? argv[1] : BLKID_CACHE_FILE); exit(1); } if ((ret = blkid_get_cache(&cache, "/dev/null")) != 0) { fprintf(stderr, "%s: error creating cache (%d)\n", argv[0], ret); exit(1); } if ((ret = blkid_probe_all(cache) < 0)) fprintf(stderr, "error probing devices\n"); blkid_put_cache(cache); return ret; } #endif ocfs2-tools-ocfs2-tools-1.8.6/ocfs2console/blkid/dev.c000066400000000000000000000051131347147137200224670ustar00rootroot00000000000000/* * dev.c - allocation/initialization/free routines for dev * * Copyright (C) 2001 Andreas Dilger * Copyright (C) 2003 Theodore Ts'o * * %Begin-Header% * This file may be redistributed under the terms of the * GNU Lesser General Public License. * %End-Header% */ #include #include #include "blkidP.h" blkid_dev blkid_new_dev(void) { blkid_dev dev; if (!(dev = (blkid_dev) calloc(1, sizeof(struct blkid_struct_dev)))) return NULL; INIT_LIST_HEAD(&dev->bid_devs); INIT_LIST_HEAD(&dev->bid_tags); return dev; } void blkid_free_dev(blkid_dev dev) { if (!dev) return; DBG(DEBUG_DEV, printf(" freeing dev %s (%s)\n", dev->bid_name, dev->bid_type)); DEB_DUMP_DEV(DEBUG_DEV, dev); list_del(&dev->bid_devs); while (!list_empty(&dev->bid_tags)) { blkid_tag tag = list_entry(dev->bid_tags.next, struct blkid_struct_tag, bit_tags); blkid_free_tag(tag); } if (dev->bid_name) free(dev->bid_name); free(dev); } /* * Given a blkid device, return its name */ extern const char *blkid_dev_devname(blkid_dev dev) { return dev->bid_name; } /* * dev iteration routines for the public libblkid interface. * * These routines do not expose the list.h implementation, which are a * contamination of the namespace, and which force us to reveal far, far * too much of our internal implemenation. I'm not convinced I want * to keep list.h in the long term, anyway. It's fine for kernel * programming, but performance is not the #1 priority for this * library, and I really don't like the tradeoff of type-safety for * performance for this application. [tytso:20030125.2007EST] */ /* * This series of functions iterate over all devices in a blkid cache */ #define DEV_ITERATE_MAGIC 0x01a5284c struct blkid_struct_dev_iterate { int magic; blkid_cache cache; struct list_head *p; }; extern blkid_dev_iterate blkid_dev_iterate_begin(blkid_cache cache) { blkid_dev_iterate iter; iter = malloc(sizeof(struct blkid_struct_dev_iterate)); if (iter) { iter->magic = DEV_ITERATE_MAGIC; iter->cache = cache; iter->p = cache->bic_devs.next; } return (iter); } /* * Return 0 on success, -1 on error */ extern int blkid_dev_next(blkid_dev_iterate iter, blkid_dev *dev) { *dev = 0; if (!iter || iter->magic != DEV_ITERATE_MAGIC || iter->p == &iter->cache->bic_devs) return -1; *dev = list_entry(iter->p, struct blkid_struct_dev, bid_devs); iter->p = iter->p->next; return 0; } extern void blkid_dev_iterate_end(blkid_dev_iterate iter) { if (!iter || iter->magic != DEV_ITERATE_MAGIC) return; iter->magic = 0; free(iter); } ocfs2-tools-ocfs2-tools-1.8.6/ocfs2console/blkid/devname.c000066400000000000000000000207331347147137200233350ustar00rootroot00000000000000/* * devname.c - get a dev by its device inode name * * Copyright (C) Andries Brouwer * Copyright (C) 1999, 2000, 2001, 2002, 2003 Theodore Ts'o * Copyright (C) 2001 Andreas Dilger * * %Begin-Header% * This file may be redistributed under the terms of the * GNU Lesser General Public License. * %End-Header% */ #include #include #if HAVE_UNISTD_H #include #endif #include #include #include #if HAVE_SYS_TYPES_H #include #endif #if HAVE_SYS_STAT_H #include #endif #if HAVE_ERRNO_H #include #endif #if HAVE_SYS_MKDEV_H #include #endif #include #include "blkidP.h" /* * Find a dev struct in the cache by device name, if available. * * If there is no entry with the specified device name, and the create * flag is set, then create an empty device entry. */ blkid_dev blkid_get_dev(blkid_cache cache, const char *devname, int flags) { blkid_dev dev = NULL, tmp; struct list_head *p; if (!cache || !devname) return NULL; list_for_each(p, &cache->bic_devs) { tmp = list_entry(p, struct blkid_struct_dev, bid_devs); if (strcmp(tmp->bid_name, devname)) continue; DBG(DEBUG_DEVNAME, printf("found devname %s in cache\n", tmp->bid_name)); dev = tmp; break; } if (!dev && (flags & BLKID_DEV_CREATE)) { dev = blkid_new_dev(); if (!dev) return NULL; dev->bid_name = blkid_strdup(devname); dev->bid_cache = cache; list_add_tail(&dev->bid_devs, &cache->bic_devs); cache->bic_flags |= BLKID_BIC_FL_CHANGED; } if (flags & BLKID_DEV_VERIFY) dev = blkid_verify(cache, dev); return dev; } /* * Probe a single block device to add to the device cache. */ static void probe_one(blkid_cache cache, const char *ptname, dev_t devno, int pri) { blkid_dev dev = NULL; struct list_head *p; const char **dir; char *devname = NULL; /* See if we already have this device number in the cache. */ list_for_each(p, &cache->bic_devs) { blkid_dev tmp = list_entry(p, struct blkid_struct_dev, bid_devs); if (tmp->bid_devno == devno) { dev = blkid_verify(cache, tmp); break; } } if (dev && dev->bid_devno == devno) goto set_pri; /* * Take a quick look at /dev/ptname for the device number. We check * all of the likely device directories. If we don't find it, or if * the stat information doesn't check out, use blkid_devno_to_devname() * to find it via an exhaustive search for the device major/minor. */ for (dir = blkid_devdirs; *dir; dir++) { struct stat st; char device[256]; sprintf(device, "%s/%s", *dir, ptname); if ((dev = blkid_get_dev(cache, device, BLKID_DEV_FIND)) && dev->bid_devno == devno) goto set_pri; if (stat(device, &st) == 0 && S_ISBLK(st.st_mode) && st.st_rdev == devno) { devname = blkid_strdup(device); break; } } if (!devname) { devname = blkid_devno_to_devname(devno); if (!devname) return; } dev = blkid_get_dev(cache, devname, BLKID_DEV_NORMAL); free(devname); set_pri: if (!pri && !strncmp(ptname, "md", 2)) pri = BLKID_PRI_MD; if (dev) dev->bid_pri = pri; return; } #define PROC_PARTITIONS "/proc/partitions" #define VG_DIR "/proc/lvm/VGs" /* * This function initializes the UUID cache with devices from the LVM * proc hierarchy. We currently depend on the names of the LVM * hierarchy giving us the device structure in /dev. (XXX is this a * safe thing to do?) */ #ifdef VG_DIR #include static dev_t lvm_get_devno(const char *lvm_device) { FILE *lvf; char buf[1024]; int ma, mi; dev_t ret = 0; DBG(DEBUG_DEVNAME, printf("opening %s\n", lvm_device)); if ((lvf = fopen(lvm_device, "r")) == NULL) { DBG(DEBUG_DEVNAME, printf("%s: (%d) %s\n", lvm_device, errno, strerror(errno))); return 0; } while (fgets(buf, sizeof(buf), lvf)) { if (sscanf(buf, "device: %d:%d", &ma, &mi) == 2) { ret = makedev(ma, mi); break; } } fclose(lvf); return ret; } static void lvm_probe_all(blkid_cache cache) { DIR *vg_list; struct dirent *vg_iter; int vg_len = strlen(VG_DIR); dev_t dev; if ((vg_list = opendir(VG_DIR)) == NULL) return; DBG(DEBUG_DEVNAME, printf("probing LVM devices under %s\n", VG_DIR)); while ((vg_iter = readdir(vg_list)) != NULL) { DIR *lv_list; char *vdirname; char *vg_name; struct dirent *lv_iter; vg_name = vg_iter->d_name; if (!strcmp(vg_name, ".") || !strcmp(vg_name, "..")) continue; vdirname = malloc(vg_len + strlen(vg_name) + 8); if (!vdirname) goto exit; sprintf(vdirname, "%s/%s/LVs", VG_DIR, vg_name); lv_list = opendir(vdirname); free(vdirname); if (lv_list == NULL) continue; while ((lv_iter = readdir(lv_list)) != NULL) { char *lv_name, *lvm_device; lv_name = lv_iter->d_name; if (!strcmp(lv_name, ".") || !strcmp(lv_name, "..")) continue; lvm_device = malloc(vg_len + strlen(vg_name) + strlen(lv_name) + 8); if (!lvm_device) { closedir(lv_list); goto exit; } sprintf(lvm_device, "%s/%s/LVs/%s", VG_DIR, vg_name, lv_name); dev = lvm_get_devno(lvm_device); sprintf(lvm_device, "%s/%s", vg_name, lv_name); DBG(DEBUG_DEVNAME, printf("LVM dev %s: devno 0x%04X\n", lvm_device, (unsigned int) dev)); probe_one(cache, lvm_device, dev, BLKID_PRI_LVM); free(lvm_device); } closedir(lv_list); } exit: closedir(vg_list); } #endif #define PROC_EVMS_VOLUMES "/proc/evms/volumes" static int evms_probe_all(blkid_cache cache) { char line[100]; int ma, mi, sz, num = 0; FILE *procpt; char device[110]; procpt = fopen(PROC_EVMS_VOLUMES, "r"); if (!procpt) return 0; while (fgets(line, sizeof(line), procpt)) { if (sscanf (line, " %d %d %d %*s %*s %[^\n ]", &ma, &mi, &sz, device) != 4) continue; DBG(DEBUG_DEVNAME, printf("Checking partition %s (%d, %d)\n", device, ma, mi)); probe_one(cache, device, makedev(ma, mi), BLKID_PRI_EVMS); num++; } fclose(procpt); return num; } /* * Read the device data for all available block devices in the system. */ int blkid_probe_all(blkid_cache cache) { FILE *proc; char line[1024]; char ptname0[128], ptname1[128], *ptname = 0; char *ptnames[2]; dev_t devs[2]; int ma, mi; unsigned long long sz; int lens[2] = { 0, 0 }; int which = 0, last = 0; ptnames[0] = ptname0; ptnames[1] = ptname1; if (!cache) return -BLKID_ERR_PARAM; if (cache->bic_flags & BLKID_BIC_FL_PROBED && time(0) - cache->bic_time < BLKID_PROBE_INTERVAL) return 0; blkid_read_cache(cache); evms_probe_all(cache); #ifdef VG_DIR lvm_probe_all(cache); #endif proc = fopen(PROC_PARTITIONS, "r"); if (!proc) return -BLKID_ERR_PROC; while (fgets(line, sizeof(line), proc)) { last = which; which ^= 1; ptname = ptnames[which]; if (sscanf(line, " %d %d %llu %128[^\n ]", &ma, &mi, &sz, ptname) != 4) continue; devs[which] = makedev(ma, mi); DBG(DEBUG_DEVNAME, printf("read partition name %s\n", ptname)); /* Skip whole disk devs unless they have no partitions * If we don't have a partition on this dev, also * check previous dev to see if it didn't have a partn. * heuristic: partition name ends in a digit. * * Skip extended partitions. * heuristic: size is 1 * * FIXME: skip /dev/{ida,cciss,rd} whole-disk devs */ lens[which] = strlen(ptname); if (isdigit(ptname[lens[which] - 1])) { DBG(DEBUG_DEVNAME, printf("partition dev %s, devno 0x%04X\n", ptname, (unsigned int) devs[which])); if (sz > 1) probe_one(cache, ptname, devs[which], 0); lens[which] = 0; lens[last] = 0; } else if (lens[last] && strncmp(ptnames[last], ptname, lens[last])) { DBG(DEBUG_DEVNAME, printf("whole dev %s, devno 0x%04X\n", ptnames[last], (unsigned int) devs[last])); probe_one(cache, ptnames[last], devs[last], 0); lens[last] = 0; } } /* Handle the last device if it wasn't partitioned */ if (lens[which]) probe_one(cache, ptname, devs[which], 0); fclose(proc); cache->bic_time = time(0); cache->bic_flags |= BLKID_BIC_FL_PROBED; blkid_flush_cache(cache); return 0; } #ifdef TEST_PROGRAM int main(int argc, char **argv) { blkid_cache cache = NULL; int ret; blkid_debug_mask = DEBUG_ALL; if (argc != 1) { fprintf(stderr, "Usage: %s\n" "Probe all devices and exit\n", argv[0]); exit(1); } if ((ret = blkid_get_cache(&cache, "/dev/null")) != 0) { fprintf(stderr, "%s: error creating cache (%d)\n", argv[0], ret); exit(1); } if (blkid_probe_all(cache) < 0) printf("%s: error probing devices\n", argv[0]); blkid_put_cache(cache); return (0); } #endif ocfs2-tools-ocfs2-tools-1.8.6/ocfs2console/blkid/devno.c000066400000000000000000000112321347147137200230230ustar00rootroot00000000000000/* * devno.c - find a particular device by its device number (major/minor) * * Copyright (C) 2000, 2001, 2003 Theodore Ts'o * Copyright (C) 2001 Andreas Dilger * * %Begin-Header% * This file may be redistributed under the terms of the * GNU Lesser General Public License. * %End-Header% */ #include #include #if HAVE_UNISTD_H #include #endif #include #include #if HAVE_SYS_TYPES_H #include #endif #if HAVE_SYS_STAT_H #include #endif #include #if HAVE_ERRNO_H #include #endif #if HAVE_SYS_MKDEV_H #include #endif #include "blkidP.h" struct dir_list { char *name; struct dir_list *next; }; char *blkid_strndup(const char *s, int length) { char *ret; if (!s) return NULL; if (!length) length = strlen(s); ret = malloc(length + 1); if (ret) { strncpy(ret, s, length); ret[length] = '\0'; } return ret; } char *blkid_strdup(const char *s) { return blkid_strndup(s, 0); } /* * This function adds an entry to the directory list */ static void add_to_dirlist(const char *name, struct dir_list **list) { struct dir_list *dp; dp = malloc(sizeof(struct dir_list)); if (!dp) return; dp->name = blkid_strdup(name); if (!dp->name) { free(dp); return; } dp->next = *list; *list = dp; } /* * This function frees a directory list */ static void free_dirlist(struct dir_list **list) { struct dir_list *dp, *next; for (dp = *list; dp; dp = next) { next = dp->next; free(dp->name); free(dp); } *list = NULL; } static void scan_dir(char *dirname, dev_t devno, struct dir_list **list, char **devname) { DIR *dir; struct dirent *dp; char path[1024]; int dirlen; struct stat st; if ((dir = opendir(dirname)) == NULL) return; dirlen = strlen(dirname) + 2; while ((dp = readdir(dir)) != 0) { if (dirlen + strlen(dp->d_name) >= sizeof(path)) continue; if (dp->d_name[0] == '.' && ((dp->d_name[1] == 0) || ((dp->d_name[1] == '.') && (dp->d_name[2] == 0)))) continue; sprintf(path, "%s/%s", dirname, dp->d_name); if (stat(path, &st) < 0) continue; if (S_ISDIR(st.st_mode)) add_to_dirlist(path, list); else if (S_ISBLK(st.st_mode) && st.st_rdev == devno) { *devname = blkid_strdup(path); DBG(DEBUG_DEVNO, printf("found 0x%Lx at %s (%p)\n", devno, path, *devname)); break; } } closedir(dir); return; } /* Directories where we will try to search for device numbers */ const char *blkid_devdirs[] = { "/devices", "/devfs", "/dev", NULL }; /* * This function finds the pathname to a block device with a given * device number. It returns a pointer to allocated memory to the * pathname on success, and NULL on failure. */ char *blkid_devno_to_devname(dev_t devno) { struct dir_list *list = NULL, *new_list = NULL; char *devname = NULL; const char **dir; /* * Add the starting directories to search in reverse order of * importance, since we are using a stack... */ for (dir = blkid_devdirs; *dir; dir++) add_to_dirlist(*dir, &list); while (list) { struct dir_list *current = list; list = list->next; DBG(DEBUG_DEVNO, printf("directory %s\n", current->name)); scan_dir(current->name, devno, &new_list, &devname); free(current->name); free(current); if (devname) break; /* * If we're done checking at this level, descend to * the next level of subdirectories. (breadth-first) */ if (list == NULL) { list = new_list; new_list = NULL; } } free_dirlist(&list); free_dirlist(&new_list); if (!devname) { DBG(DEBUG_DEVNO, printf("blkid: couldn't find devno 0x%04lx\n", (unsigned long) devno)); } else { DBG(DEBUG_DEVNO, printf("found devno 0x%04Lx as %s\n", devno, devname)); } return devname; } #ifdef TEST_PROGRAM int main(int argc, char** argv) { char *devname, *tmp; int major, minor; dev_t devno; const char *errmsg = "Couldn't parse %s: %s\n"; blkid_debug_mask = DEBUG_ALL; if ((argc != 2) && (argc != 3)) { fprintf(stderr, "Usage:\t%s device_number\n\t%s major minor\n" "Resolve a device number to a device name\n", argv[0], argv[0]); exit(1); } if (argc == 2) { devno = strtoul(argv[1], &tmp, 0); if (*tmp) { fprintf(stderr, errmsg, "device number", argv[1]); exit(1); } } else { major = strtoul(argv[1], &tmp, 0); if (*tmp) { fprintf(stderr, errmsg, "major number", argv[1]); exit(1); } minor = strtoul(argv[2], &tmp, 0); if (*tmp) { fprintf(stderr, errmsg, "minor number", argv[2]); exit(1); } devno = makedev(major, minor); } printf("Looking for device 0x%04Lx\n", devno); devname = blkid_devno_to_devname(devno); if (devname) free(devname); return 0; } #endif ocfs2-tools-ocfs2-tools-1.8.6/ocfs2console/blkid/getsize.c000066400000000000000000000076771347147137200234040ustar00rootroot00000000000000/* * getsize.c --- get the size of a partition. * * Copyright (C) 1995, 1995 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the * GNU Lesser General Public License. * %End-Header% */ #define _LARGEFILE_SOURCE #define _LARGEFILE64_SOURCE /* include this before sys/queues.h! */ #include "blkidP.h" #include #if HAVE_UNISTD_H #include #endif #if HAVE_ERRNO_H #include #endif #include #ifdef HAVE_SYS_IOCTL_H #include #endif #ifdef HAVE_LINUX_FD_H #include #endif #ifdef HAVE_SYS_DISKLABEL_H #include #include #endif #ifdef HAVE_SYS_DISK_H #ifdef HAVE_SYS_QUEUE_H #include /* for LIST_HEAD */ #endif #include #endif #ifdef __linux__ #include #endif #if defined(__linux__) && defined(_IO) && !defined(BLKGETSIZE) #define BLKGETSIZE _IO(0x12,96) /* return device size */ #endif #if defined(__linux__) && defined(_IOR) && !defined(BLKGETSIZE64) #define BLKGETSIZE64 _IOR(0x12,114,size_t) /* return device size in bytes (u64 *arg) */ #endif #ifdef APPLE_DARWIN #define BLKGETSIZE DKIOCGETBLOCKCOUNT32 #endif /* APPLE_DARWIN */ static int valid_offset(int fd, blkid_loff_t offset) { char ch; if (blkid_llseek(fd, offset, 0) < 0) return 0; if (read(fd, &ch, 1) < 1) return 0; return 1; } /* * Returns the number of blocks in a partition */ blkid_loff_t blkid_get_dev_size(int fd) { int valid_blkgetsize64 = 1; #ifdef __linux__ struct utsname ut; #endif unsigned long long size64; unsigned long size; blkid_loff_t high, low; #ifdef FDGETPRM struct floppy_struct this_floppy; #endif #ifdef HAVE_SYS_DISKLABEL_H int part = -1; struct disklabel lab; struct partition *pp; char ch; struct stat st; #endif /* HAVE_SYS_DISKLABEL_H */ #ifdef DKIOCGETBLOCKCOUNT /* For Apple Darwin */ if (ioctl(fd, DKIOCGETBLOCKCOUNT, &size64) >= 0) { if ((sizeof(blkid_loff_t) < sizeof(unsigned long long)) && (size64 << 9 > 0xFFFFFFFF)) return 0; /* EFBIG */ return (blkid_loff_t) size64 << 9; } #endif #ifdef BLKGETSIZE64 #ifdef __linux__ if ((uname(&ut) == 0) && ((ut.release[0] == '2') && (ut.release[1] == '.') && (ut.release[2] < '6') && (ut.release[3] == '.'))) valid_blkgetsize64 = 0; #endif if (valid_blkgetsize64 && ioctl(fd, BLKGETSIZE64, &size64) >= 0) { if ((sizeof(blkid_loff_t) < sizeof(unsigned long long)) && ((size64) > 0xFFFFFFFF)) return 0; /* EFBIG */ return size64; } #endif #ifdef BLKGETSIZE if (ioctl(fd, BLKGETSIZE, &size) >= 0) return (blkid_loff_t)size << 9; #endif #ifdef FDGETPRM if (ioctl(fd, FDGETPRM, &this_floppy) >= 0) return (blkid_loff_t)this_floppy.size << 9; #endif #ifdef HAVE_SYS_DISKLABEL_H #if 0 /* * This should work in theory but I haven't tested it. Anyone * on a BSD system want to test this for me? In the meantime, * binary search mechanism should work just fine. */ if ((fstat(fd, &st) >= 0) && S_ISBLK(st.st_mode)) part = st.st_rdev & 7; if (part >= 0 && (ioctl(fd, DIOCGDINFO, (char *)&lab) >= 0)) { pp = &lab.d_partitions[part]; if (pp->p_size) return pp->p_size << 9; } #endif #endif /* HAVE_SYS_DISKLABEL_H */ /* * OK, we couldn't figure it out by using a specialized ioctl, * which is generally the best way. So do binary search to * find the size of the partition. */ low = 0; for (high = 1024; valid_offset(fd, high); high *= 2) low = high; while (low < high - 1) { const blkid_loff_t mid = (low + high) / 2; if (valid_offset(fd, mid)) low = mid; else high = mid; } return low + 1; } #ifdef TEST_PROGRAM int main(int argc, char **argv) { blkid_loff_t bytes; int fd; if (argc < 2) { fprintf(stderr, "Usage: %s device\n" "Determine the size of a device\n", argv[0]); return 1; } if ((fd = open(argv[1], O_RDONLY)) < 0) perror(argv[0]); bytes = blkid_get_dev_size(fd); printf("Device %s has %Ld 1k blocks.\n", argv[1], bytes >> 10); return 0; } #endif ocfs2-tools-ocfs2-tools-1.8.6/ocfs2console/blkid/list.h000066400000000000000000000104521347147137200226730ustar00rootroot00000000000000#if !defined(_BLKID_LIST_H) && !defined(LIST_HEAD) #define _BLKID_LIST_H #ifdef __cplusplus extern "C" { #endif #ifdef __GNUC__ #define _INLINE_ static __inline__ #else /* For Watcom C */ #define _INLINE_ static inline #endif /* * Simple doubly linked list implementation. * * Some of the internal functions ("__xxx") are useful when * manipulating whole lists rather than single entries, as * sometimes we already know the next/prev entries and we can * generate better code by using them directly rather than * using the generic single-entry routines. */ struct list_head { struct list_head *next, *prev; }; #define LIST_HEAD_INIT(name) { &(name), &(name) } #define LIST_HEAD(name) \ struct list_head name = LIST_HEAD_INIT(name) #define INIT_LIST_HEAD(ptr) do { \ (ptr)->next = (ptr); (ptr)->prev = (ptr); \ } while (0) /* * Insert a new entry between two known consecutive entries. * * This is only for internal list manipulation where we know * the prev/next entries already! */ _INLINE_ void __list_add(struct list_head * add, struct list_head * prev, struct list_head * next) { next->prev = add; add->next = next; add->prev = prev; prev->next = add; } /** * list_add - add a new entry * @add: new entry to be added * @head: list head to add it after * * Insert a new entry after the specified head. * This is good for implementing stacks. */ _INLINE_ void list_add(struct list_head *add, struct list_head *head) { __list_add(add, head, head->next); } /** * list_add_tail - add a new entry * @add: new entry to be added * @head: list head to add it before * * Insert a new entry before the specified head. * This is useful for implementing queues. */ _INLINE_ void list_add_tail(struct list_head *add, struct list_head *head) { __list_add(add, head->prev, head); } /* * Delete a list entry by making the prev/next entries * point to each other. * * This is only for internal list manipulation where we know * the prev/next entries already! */ _INLINE_ void __list_del(struct list_head * prev, struct list_head * next) { next->prev = prev; prev->next = next; } /** * list_del - deletes entry from list. * @entry: the element to delete from the list. * * list_empty() on @entry does not return true after this, @entry is * in an undefined state. */ _INLINE_ void list_del(struct list_head *entry) { __list_del(entry->prev, entry->next); } /** * list_del_init - deletes entry from list and reinitialize it. * @entry: the element to delete from the list. */ _INLINE_ void list_del_init(struct list_head *entry) { __list_del(entry->prev, entry->next); INIT_LIST_HEAD(entry); } /** * list_empty - tests whether a list is empty * @head: the list to test. */ _INLINE_ int list_empty(struct list_head *head) { return head->next == head; } /** * list_splice - join two lists * @list: the new list to add. * @head: the place to add it in the first list. */ _INLINE_ void list_splice(struct list_head *list, struct list_head *head) { struct list_head *first = list->next; if (first != list) { struct list_head *last = list->prev; struct list_head *at = head->next; first->prev = head; head->next = first; last->next = at; at->prev = last; } } /** * list_entry - get the struct for this entry * @ptr: the &struct list_head pointer. * @type: the type of the struct this is embedded in. * @member: the name of the list_struct within the struct. */ #define list_entry(ptr, type, member) \ ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member))) /** * list_for_each - iterate over elements in a list * @pos: the &struct list_head to use as a loop counter. * @head: the head for your list. */ #define list_for_each(pos, head) \ for (pos = (head)->next; pos != (head); pos = pos->next) /** * list_for_each_safe - iterate over elements in a list, but don't dereference * pos after the body is done (in case it is freed) * @pos: the &struct list_head to use as a loop counter. * @pnext: the &struct list_head to use as a pointer to the next item. * @head: the head for your list (not included in iteration). */ #define list_for_each_safe(pos, pnext, head) \ for (pos = (head)->next, pnext = pos->next; pos != (head); \ pos = pnext, pnext = pos->next) #undef _INLINE_ #ifdef __cplusplus } #endif #endif /* _BLKID_LIST_H */ ocfs2-tools-ocfs2-tools-1.8.6/ocfs2console/blkid/llseek.c000066400000000000000000000056251347147137200232000ustar00rootroot00000000000000/* * llseek.c -- stub calling the llseek system call * * Copyright (C) 1994, 1995, 1996, 1997 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the * GNU Lesser General Public License. * %End-Header% */ #define _LARGEFILE_SOURCE #define _LARGEFILE64_SOURCE #if HAVE_SYS_TYPES_H #include #endif #if HAVE_ERRNO_H #include #endif #if HAVE_UNISTD_H #include #endif #ifdef __MSDOS__ #include #endif #include "blkidP.h" #ifdef __linux__ #if defined(HAVE_LSEEK64) && defined(HAVE_LSEEK64_PROTOTYPE) #define my_llseek lseek64 #elif defined(HAVE_LLSEEK) #include #ifndef HAVE_LLSEEK_PROTOTYPE extern long long llseek(int fd, long long offset, int origin); #endif #define my_llseek llseek #else /* ! HAVE_LLSEEK */ #if defined(__alpha__) || defined(__ia64__) #define llseek lseek #else /* !__alpha__ && !__ia64__*/ #include #ifndef __NR__llseek #define __NR__llseek 140 #endif #ifndef __i386__ static int _llseek(unsigned int, unsigned long, unsigned long, blkid_loff_t *, unsigned int); static _syscall5(int, _llseek, unsigned int, fd, unsigned long, offset_high, unsigned long, offset_low, blkid_loff_t *, result, unsigned int, origin) #endif static blkid_loff_t my_llseek(int fd, blkid_loff_t offset, int origin) { blkid_loff_t result; int retval; #ifndef __i386__ retval = _llseek(fd, ((unsigned long long) offset) >> 32, ((unsigned long long)offset) & 0xffffffff, &result, origin); #else retval = syscall(__NR__llseek, fd, ((unsigned long long) offset) >> 32, ((unsigned long long)offset) & 0xffffffff, &result, origin); #endif return (retval == -1 ? (blkid_loff_t) retval : result); } #endif /* __alpha__ || __ia64__ */ #endif /* HAVE_LLSEEK */ blkid_loff_t blkid_llseek(int fd, blkid_loff_t offset, int whence) { blkid_loff_t result; static int do_compat = 0; if ((sizeof(off_t) >= sizeof(blkid_loff_t)) || (offset < ((blkid_loff_t) 1 << ((sizeof(off_t)*8) -1)))) return lseek(fd, (off_t) offset, whence); if (do_compat) { errno = EOVERFLOW; return -1; } result = my_llseek(fd, offset, whence); if (result == -1 && errno == ENOSYS) { /* * Just in case this code runs on top of an old kernel * which does not support the llseek system call */ do_compat++; errno = EOVERFLOW; } return result; } #else /* !linux */ #ifndef EOVERFLOW #ifdef EXT2_ET_INVALID_ARGUMENT #define EOVERFLOW EXT2_ET_INVALID_ARGUMENT #else #define EOVERFLOW 112 #endif #endif blkid_loff_t blkid_llseek(int fd, blkid_loff_t offset, int origin) { #if defined(HAVE_LSEEK64) && defined(HAVE_LSEEK64_PROTOTYPE) return lseek64 (fd, offset, origin); #else if ((sizeof(off_t) < sizeof(blkid_loff_t)) && (offset >= ((blkid_loff_t) 1 << ((sizeof(off_t)*8) - 1)))) { errno = EOVERFLOW; return -1; } return lseek(fd, (off_t) offset, origin); #endif } #endif /* linux */ ocfs2-tools-ocfs2-tools-1.8.6/ocfs2console/blkid/probe.c000066400000000000000000000462341347147137200230310ustar00rootroot00000000000000/* * probe.c - identify a block device by its contents, and return a dev * struct with the details * * Copyright (C) 1999 by Andries Brouwer * Copyright (C) 1999, 2000, 2003 by Theodore Ts'o * Copyright (C) 2001 by Andreas Dilger * * %Begin-Header% * This file may be redistributed under the terms of the * GNU Lesser General Public License. * %End-Header% */ #include #include #include #include #include #include #ifdef HAVE_SYS_STAT_H #include #endif #ifdef HAVE_SYS_MKDEV_H #include #endif #ifdef HAVE_ERRNO_H #include #endif #include "blkidP.h" #include "uuid/uuid.h" #include "probe.h" /* * This is a special case code to check for an MDRAID device. We do * this special since it requires checking for a superblock at the end * of the device. */ static int check_mdraid(int fd, unsigned char *ret_uuid) { struct mdp_superblock_s *md; blkid_loff_t offset; char buf[4096]; if (fd < 0) return -BLKID_ERR_PARAM; offset = (blkid_get_dev_size(fd) & ~((blkid_loff_t)65535)) - 65536; if (blkid_llseek(fd, offset, 0) < 0 || read(fd, buf, 4096) != 4096) return -BLKID_ERR_IO; /* Check for magic number */ if (memcmp("\251+N\374", buf, 4)) return -BLKID_ERR_PARAM; if (!ret_uuid) return 0; *ret_uuid = 0; /* The MD UUID is not contiguous in the superblock, make it so */ md = (struct mdp_superblock_s *)buf; if (md->set_uuid0 || md->set_uuid1 || md->set_uuid2 || md->set_uuid3) { memcpy(ret_uuid, &md->set_uuid0, 4); memcpy(ret_uuid, &md->set_uuid1, 12); } return 0; } static void set_uuid(blkid_dev dev, uuid_t uuid) { char str[37]; if (!uuid_is_null(uuid)) { uuid_unparse(uuid, str); blkid_set_tag(dev, "UUID", str, sizeof(str)); } } static void get_ext2_info(blkid_dev dev, unsigned char *buf) { struct ext2_super_block *es = (struct ext2_super_block *) buf; const char *label = 0; DBG(DEBUG_PROBE, printf("ext2_sb.compat = %08X:%08X:%08X\n", blkid_le32(es->s_feature_compat), blkid_le32(es->s_feature_incompat), blkid_le32(es->s_feature_ro_compat))); if (strlen(es->s_volume_name)) label = es->s_volume_name; blkid_set_tag(dev, "LABEL", label, sizeof(es->s_volume_name)); set_uuid(dev, es->s_uuid); } static int probe_ext3(int fd __BLKID_ATTR((unused)), blkid_cache cache __BLKID_ATTR((unused)), blkid_dev dev, struct blkid_magic *id, unsigned char *buf) { struct ext2_super_block *es; es = (struct ext2_super_block *)buf; /* Distinguish between jbd and ext2/3 fs */ if (blkid_le32(es->s_feature_incompat) & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV) return -BLKID_ERR_PARAM; /* Distinguish between ext3 and ext2 */ if (!(blkid_le32(es->s_feature_compat) & EXT3_FEATURE_COMPAT_HAS_JOURNAL)) return -BLKID_ERR_PARAM; get_ext2_info(dev, buf); blkid_set_tag(dev, "SEC_TYPE", "ext2", sizeof("ext2")); return 0; } static int probe_ext2(int fd __BLKID_ATTR((unused)), blkid_cache cache __BLKID_ATTR((unused)), blkid_dev dev, struct blkid_magic *id, unsigned char *buf) { struct ext2_super_block *es; es = (struct ext2_super_block *)buf; /* Distinguish between jbd and ext2/3 fs */ if (blkid_le32(es->s_feature_incompat) & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV) return -BLKID_ERR_PARAM; get_ext2_info(dev, buf); return 0; } static int probe_jbd(int fd __BLKID_ATTR((unused)), blkid_cache cache __BLKID_ATTR((unused)), blkid_dev dev, struct blkid_magic *id __BLKID_ATTR((unused)), unsigned char *buf) { struct ext2_super_block *es = (struct ext2_super_block *) buf; if (!(blkid_le32(es->s_feature_incompat) & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV)) return -BLKID_ERR_PARAM; get_ext2_info(dev, buf); return 0; } static int probe_vfat(int fd __BLKID_ATTR((unused)), blkid_cache cache __BLKID_ATTR((unused)), blkid_dev dev, struct blkid_magic *id __BLKID_ATTR((unused)), unsigned char *buf) { struct vfat_super_block *vs; char serno[10]; const char *label = 0; int label_len = 0; vs = (struct vfat_super_block *)buf; if (strncmp(vs->vs_label, "NO NAME", 7)) { char *end = vs->vs_label + sizeof(vs->vs_label) - 1; while (*end == ' ' && end >= vs->vs_label) --end; if (end >= vs->vs_label) { label = vs->vs_label; label_len = end - vs->vs_label + 1; } } /* We can't just print them as %04X, because they are unaligned */ sprintf(serno, "%02X%02X-%02X%02X", vs->vs_serno[3], vs->vs_serno[2], vs->vs_serno[1], vs->vs_serno[0]); blkid_set_tag(dev, "LABEL", label, label_len); blkid_set_tag(dev, "UUID", serno, sizeof(serno)); return 0; } static int probe_msdos(int fd __BLKID_ATTR((unused)), blkid_cache cache __BLKID_ATTR((unused)), blkid_dev dev, struct blkid_magic *id __BLKID_ATTR((unused)), unsigned char *buf) { struct msdos_super_block *ms = (struct msdos_super_block *) buf; char serno[10]; const char *label = 0; int label_len = 0; if (strncmp(ms->ms_label, "NO NAME", 7)) { char *end = ms->ms_label + sizeof(ms->ms_label) - 1; while (*end == ' ' && end >= ms->ms_label) --end; if (end >= ms->ms_label) { label = ms->ms_label; label_len = end - ms->ms_label + 1; } } /* We can't just print them as %04X, because they are unaligned */ sprintf(serno, "%02X%02X-%02X%02X", ms->ms_serno[3], ms->ms_serno[2], ms->ms_serno[1], ms->ms_serno[0]); blkid_set_tag(dev, "UUID", serno, 0); blkid_set_tag(dev, "LABEL", label, label_len); blkid_set_tag(dev, "SEC_TYPE", "msdos", sizeof("msdos")); return 0; } static int probe_xfs(int fd __BLKID_ATTR((unused)), blkid_cache cache __BLKID_ATTR((unused)), blkid_dev dev, struct blkid_magic *id __BLKID_ATTR((unused)), unsigned char *buf) { struct xfs_super_block *xs; const char *label = 0; xs = (struct xfs_super_block *)buf; if (strlen(xs->xs_fname)) label = xs->xs_fname; blkid_set_tag(dev, "LABEL", label, sizeof(xs->xs_fname)); set_uuid(dev, xs->xs_uuid); return 0; } static int probe_reiserfs(int fd __BLKID_ATTR((unused)), blkid_cache cache __BLKID_ATTR((unused)), blkid_dev dev, struct blkid_magic *id, unsigned char *buf) { struct reiserfs_super_block *rs = (struct reiserfs_super_block *) buf; unsigned int blocksize; const char *label = 0; blocksize = blkid_le16(rs->rs_blocksize); /* If the superblock is inside the journal, we have the wrong one */ if (id->bim_kboff/(blocksize>>10) > blkid_le32(rs->rs_journal_block)) return -BLKID_ERR_BIG; /* LABEL/UUID are only valid for later versions of Reiserfs v3.6. */ if (!strcmp(id->bim_magic, "ReIsEr2Fs") || !strcmp(id->bim_magic, "ReIsEr3Fs")) { if (strlen(rs->rs_label)) label = rs->rs_label; set_uuid(dev, rs->rs_uuid); } blkid_set_tag(dev, "LABEL", label, sizeof(rs->rs_label)); return 0; } static int probe_jfs(int fd __BLKID_ATTR((unused)), blkid_cache cache __BLKID_ATTR((unused)), blkid_dev dev, struct blkid_magic *id __BLKID_ATTR((unused)), unsigned char *buf) { struct jfs_super_block *js; const char *label = 0; js = (struct jfs_super_block *)buf; if (strlen((char *) js->js_label)) label = (char *) js->js_label; blkid_set_tag(dev, "LABEL", label, sizeof(js->js_label)); set_uuid(dev, js->js_uuid); return 0; } static int probe_romfs(int fd __BLKID_ATTR((unused)), blkid_cache cache __BLKID_ATTR((unused)), blkid_dev dev, struct blkid_magic *id __BLKID_ATTR((unused)), unsigned char *buf) { struct romfs_super_block *ros; const char *label = 0; ros = (struct romfs_super_block *)buf; if (strlen((char *) ros->ros_volume)) label = (char *) ros->ros_volume; blkid_set_tag(dev, "LABEL", label, 0); return 0; } static int probe_swap0(int fd __BLKID_ATTR((unused)), blkid_cache cache __BLKID_ATTR((unused)), blkid_dev dev, struct blkid_magic *id __BLKID_ATTR((unused)), unsigned char *buf __BLKID_ATTR((unused))) { blkid_set_tag(dev, "UUID", 0, 0); blkid_set_tag(dev, "LABEL", 0, 0); return 0; } static int probe_swap1(int fd, blkid_cache cache __BLKID_ATTR((unused)), blkid_dev dev, struct blkid_magic *id __BLKID_ATTR((unused)), unsigned char *buf __BLKID_ATTR((unused))) { struct swap_id_block *sws; probe_swap0(fd, cache, dev, id, buf); /* * Version 1 swap headers are always located at offset of 1024 * bytes, although the swap signature itself is located at the * end of the page (which may vary depending on hardware * pagesize). */ if (lseek(fd, 1024, SEEK_SET) < 0) return 1; if (!(sws = (struct swap_id_block *)malloc(1024))) return 1; if (read(fd, sws, 1024) != 1024) { free(sws); return 1; } /* arbitrary sanity check.. is there any garbage down there? */ if (sws->sws_pad[32] == 0 && sws->sws_pad[33] == 0) { if (sws->sws_volume[0]) blkid_set_tag(dev, "LABEL", sws->sws_volume, sizeof(sws->sws_volume)); if (sws->sws_uuid[0]) set_uuid(dev, sws->sws_uuid); } free(sws); return 0; } static const char *udf_magic[] = { "BEA01", "BOOT2", "CD001", "CDW02", "NSR02", "NSR03", "TEA01", 0 }; static int probe_udf(int fd, blkid_cache cache __BLKID_ATTR((unused)), blkid_dev dev __BLKID_ATTR((unused)), struct blkid_magic *id __BLKID_ATTR((unused)), unsigned char *buf __BLKID_ATTR((unused))) { int j, bs; struct iso_volume_descriptor isosb; const char ** m; /* determine the block size by scanning in 2K increments (block sizes larger than 2K will be null padded) */ for (bs = 1; bs < 16; bs++) { lseek(fd, bs*2048+32768, SEEK_SET); if (read(fd, (char *)&isosb, sizeof(isosb)) != sizeof(isosb)) return 1; if (isosb.id[0]) break; } /* Scan up to another 64 blocks looking for additional VSD's */ for (j = 1; j < 64; j++) { if (j > 1) { lseek(fd, j*bs*2048+32768, SEEK_SET); if (read(fd, (char *)&isosb, sizeof(isosb)) != sizeof(isosb)) return 1; } /* If we find NSR0x then call it udf: NSR01 for UDF 1.00 NSR02 for UDF 1.50 NSR03 for UDF 2.00 */ if (!strncmp(isosb.id, "NSR0", 4)) return 0; for (m = udf_magic; *m; m++) if (!strncmp(*m, isosb.id, 5)) break; if (*m == 0) return 1; } return 1; } static int probe_ocfs(int fd __BLKID_ATTR((unused)), blkid_cache cache __BLKID_ATTR((unused)), blkid_dev dev, struct blkid_magic *id __BLKID_ATTR((unused)), unsigned char *buf) { struct ocfs_volume_header ovh; struct ocfs_volume_label ovl; __u32 major; memcpy(&ovh, buf, sizeof(ovh)); memcpy(&ovl, buf+512, sizeof(ovl)); major = ocfsmajor(ovh); if (major == 1) blkid_set_tag(dev,"SEC_TYPE","ocfs1",sizeof("ocfs1")); else if (major >= 9) blkid_set_tag(dev,"SEC_TYPE","ntocfs",sizeof("ntocfs")); blkid_set_tag(dev, "LABEL", ovl.label, ocfslabellen(ovl)); blkid_set_tag(dev, "MOUNT", ovh.mount, ocfsmountlen(ovh)); set_uuid(dev, ovl.vol_id); return 0; } static int probe_ocfs2(int fd __BLKID_ATTR((unused)), blkid_cache cache __BLKID_ATTR((unused)), blkid_dev dev, struct blkid_magic *id __BLKID_ATTR((unused)), unsigned char *buf) { struct ocfs2_super_block *osb; osb = (struct ocfs2_super_block *)buf; blkid_set_tag(dev, "LABEL", osb->s_label, sizeof(osb->s_label)); set_uuid(dev, osb->s_uuid); return 0; } static int probe_oracleasm(int fd __BLKID_ATTR((unused)), blkid_cache cache __BLKID_ATTR((unused)), blkid_dev dev, struct blkid_magic *id __BLKID_ATTR((unused)), unsigned char *buf) { struct oracle_asm_disk_label *dl; dl = (struct oracle_asm_disk_label *)buf; blkid_set_tag(dev, "LABEL", dl->dl_id, sizeof(dl->dl_id)); return 0; } /* * BLKID_BLK_OFFS is at least as large as the highest bim_kboff defined * in the type_array table below + bim_kbalign. * * When probing for a lot of magics, we handle everything in 1kB buffers so * that we don't have to worry about reading each combination of block sizes. */ #define BLKID_BLK_OFFS 64 /* currently reiserfs */ /* * Various filesystem magics that we can check for. Note that kboff and * sboff are in kilobytes and bytes respectively. All magics are in * byte strings so we don't worry about endian issues. */ static struct blkid_magic type_array[] = { /* type kboff sboff len magic probe */ { "oracleasm", 0, 32, 8, "ORCLDISK", probe_oracleasm }, { "ntfs", 0, 3, 8, "NTFS ", 0 }, { "jbd", 1, 0x38, 2, "\123\357", probe_jbd }, { "ext3", 1, 0x38, 2, "\123\357", probe_ext3 }, { "ext2", 1, 0x38, 2, "\123\357", probe_ext2 }, { "reiserfs", 8, 0x34, 8, "ReIsErFs", probe_reiserfs }, { "reiserfs", 64, 0x34, 9, "ReIsEr2Fs", probe_reiserfs }, { "reiserfs", 64, 0x34, 9, "ReIsEr3Fs", probe_reiserfs }, { "reiserfs", 64, 0x34, 8, "ReIsErFs", probe_reiserfs }, { "reiserfs", 8, 20, 8, "ReIsErFs", probe_reiserfs }, { "vfat", 0, 0x52, 5, "MSWIN", probe_vfat }, { "vfat", 0, 0x52, 8, "FAT32 ", probe_vfat }, { "vfat", 0, 0x36, 5, "MSDOS", probe_msdos }, { "vfat", 0, 0x36, 8, "FAT16 ", probe_msdos }, { "vfat", 0, 0x36, 8, "FAT12 ", probe_msdos }, { "minix", 1, 0x10, 2, "\177\023", 0 }, { "minix", 1, 0x10, 2, "\217\023", 0 }, { "minix", 1, 0x10, 2, "\150\044", 0 }, { "minix", 1, 0x10, 2, "\170\044", 0 }, { "vxfs", 1, 0, 4, "\365\374\001\245", 0 }, { "xfs", 0, 0, 4, "XFSB", probe_xfs }, { "romfs", 0, 0, 8, "-rom1fs-", probe_romfs }, { "bfs", 0, 0, 4, "\316\372\173\033", 0 }, { "cramfs", 0, 0, 4, "E=\315\034", 0 }, { "qnx4", 0, 4, 6, "QNX4FS", 0 }, { "udf", 32, 1, 5, "BEA01", probe_udf }, { "udf", 32, 1, 5, "BOOT2", probe_udf }, { "udf", 32, 1, 5, "CD001", probe_udf }, { "udf", 32, 1, 5, "CDW02", probe_udf }, { "udf", 32, 1, 5, "NSR02", probe_udf }, { "udf", 32, 1, 5, "NSR03", probe_udf }, { "udf", 32, 1, 5, "TEA01", probe_udf }, { "iso9660", 32, 1, 5, "CD001", 0 }, { "iso9660", 32, 9, 5, "CDROM", 0 }, { "jfs", 32, 0, 4, "JFS1", probe_jfs }, { "hfs", 1, 0, 2, "BD", 0 }, { "ufs", 8, 0x55c, 4, "T\031\001\000", 0 }, { "hpfs", 8, 0, 4, "I\350\225\371", 0 }, { "sysv", 0, 0x3f8, 4, "\020~\030\375", 0 }, { "swap", 0, 0xff6, 10, "SWAP-SPACE", probe_swap0 }, { "swap", 0, 0xff6, 10, "SWAPSPACE2", probe_swap1 }, { "swap", 0, 0x1ff6, 10, "SWAP-SPACE", probe_swap0 }, { "swap", 0, 0x1ff6, 10, "SWAPSPACE2", probe_swap1 }, { "swap", 0, 0x3ff6, 10, "SWAP-SPACE", probe_swap0 }, { "swap", 0, 0x3ff6, 10, "SWAPSPACE2", probe_swap1 }, { "swap", 0, 0x7ff6, 10, "SWAP-SPACE", probe_swap0 }, { "swap", 0, 0x7ff6, 10, "SWAPSPACE2", probe_swap1 }, { "swap", 0, 0xfff6, 10, "SWAP-SPACE", probe_swap0 }, { "swap", 0, 0xfff6, 10, "SWAPSPACE2", probe_swap1 }, { "ocfs", 0, 8, 9, "OracleCFS", probe_ocfs }, { "ocfs2", 1, 0, 6, "OCFSV2", probe_ocfs2 }, { "ocfs2", 2, 0, 6, "OCFSV2", probe_ocfs2 }, { "ocfs2", 4, 0, 6, "OCFSV2", probe_ocfs2 }, { "ocfs2", 8, 0, 6, "OCFSV2", probe_ocfs2 }, { NULL, 0, 0, 0, NULL, NULL } }; /* * Verify that the data in dev is consistent with what is on the actual * block device (using the devname field only). Normally this will be * called when finding items in the cache, but for long running processes * is also desirable to revalidate an item before use. * * If we are unable to revalidate the data, we return the old data and * do not set the BLKID_BID_FL_VERIFIED flag on it. */ blkid_dev blkid_verify(blkid_cache cache, blkid_dev dev) { struct blkid_magic *id; unsigned char *bufs[BLKID_BLK_OFFS + 1], *buf; const char *type; struct stat st; time_t diff, now; int fd, idx; if (!dev) return NULL; now = time(0); diff = now - dev->bid_time; if ((now < dev->bid_time) || (diff < BLKID_PROBE_MIN) || (dev->bid_flags & BLKID_BID_FL_VERIFIED && diff < BLKID_PROBE_INTERVAL)) return dev; DBG(DEBUG_PROBE, printf("need to revalidate %s (time since last check %lu)\n", dev->bid_name, diff)); if (((fd = open(dev->bid_name, O_RDONLY)) < 0) || (fstat(fd, &st) < 0)) { if (errno == ENXIO || errno == ENODEV || errno == ENOENT) { blkid_free_dev(dev); return NULL; } /* We don't have read permission, just return cache data. */ DBG(DEBUG_PROBE, printf("returning unverified data for %s\n", dev->bid_name)); return dev; } memset(bufs, 0, sizeof(bufs)); /* * Iterate over the type array. If we already know the type, * then try that first. If it doesn't work, then blow away * the type information, and try again. * */ try_again: type = 0; if (!dev->bid_type || !strcmp(dev->bid_type, "mdraid")) { uuid_t uuid; if (check_mdraid(fd, uuid) == 0) { set_uuid(dev, uuid); type = "mdraid"; goto found_type; } } for (id = type_array; id->bim_type; id++) { if (dev->bid_type && strcmp(id->bim_type, dev->bid_type)) continue; idx = id->bim_kboff + (id->bim_sboff >> 10); if (idx > BLKID_BLK_OFFS || idx < 0) continue; buf = bufs[idx]; if (!buf) { if (lseek(fd, idx << 10, SEEK_SET) < 0) continue; if (!(buf = (unsigned char *)malloc(1024))) continue; if (read(fd, buf, 1024) != 1024) { free(buf); continue; } bufs[idx] = buf; } if (memcmp(id->bim_magic, buf + (id->bim_sboff&0x3ff), id->bim_len)) continue; if ((id->bim_probe == NULL) || (id->bim_probe(fd, cache, dev, id, buf) == 0)) { type = id->bim_type; goto found_type; } } if (!id->bim_type && dev->bid_type) { /* * Zap the device filesystem type and try again */ blkid_set_tag(dev, "TYPE", 0, 0); blkid_set_tag(dev, "SEC_TYPE", 0, 0); blkid_set_tag(dev, "LABEL", 0, 0); blkid_set_tag(dev, "UUID", 0, 0); goto try_again; } if (!dev->bid_type) { blkid_free_dev(dev); return NULL; } found_type: if (dev && type) { dev->bid_devno = st.st_rdev; dev->bid_time = time(0); dev->bid_flags |= BLKID_BID_FL_VERIFIED; cache->bic_flags |= BLKID_BIC_FL_CHANGED; blkid_set_tag(dev, "TYPE", type, 0); DBG(DEBUG_PROBE, printf("%s: devno 0x%04Lx, type %s\n", dev->bid_name, st.st_rdev, type)); } close(fd); return dev; } int blkid_known_fstype(const char *fstype) { struct blkid_magic *id; for (id = type_array; id->bim_type; id++) { if (strcmp(fstype, id->bim_type) == 0) return 1; } return 0; } #ifdef TEST_PROGRAM int main(int argc, char **argv) { blkid_dev dev; blkid_cache cache; int ret; blkid_debug_mask = DEBUG_ALL; if (argc != 2) { fprintf(stderr, "Usage: %s device\n" "Probe a single device to determine type\n", argv[0]); exit(1); } if ((ret = blkid_get_cache(&cache, "/dev/null")) != 0) { fprintf(stderr, "%s: error creating cache (%d)\n", argv[0], ret); exit(1); } dev = blkid_get_dev(cache, argv[1], BLKID_DEV_NORMAL); if (!dev) { printf("%s: %s has an unsupported type\n", argv[0], argv[1]); return (1); } printf("%s is type %s\n", argv[1], dev->bid_type ? dev->bid_type : "(null)"); if (dev->bid_label) printf("\tlabel is '%s'\n", dev->bid_label); if (dev->bid_uuid) printf("\tuuid is %s\n", dev->bid_uuid); blkid_free_dev(dev); return (0); } #endif ocfs2-tools-ocfs2-tools-1.8.6/ocfs2console/blkid/probe.h000066400000000000000000000210271347147137200230270ustar00rootroot00000000000000/* * probe.h - constants and on-disk structures for extracting device data * * Copyright (C) 1999 by Andries Brouwer * Copyright (C) 1999, 2000, 2003 by Theodore Ts'o * Copyright (C) 2001 by Andreas Dilger * * %Begin-Header% * This file may be redistributed under the terms of the * GNU Lesser General Public License. * %End-Header% */ #ifndef _BLKID_PROBE_H #define _BLKID_PROBE_H #include struct blkid_magic; typedef int (*blkid_probe_t)(int fd, blkid_cache cache, blkid_dev dev, struct blkid_magic *id, unsigned char *buf); struct blkid_magic { const char *bim_type; /* type name for this magic */ long bim_kboff; /* kilobyte offset of superblock */ unsigned bim_sboff; /* byte offset within superblock */ unsigned bim_len; /* length of magic */ const char *bim_magic; /* magic string */ blkid_probe_t bim_probe; /* probe function */ }; /* * Structures for each of the content types we want to extract information * from. We do not necessarily need the magic field here, because we have * already identified the content type before we get this far. It may still * be useful if there are probe functions which handle multiple content types. */ struct ext2_super_block { __u32 s_inodes_count; __u32 s_blocks_count; __u32 s_r_blocks_count; __u32 s_free_blocks_count; __u32 s_free_inodes_count; __u32 s_first_data_block; __u32 s_log_block_size; __u32 s_dummy3[7]; unsigned char s_magic[2]; __u16 s_state; __u32 s_dummy5[8]; __u32 s_feature_compat; __u32 s_feature_incompat; __u32 s_feature_ro_compat; unsigned char s_uuid[16]; char s_volume_name[16]; }; #define EXT3_FEATURE_COMPAT_HAS_JOURNAL 0x00000004 #define EXT3_FEATURE_INCOMPAT_RECOVER 0x00000004 #define EXT3_FEATURE_INCOMPAT_JOURNAL_DEV 0x00000008 struct xfs_super_block { unsigned char xs_magic[4]; __u32 xs_blocksize; __u64 xs_dblocks; __u64 xs_rblocks; __u32 xs_dummy1[2]; unsigned char xs_uuid[16]; __u32 xs_dummy2[15]; char xs_fname[12]; __u32 xs_dummy3[2]; __u64 xs_icount; __u64 xs_ifree; __u64 xs_fdblocks; }; struct reiserfs_super_block { __u32 rs_blocks_count; __u32 rs_free_blocks; __u32 rs_root_block; __u32 rs_journal_block; __u32 rs_journal_dev; __u32 rs_orig_journal_size; __u32 rs_dummy2[5]; __u16 rs_blocksize; __u16 rs_dummy3[3]; unsigned char rs_magic[12]; __u32 rs_dummy4[5]; unsigned char rs_uuid[16]; char rs_label[16]; }; struct jfs_super_block { unsigned char js_magic[4]; __u32 js_version; __u64 js_size; __u32 js_bsize; __u32 js_dummy1; __u32 js_pbsize; __u32 js_dummy2[27]; unsigned char js_uuid[16]; unsigned char js_label[16]; unsigned char js_loguuid[16]; }; struct romfs_super_block { unsigned char ros_magic[8]; __u32 ros_dummy1[2]; unsigned char ros_volume[16]; }; struct swap_id_block { /* unsigned char sws_boot[1024]; */ __u32 sws_version; __u32 sws_lastpage; __u32 sws_nrbad; unsigned char sws_uuid[16]; unsigned char sws_volume[16]; unsigned char sws_pad[117]; __u32 sws_badpg; }; /* Yucky misaligned values */ struct vfat_super_block { /* 00*/ unsigned char vs_ignored[3]; /* 03*/ unsigned char vs_sysid[8]; /* 0b*/ unsigned char vs_sector_size[2]; /* 0d*/ __u8 vs_cluster_size; /* 0e*/ __u16 vs_reserved; /* 10*/ __u8 vs_fats; /* 11*/ unsigned char vs_dir_entries[2]; /* 13*/ unsigned char vs_sectors[2]; /* 15*/ unsigned char vs_media; /* 16*/ __u16 vs_fat_length; /* 18*/ __u16 vs_secs_track; /* 1a*/ __u16 vs_heads; /* 1c*/ __u32 vs_hidden; /* 20*/ __u32 vs_total_sect; /* 24*/ __u32 vs_fat32_length; /* 28*/ __u16 vs_flags; /* 2a*/ __u8 vs_version[2]; /* 2c*/ __u32 vs_root_cluster; /* 30*/ __u16 vs_insfo_sector; /* 32*/ __u16 vs_backup_boot; /* 34*/ __u16 vs_reserved2[6]; /* 40*/ unsigned char vs_unknown[3]; /* 43*/ unsigned char vs_serno[4]; /* 47*/ char vs_label[11]; /* 52*/ unsigned char vs_magic[8]; /* 5a*/ unsigned char vs_dummy2[164]; /*1fe*/ unsigned char vs_pmagic[2]; }; /* Yucky misaligned values */ struct msdos_super_block { /* 00*/ unsigned char ms_ignored[3]; /* 03*/ unsigned char ms_sysid[8]; /* 0b*/ unsigned char ms_sector_size[2]; /* 0d*/ __u8 ms_cluster_size; /* 0e*/ __u16 ms_reserved; /* 10*/ __u8 ms_fats; /* 11*/ unsigned char ms_dir_entries[2]; /* 13*/ unsigned char ms_sectors[2]; /* 15*/ unsigned char ms_media; /* 16*/ __u16 ms_fat_length; /* 18*/ __u16 ms_secs_track; /* 1a*/ __u16 ms_heads; /* 1c*/ __u32 ms_hidden; /* 20*/ __u32 ms_total_sect; /* 24*/ unsigned char ms_unknown[3]; /* 27*/ unsigned char ms_serno[4]; /* 2b*/ char ms_label[11]; /* 36*/ unsigned char ms_magic[8]; /* 3d*/ unsigned char ms_dummy2[192]; /*1fe*/ unsigned char ms_pmagic[2]; }; struct minix_super_block { __u16 ms_ninodes; __u16 ms_nzones; __u16 ms_imap_blocks; __u16 ms_zmap_blocks; __u16 ms_firstdatazone; __u16 ms_log_zone_size; __u32 ms_max_size; unsigned char ms_magic[2]; __u16 ms_state; __u32 ms_zones; }; struct mdp_superblock_s { __u32 md_magic; __u32 major_version; __u32 minor_version; __u32 patch_version; __u32 gvalid_words; __u32 set_uuid0; __u32 ctime; __u32 level; __u32 size; __u32 nr_disks; __u32 raid_disks; __u32 md_minor; __u32 not_persistent; __u32 set_uuid1; __u32 set_uuid2; __u32 set_uuid3; }; struct hfs_super_block { char h_magic[2]; char h_dummy[18]; __u32 h_blksize; }; struct ocfs_volume_header { unsigned char minor_version[4]; unsigned char major_version[4]; unsigned char signature[128]; unsigned char mount[128]; unsigned char mount_len[2]; }; struct ocfs_volume_label { unsigned char disk_lock[48]; unsigned char label[64]; unsigned char label_len[2]; unsigned char vol_id[16]; unsigned char vol_id_len[2]; }; #define ocfsmajor(o) ((__u32)o.major_version[0] \ + (((__u32) o.major_version[1]) << 8) \ + (((__u32) o.major_version[2]) << 16) \ + (((__u32) o.major_version[3]) << 24)) #define ocfslabellen(o) ((__u32)o.label_len[0] + (((__u32) o.label_len[1]) << 8)) #define ocfsmountlen(o) ((__u32)o.mount_len[0] + (((__u32) o.mount_len[1])<<8)) #define OCFS_MAGIC "OracleCFS" struct ocfs2_super_block { unsigned char signature[8]; unsigned char s_dummy1[184]; unsigned char s_dummy2[80]; unsigned char s_label[64]; unsigned char s_uuid[16]; }; #define OCFS2_MIN_BLOCKSIZE 512 #define OCFS2_MAX_BLOCKSIZE 4096 #define OCFS2_SUPER_BLOCK_BLKNO 2 #define OCFS2_SUPER_BLOCK_SIGNATURE "OCFSV2" struct oracle_asm_disk_label { char dummy[32]; char dl_tag[8]; char dl_id[24]; }; #define ORACLE_ASM_DISK_LABEL_MARKED "ORCLDISK" #define ORACLE_ASM_DISK_LABEL_OFFSET 32 #define ISODCL(from, to) (to - from + 1) struct iso_volume_descriptor { char type[ISODCL(1,1)]; /* 711 */ char id[ISODCL(2,6)]; char version[ISODCL(7,7)]; char data[ISODCL(8,2048)]; }; /* * Byte swap functions */ #ifdef __GNUC__ #define _INLINE_ static __inline__ #else /* For Watcom C */ #define _INLINE_ static inline #endif static __u16 blkid_swab16(__u16 val); static __u32 blkid_swab32(__u32 val); static __u64 blkid_swab64(__u64 val); #if ((defined __GNUC__) && \ (defined(__i386__) || defined(__i486__) || defined(__i586__))) #define _BLKID_HAVE_ASM_BITOPS_ _INLINE_ __u32 blkid_swab32(__u32 val) { #ifdef EXT2FS_REQUIRE_486 __asm__("bswap %0" : "=r" (val) : "0" (val)); #else __asm__("xchgb %b0,%h0\n\t" /* swap lower bytes */ "rorl $16,%0\n\t" /* swap words */ "xchgb %b0,%h0" /* swap higher bytes */ :"=q" (val) : "0" (val)); #endif return val; } _INLINE_ __u16 blkid_swab16(__u16 val) { __asm__("xchgb %b0,%h0" /* swap bytes */ \ : "=q" (val) \ : "0" (val)); \ return val; } _INLINE_ __u64 blkid_swab64(__u64 val) { return (blkid_swab32(val >> 32) | (((__u64) blkid_swab32(val & 0xFFFFFFFFUL)) << 32)); } #endif #if !defined(_BLKID_HAVE_ASM_BITOPS_) _INLINE_ __u16 blkid_swab16(__u16 val) { return (val >> 8) | (val << 8); } _INLINE_ __u32 blkid_swab32(__u32 val) { return ((val>>24) | ((val>>8)&0xFF00) | ((val<<8)&0xFF0000) | (val<<24)); } _INLINE_ __u64 blkid_swab64(__u64 val) { return (blkid_swab32(val >> 32) | (((__u64) blkid_swab32(val & 0xFFFFFFFFUL)) << 32)); } #endif #if __BYTE_ORDER == __BIG_ENDIAN #define blkid_le16(x) blkid_swab16(x) #define blkid_le32(x) blkid_swab32(x) #define blkid_le64(x) blkid_swab64(x) #define blkid_be16(x) (x) #define blkid_be32(x) (x) #define blkid_be64(x) (x) #else #define blkid_le16(x) (x) #define blkid_le32(x) (x) #define blkid_le64(x) (x) #define blkid_be16(x) blkid_swab16(x) #define blkid_be32(x) blkid_swab32(x) #define blkid_be64(x) blkid_swab64(x) #endif #undef _INLINE_ #endif /* _BLKID_PROBE_H */ ocfs2-tools-ocfs2-tools-1.8.6/ocfs2console/blkid/read.c000066400000000000000000000222141347147137200226250ustar00rootroot00000000000000/* * read.c - read the blkid cache from disk, to avoid scanning all devices * * Copyright (C) 2001, 2003 Theodore Y. Ts'o * Copyright (C) 2001 Andreas Dilger * * %Begin-Header% * This file may be redistributed under the terms of the * GNU Lesser General Public License. * %End-Header% */ #include #include #include #include #include #include #include #include #if HAVE_ERRNO_H #include #endif #include "blkidP.h" #include "uuid/uuid.h" #ifdef HAVE_STRTOULL #define __USE_ISOC9X #define STRTOULL strtoull /* defined in stdlib.h if you try hard enough */ #else /* FIXME: need to support real strtoull here */ #define STRTOULL strtoul #endif #if HAVE_STDLIB_H #include #endif /* * File format: * * ...]>device_name * * The following tags are required for each entry: * unique (within this file) ID number of this device * (ascii time_t) time this entry was last read from disk * (detected) type of filesystem/data for this partition * * The following tags may be present, depending on the device contents * (user supplied) label (volume name, etc) * (generated) universally unique identifier (serial no) */ static char *skip_over_blank(char *cp) { while (*cp && isspace(*cp)) cp++; return cp; } static char *skip_over_word(char *cp) { char ch; while ((ch = *cp)) { /* If we see a backslash, skip the next character */ if (ch == '\\') { cp++; if (*cp == '\0') break; cp++; continue; } if (isspace(ch) || ch == '<' || ch == '>') break; cp++; } return cp; } static char *strip_line(char *line) { char *p; line = skip_over_blank(line); p = line + strlen(line) - 1; while (*line) { if (isspace(*p)) *p-- = '\0'; else break; } return line; } #if 0 static char *parse_word(char **buf) { char *word, *next; word = *buf; if (*word == '\0') return NULL; word = skip_over_blank(word); next = skip_over_word(word); if (*next) { char *end = next - 1; if (*end == '"' || *end == '\'') *end = '\0'; *next++ = '\0'; } *buf = next; if (*word == '"' || *word == '\'') word++; return word; } #endif /* * Start parsing a new line from the cache. * * line starts with " continue parsing line * line starts with " skip line * line starts with other, return -BLKID_ERR_CACHE -> error */ static int parse_start(char **cp) { char *p; p = strip_line(*cp); /* Skip comment or blank lines. We can't just NUL the first '#' char, * in case it is inside quotes, or escaped. */ if (*p == '\0' || *p == '#') return 0; if (!strncmp(p, "", 9)) { DBG(DEBUG_READ, printf("found device trailer %9s\n", *cp)); *cp += 9; return 0; } return -BLKID_ERR_CACHE; } /* * Allocate a new device struct with device name filled in. Will handle * finding the device on lines of the form: * devname * devnamebar */ static int parse_dev(blkid_cache cache, blkid_dev *dev, char **cp) { char *start, *tmp, *end, *name; int ret; if ((ret = parse_start(cp)) <= 0) return ret; start = tmp = strchr(*cp, '>'); if (!start) { DBG(DEBUG_READ, printf("blkid: short line parsing dev: %s\n", *cp)); return -BLKID_ERR_CACHE; } start = skip_over_blank(start + 1); end = skip_over_word(start); DBG(DEBUG_READ, printf("device should be %*s\n", end - start, start)); if (**cp == '>') *cp = end; else (*cp)++; *tmp = '\0'; if (!(tmp = strrchr(end, '<')) || parse_end(&tmp) < 0) { DBG(DEBUG_READ, printf("blkid: missing ending: %s\n", end)); } else if (tmp) *tmp = '\0'; if (end - start <= 1) { DBG(DEBUG_READ, printf("blkid: empty device name: %s\n", *cp)); return -BLKID_ERR_CACHE; } name = blkid_strndup(start, end-start); if (name == NULL) return -BLKID_ERR_MEM; DBG(DEBUG_READ, printf("found dev %s\n", name)); if (!(*dev = blkid_get_dev(cache, name, BLKID_DEV_CREATE))) return -BLKID_ERR_MEM; free(name); return 1; } /* * Extract a tag of the form NAME="value" from the line. */ static int parse_token(char **name, char **value, char **cp) { char *end; if (!name || !value || !cp) return -BLKID_ERR_PARAM; if (!(*value = strchr(*cp, '='))) return 0; **value = '\0'; *name = strip_line(*cp); *value = skip_over_blank(*value + 1); if (**value == '"') { end = strchr(*value + 1, '"'); if (!end) { DBG(DEBUG_READ, printf("unbalanced quotes at: %s\n", *value)); *cp = *value; return -BLKID_ERR_CACHE; } (*value)++; *end = '\0'; end++; } else { end = skip_over_word(*value); if (*end) { *end = '\0'; end++; } } *cp = end; return 1; } /* * Extract a tag of the form value from the line. */ /* static int parse_xml(char **name, char **value, char **cp) { char *end; if (!name || !value || !cp) return -BLKID_ERR_PARAM; *name = strip_line(*cp); if ((*name)[0] != '<' || (*name)[1] == '/') return 0; FIXME: finish this. } */ /* * Extract a tag from the line. * * Return 1 if a valid tag was found. * Return 0 if no tag found. * Return -ve error code. */ static int parse_tag(blkid_cache cache, blkid_dev dev, char **cp) { char *name; char *value; int ret; if (!cache || !dev) return -BLKID_ERR_PARAM; if ((ret = parse_token(&name, &value, cp)) <= 0 /* && (ret = parse_xml(&name, &value, cp)) <= 0 */) return ret; /* Some tags are stored directly in the device struct */ if (!strcmp(name, "DEVNO")) dev->bid_devno = STRTOULL(value, 0, 0); else if (!strcmp(name, "PRI")) dev->bid_pri = strtol(value, 0, 0); else if (!strcmp(name, "TIME")) /* FIXME: need to parse a long long eventually */ dev->bid_time = strtol(value, 0, 0); else ret = blkid_set_tag(dev, name, value, strlen(value)); DBG(DEBUG_READ, printf(" tag: %s=\"%s\"\n", name, value)); return ret < 0 ? ret : 1; } /* * Parse a single line of data, and return a newly allocated dev struct. * Add the new device to the cache struct, if one was read. * * Lines are of the form /dev/foo * * Returns -ve value on error. * Returns 0 otherwise. * If a valid device was read, *dev_p is non-NULL, otherwise it is NULL * (e.g. comment lines, unknown XML content, etc). */ static int blkid_parse_line(blkid_cache cache, blkid_dev *dev_p, char *cp) { blkid_dev dev; int ret; if (!cache || !dev_p) return -BLKID_ERR_PARAM; *dev_p = NULL; DBG(DEBUG_READ, printf("line: %s\n", cp)); if ((ret = parse_dev(cache, dev_p, &cp)) <= 0) return ret; dev = *dev_p; while ((ret = parse_tag(cache, dev, &cp)) > 0) { ; } if (dev->bid_type == NULL) { DBG(DEBUG_READ, printf("blkid: device %s has no TYPE\n",dev->bid_name)); blkid_free_dev(dev); } DEB_DUMP_DEV(DEBUG_READ, dev); return ret; } /* * Parse the specified filename, and return the data in the supplied or * a newly allocated cache struct. If the file doesn't exist, return a * new empty cache struct. */ void blkid_read_cache(blkid_cache cache) { FILE *file; char buf[4096]; int fd, lineno = 0; struct stat st; if (!cache) return; /* * If the file doesn't exist, then we just return an empty * struct so that the cache can be populated. */ if ((fd = open(cache->bic_filename, O_RDONLY)) < 0) return; if (fstat(fd, &st) < 0) goto errout; if ((st.st_mtime == cache->bic_ftime) || (cache->bic_flags & BLKID_BIC_FL_CHANGED)) { DBG(DEBUG_CACHE, printf("skipping re-read of %s\n", cache->bic_filename)); goto errout; } DBG(DEBUG_CACHE, printf("reading cache file %s\n", cache->bic_filename)); file = fdopen(fd, "r"); if (!file) goto errout; while (fgets(buf, sizeof(buf), file)) { blkid_dev dev; unsigned int end; lineno++; if (buf[0] == 0) continue; end = strlen(buf) - 1; /* Continue reading next line if it ends with a backslash */ while (buf[end] == '\\' && end < sizeof(buf) - 2 && fgets(buf + end, sizeof(buf) - end, file)) { end = strlen(buf) - 1; lineno++; } if (blkid_parse_line(cache, &dev, buf) < 0) { DBG(DEBUG_READ, printf("blkid: bad format on line %d\n", lineno)); continue; } } fclose(file); /* * Initially we do not need to write out the cache file. */ cache->bic_flags &= ~BLKID_BIC_FL_CHANGED; cache->bic_ftime = st.st_mtime; return; errout: close(fd); return; } #ifdef TEST_PROGRAM int main(int argc, char**argv) { blkid_cache cache = NULL; int ret; blkid_debug_mask = DEBUG_ALL; if (argc > 2) { fprintf(stderr, "Usage: %s [filename]\n" "Test parsing of the cache (filename)\n", argv[0]); exit(1); } if ((ret = blkid_get_cache(&cache, argv[1])) < 0) fprintf(stderr, "error %d reading cache file %s\n", ret, argv[1] ? argv[1] : BLKID_CACHE_FILE); blkid_put_cache(cache); return ret; } #endif ocfs2-tools-ocfs2-tools-1.8.6/ocfs2console/blkid/resolve.c000066400000000000000000000057301347147137200233750ustar00rootroot00000000000000/* * resolve.c - resolve names and tags into specific devices * * Copyright (C) 2001, 2003 Theodore Ts'o. * Copyright (C) 2001 Andreas Dilger * * %Begin-Header% * This file may be redistributed under the terms of the * GNU Lesser General Public License. * %End-Header% */ #include #if HAVE_UNISTD_H #include #endif #include #include #include #include #include #include "blkidP.h" #include "probe.h" /* * Find a tagname (e.g. LABEL or UUID) on a specific device. */ char *blkid_get_tag_value(blkid_cache cache, const char *tagname, const char *devname) { blkid_tag found; blkid_dev dev; blkid_cache c = cache; char *ret = NULL; DBG(DEBUG_RESOLVE, printf("looking for %s on %s\n", tagname, devname)); if (!devname) return NULL; if (!cache) { if (blkid_get_cache(&c, NULL) < 0) return NULL; } if ((dev = blkid_get_dev(c, devname, BLKID_DEV_NORMAL)) && (found = blkid_find_tag_dev(dev, tagname))) ret = blkid_strdup(found->bit_val); if (!cache) blkid_put_cache(c); return ret; } /* * Locate a device name from a token (NAME=value string), or (name, value) * pair. In the case of a token, value is ignored. If the "token" is not * of the form "NAME=value" and there is no value given, then it is assumed * to be the actual devname and a copy is returned. */ char *blkid_get_devname(blkid_cache cache, const char *token, const char *value) { blkid_dev dev; blkid_cache c = cache; char *t = 0, *v = 0; char *ret = NULL; if (!token) return NULL; if (!cache) { if (blkid_get_cache(&c, NULL) < 0) return NULL; } DBG(DEBUG_RESOLVE, printf("looking for %s%s%s %s\n", token, value ? "=" : "", value ? value : "", cache ? "in cache" : "from disk")); if (!value) { if (!strchr(token, '=')) return blkid_strdup(token); blkid_parse_tag_string(token, &t, &v); if (!t || !v) goto errout; token = t; value = v; } dev = blkid_find_dev_with_tag(c, token, value); if (!dev) goto errout; ret = blkid_strdup(blkid_dev_devname(dev)); errout: if (t) free(t); if (v) free(v); if (!cache) { blkid_put_cache(c); } return (ret); } #ifdef TEST_PROGRAM int main(int argc, char **argv) { char *value; blkid_cache cache; blkid_debug_mask = DEBUG_ALL; if (argc != 2 && argc != 3) { fprintf(stderr, "Usage:\t%s tagname=value\n" "\t%s tagname devname\n" "Find which device holds a given token or\n" "Find what the value of a tag is in a device\n", argv[0], argv[0]); exit(1); } if (blkid_get_cache(&cache, "/dev/null") < 0) { fprintf(stderr, "Couldn't get blkid cache\n"); exit(1); } if (argv[2]) { value = blkid_get_tag_value(cache, argv[1], argv[2]); printf("%s has tag %s=%s\n", argv[2], argv[1], value ? value : ""); } else { value = blkid_get_devname(cache, argv[1], NULL); printf("%s has tag %s\n", value ? value : "", argv[1]); } blkid_put_cache(cache); return value ? 0 : 1; } #endif ocfs2-tools-ocfs2-tools-1.8.6/ocfs2console/blkid/save.c000066400000000000000000000101531347147137200226470ustar00rootroot00000000000000/* * save.c - write the cache struct to disk * * Copyright (C) 2001 by Andreas Dilger * Copyright (C) 2003 Theodore Ts'o * * %Begin-Header% * This file may be redistributed under the terms of the * GNU Lesser General Public License. * %End-Header% */ #include #include #include #include #include #ifdef HAVE_SYS_STAT_H #include #endif #ifdef HAVE_SYS_MKDEV_H #include #endif #ifdef HAVE_ERRNO_H #include #endif #include "blkidP.h" static int save_dev(blkid_dev dev, FILE *file) { struct list_head *p; if (!dev || dev->bid_name[0] != '/') return 0; DBG(DEBUG_SAVE, printf("device %s, type %s\n", dev->bid_name, dev->bid_type)); fprintf(file, "bid_devno, dev->bid_time); if (dev->bid_pri) fprintf(file, " PRI=\"%d\"", dev->bid_pri); list_for_each(p, &dev->bid_tags) { blkid_tag tag = list_entry(p, struct blkid_struct_tag, bit_tags); fprintf(file, " %s=\"%s\"", tag->bit_name,tag->bit_val); } fprintf(file, ">%s\n", dev->bid_name); return 0; } /* * Write out the cache struct to the cache file on disk. */ int blkid_flush_cache(blkid_cache cache) { struct list_head *p; char *tmp = NULL; const char *opened = NULL; const char *filename; FILE *file = NULL; int fd, ret = 0; struct stat st; if (!cache) return -BLKID_ERR_PARAM; if (list_empty(&cache->bic_devs) || !(cache->bic_flags & BLKID_BIC_FL_CHANGED)) { DBG(DEBUG_SAVE, printf("skipping cache file write\n")); return 0; } filename = cache->bic_filename ? cache->bic_filename: BLKID_CACHE_FILE; /* If we can't write to the cache file, then don't even try */ if (((ret = stat(filename, &st)) < 0 && errno != ENOENT) || (ret == 0 && access(filename, W_OK) < 0)) { DBG(DEBUG_SAVE, printf("can't write to cache file %s\n", filename)); return 0; } /* * Try and create a temporary file in the same directory so * that in case of error we don't overwrite the cache file. * If the cache file doesn't yet exist, it isn't a regular * file (e.g. /dev/null or a socket), or we couldn't create * a temporary file then we open it directly. */ if (ret == 0 && S_ISREG(st.st_mode)) { tmp = malloc(strlen(filename) + 8); if (tmp) { sprintf(tmp, "%s-XXXXXX", filename); fd = mkstemp(tmp); if (fd >= 0) { file = fdopen(fd, "w"); opened = tmp; } fchmod(fd, 0644); } } if (!file) { file = fopen(filename, "w"); opened = filename; } DBG(DEBUG_SAVE, printf("writing cache file %s (really %s)\n", filename, opened)); if (!file) { ret = errno; goto errout; } list_for_each(p, &cache->bic_devs) { blkid_dev dev = list_entry(p, struct blkid_struct_dev, bid_devs); if (!dev->bid_type) continue; if ((ret = save_dev(dev, file)) < 0) break; } if (ret >= 0) { cache->bic_flags &= ~BLKID_BIC_FL_CHANGED; ret = 1; } fclose(file); if (opened != filename) { if (ret < 0) { unlink(opened); DBG(DEBUG_SAVE, printf("unlinked temp cache %s\n", opened)); } else { char *backup; backup = malloc(strlen(filename) + 5); if (backup) { sprintf(backup, "%s.old", filename); unlink(backup); link(filename, backup); free(backup); } rename(opened, filename); DBG(DEBUG_SAVE, printf("moved temp cache %s\n", opened)); } } errout: if (tmp) free(tmp); return ret; } #ifdef TEST_PROGRAM int main(int argc, char **argv) { blkid_cache cache = NULL; int ret; blkid_debug_mask = DEBUG_ALL; if (argc > 2) { fprintf(stderr, "Usage: %s [filename]\n" "Test loading/saving a cache (filename)\n", argv[0]); exit(1); } if ((ret = blkid_get_cache(&cache, "/dev/null")) != 0) { fprintf(stderr, "%s: error creating cache (%d)\n", argv[0], ret); exit(1); } if ((ret = blkid_probe_all(cache)) < 0) { fprintf(stderr, "error (%d) probing devices\n", ret); exit(1); } cache->bic_filename = blkid_strdup(argv[1]); if ((ret = blkid_flush_cache(cache)) < 0) { fprintf(stderr, "error (%d) saving cache\n", ret); exit(1); } blkid_put_cache(cache); return ret; } #endif ocfs2-tools-ocfs2-tools-1.8.6/ocfs2console/blkid/tag.c000066400000000000000000000171761347147137200225000ustar00rootroot00000000000000/* * tag.c - allocation/initialization/free routines for tag structs * * Copyright (C) 2001 Andreas Dilger * Copyright (C) 2003 Theodore Ts'o * * %Begin-Header% * This file may be redistributed under the terms of the * GNU Lesser General Public License. * %End-Header% */ #include #include #include #include "blkidP.h" static blkid_tag blkid_new_tag(void) { blkid_tag tag; if (!(tag = (blkid_tag) calloc(1, sizeof(struct blkid_struct_tag)))) return NULL; INIT_LIST_HEAD(&tag->bit_tags); INIT_LIST_HEAD(&tag->bit_names); return tag; } void blkid_free_tag(blkid_tag tag) { if (!tag) return; DBG(DEBUG_TAG, printf(" freeing tag %s=%s\n", tag->bit_name, tag->bit_val ? tag->bit_val : "(NULL)")); DEB_DUMP_TAG(DEBUG_TAG, tag); list_del(&tag->bit_tags); /* list of tags for this device */ list_del(&tag->bit_names); /* list of tags with this type */ if (tag->bit_name) free(tag->bit_name); if (tag->bit_val) free(tag->bit_val); free(tag); } /* * Find the desired tag on a device. If value is NULL, then the * first such tag is returned, otherwise return only exact tag if found. */ blkid_tag blkid_find_tag_dev(blkid_dev dev, const char *type) { struct list_head *p; if (!dev || !type) return NULL; list_for_each(p, &dev->bid_tags) { blkid_tag tmp = list_entry(p, struct blkid_struct_tag, bit_tags); if (!strcmp(tmp->bit_name, type)) return tmp; } return NULL; } /* * Find the desired tag type in the cache. * We return the head tag for this tag type. */ static blkid_tag blkid_find_head_cache(blkid_cache cache, const char *type) { blkid_tag head = NULL, tmp; struct list_head *p; if (!cache || !type) return NULL; list_for_each(p, &cache->bic_tags) { tmp = list_entry(p, struct blkid_struct_tag, bit_tags); if (!strcmp(tmp->bit_name, type)) { DBG(DEBUG_TAG, printf(" found cache tag head %s\n", type)); head = tmp; break; } } return head; } /* * Set a tag on an existing device. * * If value is NULL, then delete the tagsfrom the device. */ int blkid_set_tag(blkid_dev dev, const char *name, const char *value, const int vlength) { blkid_tag t = 0, head = 0; char *val = 0; if (!dev || !name) return -BLKID_ERR_PARAM; if (!(val = blkid_strndup(value, vlength)) && value) return -BLKID_ERR_MEM; t = blkid_find_tag_dev(dev, name); if (!value) { if (t) blkid_free_tag(t); } else if (t) { if (!strcmp(t->bit_val, val)) { /* Same thing, exit */ free(val); return 0; } free(t->bit_val); t->bit_val = val; } else { /* Existing tag not present, add to device */ if (!(t = blkid_new_tag())) goto errout; t->bit_name = blkid_strdup(name); t->bit_val = val; t->bit_dev = dev; list_add_tail(&t->bit_tags, &dev->bid_tags); if (dev->bid_cache) { head = blkid_find_head_cache(dev->bid_cache, t->bit_name); if (!head) { head = blkid_new_tag(); if (!head) goto errout; DBG(DEBUG_TAG, printf(" creating new cache tag head %s\n", name)); head->bit_name = blkid_strdup(name); if (!head->bit_name) goto errout; list_add_tail(&head->bit_tags, &dev->bid_cache->bic_tags); } list_add_tail(&t->bit_names, &head->bit_names); } } /* Link common tags directly to the device struct */ if (!strcmp(name, "TYPE")) dev->bid_type = val; else if (!strcmp(name, "LABEL")) dev->bid_label = val; else if (!strcmp(name, "UUID")) dev->bid_uuid = val; if (dev->bid_cache) dev->bid_cache->bic_flags |= BLKID_BIC_FL_CHANGED; return 0; errout: if (t) blkid_free_tag(t); else if (val) free(val); if (head) blkid_free_tag(head); return -BLKID_ERR_MEM; } /* * Parse a "NAME=value" string. This is slightly different than * parse_token, because that will end an unquoted value at a space, while * this will assume that an unquoted value is the rest of the token (e.g. * if we are passed an already quoted string from the command-line we don't * have to both quote and escape quote so that the quotes make it to * us). * * Returns 0 on success, and -1 on failure. */ int blkid_parse_tag_string(const char *token, char **ret_type, char **ret_val) { char *name, *value, *cp; DBG(DEBUG_TAG, printf("trying to parse '%s' as a tag\n", token)); if (!token || !(cp = strchr(token, '='))) return -1; name = blkid_strdup(token); if (!name) return -1; value = name + (cp - token); *value++ = '\0'; if (*value == '"' || *value == '\'') { char c = *value++; if (!(cp = strrchr(value, c))) goto errout; /* missing closing quote */ *cp = '\0'; } value = blkid_strdup(value); if (!value) goto errout; *ret_type = name; *ret_val = value; return 0; errout: free(name); return -1; } /* * Tag iteration routines for the public libblkid interface. * * These routines do not expose the list.h implementation, which are a * contamination of the namespace, and which force us to reveal far, far * too much of our internal implemenation. I'm not convinced I want * to keep list.h in the long term, anyway. It's fine for kernel * programming, but performance is not the #1 priority for this * library, and I really don't like the tradeoff of type-safety for * performance for this application. [tytso:20030125.2007EST] */ /* * This series of functions iterate over all tags in a device */ #define TAG_ITERATE_MAGIC 0x01a5284c struct blkid_struct_tag_iterate { int magic; blkid_dev dev; struct list_head *p; }; extern blkid_tag_iterate blkid_tag_iterate_begin(blkid_dev dev) { blkid_tag_iterate iter; iter = malloc(sizeof(struct blkid_struct_tag_iterate)); if (iter) { iter->magic = TAG_ITERATE_MAGIC; iter->dev = dev; iter->p = dev->bid_tags.next; } return (iter); } /* * Return 0 on success, -1 on error */ extern int blkid_tag_next(blkid_tag_iterate iter, const char **type, const char **value) { blkid_tag tag; *type = 0; *value = 0; if (!iter || iter->magic != TAG_ITERATE_MAGIC || iter->p == &iter->dev->bid_tags) return -1; tag = list_entry(iter->p, struct blkid_struct_tag, bit_tags); *type = tag->bit_name; *value = tag->bit_val; iter->p = iter->p->next; return 0; } extern void blkid_tag_iterate_end(blkid_tag_iterate iter) { if (!iter || iter->magic != TAG_ITERATE_MAGIC) return; iter->magic = 0; free(iter); } /* * This function returns a device which matches a particular * type/value pair. If there is more than one device that matches the * search specification, it returns the one with the highest priority * value. This allows us to give preference to EVMS or LVM devices. * * XXX there should also be an interface which uses an iterator so we * can get all of the devices which match a type/value search parameter. */ extern blkid_dev blkid_find_dev_with_tag(blkid_cache cache, const char *type, const char *value) { blkid_tag head; blkid_dev dev; int pri; struct list_head *p; if (!cache || !type || !value) return NULL; blkid_read_cache(cache); DBG(DEBUG_TAG, printf("looking for %s=%s in cache\n", type, value)); try_again: pri = -1; dev = 0; head = blkid_find_head_cache(cache, type); if (head) { list_for_each(p, &head->bit_names) { blkid_tag tmp = list_entry(p, struct blkid_struct_tag, bit_names); if (!strcmp(tmp->bit_val, value) && tmp->bit_dev->bid_pri > pri) { dev = tmp->bit_dev; pri = dev->bid_pri; } } } if (dev && !(dev->bid_flags & BLKID_BID_FL_VERIFIED)) { dev = blkid_verify(cache, dev); if (dev && (dev->bid_flags & BLKID_BID_FL_VERIFIED)) goto try_again; } if (!dev && !(cache->bic_flags & BLKID_BIC_FL_PROBED)) { if (blkid_probe_all(cache) < 0) return NULL; goto try_again; } return dev; } ocfs2-tools-ocfs2-tools-1.8.6/ocfs2console/blkid/version.c000066400000000000000000000017601347147137200234020ustar00rootroot00000000000000/* * version.c --- Return the version of the blkid library * * Copyright (C) 2004 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Public * License. * %End-Header% */ #if HAVE_UNISTD_H #include #endif #include #include #include #include "blkid.h" #define E2FSPROGS_VERSION "1.37" #define E2FSPROGS_DATE "21-Mar-2005" static const char *lib_version = E2FSPROGS_VERSION; static const char *lib_date = E2FSPROGS_DATE; int blkid_parse_version_string(const char *ver_string) { const char *cp; int version = 0; for (cp = ver_string; *cp; cp++) { if (*cp == '.') continue; if (!isdigit(*cp)) break; version = (version * 10) + (*cp - '0'); } return version; } int blkid_get_library_version(const char **ver_string, const char **date_string) { if (ver_string) *ver_string = lib_version; if (date_string) *date_string = lib_date; return blkid_parse_version_string(lib_version); } ocfs2-tools-ocfs2-tools-1.8.6/ocfs2console/ocfs2console000077500000000000000000000017731347147137200230150ustar00rootroot00000000000000#!/usr/bin/python -W ignore::DeprecationWarning from ocfs2interface.about import process_args nodeconf = process_args() # # Current pygtk treats no DISPLAY as a WARNING. This means that # pygtk initialization tries to continue even after gtk_init() has failed. # All sorts of fun ensues. To prevent this, we turn the warning into an # error for the duration of gtk initialization. # # Originally reported as Novell bugzilla #448523. # import warnings warnings.filterwarnings("error") try: import gtk except Exception, e: import sys if str(e).lower().find('display') == -1: print >>sys.stderr, '''ERROR: Unable to initialize the windowing system: %s\n''' % e else: print >>sys.stderr, '''ERROR: ocfs2console needs an X11 display. Make sure a proper setup for your display environment exists.\n''' sys.exit(1) warnings.filters.pop(0) if nodeconf: from ocfs2interface.nodeconfig import node_config node_config() else: from ocfs2interface.console import main main() ocfs2-tools-ocfs2-tools-1.8.6/ocfs2console/ocfs2console.8.in000066400000000000000000000012021347147137200235500ustar00rootroot00000000000000.TH "ocfs2console" "8" "September 2010" "Version @VERSION@" "OCFS2 Manual Pages" .SH "NAME" ocfs2console - GUI console for \fIOCFS2\fR. .SH "SYNOPSIS" .B ocfs2console .SH DESCRIPTION .PP \fBocfs2console\fP is a GUI front\-end for managing \fIOCFS2\fR volumes. This tool is especially recommended to configure the \fIO2CB\fR cluster. One can also use this tool to format, tune, mount and umount \fIOCFS2\fR volumes. .SH SEE ALSO .BR mkfs.ocfs2(8) .BR fsck.ocfs2(8) .BR tunefs.ocfs2(8) .BR mounted.ocfs2(8) .BR debugfs.ocfs2(8) .BR o2cb(7) .SH AUTHORS Oracle Corporation .SH COPYRIGHT Copyright \(co 2004, 2010 Oracle. All rights reserved. ocfs2-tools-ocfs2-tools-1.8.6/ocfs2console/ocfs2interface/000077500000000000000000000000001347147137200233555ustar00rootroot00000000000000ocfs2-tools-ocfs2-tools-1.8.6/ocfs2console/ocfs2interface/.gitignore000066400000000000000000000001271347147137200253450ustar00rootroot00000000000000*.d *.pyc *.pyo plistmodule.so gidlemodule.so ocfs2module.so o2cbmodule.so confdefs.py ocfs2-tools-ocfs2-tools-1.8.6/ocfs2console/ocfs2interface/Makefile000066400000000000000000000062631347147137200250240ustar00rootroot00000000000000TOPDIR = ../.. include $(TOPDIR)/Preamble.make INCLUDES = -I$(TOPDIR)/include CFLAGS += -fPIC PYMOD_CFLAGS = -fno-strict-aliasing $(PYTHON_INCLUDES) LIBOCFS2_LIBS = -L$(TOPDIR)/libocfs2 -locfs2 -laio LIBOCFS2_DEPS = $(TOPDIR)/libocfs2/libocfs2.a LIBO2DLM_LIBS = -L$(TOPDIR)/libo2dlm -lo2dlm $(DL_LIBS) LIBO2DLM_DEPS = $(TOPDIR)/libo2dlm/libo2dlm.a ifneq ($(BUILD_FSDLM_SUPPORT),) LIBO2CB_LIBS = -L$(TOPDIR)/libo2cb -lo2cb -ldlm_lt else LIBO2CB_LIBS = -L$(TOPDIR)/libo2cb -lo2cb endif ifneq ($(BUILD_CMAP_SUPPORT),) LIBO2CB_LIBS += -lcmap endif LIBO2CB_DEPS = $(TOPDIR)/libo2cb/libo2cb.a ifdef HAVE_BLKID BLKID_DEPS = else BLKID_DEPS = $(TOPDIR)/ocfs2console/blkid/libblkid-internal.a BLKID_CFLAGS = -I$(TOPDIR)/ocfs2console BLKID_LIBS = -L$(TOPDIR)/ocfs2console/blkid -lblkid-internal $(UUID_LIBS) endif GLIB_CPPFLAGS = $(GLIB_CFLAGS) -DG_DISABLE_DEPRECATED PLIST_CBITS = ocfsplist.c PLIST_CFILES = $(PLIST_CBITS) plistmodule.c PLIST_HFILES = $(subst .c,.h,$(PLIST_CBITS)) ocfsplist_CPPFLAGS = $(GLIB_CPPFLAGS) $(BLKID_CFLAGS) plistmodule_CPPFLAGS = $(GLIB_CPPFLAGS) plistmodule_CFLAGS = $(PYMOD_CFLAGS) GIDLE_CFILES = gidlemodule.c gidlemodule_CPPFLAGS = $(GLIB_CPPFLAGS) gidlemodule_CFLAGS = $(PYMOD_CFLAGS) O2CB_CFILES = o2cbmodule.c o2cbmodule_CPPFLAGS = $(O2CB_CPPFLAGS) o2cbmodule_CFLAGS = $(PYMOD_CFLAGS) OCFS2_CFILES = ocfs2module.c ocfs2module_CPPFLAGS = $(OCFS2_CPPFLAGS) ocfs2module_CFLAGS = $(PYMOD_CFLAGS) PLIST_OBJS = $(subst .c,.o,$(PLIST_CFILES)) GIDLE_OBJS = $(subst .c,.o,$(GIDLE_CFILES)) OCFS2_OBJS = $(subst .c,.o,$(OCFS2_CFILES)) O2CB_OBJS = $(subst .c,.o,$(O2CB_CFILES)) # These are UNINST_ because the python install rule will handle them. UNINST_LIBRARIES = plistmodule.so gidlemodule.so ocfs2module.so o2cbmodule.so PYSRC = \ __init__.py \ about.py \ bosa.py \ classlabel.py \ console.py \ format.py \ fsck.py \ fstab.py \ fswidgets.py \ general.py \ guiutil.py \ ipwidget.py \ ls.py \ mount.py \ menu.py \ nodeconfig.py \ o2cb_ctl.py \ partitionview.py \ process.py \ pushconfig.py \ terminal.py \ toolbar.py \ tune.py BUILT_PYSRC = confdefs.py PYLIB = $(UNINST_LIBRARIES) $(PYSRC) $(BUILT_PYSRC) INSTALL_RULES = install-pylib DIST_FILES = $(PLIST_CFILES) $(PLIST_HFILES) $(GIDLE_CFILES) $(OCFS2_CFILES) $(O2CB_CFILES) $(PYSRC) $(addsuffix .in,$(BUILT_PYSRC)) plistmodule.so: $(PLIST_OBJS) $(LIBOCFS2_DEPS) $(LIBO2DLM_DEPS) $(LIBO2CB_DEPS) $(BLKID_DEPS) $(LINK) -shared $(LIBOCFS2_LIBS) $(LIBO2DLM_LIBS) $(LIBO2CB_LIBS) $(BLKID_LIBS) $(COM_ERR_LIBS) $(GLIB_LIBS) $(shell python-config --libs) gidlemodule.so: $(GIDLE_OBJS) $(LINK) -shared $(GLIB_LIBS) $(shell python-config --libs) ocfs2module.so: $(OCFS2_OBJS) $(LIBOCFS2_DEPS) $(LIBO2DLM_DEPS) $(LIBO2CB_DEPS) $(LINK) -shared $(LIBOCFS2_LIBS) $(LIBO2DLM_LIBS) $(LIBO2CB_LIBS) $(COM_ERR_LIBS) $(UUID_LIBS) $(shell python-config --libs) o2cbmodule.so: $(O2CB_OBJS) $(LIBO2CB_DEPS) $(LINK) -shared $(LIBOCFS2_LIBS) $(LIBO2CB_LIBS) $(COM_ERR_LIBS) $(shell python-config --libs) install-pylib: $(SHELL) $(TOPDIR)/mkinstalldirs $(DESTDIR)$(pyexecdir)/ocfs2interface for f in $(PYLIB); do \ $(INSTALL_DATA) $$f $(DESTDIR)$(pyexecdir)/ocfs2interface/$$f; \ done include $(TOPDIR)/Postamble.make ocfs2-tools-ocfs2-tools-1.8.6/ocfs2console/ocfs2interface/__init__.py000066400000000000000000000015441347147137200254720ustar00rootroot00000000000000# OCFS2Console - GUI frontend for OCFS2 management and debugging # Copyright (C) 2002, 2005 Oracle. All rights reserved. # # 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. '''Just a dummy file, so packages work''' ocfs2-tools-ocfs2-tools-1.8.6/ocfs2console/ocfs2interface/about.py000066400000000000000000000070611347147137200250450ustar00rootroot00000000000000# OCFS2Console - GUI frontend for OCFS2 management and debugging # Copyright (C) 2002, 2005 Oracle. All rights reserved. # # 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. import sys from confdefs import OCFS2TOOLS_VERSION def print_version(): print 'OCFS2Console version %s' % OCFS2TOOLS_VERSION def print_usage(name): print '''Usage: %s [OPTION]... Options: -N, --node-config run node configurator only -V, --version print version information and exit --help display this help and exit''' % name def process_args(): nodeconf = False for arg in sys.argv[1:]: if arg in ('--version', '-V'): print_version() sys.exit(0) elif arg in ('--help',): print_usage(sys.argv[0]) sys.exit(0) elif arg in ('--node-config', '-N'): nodeconf = True return nodeconf def process_gui_args(): if len(sys.argv) > 1 and sys.argv[1] not in ('--clusterconf', '-C'): print_usage(sys.argv[0]) sys.exit(1) def about(parent): import gtk from guiutil import set_props if gtk.pygtk_version >= (2,6,0): copyright = 'Copyright (C) 2002, 2007 Oracle. All rights reserved.' license = ''' 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. ''' blurb = 'GUI frontend for OCFS2 management' #logo = gtk.gdk.pixbuf_new_from_file('logo.png') dialog = gtk.AboutDialog() dialog.set_transient_for(parent) dialog.set_destroy_with_parent(True) set_props(dialog, name='OCFS2 Console', version=OCFS2TOOLS_VERSION, copyright=copyright, license=license, website='http://oss.oracle.com', comments=blurb) #logo=logo) else: dialog = gtk.MessageDialog(parent=parent, flags=gtk.DIALOG_DESTROY_WITH_PARENT, buttons=gtk.BUTTONS_CLOSE) dialog.label.set_text( '''OCFS2 Console Version %s Copyright (C) 2002, 2005 Oracle. All Rights Reserved.''' % OCFS2TOOLS_VERSION) dialog.run() dialog.destroy() def main(): process_args() process_gui_args() about(None) if __name__ == '__main__': main() ocfs2-tools-ocfs2-tools-1.8.6/ocfs2console/ocfs2interface/bosa.py000066400000000000000000000271571347147137200246670ustar00rootroot00000000000000# OCFS2Console - GUI frontend for OCFS2 management and debugging # Copyright (C) 2002, 2005 Oracle. All rights reserved. # # 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. import gtk import gobject import pango import ocfs2 try: IdleBase = gobject.Idle except AttributeError: import gidle IdleBase = gidle.Idle from guiutil import set_props from ls import fields INFO_LABEL_FONT = pango.FontDescription('monospace') ( COLUMN_NAME, COLUMN_INFO_OBJECT, COLUMN_ICON, COLUMN_ITALIC ) = range(4) STOCK_LOADING = gtk.STOCK_REFRESH STOCK_EMPTY = gtk.STOCK_STOP STOCK_ERROR = gtk.STOCK_DIALOG_ERROR try: STOCK_FILE = gtk.STOCK_FILE except AttributeError: STOCK_FILE = gtk.STOCK_NEW try: STOCK_DIRECTORY = gtk.STOCK_DIRECTORY except AttributeError: STOCK_DIRECTORY = gtk.STOCK_OPEN INVALID_DENTRY = 'poop' class InfoLabel(gtk.Label): def __init__(self, field_type): gtk.Label.__init__(self) self.set_selectable(True) self.field_type = field_type if field_type.right_justify: set_props(self, xalign=1.0) else: set_props(self, xalign=0.0) self.modify_font(INFO_LABEL_FONT) if hasattr(field_type, 'width_chars'): context = self.get_pango_context() desc = INFO_LABEL_FONT.copy() desc.set_size(context.get_font_description().get_size()) metrics = context.get_metrics(desc, context.get_language()) char_width = metrics.get_approximate_char_width() digit_width = metrics.get_approximate_digit_width() char_pixels = pango.PIXELS(max(char_width, digit_width)) self.set_size_request(char_pixels * field_type.width_chars, -1) def update(self, dentry, dinode): field = self.field_type(dentry, dinode) self.set_text(field.text) def clear(self): self.set_text('') class Browser(gtk.VBox): def __init__(self, device=None): self.device = device gtk.VBox.__init__(self, spacing=4) label = gtk.Label('/') set_props(label, xalign=0.0, selectable=True, wrap=True) self.pack_start(label, expand=False) self.path_label = label self.scrl_win = gtk.ScrolledWindow() self.scrl_win.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) self.add(self.scrl_win) self.make_dentry_store() self.make_file_view() self.make_ls_fields() self.connect('destroy', self.destroy_handler) self.refresh() def make_dentry_store(self): def tree_compare(store, a, b): d1 = self.get_dentry(store, a) d2 = self.get_dentry(store, b) if d1 is d2: return 0 elif d1 is INVALID_DENTRY: return 1 elif d2 is INVALID_DENTRY: return -1 elif d1 and not d2: return 1 elif not d1 and d2: return -1 elif d1.file_type != d2.file_type: if d1.file_type == ocfs2.FT_DIR: return -1 elif d2.file_type == ocfs2.FT_DIR: return 1 else: return cmp(d1.name, d2.name) else: return cmp(d1.name, d2.name) self.store = gtk.TreeStore(str, gobject.TYPE_PYOBJECT, str, gobject.TYPE_BOOLEAN) self.store.set_sort_func(COLUMN_NAME, tree_compare) self.store.set_sort_column_id(COLUMN_NAME, gtk.SORT_ASCENDING) def make_file_view(self): tv = gtk.TreeView(self.store) self.scrl_win.add(tv) set_props(tv, headers_visible=False) column = gtk.TreeViewColumn() renderer = gtk.CellRendererPixbuf() column.pack_start(renderer, expand=False) column.set_attributes(renderer, stock_id=COLUMN_ICON) renderer = gtk.CellRendererText() renderer.set_property('style', pango.STYLE_ITALIC) column.pack_start(renderer, expand=True) column.set_attributes(renderer, text=COLUMN_NAME, style_set=COLUMN_ITALIC) tv.append_column(column) tv.connect('test_expand_row', self.tree_expand_row) tv.connect('test_collapse_row', self.tree_collapse_row) sel = tv.get_selection() sel.connect('changed', self.select_dentry) def make_ls_fields(self): table = gtk.Table(rows=2, columns=7) set_props(table, row_spacing=4, column_spacing=4, border_width=4) self.pack_end(table, expand=False, fill=False) self.info_labels = [] for column, field in enumerate(fields): label = gtk.Label(field.label) if field.right_justify: set_props(label, xalign=1.0) else: set_props(label, xalign=0.0) xoptions = yoptions = gtk.FILL xpadding = 2 table.attach(label, column, column + 1, 0, 1, xoptions, yoptions, xpadding) label = InfoLabel(field) table.attach(label, column, column + 1, 1, 2, xoptions, yoptions, xpadding) self.info_labels.append(label) def destroy_handler(self, obj): self.cleanup() def make_dentry_node(self, dentry, stock_id, parent=None): return self.store.append(parent, (dentry.name, dentry, stock_id, False)) def make_file_node(self, dentry, parent=None): self.make_dentry_node(dentry, STOCK_FILE, parent) def make_dir_node(self, dentry, parent=None): iter = self.make_dentry_node(dentry, STOCK_DIRECTORY, parent) self.store.append(iter, ('.', dentry, None, False)) def make_loading_node(self, parent=None): self.store.append(parent, ('Loading...', None, STOCK_LOADING, True)) def make_empty_node(self, parent=None): self.store.append(parent, ('Empty', None, STOCK_EMPTY, True)) def make_error_node(self, parent=None): self.store.append(parent, ('Error', INVALID_DENTRY, STOCK_ERROR, True)) def cleanup(self): if hasattr(self, 'levels'): for level in self.levels: level.destroy() self.levels = [] def refresh(self): self.cleanup() self.store.clear() self.fs = None if self.device: try: self.fs = ocfs2.Filesystem(self.device) except ocfs2.error: self.make_error_node() if self.fs: self.add_level() else: self.make_empty_node() def add_level(self, dentry=None, parent=None): if parent: iter = self.store.iter_children(parent) name = self.store[iter][COLUMN_NAME] if name != '.': return del self.store[iter] try: diriter = self.fs.iterdir(dentry) except ocfs2.error: self.make_error_node(parent) return self.make_loading_node(parent) level = TreeLevel(diriter, dentry, parent) self.levels.append(level) if parent: self.store[parent][COLUMN_INFO_OBJECT] = level level.set_callback(self.populate_level, level) level.attach() def populate_level(self, level): try: dentry = level.diriter.next() except (StopIteration, ocfs2.error), e: self.destroy_level(level, isinstance(e, ocfs2.error)) return False if dentry.file_type == ocfs2.FT_DIR: self.make_dir_node(dentry, level.parent) else: self.make_file_node(dentry, level.parent) return True def destroy_level(self, level, error=False): if error: self.make_error_node(level.parent) else: children = self.store.iter_n_children(level.parent) if children < 2: self.make_empty_node(level.parent) if level.parent: self.store[level.parent][COLUMN_INFO_OBJECT] = level.dentry # Argh, ancient pygtk can't handle None being passed to # iter_children iter = self.store.iter_children(level.parent) else: iter = self.store.get_iter_first() self.store.remove(iter) self.levels.remove(level) del level.diriter def tree_expand_row(self, tv, iter, path): info_obj = self.store[iter][COLUMN_INFO_OBJECT] if isinstance(info_obj, TreeLevel): level.collapsed = False level.foreground(level) else: self.add_level(info_obj, iter) def tree_collapse_row(self, tv, iter, path): info_obj = self.store[iter][COLUMN_INFO_OBJECT] if isinstance(info_obj, TreeLevel): level = info_obj level.collapsed = True level.background() def select_dentry(self, sel): store, iter = sel.get_selected() if store and iter: dentry = self.get_dentry(store, iter) else: dentry = None if dentry: self.display_dentry(dentry) else: self.display_clear() if iter: iter = store.iter_parent(iter) self.path_label.set_text(self.get_fs_path(store, iter)) def display_dentry(self, dentry): dinode = self.fs.read_cached_inode(dentry.inode) for label in self.info_labels: label.update(dentry, dinode) def display_clear(self): for label in self.info_labels: label.clear() def get_dentry(self, store, iter): info_obj = store[iter][COLUMN_INFO_OBJECT] if isinstance(info_obj, ocfs2.DirEntry): return info_obj elif isinstance(info_obj, TreeLevel): return info_obj.dentry else: return None def get_fs_path(self, store, iter): parts = [] while iter: dentry = self.get_dentry(store, iter) parts.append(dentry.name) iter = store.iter_parent(iter) parts.reverse() return '/' + '/'.join(parts) class TreeLevel(IdleBase): def __init__(self, diriter, dentry=None, parent=None): IdleBase.__init__(self) self.diriter = diriter self.dentry = dentry if parent: self.parent = parent.copy() else: self.parent = None self.collapsed = False def foreground(self): if not self.collapsed: self.priority = gobject.PRIORITY_DEFAULT_IDLE def background(self): self.priority = gobject.PRIORITY_LOW def main(): import sys def dummy(*args): gtk.main_quit() window = gtk.Window() window.set_default_size(400, 300) window.connect('delete_event', dummy) browser = Browser(sys.argv[1]) window.add(browser) window.show_all() gtk.main() if __name__ == '__main__': main() ocfs2-tools-ocfs2-tools-1.8.6/ocfs2console/ocfs2interface/classlabel.py000066400000000000000000000022601347147137200260340ustar00rootroot00000000000000# OCFS2Console - GUI frontend for OCFS2 management and debugging # Copyright (C) 2005 Oracle. All rights reserved. # # 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. import re caps = re.compile('(?!\A)[A-Z][a-z]') def make_title(m): return ' ' + m.group() class class_label(object): def __get__(self, obj, cls): return caps.sub(make_title, cls.__name__) class_label = class_label() def main(): import sys cls = type(sys.argv[1], (), {'label': class_label}) print cls.label if __name__ == '__main__': main() ocfs2-tools-ocfs2-tools-1.8.6/ocfs2console/ocfs2interface/confdefs.py.in000066400000000000000000000015341347147137200261260ustar00rootroot00000000000000# OCFS2Console - GUI frontend for OCFS2 management and debugging # Copyright (C) 2002, 2007 Oracle. All rights reserved. # # 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. OCFS2TOOLS_VERSION = '@VERSION@' ocfs2-tools-ocfs2-tools-1.8.6/ocfs2console/ocfs2interface/console.py000066400000000000000000000103341347147137200253720ustar00rootroot00000000000000# OCFS2Console - GUI frontend for OCFS2 management and debugging # Copyright (C) 2002, 2005 Oracle. All rights reserved. # # 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. import gtk from guiutil import set_props, error_box from partitionview import PartitionView from menu import Menu from toolbar import Toolbar from about import about, process_gui_args from mount import mount, unmount from format import format_partition from fsck import fsck_volume from tune import tune_label, tune_slots from general import General from bosa import Browser from nodeconfig import node_config from pushconfig import push_config info_items = ( ('General', General), ('File Listing', Browser), ) class Console(gtk.Window): def __init__(self): gtk.Window.__init__(self) set_props(self, title='OCFS2 Console', default_width=600, default_height=460, border_width=0) self.connect('delete_event', self.cleanup) notebook = gtk.Notebook() notebook.set_tab_pos(gtk.POS_TOP) info_frames = [] for desc, info in info_items: frame = gtk.Frame() set_props(frame, shadow=gtk.SHADOW_NONE, border_width=0) notebook.add_with_properties(frame, 'tab_label', desc) info_frames.append((frame, info)) self.pv = PartitionView(info_frames) self.pv.set_size_request(-1, 100) vbox = gtk.VBox() self.add(vbox) menu = Menu(self) menubar, sel_items, unmounted_items = menu.get_widgets() vbox.pack_start(menubar, expand=False, fill=False) self.pv.add_sel_widgets(sel_items) self.pv.add_unmount_widgets(unmounted_items) toolbar = Toolbar(self) tb, buttons, filter_entry = toolbar.get_widgets() vbox.pack_start(tb, expand=False, fill=False) self.pv.add_mount_widgets([buttons['unmount']]) self.pv.add_unmount_widgets([buttons['mount']]) filter_entry.connect('activate', self.refresh) self.pv.set_filter_entry(filter_entry) vpaned = gtk.VPaned() vpaned.set_border_width(4) vbox.pack_start(vpaned, expand=True, fill=True) scrl_win = gtk.ScrolledWindow() set_props(scrl_win, hscrollbar_policy=gtk.POLICY_AUTOMATIC, vscrollbar_policy=gtk.POLICY_AUTOMATIC) scrl_win.add(self.pv) vpaned.pack1(scrl_win) vpaned.pack2(notebook) self.pv.grab_focus() self.show_all() self.refresh() def cleanup(self, *args): gtk.main_quit() def about(self): about(self) def refresh(self, *args): self.pv.refresh_partitions() def mount(self): device, mountpoint = self.pv.get_sel_values() mount(self, device) def unmount(self): device, mountpoint = self.pv.get_sel_values() unmount(self, device, mountpoint) def format(self): format_partition(self, self.pv.get_device()) def relabel(self): tune_label(self, self.pv.get_device()) def slot_num(self): tune_slots(self, self.pv.get_device()) def check(self): fsck_volume(self, self.pv.get_device(), check=True) def repair(self): fsck_volume(self, self.pv.get_device(), check=False) def node_config(self): node_config(self) def push_config(self): push_config(self) def main(): from about import process_gui_args process_gui_args() console = Console() gtk.main() if __name__ == '__main__': main() ocfs2-tools-ocfs2-tools-1.8.6/ocfs2console/ocfs2interface/format.py000066400000000000000000000104331347147137200252200ustar00rootroot00000000000000# OCFS2Console - GUI frontend for OCFS2 management and debugging # Copyright (C) 2002, 2005 Oracle. All rights reserved. # # 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. import gtk from plist import partition_list from guiutil import Dialog, set_props, error_box, format_bytes from process import Process from fswidgets import BaseCombo, NumSlots, VolumeLabel, ClusterSize, BlockSize base_command = ('mkfs.ocfs2', '-x') class Device(BaseCombo): def fill(self, partitions, device): self.set_choices([('%s (%s)' % p, p[0] == device) for p in partitions]) def get_device(self): return self.get_choice().split(' ')[0] label = 'Available _devices' class FormatVolumeLabel(VolumeLabel): def __init__(self): VolumeLabel.__init__(self) self.set_text('oracle') entries = (Device, FormatVolumeLabel, ClusterSize, NumSlots, BlockSize) def format_partition(parent, device): partitions = [] def add_partition(device, fstype): partitions.append((device, fstype)) partition_list(add_partition, unmounted=True) if not partitions: error_box(parent, 'No unmounted partitions') return False def sort_partition(x, y): return cmp(x[0], y[0]) partitions.sort(sort_partition) dialog = Dialog(parent=parent, title='Format', buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OK, gtk.RESPONSE_OK)) dialog.set_alternative_button_order((gtk.RESPONSE_OK, gtk.RESPONSE_CANCEL)) dialog.set_default_response(gtk.RESPONSE_OK) table = gtk.Table(rows=5, columns=2) set_props(table, row_spacing=6, column_spacing=6, border_width=6, parent=dialog.vbox) widgets = [] for row, widget_type in enumerate(entries): widget = widget_type() label = gtk.Label() label.set_text_with_mnemonic(widget_type.label + ':') label.set_mnemonic_widget(widget) set_props(label, xalign=0.0) table.attach(label, 0, 1, row, row + 1) if widget_type == Device: widget.fill(partitions, device) if isinstance(widget, gtk.Entry): widget.set_activates_default(True) if isinstance(widget, gtk.SpinButton): attach_widget = gtk.HBox() attach_widget.pack_start(widget, expand=False, fill=False) else: attach_widget = widget table.attach(attach_widget, 1, 2, row, row + 1) widgets.append(widget) widgets[0].grab_focus() dialog.show_all() while 1: if dialog.run() != gtk.RESPONSE_OK: dialog.destroy() return False dev = widgets[0].get_device() msg = 'Are you sure you want to format %s?' % dev ask = gtk.MessageDialog(parent=dialog, flags=gtk.DIALOG_DESTROY_WITH_PARENT, type=gtk.MESSAGE_QUESTION, buttons=gtk.BUTTONS_YES_NO, message_format=msg) if ask.run() == gtk.RESPONSE_YES: break else: ask.destroy() command = list(base_command) for widget in widgets[1:]: arg = widget.get_arg() if arg: command.extend(arg) command.append(dev) dialog.destroy() mkfs = Process(command, 'Format', 'Formatting...', parent, spin_now=True) success, output, k = mkfs.reap() if not success: error_box(parent, 'Format error: %s' % output) return False return True def main(): format_partition(None, None) if __name__ == '__main__': main() ocfs2-tools-ocfs2-tools-1.8.6/ocfs2console/ocfs2interface/fsck.py000066400000000000000000000050021347147137200246520ustar00rootroot00000000000000# OCFS2Console - GUI frontend for OCFS2 management and debugging # Copyright (C) 2005 Oracle. All rights reserved. # # 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. import gtk import gobject from terminal import TerminalDialog, terminal_ok as fsck_ok base_command = ('fsck.ocfs2',) def fsck_volume(parent, device, check=False): if check: check_str = 'check' else: check_str = 'repair' title = 'File System ' + check_str.capitalize() dialog = TerminalDialog(parent=parent, title=title) terminal = dialog.terminal dialog.finished = False terminal.connect('child-exited', child_exited, dialog) command = fsck_command(device, check) gobject.idle_add(start_command, terminal, command, dialog) while 1: dialog.run() if dialog.finished: break msg = ('File system %s is still running. You should not close this ' 'window until it is finished' % check_str) info = gtk.MessageDialog(parent=dialog, flags=gtk.DIALOG_DESTROY_WITH_PARENT, type=gtk.MESSAGE_WARNING, buttons=gtk.BUTTONS_CLOSE, message_format=msg) info.run() info.destroy() dialog.destroy() def start_command(terminal, command, dialog): terminal.fork_command(command=command[0], argv=command) return False def child_exited(terminal, dialog): dialog.finished = True def fsck_command(device, check): command = list(base_command) if check: command.append('-n') else: command.append('-y') command.append("'%s'" % device) realcommand = '%s; sleep 1' % ' '.join(command) return ['/bin/sh', '-c', realcommand] def main(): import sys fsck_volume(None, sys.argv[1], check=True) if __name__ == '__main__': main() ocfs2-tools-ocfs2-tools-1.8.6/ocfs2console/ocfs2interface/fstab.py000066400000000000000000000051721347147137200250330ustar00rootroot00000000000000# OCFS2Console - GUI frontend for OCFS2 management and debugging # Copyright (C) 2005 Oracle. All rights reserved. # # 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. PATH_FSTAB = '/etc/fstab' class FSTab: def __init__(self): self.refresh() def refresh(self): self.entries = [] try: fstab_file = open(PATH_FSTAB) lines = fstab_file.readlines() fstab_file.close() except IOError: return for line in lines: line = line.strip() if line.startswith('#'): continue try: entry = FSTabEntry(*line.split()) except (ValueError, TypeError): continue self.entries.append(entry) def get(self, device=None, label=None, uuid=None): valid_specs = {} if device: valid_specs[device] = True if label: spec = 'LABEL=' + label valid_specs[spec] = True if uuid: spec = 'UUID=' + uuid.lower() valid_specs[spec] = True for entry in self.entries: if entry.spec in valid_specs: return entry return None class FSTabEntry: def __init__(self, spec, mountpoint, vfstype, options, freq=0, passno=0): symtab = locals() code = self.__init__.func_code names = [] for attr in code.co_varnames[1:code.co_argcount]: setattr(self, attr, symtab[attr]) names.append(attr) self.entry_fmt = '\t'.join(['%%(%s)s' % f for f in names]) if self.spec.startswith('UUID='): self.spec = 'UUID=' + self.spec[5:].lower() def __str__(self): return self.entry_fmt % self.__dict__ def __repr__(self): return "" % str(self) def main(): import sys spec = sys.argv[1] fstab = FSTab() print fstab.get(device=spec, label=spec, uuid=spec) if __name__ == '__main__': main() ocfs2-tools-ocfs2-tools-1.8.6/ocfs2console/ocfs2interface/fswidgets.py000066400000000000000000000112211347147137200257230ustar00rootroot00000000000000# OCFS2Console - GUI frontend for OCFS2 management and debugging # Copyright (C) 2002, 2005 Oracle. All rights reserved. # # 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. import gtk import ocfs2 from guiutil import set_props, format_bytes if gtk.pygtk_version >= (2,4,0): class BaseCombo(gtk.ComboBox): def __init__(self): self.store = gtk.ListStore(str) gtk.ComboBox.__init__(self, model=self.store) cell = gtk.CellRendererText() self.pack_start(cell) self.set_attributes(cell, text=0) def get_choice(self): return self.store[self.get_active_iter()][0] def set_choices(self, choices): selected = False for choice, select in choices: iter = self.store.append((choice,)) if select: self.set_active_iter(iter) selected = True if not selected: self.set_active(0) else: class BaseCombo(gtk.Combo): def __init__(self): gtk.Combo.__init__(self) self.entry.set_editable(False) def get_choice(self): return self.entry.get_text() def set_choices(self, choices): for choice, select in choices: item = gtk.ListItem(choice) item.show() self.list.add(item) if select: item.select() class ValueCombo(BaseCombo): def __init__(self, minimum, maximum): BaseCombo.__init__(self) choices = [('Auto', False)] size = minimum while size <= maximum: choices.append((format_bytes(size), False)) size = size << 1 self.set_choices(choices) def get_arg(self): text = self.get_choice() if text != 'Auto': s = text.replace(' ', '') s = s.replace('B', '') s = s.replace('bytes', '') return (self.arg, s) else: return None class NumSlots(gtk.SpinButton): def __init__(self): adjustment = gtk.Adjustment(4, 1, ocfs2.MAX_SLOTS, 1, 10) gtk.SpinButton.__init__(self, adjustment=adjustment) self.set_numeric(True) label = 'Number of _node slots' def get_arg(self): s = self.get_text() if s: return ('-N', s) else: return None class VolumeLabel(gtk.Entry): def __init__(self): gtk.Entry.__init__(self, max=ocfs2.MAX_VOL_LABEL_LEN) label = 'Volume _label' def get_arg(self): s = self.get_text() if s: return ('-L', s) else: return None class ClusterSize(ValueCombo): def __init__(self): ValueCombo.__init__(self, ocfs2.MIN_CLUSTERSIZE, ocfs2.MAX_CLUSTERSIZE) self.arg = '-C' label = 'Cluster _size' class BlockSize(ValueCombo): def __init__(self): ValueCombo.__init__(self, ocfs2.MIN_BLOCKSIZE, ocfs2.MAX_BLOCKSIZE) self.arg = '-b' label = '_Block size' def main(): import types widgets = [] symbols = globals() for class_type in symbols.itervalues(): if isinstance(class_type, types.TypeType) and hasattr(class_type, 'label'): widgets.append(class_type) def dummy(*args): gtk.main_quit() window = gtk.Window() window.connect('delete_event', dummy) table = gtk.Table(rows=len(widgets), columns=2) set_props(table, row_spacing=4, column_spacing=4, border_width=4, parent=window) for row, widget_type in enumerate(widgets): label = gtk.Label() label.set_text_with_mnemonic(widget_type.label + ':') set_props(label, xalign=1.0) table.attach(label, 0, 1, row, row + 1) widget = widget_type() table.attach(widget, 1, 2, row, row + 1) window.show_all() gtk.main() if __name__ == '__main__': main() ocfs2-tools-ocfs2-tools-1.8.6/ocfs2console/ocfs2interface/general.py000066400000000000000000000076471347147137200253620ustar00rootroot00000000000000# OCFS2Console - GUI frontend for OCFS2 management and debugging # Copyright (C) 2002, 2005 Oracle. All rights reserved. # # 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. import gtk import ocfs2 from classlabel import class_label from guiutil import set_props, format_bytes EMPTY_TEXT = 'N/A' class Field(object): def __init__(self, fs, super, dinode): self.fs = fs self.super = super self.dinode = dinode def get_text(self): if self.super: return self.real_get_text() else: return EMPTY_TEXT text = property(get_text) label = class_label class Version(Field): def real_get_text(self): return '%d.%d' % (self.super.s_major_rev_level, self.super.s_minor_rev_level) class Label(Field): def real_get_text(self): text = self.super.s_label if not text: text = EMPTY_TEXT return text class UUID(Field): def real_get_text(self): return self.super.uuid_unparsed class MaximumNodes(Field): def real_get_text(self): return str(self.super.s_max_slots) class FSSize(Field): def real_get_text(self): return format_bytes(getattr(self.fs, self.member)) class ClusterSize(FSSize): member = 'fs_clustersize' class BlockSize(FSSize): member = 'fs_blocksize' class Space(Field): def real_get_text(self): if self.dinode: block_bits = self.fs.fs_clustersize >> self.super.s_blocksize_bits bytes = self.get_bits() * block_bits * self.fs.fs_blocksize return format_bytes(bytes, show_bytes=True) else: return EMPTY_TEXT class FreeSpace(Space): def get_bits(self): return self.dinode.i_total - self.dinode.i_used class TotalSpace(Space): def get_bits(self): return self.dinode.i_total fields = (Version, Label, UUID, MaximumNodes, ClusterSize, BlockSize, FreeSpace, TotalSpace) class General(gtk.Table): def __init__(self, device=None): gtk.Table.__init__(self, rows=5, columns=2) set_props(self, row_spacing=4, column_spacing=4, border_width=4) fs = super = dinode = None if device: try: fs = ocfs2.Filesystem(device) super = fs.fs_super blkno = fs.lookup_system_inode(ocfs2.GLOBAL_BITMAP_SYSTEM_INODE) dinode = fs.read_cached_inode(blkno) except ocfs2.error: pass for row, field_type in enumerate(fields): field = field_type(fs, super, dinode) label = gtk.Label(field.label + ':') set_props(label, xalign=1.0) self.attach(label, 0, 1, row, row + 1, xoptions=gtk.FILL, yoptions=gtk.FILL) label = gtk.Label(field.text) set_props(label, xalign=0.0) self.attach(label, 1, 2, row, row + 1, xoptions=gtk.FILL, yoptions=gtk.FILL) def main(): import sys def dummy(*args): gtk.main_quit() window = gtk.Window() window.connect('delete_event', dummy) general = General(sys.argv[1]) window.add(general) window.show_all() gtk.main() if __name__ == '__main__': main() ocfs2-tools-ocfs2-tools-1.8.6/ocfs2console/ocfs2interface/gidlemodule.c000066400000000000000000000204331347147137200260150ustar00rootroot00000000000000/* * gidlemodule.c * * Richer interface to GLIB's idle sources. * * Copyright (C) 2005 Oracle. All rights reserved. * * Author: Manish Singh * * 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 recieved a copy of the GNU General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. */ #include #include typedef struct { PyObject_HEAD GSource *idle; gboolean attached; PyObject *inst_dict; PyObject *weakreflist; } Idle; #define CHECK_DESTROYED(self, ret) G_STMT_START { \ if ((self)->idle == NULL) \ { \ PyErr_SetString (PyExc_RuntimeError, "idle is destroyed"); \ return (ret); \ } \ } G_STMT_END static PyObject * idle_attach (Idle *self) { CHECK_DESTROYED (self, NULL); self->attached = TRUE; return PyInt_FromLong (g_source_attach (self->idle, NULL)); } static PyObject * idle_destroy (Idle *self) { CHECK_DESTROYED (self, NULL); g_source_destroy (self->idle); self->idle = NULL; Py_INCREF (Py_None); return Py_None; } /* The next three functions are based on code from PyGTK gobjectmodule.c * Copyright (C) 1998-2003 James Henstridge */ static void destroy_notify (gpointer user_data) { PyObject *obj = (PyObject *) user_data; Py_DECREF (obj); } static gboolean handler_marshal (gpointer user_data) { PyObject *tuple, *ret; gboolean res; g_return_val_if_fail (user_data != NULL, FALSE); tuple = (PyObject *) user_data; ret = PyObject_CallObject (PyTuple_GetItem(tuple, 0), PyTuple_GetItem(tuple, 1)); if (!ret) { PyErr_Print(); res = FALSE; } else { res = PyObject_IsTrue(ret); Py_DECREF(ret); } return res; } static PyObject * idle_set_callback (Idle *self, PyObject *args) { PyObject *first, *callback, *cbargs = NULL, *data; gint len; CHECK_DESTROYED (self, NULL); len = PyTuple_Size (args); if (len < 1) { PyErr_SetString (PyExc_TypeError, "set_callback requires at least 1 argument"); return NULL; } first = PySequence_GetSlice (args, 0, 1); if (!PyArg_ParseTuple (first, "O:set_callback", &callback)) { Py_DECREF (first); return NULL; } Py_DECREF (first); if (!PyCallable_Check (callback)) { PyErr_SetString (PyExc_TypeError, "first argument not callable"); return NULL; } cbargs = PySequence_GetSlice (args, 1, len); if (cbargs == NULL) return NULL; data = Py_BuildValue ("(ON)", callback, cbargs); if (data == NULL) return NULL; g_source_set_callback (self->idle, handler_marshal, data, destroy_notify); Py_INCREF (Py_None); return Py_None; } static PyMethodDef idle_methods[] = { {"attach", (PyCFunction)idle_attach, METH_NOARGS}, {"destroy", (PyCFunction)idle_destroy, METH_NOARGS}, {"set_callback", (PyCFunction)idle_set_callback, METH_VARARGS}, {NULL, NULL} }; static PyObject * idle_get_dict (Idle *self, void *closure) { if (self->inst_dict == NULL) { self->inst_dict = PyDict_New (); if (self->inst_dict == NULL) return NULL; } Py_INCREF(self->inst_dict); return self->inst_dict; } static PyObject * idle_get_priority (Idle *self, void *closure) { CHECK_DESTROYED (self, NULL); return PyInt_FromLong (g_source_get_priority (self->idle)); } static int idle_set_priority (Idle *self, PyObject *value, void *closure) { CHECK_DESTROYED (self, -1); if (value == NULL) { PyErr_SetString (PyExc_TypeError, "cannot delete priority"); return -1; } if (!PyInt_Check (value)) { PyErr_SetString (PyExc_TypeError, "type mismatch"); return -1; } g_source_set_priority (self->idle, PyInt_AsLong (value)); return 0; } static PyObject * idle_get_can_recurse (Idle *self, void *closure) { CHECK_DESTROYED (self, NULL); return PyBool_FromLong (g_source_get_can_recurse (self->idle)); } static int idle_set_can_recurse (Idle *self, PyObject *value, void *closure) { CHECK_DESTROYED (self, -1); if (value == NULL) { PyErr_SetString (PyExc_TypeError, "cannot delete can_recurse"); return -1; } if (!PyInt_Check (value)) { PyErr_SetString (PyExc_TypeError, "type mismatch"); return -1; } g_source_set_can_recurse (self->idle, PyInt_AsLong (value)); return 0; } static PyObject * idle_get_id (Idle *self, void *closure) { CHECK_DESTROYED (self, NULL); if (!self->attached) { PyErr_SetString (PyExc_RuntimeError, "idle is not attached"); return NULL; } return PyInt_FromLong (g_source_get_id (self->idle)); } static PyGetSetDef idle_getsets[] = { {"__dict__", (getter)idle_get_dict, (setter)0}, {"priority", (getter)idle_get_priority, (setter)idle_set_priority}, {"can_recurse", (getter)idle_get_can_recurse, (setter)idle_set_can_recurse}, {"id", (getter)idle_get_id, (setter)0}, {NULL} }; static void idle_dealloc (Idle *self) { PyObject_ClearWeakRefs ((PyObject *) self); PyObject_GC_UnTrack ((PyObject *) self); if (self->idle) g_source_unref (self->idle); self->idle = NULL; Py_XDECREF (self->inst_dict); self->inst_dict = NULL; PyObject_GC_Del (self); } static PyObject * idle_repr (Idle *self) { gchar buf[256]; g_snprintf (buf, sizeof (buf), "<%sattached glib idle source at 0x%lx>", self->attached ? "" : "un", (long)self); return PyString_FromString (buf); } static int idle_clear (Idle *self) { Py_XDECREF (self->inst_dict); self->inst_dict = NULL; if (self->idle) g_source_unref (self->idle); self->idle = NULL; return 0; } static int idle_init (Idle *self, PyObject *args, PyObject *kwargs) { gint priority = G_PRIORITY_DEFAULT_IDLE; static gchar *kwlist[] = { "priority", NULL }; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|i:gidle.Idle.__init__", kwlist, &priority)) return -1; self->idle = g_idle_source_new (); g_source_set_priority (self->idle, priority); self->attached = FALSE; self->inst_dict = NULL; self->weakreflist = NULL; return 0; } static void idle_free (PyObject *op) { PyObject_GC_Del (op); } static PyTypeObject Idle_Type = { PyObject_HEAD_INIT(NULL) 0, /* ob_size */ "gidle.Idle", /* tp_name */ sizeof(Idle), /* tp_basicsize */ 0, /* tp_itemsize */ (destructor)idle_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_compare */ (reprfunc)idle_repr, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, /* tp_flags */ NULL, /* tp_doc */ 0, /* tp_traverse */ (inquiry)idle_clear, /* tp_clear */ 0, /* tp_richcompare */ offsetof (Idle, weakreflist), /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ idle_methods, /* tp_methods */ 0, /* tp_members */ idle_getsets, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ offsetof (Idle, inst_dict), /* tp_dictoffset */ (initproc)idle_init, /* tp_init */ 0, /* tp_alloc */ 0, /* tp_new */ (freefunc)idle_free, /* tp_free */ }; static PyMethodDef gidle_methods[] = { {NULL, NULL} /* sentinel */ }; void initgidle (void) { PyObject *m; Idle_Type.tp_new = PyType_GenericNew; if (PyType_Ready (&Idle_Type) < 0) return; m = Py_InitModule ("gidle", gidle_methods); Py_INCREF (&Idle_Type); PyModule_AddObject (m, "Idle", (PyObject *) &Idle_Type); if (PyErr_Occurred ()) Py_FatalError ("can't initialize module gidle"); } ocfs2-tools-ocfs2-tools-1.8.6/ocfs2console/ocfs2interface/guiutil.py000066400000000000000000000042241347147137200254130ustar00rootroot00000000000000# OCFS2Console - GUI frontend for OCFS2 management and debugging # Copyright (C) 2002, 2005 Oracle. All rights reserved. # # 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. import gtk def set_props(obj, **kwargs): for k, v in kwargs.items(): obj.set_property(k, v) suffixes = ('K', 'MB', 'GB', 'TB') def format_bytes(bytes, show_bytes=False): if bytes == 1: return '1 byte' elif bytes < 1024: return str(bytes) + ' bytes' fbytes = float(bytes) i = -1 while i < 3 and fbytes >= 1024: fbytes /= 1024 i += 1 if show_bytes: return '%.1f %s (%sb)' % (fbytes, suffixes[i], bytes) else: return '%.0f %s' % (fbytes, suffixes[i]) def error_box(parent, msg): dialog = gtk.MessageDialog(parent=parent, flags=gtk.DIALOG_DESTROY_WITH_PARENT, type=gtk.MESSAGE_ERROR, buttons=gtk.BUTTONS_OK, message_format=msg) dialog.run() dialog.destroy() def make_callback(obj, callback, sub_callback): cb = getattr(obj, callback) if sub_callback: sub_cb = getattr(obj, sub_callback) def cb_func(*args): cb() sub_cb() else: def cb_func(*args): cb() return cb_func if hasattr(gtk.Dialog, 'set_alternative_button_order'): Dialog = gtk.Dialog else: class Dialog(gtk.Dialog): def set_alternative_button_order(self, new_order=None): pass ocfs2-tools-ocfs2-tools-1.8.6/ocfs2console/ocfs2interface/ipwidget.py000066400000000000000000000112111347147137200255370ustar00rootroot00000000000000# # class to create an IP address entry widget and to sanity check entered values # # Jonathan Blandford # Michael Fulbright # # Copyright 2002 Red Hat, Inc. # # This software may be freely redistributed under the terms of the GNU # library public license. # # You should have received a copy of the GNU Library Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. # # Modified by Manish Singh import re import string import gtk import gobject # we're not gettexted - Manish def _(text): return text # The below is from network.py from anaconda # # Matt Wilson # Erik Troan # Mike Fulbright # Brent Fox # # Copyright 2001-2003 Red Hat, Inc. # # sanity check an IP string. if valid, returns octets, if invalid, return None def sanityCheckIPString(ip_string): ip_re = re.compile('^([0-2]?[0-9]?[0-9])\\.([0-2]?[0-9]?[0-9])\\.([0-2]?[0-9]?[0-9])\\.([0-2]?[0-9]?[0-9])$') #Sanity check the string m = ip_re.match (ip_string) try: if not m: return None octets = m.groups() if len(octets) != 4: return None for octet in octets: if (int(octet) < 0) or (int(octet) > 255): return None except TypeError: return None return octets ip_fields = ['entry1', 'entry2', 'entry3', 'entry4'] # Includes an error message, and the widget with problems class IPError(Exception): pass class IPMissing(Exception): pass class IPEditor(gtk.HBox): def __init__ (self): gtk.HBox.__init__(self) self.entrys = {} for name in ip_fields: self.entrys[name] = gtk.Entry(3) self.entrys[name].set_size_request(50,-1) self.entrys[name].set_max_length(3) self.entrys[name].set_activates_default(True) for i in range(0, len(ip_fields)): name = ip_fields[i] if name != 'entry4': nname = self.entrys[ip_fields[i+1]] else: nname = None self.entrys[name].connect('insert_text', self.entry_insert_text_cb, nname) for name in ip_fields: self.pack_start(self.entrys[name], False, False) if name != 'entry4': self.pack_start(gtk.Label('.'), False, False) def getFocusableWidget(self): return self.entrys['entry1'] def clear_entries (self): for name in ip_fields: self.entrys[name].set_text('') def hydrate (self, ip_string): self.clear_entries() octets = sanityCheckIPString(ip_string) if octets is None: return i = 0 for name in ip_fields: self.entrys[name].set_text(octets[i]) i = i + 1 def dehydrate (self): widget = None # test if empty empty = 1 for e in ['entry1', 'entry2', 'entry3', 'entry4']: if len(string.strip(self.entrys[e].get_text())) > 0: empty = 0 break if empty: raise IPMissing, (_("IP Address is missing"), widget) try: widget = self.entrys['entry1'] if int(widget.get_text()) > 255 or int(widget.get_text()) <= 0: raise IPError, (_("IP Addresses must contain numbers between 1 and 255"), widget) for ent in ['entry2', 'entry3', 'entry4']: widget = self.entrys[ent] if int(widget.get_text()) > 255: raise IPError, (_("IP Addresses must contain numbers between 0 and 255"), widget) except ValueError, msg: raise IPError, (_("IP Addresses must contain numbers between 0 and 255"), widget) return self.entrys['entry1'].get_text() + "." + self.entrys['entry2'].get_text() + "." +self.entrys['entry3'].get_text() + "." +self.entrys['entry4'].get_text() def entry_insert_text_cb(self, entry, text, length, pos, next): if text == '.': entry.emit_stop_by_name ("insert_text") if next: next.grab_focus() return reg = re.compile ("[^0-9]+") if reg.match (text): entry.emit_stop_by_name ("insert_text") get_text = dehydrate set_text = hydrate if __name__ == "__main__": def output(xxx, data): try: print data.dehydrate() except: print "oops errors" gtk.main_quit() win = gtk.Window() win.connect('destroy', gtk.main_quit) vbox = gtk.VBox() ip = IPEditor() vbox = gtk.VBox() vbox.pack_start(ip) button = gtk.Button("Quit") button.connect("pressed", output, ip) vbox.pack_start(button, False, False) win.add(vbox) win.show_all() gtk.main() ocfs2-tools-ocfs2-tools-1.8.6/ocfs2console/ocfs2interface/ls.py000066400000000000000000000101651347147137200243500ustar00rootroot00000000000000# OCFS2Console - GUI frontend for OCFS2 management and debugging # Copyright (C) 2005 Oracle. All rights reserved. # # 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. import stat import pwd import grp import time import ocfs2 from classlabel import class_label class Field(object): def __init__(self, dentry, dinode): self.dentry = dentry self.dinode = dinode def real_get_text(self): return str(getattr(self.dinode, self.dinode_member)) def get_text(self): return self.real_get_text() text = property(get_text) label = class_label right_justify = False file_type = { ocfs2.FT_UNKNOWN : '?', ocfs2.FT_REG_FILE : '-', ocfs2.FT_DIR : 'd', ocfs2.FT_CHRDEV : 'c', ocfs2.FT_BLKDEV : 'b', ocfs2.FT_FIFO : 'p', ocfs2.FT_SOCK : 's', ocfs2.FT_SYMLINK : 'l', } class Mode(Field): width_chars = 10 def real_get_text(self): text = ['-'] * 10 text[0] = file_type[self.dentry.file_type] mode = self.dinode.i_mode pos = 0 for t in 'USR', 'GRP', 'OTH': for b in 'R', 'W', 'X': pos += 1 if mode & getattr(stat, 'S_I%s%s' % (b, t)): text[pos] = b.lower() pos = 0 for t, b in (('UID', 'S'), ('GID', 'S'), ('VTX', 'T')): pos += 3 if mode & getattr(stat, 'S_IS%s' % t): if text[pos] == 'x': text[pos] = b else: text[pos] = b.lower() return ''.join(text) class Links(Field): label = '# Links' dinode_member = 'i_links_count' right_justify = True width_chars = 5 class ID2Name(Field): width_chars = 8 def real_get_text(self): idnum = getattr(self.dinode, self.dinode_member) try: return self.get_name(idnum)[0] except KeyError: return str(idnum) class Owner(ID2Name): dinode_member = 'i_uid' get_name = pwd.getpwuid class Group(ID2Name): dinode_member = 'i_gid' get_name = grp.getgrgid class Size(Field): dinode_member = 'i_size' width_chars = 15 class AllocSize(Field): width_chars = 15 def real_get_text(self): return str(self.dinode.i_clusters * self.dinode.fs.fs_clustersize) class Timestamp(Field): width_chars = 12 # Ported from GNU coreutils ls time_formats = ('%b %e %Y', '%b %e %H:%M') def real_get_text(self): when = self.dinode.i_mtime when_local = time.localtime(when) current_time = long(time.time()) six_months_ago = current_time - 31556952 / 2 recent = (six_months_ago <= when and when < current_time) fmt = self.time_formats[recent] return time.strftime(fmt, when_local) class Name(Field): def real_get_text(self): return self.dentry.name fields = (Mode, Links, Owner, Group, Size, AllocSize, Timestamp) def main(): import sys fs = ocfs2.Filesystem(sys.argv[1]) dentries = [] def walk(dentry, offset, blocksize): dentries.append(dentry) fs.dir_iterate(walk) try: dentry = dentries[int(sys.argv[2])] except (IndexError, ValueError): try: dentry = dentries[0] except IndexError: return dinode = fs.read_cached_inode(dentry.inode) for field_type in fields: field = field_type(dentry, dinode) print '%s: %s' % (field.label, field.text) if __name__ == '__main__': main() ocfs2-tools-ocfs2-tools-1.8.6/ocfs2console/ocfs2interface/menu.py000066400000000000000000000116341347147137200247000ustar00rootroot00000000000000# OCFS2Console - GUI frontend for OCFS2 management and debugging # Copyright (C) 2002, 2005 Oracle. All rights reserved. # # 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. import gtk from guiutil import make_callback from fsck import fsck_ok from pushconfig import pushconfig_ok UNMOUNTED_ONLY = 42 NEED_SELECTION = 43 try: stock_about = gtk.STOCK_ABOUT except AttributeError: stock_about = '' file_menu_data = ( ('/_File', None, None, 0, ''), ('/File/_Quit', None, 'cleanup', 0, '', gtk.STOCK_QUIT) ) help_menu_data = ( ('/_Help', None, None, 0, ''), ('/Help/_About', None, 'about', 0, '', stock_about) ) if pushconfig_ok: cluster_menu_push_config_data = ( ('/Cluster/_Propagate Configuration...', None, 'push_config'), ) else: cluster_menu_push_config_data = () cluster_menu_head_data = ( ('/_Cluster', None, None, 0, ''), ('/Cluster/_Configure Nodes...', None, 'node_config') ) cluster_menu_data = cluster_menu_head_data + cluster_menu_push_config_data if fsck_ok: task_menu_fsck_data = ( ('/Tasks/Chec_k...', 'K', 'check', 'refresh', NEED_SELECTION), ('/Tasks/_Repair...', 'R', 'repair', 'refresh', UNMOUNTED_ONLY), ('/Tasks/---', None, None, 0, '') ) else: task_menu_fsck_data = () task_menu_head_data = ( ('/_Tasks', None, None, 0, ''), ('/Tasks/_Format...', 'F', 'format', 'refresh'), ('/Tasks/---', None, None, 0, '') ) task_menu_tail_data = ( ('/Tasks/Change _Label...', None, 'relabel', 'refresh', UNMOUNTED_ONLY), ('/Tasks/_Edit Node Slot Count...', None, 'slot_num', 'refresh', UNMOUNTED_ONLY), ) task_menu_data = task_menu_head_data + task_menu_fsck_data + task_menu_tail_data menu_data = file_menu_data + cluster_menu_data + task_menu_data + help_menu_data class Menu: def __init__(self, window): self.window = window self.items = [] for data in menu_data: item = list(data) data_list = [None] * 6 data_list[0:len(data)] = data path, accel, callback, sub_callback, item_type, extra = data_list if self.is_special(item_type): del item[4:] if callback: if sub_callback: del item[3:] item[2] = make_callback(window, callback, sub_callback) self.items.append(tuple(item)) def get_widgets(self): accel_group = gtk.AccelGroup() self.window.add_accel_group(accel_group) item_factory = gtk.ItemFactory(gtk.MenuBar, '
', accel_group) item_factory.create_items(self.items) menubar = item_factory.get_widget('
') self.unmounted_widgets = [] self.need_sel_widgets = [] for data in menu_data: if len(data) >= 5 and self.is_special(data[4]): path = data[0].replace('_', '') menuitem = item_factory.get_item('
%s' % path) widget_list = self.get_special_list(data[4]) widget_list.append(menuitem) self.window.item_factory = item_factory return menubar, self.need_sel_widgets, self.unmounted_widgets def is_special(self, data): return data in (UNMOUNTED_ONLY, NEED_SELECTION) def get_special_list(self, data): if data == UNMOUNTED_ONLY: return self.unmounted_widgets elif data == NEED_SELECTION: return self.need_sel_widgets def main(): def dummy(*args): gtk.main_quit() window = gtk.Window() window.connect('delete_event', dummy) window.refresh = dummy for i in menu_data: if i[2]: setattr(window, i[2], dummy) menu = Menu(window) vbox = gtk.VBox() window.add(vbox) vbox.add(menu.get_widgets()[0]) window.show_all() gtk.main() if __name__ == '__main__': main() ocfs2-tools-ocfs2-tools-1.8.6/ocfs2console/ocfs2interface/mount.py000066400000000000000000000117731347147137200251020ustar00rootroot00000000000000# OCFS2Console - GUI frontend for OCFS2 management and debugging # Copyright (C) 2002, 2005 Oracle. All rights reserved. # # 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. import gtk import ocfs2 from guiutil import set_props, error_box from fstab import FSTab from process import Process def mount(parent, device): mountpoint, options = query_mount(parent, device) if not mountpoint: return None command = ('mount', '-t', 'ocfs2', device, mountpoint) if options: command = list(command) command[1:1] = ('-o', options) p = Process(command, 'Mount', 'Mounting...', parent, spin_now=True) success, output, killed = p.reap() if not success: if killed: error_box(parent, 'mount died unexpectedly! Your system is ' 'probably in an inconsistent state. You ' 'should reboot at the earliest opportunity') else: error_box(parent, '%s: Could not mount %s' % (output, device)) return None else: return mountpoint def unmount(parent, device, mountpoint): command = ('umount', mountpoint) p = Process(command, 'Unmount', 'Unmounting...', parent) success, output, killed = p.reap() if not success: if killed: error_box(parent, 'umount died unexpectedly! Your system is ' 'probably in an inconsistent state. You ' 'should reboot at the earliest opportunity') else: error_box(parent, '%s: Could not unmount %s mounted on %s' % (output, device, mountpoint)) def query_mount(parent, device): default_mountpoint, default_options = get_defaults(device) dialog = gtk.Dialog(parent=parent, flags=gtk.DIALOG_DESTROY_WITH_PARENT, buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OK, gtk.RESPONSE_OK)) table = gtk.Table(rows=2, columns=2) set_props(table, row_spacing=6, column_spacing=6, border_width=6, parent=dialog.vbox) def text_changed(entry): text = entry.get_text() valid = len(text) > 1 and text.startswith('/') dialog.set_response_sensitive(gtk.RESPONSE_OK, valid) mountpoint = gtk.Entry() mountpoint.connect('changed', text_changed) mountpoint.set_text(default_mountpoint) text_changed(mountpoint) options = gtk.Entry() options.set_text(default_options) row = 0 for prompt, entry in (('_Mountpoint', mountpoint), ('O_ptions', options)): label = gtk.Label() label.set_text_with_mnemonic(prompt + ':') set_props(label, xalign=0.0) table.attach(label, 0, 1, row, row + 1) entry.set_activates_default(True) label.set_mnemonic_widget(entry) table.attach(entry, 1, 2, row, row + 1) row = row + 1 dialog.show_all() if dialog.run() == gtk.RESPONSE_OK: mount_params = mountpoint.get_text(), options.get_text() else: mount_params = None, None dialog.destroy() return mount_params def get_defaults(device): label, uuid = get_ocfs2_id(device) fstab = FSTab() entry = fstab.get(device=device, label=label, uuid=uuid) if entry and entry.vfstype == 'ocfs2': return entry.mountpoint, entry.options else: return '', '' def get_ocfs2_id(device): try: fs = ocfs2.Filesystem(device) super = fs.fs_super label = super.s_label uuid = super.uuid_unparsed except ocfs2.error: label = uuid = None return (label, uuid) def main(): import sys device = sys.argv[1] def dummy(*args): gtk.main_quit() window = gtk.Window() window.connect('delete-event', dummy) vbbox = gtk.VButtonBox() window.add(vbbox) window.mountpoint = None def test_mount(b): window.mountpoint = mount(window, device) button = gtk.Button('Mount') button.connect('clicked', test_mount) vbbox.add(button) def test_unmount(b): unmount(window, device, window.mountpoint) button = gtk.Button('Unmount') button.connect('clicked', test_unmount) vbbox.add(button) window.show_all() gtk.main() if __name__ == '__main__': main() ocfs2-tools-ocfs2-tools-1.8.6/ocfs2console/ocfs2interface/nodeconfig.py000066400000000000000000000422421347147137200260460ustar00rootroot00000000000000# OCFS2Console - GUI frontend for OCFS2 management and debugging # Copyright (C) 2002, 2005 Oracle. All rights reserved. # # 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. import os import gtk import gobject import pango import ocfs2 import o2cb import o2cb_ctl from guiutil import Dialog, set_props, error_box from ipwidget import IPEditor, IPMissing, IPError DEFAULT_CLUSTER_NAME = 'ocfs2' PORT_DEFAULT = 7777 PORT_MINIMUM = 1000 PORT_MAXIMUM = 65534 ( COLUMN_NEW_NODE, COLUMN_NAME, COLUMN_NODE, COLUMN_IP_ADDRESS, COLUMN_IP_PORT ) = range(5) class Field: def __init__(self, name, column, title, widget_type, field_type): self.name = name self.column = column self.title = title self.widget_type = widget_type self.type = field_type fields = ( Field('new_node', COLUMN_NEW_NODE, 'Active', None, bool), Field('name', COLUMN_NAME, 'Name', gtk.Entry, str), Field('node', COLUMN_NODE, 'Node', None, int), Field('ip_address', COLUMN_IP_ADDRESS, 'IP Address', IPEditor, str), Field('ip_port', COLUMN_IP_PORT, 'IP Port', gtk.SpinButton, str), ) # Hate your ancient distros shipping old pygtks typemap = { bool: gobject.TYPE_BOOLEAN } class ConfigError(Exception): pass class ClusterConfig(Dialog): def __init__(self, cluster_name=DEFAULT_CLUSTER_NAME, parent=None): self.cluster_name = cluster_name Dialog.__init__(self, parent=parent, title='Node Configuration', buttons=(gtk.STOCK_APPLY, gtk.RESPONSE_APPLY, gtk.STOCK_CLOSE, gtk.RESPONSE_CLOSE)) if parent is None: self.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_NORMAL) frame = gtk.Frame() frame.set_shadow_type(gtk.SHADOW_NONE) self.vbox.add(frame) label = gtk.Label() label.set_text_with_mnemonic('_Nodes:') frame.set_label_widget(label) hbox = gtk.HBox(spacing=4) hbox.set_border_width(4) frame.add(hbox) scrl_win = gtk.ScrolledWindow() scrl_win.set_policy(hscrollbar_policy=gtk.POLICY_AUTOMATIC, vscrollbar_policy=gtk.POLICY_AUTOMATIC) hbox.pack_start(scrl_win) self.setup_treeview() label.set_mnemonic_widget(self.tv) scrl_win.add(self.tv) vbbox = gtk.VButtonBox() set_props(vbbox, layout_style=gtk.BUTTONBOX_START, spacing=5, border_width=5) hbox.pack_end(vbbox, expand=False, fill=False) self.add_button = gtk.Button(stock=gtk.STOCK_ADD) self.add_button.connect('clicked', self.add_node) vbbox.add(self.add_button) try: edit_construct_args = {'stock': gtk.STOCK_EDIT} except AttributeError: edit_construct_args = {'label': '_Edit'} self.edit_button = gtk.Button(**edit_construct_args) self.edit_button.connect('clicked', self.edit_node) vbbox.add(self.edit_button) self.remove_button = gtk.Button(stock=gtk.STOCK_REMOVE) self.remove_button.connect('clicked', self.remove_node) vbbox.add(self.remove_button) self.can_edit(False) self.can_apply(False) self.sel = self.tv.get_selection() self.sel.connect('changed', self.on_select) self.tv.connect('row_activated', self.on_row_activated) self.load_cluster_state() def load_cluster_state(self): try: nodes = o2cb_ctl.get_cluster_nodes(self.cluster_name, self) except o2cb_ctl.CtlError, e: raise ConfError, str(e) store = gtk.ListStore(*[typemap.get(f.type, f.type) for f in fields]) node_data = list((None,) * len(fields)) for node in nodes: for field in fields: val = node.get(field.name, False) node_data[field.column] = field.type(val) store.append(node_data) def node_compare(store, a, b): n1 = store[a][COLUMN_NODE] n2 = store[b][COLUMN_NODE] if n1 < 0 and n2 >= 0: return 1 elif n1 >= 0 and n2 < 0: return -1 else: return cmp(abs(n1), abs(n2)) store.set_sort_func(COLUMN_NODE, node_compare) store.set_sort_column_id(COLUMN_NODE, gtk.SORT_ASCENDING) self.new_nodes = 0 self.store = store self.tv.set_model(store) if len(store): self.sel.select_iter(self.store.get_iter_first()) def setup_treeview(self): self.tv = gtk.TreeView() self.tv.set_size_request(350, 200) for field in fields: if field.column == COLUMN_NEW_NODE: self.tv.insert_column_with_data_func(-1, field.title, gtk.CellRendererPixbuf(), self.active_set_func) elif field.column == COLUMN_NODE: self.tv.insert_column_with_data_func(-1, field.title, gtk.CellRendererText(), self.node_set_func) else: cell_renderer = gtk.CellRendererText() cell_renderer.set_property('style', pango.STYLE_ITALIC) self.tv.insert_column_with_attributes(-1, field.title, cell_renderer, text=field.column, style_set=COLUMN_NEW_NODE) def active_set_func(self, tree_column, cell, model, iter): if model[iter][COLUMN_NEW_NODE]: stock_id = None else: stock_id = gtk.STOCK_EXECUTE cell.set_property('stock_id', stock_id) def node_set_func(self, tree_column, cell, model, iter): if model[iter][COLUMN_NEW_NODE]: text = '' else: text = str(model[iter][COLUMN_NODE]) cell.set_property('text', text) def on_select(self, sel): store, iter = sel.get_selected() if iter: editable = store[iter][COLUMN_NEW_NODE] else: editable = False self.can_edit(editable) def on_row_activated(self, tv, path, col): if self.node_editable: self.edit_node(None) def can_edit(self, state): self.node_editable = state self.edit_button.set_sensitive(state) self.remove_button.set_sensitive(state) def can_apply(self, state): self.set_response_sensitive(gtk.RESPONSE_APPLY, state) def add_node(self, b): if len(self.store) == o2cb.O2NM_MAX_NODES: error_box(self, 'Cannot have more than %d nodes in a cluster' % o2cb.O2NM_MAX_NODES) return node_attrs = self.node_query(title='Add Node') if node_attrs is None: return self.new_nodes += 1 name, ip_addr, ip_port = node_attrs iter = self.store.append((True, name, -self.new_nodes, ip_addr, ip_port)) self.sel.select_iter(iter) self.tv.scroll_to_cell(self.store.get_path(iter)) self.can_apply(True) def edit_node(self, b): store, iter = self.sel.get_selected() attrs = store[iter] node_attrs = self.node_query(title='Edit Node', defaults=attrs, editing=True) if node_attrs is None: return (attrs[COLUMN_NAME], attrs[COLUMN_IP_ADDRESS], attrs[COLUMN_IP_PORT]) = node_attrs def remove_node(self, b): store, iter = self.sel.get_selected() msg = ('Are you sure you want to delete node %s?' % store[iter][COLUMN_NAME]) ask = gtk.MessageDialog(parent=self, flags=gtk.DIALOG_DESTROY_WITH_PARENT, type=gtk.MESSAGE_QUESTION, buttons=gtk.BUTTONS_YES_NO, message_format=msg) response = ask.run() ask.destroy() if response == gtk.RESPONSE_YES: del store[iter] self.new_nodes -= 1 if self.new_nodes == 0: self.can_apply(False) self.sel.select_iter(self.store.get_iter_first()) def new_node_attrs(self): attrs = [] for row in self.store: if row[COLUMN_NEW_NODE]: attrs.append((row[COLUMN_NAME], row[COLUMN_IP_ADDRESS], row[COLUMN_IP_PORT])) return attrs def apply_changes(self): success = False for name, ip_address, ip_port in self.new_node_attrs(): success, output, k = o2cb_ctl.add_node(name, self.cluster_name, ip_address, ip_port, self) if not success: error_box(self, '%s\nCould not add node %s' % (output, name)) break self.load_cluster_state() return success def node_query(self, title='Node Attributes', defaults=None, editing=False): existing_names = {} existing_ip_addrs = {} for row in self.store: name = row[COLUMN_NAME] ip_addr = row[COLUMN_IP_ADDRESS] if not (editing and name == defaults[COLUMN_NAME]): existing_names[name] = 1 if not (editing and ip_addr == defaults[COLUMN_IP_ADDRESS]): existing_ip_addrs[ip_addr] = name dialog = Dialog(parent=self, title=title, buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OK, gtk.RESPONSE_OK)) dialog.set_alternative_button_order((gtk.RESPONSE_OK, gtk.RESPONSE_CANCEL)) dialog.set_default_response(gtk.RESPONSE_OK) table = gtk.Table(rows=4, columns=2) set_props(table, row_spacing=4, column_spacing=4, border_width=4, parent=dialog.vbox) widgets = [] for row, field in enumerate(fields): if field.widget_type is None: widgets.append(None) continue label = gtk.Label(field.title + ':') set_props(label, xalign=1.0) table.attach(label, 0, 1, row, row + 1) widget = field.widget_type() table.attach(widget, 1, 2, row, row + 1) if field.column == COLUMN_NAME: widget.set_max_length(o2cb.O2NM_MAX_NAME_LEN) elif field.column == COLUMN_IP_PORT: widget.set_numeric(True) adjustment = gtk.Adjustment(PORT_DEFAULT, PORT_MINIMUM, PORT_MAXIMUM, 1, 100) widget.set_adjustment(adjustment) widget.set_value(PORT_DEFAULT) widgets.append(widget) if defaults: for w, d in zip(widgets, defaults): if w and d: w.set_text(d) dialog.show_all() while 1: if dialog.run() != gtk.RESPONSE_OK: dialog.destroy() return None ip_port = widgets[COLUMN_IP_PORT].get_text() name = widgets[COLUMN_NAME].get_text() if not name: error_box(dialog, 'Node name not specified') continue try: ip_addr = widgets[COLUMN_IP_ADDRESS].get_text() except (IPMissing, IPError), msg: error_box(dialog, msg[0]) continue if name in existing_names: error_box(dialog, 'Node %s already exists in the configuration' % name) elif ip_addr in existing_ip_addrs: error_box(dialog, 'IP %s is already assigned to node %s' % (ip_addr, existing_ip_addrs[ip_addr])) else: break dialog.destroy() return name, ip_addr, ip_port def run(self): self.show_all() while 1: if Dialog.run(self) == gtk.RESPONSE_APPLY: self.apply_changes() elif len(self.new_node_attrs()): msg = ('New nodes have been created, but they have not been ' 'applied to the cluster configuration. Do you want to ' 'apply the changes now?') ask = gtk.MessageDialog(parent=self, flags=gtk.DIALOG_DESTROY_WITH_PARENT, type=gtk.MESSAGE_QUESTION, buttons=gtk.BUTTONS_YES_NO, message_format=msg) if ask.run() == gtk.RESPONSE_NO: break elif self.apply_changes(): break else: break def node_config(parent=None): success, output, k = o2cb_ctl.init_status(None, parent) if not success: msg = ('Could not query the state of the cluster stack.' 'This must be resolved before any OCFS2 filesystem' 'can be mounted.') info = gtk.MessageDialog(parent=parent, flags=gtk.DIALOG_DESTROY_WITH_PARENT, type=gtk.MESSAGE_WARNING, buttons=gtk.BUTTONS_CLOSE, message_format=msg) info.run() info.destroy() return if (output.find('Not loaded') != -1) or (output.find('Not mounted') != -1): success, output, k = o2cb_ctl.init_load(parent) if success: msg_type = gtk.MESSAGE_INFO msg = ('The cluster stack has been started. It needs to be ' 'running for any clustering functionality to happen. ' 'Please run "%s enable" to have it started upon bootup.' % o2cb_ctl.O2CB_INIT) else: msg_type = gtk.MESSAGE_WARNING msg = ('Could not start cluster stack. This must be resolved ' 'before any OCFS2 filesystem can be mounted') info = gtk.MessageDialog(parent=parent, flags=gtk.DIALOG_DESTROY_WITH_PARENT, type=msg_type, buttons=gtk.BUTTONS_CLOSE, message_format=msg) info.run() info.destroy() if not success: return try: cluster_name = o2cb_ctl.get_active_cluster_name(parent) except o2cb_ctl.CtlError, e: error_box(parent, str(e)) return try: conf = ClusterConfig(cluster_name, parent) except ConfigError, e: error_box(parent, '%s: Could not query cluster configuration' % str(e)) return conf.run() conf.destroy() success, output, k = o2cb_ctl.init_status(cluster_name, parent) if not success: msg = ('Could not query the state of the cluster.' 'This must be resolved before any OCFS2 filesystem' 'can be mounted.') info = gtk.MessageDialog(parent=parent, flags=gtk.DIALOG_DESTROY_WITH_PARENT, type=gtk.MESSAGE_WARNING, buttons=gtk.BUTTONS_CLOSE, message_format=msg) info.run() info.destroy() return if output.find('Online') == -1: success, output, k = o2cb_ctl.init_online(cluster_name, parent) if not success: msg = ('Could not bring OCFS2 cluster online. This must be ' 'resolved before any OCFS2 filesystem can be mounted') info = gtk.MessageDialog(parent=parent, flags=gtk.DIALOG_DESTROY_WITH_PARENT, type=gtk.MESSAGE_WARNING, buttons=gtk.BUTTONS_CLOSE, message_format=msg) info.run() info.destroy() def main(): from about import process_gui_args process_gui_args() node_config() if __name__ == '__main__': main() ocfs2-tools-ocfs2-tools-1.8.6/ocfs2console/ocfs2interface/o2cb_ctl.py000066400000000000000000000114661347147137200254260ustar00rootroot00000000000000# OCFS2Console - GUI frontend for OCFS2 management and debugging # Copyright (C) 2002, 2005 Oracle. All rights reserved. # # 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. from cStringIO import StringIO from process import Process from guiutil import error_box DEFAULT_CLUSTER_NAME = 'ocfs2' O2CB_INIT = '/etc/init.d/o2cb' O2CB_CTL = 'o2cb_ctl' class CtlError(Exception): pass class O2CBProcess(Process): def __init__(self, args, desc, parent=None): if isinstance(args, basestring): command = '%s %s' % (self.program, args) else: command = (self.program,) + tuple(args) Process.__init__(self, command, self.title, desc, parent) class O2CBCtl(O2CBProcess): program = O2CB_CTL title = 'Cluster Control' class O2CBInit(O2CBProcess): program = O2CB_INIT title = 'Cluster Stack' def init_load(parent=None): args = ('load',) o2cb_init = O2CBInit(args, 'Starting cluster stack...', parent) return o2cb_init.reap() def init_online(cluster_name, parent=None): desc = 'Starting %s cluster...' % cluster_name args = ('online', cluster_name) o2cb_init = O2CBInit(args, desc, parent) return o2cb_init.reap() def init_status(cluster_name=None, parent=None): if cluster_name: desc = 'Querying status of %s...' % cluster_name args = ('status', cluster_name) else: desc = 'Querying cluster software status...' args = ('status',) o2cb_init = O2CBInit(args, desc, parent) return o2cb_init.reap() def query_clusters(parent=None): args = '-I -t cluster -o' o2cb_ctl = O2CBCtl(args, 'Querying cluster...', parent) return o2cb_ctl.reap() def query_nodes(parent=None): args = '-I -t node -o' o2cb_ctl = O2CBCtl(args, 'Querying nodes...', parent) return o2cb_ctl.reap() def add_node(name, cluster_name, ip_address, ip_port, parent=None): desc = 'Adding node %s...' % name args = ('-C', '-n', name, '-t', 'node', '-a', 'cluster=%s' % cluster_name, '-a', 'ip_address=%s' % ip_address, '-a', 'ip_port=%s' % ip_port, '-i') o2cb_ctl = O2CBCtl(args, desc, parent) return o2cb_ctl.reap() def get_active_cluster_name(parent=None): cluster_name = None success, output, k = query_clusters(parent) if success: names = [] buffer = StringIO(output) for line in buffer: if line.startswith('#'): continue try: name, rest = line.split(':', 1) except ValueError: continue names.append(name) if DEFAULT_CLUSTER_NAME in names: cluster_name = DEFAULT_CLUSTER_NAME elif len(names): cluster_name = names[0] if cluster_name is None: args = '-C -n %s -t cluster -i' % DEFAULT_CLUSTER_NAME o2cb_ctl = O2CBCtl(args, 'Creating cluster...', parent) success, output, k = o2cb_ctl.reap() if success: cluster_name = DEFAULT_CLUSTER_NAME else: errmsg = '%s\nCould not create cluster %s' % (output, DEFAULT_CLUSTER_NAME) raise CtlError, errmsg return cluster_name def get_cluster_nodes(cluster_name, parent=None): success, output, k = query_nodes(parent) if not success: raise CtlError, output nodes = [] buffer = StringIO(output) for line in buffer: if line.startswith('#'): continue try: name, cluster, node, ip_address, ip_port, state = line.split(':') except ValueError: continue if cluster == cluster_name: info = {} symtab = locals() for sym in 'name', 'node', 'ip_address', 'ip_port': info[sym] = symtab[sym] nodes.append(info) return nodes def main(): success, output, k = init_load() if success: print 'Success:\n' + output else: print 'Failed:\n' + output success, output, k = query_nodes() if success: print 'Success:\n' + output else: print 'Failed:\n' + output if __name__ == '__main__': main() ocfs2-tools-ocfs2-tools-1.8.6/ocfs2console/ocfs2interface/o2cbmodule.c000066400000000000000000000312331347147137200255560ustar00rootroot00000000000000/* * o2cbmodule.c * * O2CB python binding. * * Copyright (C) 2005 Oracle. All rights reserved. * * Author: Manish Singh * * 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 recieved a copy of the GNU General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. */ #include #include #include "o2cb/o2cb.h" /* FIXME: right now we don't wrap the init function */ typedef struct { PyObject_HEAD PyObject *name; } O2CBObject; #define O2CB_OBJECT_NAME(obj) (((O2CBObject *) (obj))->name) typedef O2CBObject Cluster; typedef struct { O2CBObject object; Cluster *cluster; } Node; static PyObject *o2cb_error; #define CHECK_ERROR(call) do { \ ret = call; \ if (ret) \ { \ PyErr_SetString (o2cb_error, error_message (ret)); \ return NULL; \ } \ } while (0) static void o2cb_object_dealloc (O2CBObject *self) { Py_XDECREF (self->name); PyObject_DEL (self); } static PyObject * o2cb_object_repr (O2CBObject *self, const char *type_name) { return PyString_FromFormat ("", type_name, PyString_AS_STRING (self->name)); } static PyMemberDef o2cb_object_members[] = { {"name", T_OBJECT, offsetof (O2CBObject, name), RO}, {0} }; static PyObject * o2cb_object_new (O2CBObject *self, const char *name) { if (self == NULL) return NULL; self->name = PyString_FromString (name); if (self->name == NULL) { PyObject_DEL (self); return NULL; } return (PyObject *) self; } static PyObject * node_number (Node *self, void *closure) { errcode_t ret; uint16_t node_num; CHECK_ERROR (o2cb_get_node_num (PyString_AS_STRING (self->cluster->name), PyString_AS_STRING (O2CB_OBJECT_NAME (self)), &node_num)); return PyInt_FromLong (node_num); } static PyGetSetDef node_getsets[] = { {"number", (getter)node_number, (setter)0}, {NULL} }; static void node_dealloc (Node *self) { Py_XDECREF (self->cluster); o2cb_object_dealloc ((O2CBObject *) self); } static PyObject * node_repr (Node *self) { return o2cb_object_repr ((O2CBObject *) self, "Node"); } static PyTypeObject Node_Type = { PyObject_HEAD_INIT(NULL) 0, /* ob_size */ "o2cb.Node", /* tp_name */ sizeof(Node), /* tp_basicsize */ 0, /* tp_itemsize */ (destructor)node_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_compare */ (reprfunc)node_repr, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT, /* tp_flags */ NULL, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ 0, /* tp_methods */ o2cb_object_members, /* tp_members */ node_getsets, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ 0, /* tp_init */ 0, /* tp_alloc */ 0, /* tp_new */ }; static PyObject * node_new (Cluster *cluster, const char *name) { Node *self; self = PyObject_NEW (Node, &Node_Type); self = (Node *) o2cb_object_new ((O2CBObject *) self, name); if (self) { Py_INCREF (cluster); self->cluster = cluster; } return (PyObject *) self; } static PyObject * cluster_add_node (Cluster *self, PyObject *args, PyObject *kwargs) { /* XXX: We could be smarter about type conversions here */ const char *node_name, *node_num, *ip_address, *ip_port, *local; errcode_t ret; static char *kwlist[] = { "node_name", "node_num", "ip_address", "ip_port", "local", NULL }; if (!PyArg_ParseTupleAndKeywords (args, kwargs, "sssss:add_node", kwlist, &node_name, &node_num, &ip_address, &ip_port, &local)) return NULL; CHECK_ERROR (o2cb_add_node (PyString_AS_STRING (self->name), node_name, node_num, ip_address, ip_port, local)); return node_new (self, node_name); } #if 0 #include "o2cb_abi.h" static PyObject * cluster_create_heartbeat_region_disk (Cluster *self, PyObject *args, PyObject *kwargs) { const char *region_name, *device_name; int block_bytes; uint64_t start_block, blocks; errcode_t ret; static char *kwlist[] = { "region_name", "device_name", "block_bytes", "start_block", "blocks", NULL }; if (!PyArg_ParseTupleAndKeywords (args, kwargs, "ssiKK:create_heartbeat_region_disk", kwlist, ®ion_name, &device_name, &block_bytes, &start_block, &blocks)) return NULL; CHECK_ERROR (o2cb_create_heartbeat_region_disk (PyString_AS_STRING (self->name), region_name, device_name, block_bytes, start_block, blocks)); Py_INCREF (Py_None); return Py_None; } static PyObject * cluster_remove_heartbeat_region_disk (Cluster *self, PyObject *args, PyObject *kwargs) { const char *region_name; errcode_t ret; static char *kwlist[] = { "region_name", NULL }; if (!PyArg_ParseTupleAndKeywords (args, kwargs, "s:remove_heartbeat_region_disk", kwlist, ®ion_name)) return NULL; CHECK_ERROR (o2cb_remove_heartbeat_region_disk (PyString_AS_STRING (self->name), region_name)); Py_INCREF (Py_None); return Py_None; } #endif static PyMethodDef cluster_methods[] = { {"add_node", (PyCFunction)cluster_add_node, METH_KEYWORDS}, // {"create_heartbeat_region_disk", (PyCFunction)cluster_create_heartbeat_region_disk, METH_KEYWORDS}, // {"remove_heartbeat_region_disk", (PyCFunction)cluster_remove_heartbeat_region_disk, METH_KEYWORDS}, {NULL, NULL} }; static PyObject * cluster_nodes (Cluster *self, void *closure) { char **nodes, **name; errcode_t ret; PyObject *list, *node; int status; CHECK_ERROR (o2cb_list_nodes (PyString_AS_STRING (self->name), &nodes)); list = PyList_New (0); if (list == NULL) goto cleanup; for (name = nodes; *name != NULL; name++) { node = node_new (self, *name); if (node == NULL) goto err; status = PyList_Append (list, node); Py_DECREF (node); if (status) goto err; } goto cleanup; err: Py_DECREF (list); cleanup: o2cb_free_nodes_list (nodes); return list; } static PyGetSetDef cluster_getsets[] = { {"nodes", (getter)cluster_nodes, (setter)0}, {NULL} }; static PyObject * cluster_repr (Cluster *self) { return o2cb_object_repr (self, "Cluster"); } static int cluster_init (Cluster *self, PyObject *args, PyObject *kwargs) { errcode_t ret; const char *name; static char *kwlist[] = { "name", NULL }; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s:o2cb.Cluster.__init__", kwlist, &name)) return -1; self->name = PyString_FromString (name); if (self->name == NULL) return -1; ret = o2cb_create_cluster (name); if (ret) { Py_DECREF (self->name); PyErr_SetString (o2cb_error, error_message (ret)); return -1; } return 0; } static PyTypeObject Cluster_Type = { PyObject_HEAD_INIT(NULL) 0, /* ob_size */ "o2cb.Cluster", /* tp_name */ sizeof(Cluster), /* tp_basicsize */ 0, /* tp_itemsize */ (destructor)o2cb_object_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_compare */ (reprfunc)cluster_repr, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT, /* tp_flags */ NULL, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ cluster_methods, /* tp_methods */ o2cb_object_members, /* tp_members */ cluster_getsets, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ (initproc)cluster_init, /* tp_init */ 0, /* tp_alloc */ 0, /* tp_new */ }; static PyObject * cluster_new (const char *name) { Cluster *self; self = PyObject_NEW (Cluster, &Cluster_Type); return o2cb_object_new (self, name); } static PyObject * list_clusters (PyObject *self) { char **clusters, **name; errcode_t ret; PyObject *list, *cluster; int status; CHECK_ERROR (o2cb_list_clusters (&clusters)); list = PyList_New (0); if (list == NULL) goto cleanup; for (name = clusters; *name != NULL; name++) { cluster = cluster_new (*name); if (cluster == NULL) goto err; status = PyList_Append (list, cluster); Py_DECREF (cluster); if (status) goto err; } goto cleanup; err: Py_DECREF (list); cleanup: o2cb_free_cluster_list (clusters); return list; } #if 0 static PyObject * create_heartbeat_region_disk (PyObject *self, PyObject *args, PyObject *kwargs) { const char *cluster_name, *region_name, *device_name; int block_bytes; uint64_t start_block, blocks; errcode_t ret; static char *kwlist[] = { "cluster_name", "region_name", "device_name", "block_bytes", "start_block", "blocks", NULL }; if (!PyArg_ParseTupleAndKeywords (args, kwargs, "zssiKK:create_heartbeat_region_disk", kwlist, &cluster_name, ®ion_name, &device_name, &block_bytes, &start_block, &blocks)) return NULL; CHECK_ERROR (o2cb_create_heartbeat_region_disk (cluster_name, region_name, device_name, block_bytes, start_block, blocks)); Py_INCREF (Py_None); return Py_None; } static PyObject * remove_heartbeat_region_disk (PyObject *self, PyObject *args, PyObject *kwargs) { const char *cluster_name, *region_name; errcode_t ret; static char *kwlist[] = { "cluster_name", "region_name", NULL }; if (!PyArg_ParseTupleAndKeywords (args, kwargs, "zs:remove_heartbeat_region_disk", kwlist, &cluster_name, ®ion_name)) return NULL; CHECK_ERROR (o2cb_remove_heartbeat_region_disk (cluster_name, region_name)); Py_INCREF (Py_None); return Py_None; } #endif static PyObject * get_hb_ctl_path (PyObject *self) { errcode_t ret; char hb_ctl_path[PATH_MAX]; CHECK_ERROR (o2cb_get_hb_ctl_path (hb_ctl_path, sizeof (hb_ctl_path))); return PyString_FromString (hb_ctl_path); } static PyMethodDef o2cb_methods[] = { {"list_clusters", (PyCFunction)list_clusters, METH_NOARGS}, // {"create_heartbeat_region_disk", (PyCFunction)create_heartbeat_region_disk, METH_KEYWORDS}, // {"remove_heartbeat_region_disk", (PyCFunction)remove_heartbeat_region_disk, METH_KEYWORDS}, {"get_hb_ctl_path", (PyCFunction)get_hb_ctl_path, METH_NOARGS}, {NULL, NULL} /* sentinel */ }; static void add_constants (PyObject *m) { #define ADD_INT_CONSTANT(name) \ PyModule_AddIntConstant (m, "O2NM_" #name, O2NM_ ## name) ADD_INT_CONSTANT (API_VERSION); ADD_INT_CONSTANT (MAX_NODES); ADD_INT_CONSTANT (INVALID_NODE_NUM); ADD_INT_CONSTANT (MAX_NAME_LEN); #undef ADD_INT_CONSTANT } void inito2cb (void) { PyObject *m; if (PyType_Ready (&Node_Type) < 0) return; Cluster_Type.tp_new = PyType_GenericNew; if (PyType_Ready (&Cluster_Type) < 0) return; initialize_o2cb_error_table (); m = Py_InitModule ("o2cb", o2cb_methods); o2cb_error = PyErr_NewException ("o2cb.error", PyExc_RuntimeError, NULL); if (o2cb_error) { Py_INCREF (o2cb_error); PyModule_AddObject (m, "error", o2cb_error); } Py_INCREF (&Node_Type); PyModule_AddObject (m, "Node", (PyObject *) &Node_Type); Py_INCREF (&Cluster_Type); PyModule_AddObject (m, "Cluster", (PyObject *) &Cluster_Type); add_constants (m); if (PyErr_Occurred ()) Py_FatalError ("can't initialize module o2cb"); } ocfs2-tools-ocfs2-tools-1.8.6/ocfs2console/ocfs2interface/ocfs2module.c000066400000000000000000000722111347147137200257460ustar00rootroot00000000000000/* * ocfs2module.c * * OCFS2 python binding. * * Copyright (C) 2004, 2005 Oracle. All rights reserved. * * Author: Manish Singh * * 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 recieved a copy of the GNU General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. */ #include #include #include #include #include "ocfs2/ocfs2.h" #define Filesystem_Check(op) PyObject_TypeCheck(op, &Filesystem_Type) #define DInode_Check(op) PyObject_TypeCheck(op, &DInode_Type) #define DirEntry_Check(op) PyObject_TypeCheck(op, &DirEntry_Type) #define SuperBlock_Check(op) PyObject_TypeCheck(op, &SuperBlock_Type) #define DirScanIter_Check(op) PyObject_TypeCheck(op, &DirScanIter_Type) typedef struct { PyObject_HEAD PyObject *device; ocfs2_filesys *fs; } Filesystem; typedef struct { PyObject_HEAD Filesystem *fs_obj; struct ocfs2_dinode dinode; } DInode; typedef struct { PyObject_HEAD Filesystem *fs_obj; struct ocfs2_dir_entry dentry; } DirEntry; typedef struct { PyObject_HEAD Filesystem *fs_obj; struct ocfs2_super_block super; } SuperBlock; typedef struct { PyObject_HEAD Filesystem *fs_obj; ocfs2_dir_scan *scan; } DirScanIter; static PyObject *ocfs2_error; #define CHECK_ERROR(call) do { \ ret = call; \ if (ret) \ { \ PyErr_SetString (ocfs2_error, error_message (ret)); \ return NULL; \ } \ } while (0) #define DINODE_U64_GETTER(name) \ static PyObject * \ dinode_ ## name (DInode *self, void *closure) \ { \ return PyLong_FromUnsignedLongLong (self->dinode.i_ ## name); \ } #define DINODE_GETTER_ENTRY(name) \ {"i_" #name, (getter)dinode_ ## name, (setter)0} DINODE_U64_GETTER(size) DINODE_U64_GETTER(atime) DINODE_U64_GETTER(ctime) DINODE_U64_GETTER(mtime) DINODE_U64_GETTER(dtime) DINODE_U64_GETTER(blkno) DINODE_U64_GETTER(last_eb_blk) static PyObject * dinode_rdev (DInode *self, void *closure) { return PyLong_FromUnsignedLongLong (self->dinode.id1.dev1.i_rdev); } static PyObject * dinode_jflags (DInode *self, void *closure) { return PyInt_FromLong (self->dinode.id1.journal1.ij_flags); } static PyGetSetDef dinode_getsets[] = { DINODE_GETTER_ENTRY (size), DINODE_GETTER_ENTRY (atime), DINODE_GETTER_ENTRY (ctime), DINODE_GETTER_ENTRY (mtime), DINODE_GETTER_ENTRY (dtime), DINODE_GETTER_ENTRY (blkno), DINODE_GETTER_ENTRY (last_eb_blk), DINODE_GETTER_ENTRY (rdev), {"ij_flags", (getter)dinode_jflags, (setter)0}, {NULL} }; #undef DINODE_U64_GETTER #undef DINODE_GETTER_ENTRY #define DINODE_STR_MEMBER(name) \ {"i_" #name, T_STRING_INPLACE, offsetof (DInode, dinode.i_ ## name), RO} #define DINODE_S16_MEMBER(name) \ {"i_" #name, T_SHORT, offsetof (DInode, dinode.i_ ## name), RO} #define DINODE_U16_MEMBER(name) \ {"i_" #name, T_USHORT, offsetof (DInode, dinode.i_ ## name), RO} #define DINODE_U32_MEMBER(name) \ {"i_" #name, T_UINT, offsetof (DInode, dinode.i_ ## name), RO} #define DINODE_BITMAP_MEMBER(name) \ {"i_" #name, T_UINT, offsetof (DInode, dinode.id1.bitmap1.i_ ## name), RO} static PyMemberDef dinode_members[] = { DINODE_STR_MEMBER (signature), DINODE_U32_MEMBER (generation), DINODE_S16_MEMBER (suballoc_slot), DINODE_U16_MEMBER (suballoc_bit), DINODE_U32_MEMBER (clusters), DINODE_U32_MEMBER (uid), DINODE_U32_MEMBER (gid), DINODE_U16_MEMBER (mode), DINODE_U16_MEMBER (links_count), DINODE_U32_MEMBER (flags), DINODE_U32_MEMBER (fs_generation), DINODE_BITMAP_MEMBER (used), DINODE_BITMAP_MEMBER (total), {"fs", T_OBJECT, offsetof (DInode, fs_obj), RO}, {0} }; #undef DINODE_STR_MEMBER #undef DINODE_S16_MEMBER #undef DINODE_U16_MEMBER #undef DINODE_U32_MEMBER #undef DINODE_BITMAP_MEMBER static void dinode_dealloc (DInode *self) { Py_DECREF (self->fs_obj); PyObject_DEL (self); } static PyObject * dinode_repr (DInode *self) { char blkno[32]; snprintf (blkno, sizeof (blkno), "%"PRIu64, (uint64_t)self->dinode.i_blkno); return PyString_FromFormat ("", blkno, PyString_AS_STRING (self->fs_obj->device)); } static PyTypeObject DInode_Type = { PyObject_HEAD_INIT(NULL) 0, /* ob_size */ "ocfs2.DInode", /* tp_name */ sizeof(DInode), /* tp_basicsize */ 0, /* tp_itemsize */ (destructor)dinode_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_compare */ (reprfunc)dinode_repr, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT, /* tp_flags */ NULL, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ 0, /* tp_methods */ dinode_members, /* tp_members */ dinode_getsets, /* tp_getset */ }; static PyObject * dinode_new (Filesystem *fs_obj, struct ocfs2_dinode *dinode) { DInode *self; self = PyObject_NEW (DInode, &DInode_Type); if (self == NULL) return NULL; Py_INCREF (fs_obj); self->fs_obj = fs_obj; memcpy (&self->dinode, dinode, sizeof (*dinode)); return (PyObject *) self; } static PyObject * dir_entry_name (DirEntry *self, void *closure) { return PyString_FromStringAndSize (self->dentry.name, self->dentry.name_len); } static PyObject * dir_entry_inode (DirEntry *self, void *closure) { return PyLong_FromUnsignedLongLong (self->dentry.inode); } static PyGetSetDef dir_entry_getsets[] = { {"name", (getter)dir_entry_name, (setter)0}, {"inode", (getter)dir_entry_inode, (setter)0}, {NULL} }; static PyMemberDef dir_entry_members[] = { {"rec_len", T_USHORT, offsetof (DirEntry, dentry.rec_len), RO}, {"file_type", T_UBYTE, offsetof (DirEntry, dentry.file_type), RO}, {"fs", T_OBJECT, offsetof (DirEntry, fs_obj), RO}, {0} }; static void dir_entry_dealloc (DirEntry *self) { PyObject_DEL (self); } static PyObject * dir_entry_repr (DirEntry *self) { char name[OCFS2_MAX_FILENAME_LEN + 1]; strncpy (name, self->dentry.name, self->dentry.name_len); name[self->dentry.name_len] = '\0'; return PyString_FromFormat ("", name, PyString_AS_STRING (self->fs_obj->device)); } static PyTypeObject DirEntry_Type = { PyObject_HEAD_INIT(NULL) 0, /* ob_size */ "ocfs2.DirEntry", /* tp_name */ sizeof(DirEntry), /* tp_basicsize */ 0, /* tp_itemsize */ (destructor)dir_entry_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_compare */ (reprfunc)dir_entry_repr, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT, /* tp_flags */ NULL, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ 0, /* tp_methods */ dir_entry_members, /* tp_members */ dir_entry_getsets, /* tp_getset */ }; static PyObject * dir_entry_new (Filesystem *fs_obj, struct ocfs2_dir_entry *dentry) { DirEntry *self; self = PyObject_NEW (DirEntry, &DirEntry_Type); if (self == NULL) return NULL; Py_INCREF (fs_obj); self->fs_obj = fs_obj; memcpy (&self->dentry, dentry, sizeof (*dentry)); return (PyObject *) self; } #define SUPER_U64_GETTER(name) \ static PyObject * \ super_ ## name (SuperBlock *self, void *closure) \ { \ return PyLong_FromUnsignedLongLong (self->super.s_ ## name); \ } #define SUPER_GETTER_ENTRY(name) \ {"s_" #name, (getter)super_ ## name, (setter)0} SUPER_U64_GETTER (lastcheck) SUPER_U64_GETTER (root_blkno) SUPER_U64_GETTER (system_dir_blkno) SUPER_U64_GETTER (first_cluster_group) static PyObject * super_uuid (SuperBlock *self, void *closure) { return PyString_FromStringAndSize ((char *)self->super.s_uuid, sizeof (self->super.s_uuid)); } static PyObject * super_uuid_unparsed (SuperBlock *self, void *closure) { char buf[40]; uuid_unparse (self->super.s_uuid, buf); return PyString_FromString (buf); } static PyGetSetDef super_getsets[] = { SUPER_GETTER_ENTRY (lastcheck), SUPER_GETTER_ENTRY (root_blkno), SUPER_GETTER_ENTRY (system_dir_blkno), SUPER_GETTER_ENTRY (first_cluster_group), SUPER_GETTER_ENTRY (uuid), {"uuid_unparsed", (getter)super_uuid_unparsed, (setter)0}, {NULL} }; #undef SUPER_U64_GETTER #undef SUPER_GETTER_ENTRY #define SUPER_U16_MEMBER(name) \ {"s_" #name, T_USHORT, offsetof (SuperBlock, super.s_ ## name), RO} #define SUPER_U32_MEMBER(name) \ {"s_" #name, T_UINT, offsetof (SuperBlock, super.s_ ## name), RO} static PyMemberDef super_members[] = { SUPER_U16_MEMBER (major_rev_level), SUPER_U16_MEMBER (minor_rev_level), SUPER_U16_MEMBER (mnt_count), SUPER_U16_MEMBER (state), SUPER_U16_MEMBER (errors), SUPER_U32_MEMBER (checkinterval), SUPER_U32_MEMBER (creator_os), SUPER_U32_MEMBER (feature_compat), SUPER_U32_MEMBER (feature_incompat), SUPER_U32_MEMBER (feature_ro_compat), SUPER_U32_MEMBER (blocksize_bits), SUPER_U32_MEMBER (clustersize_bits), SUPER_U16_MEMBER (max_slots), {"s_label", T_STRING_INPLACE, offsetof (SuperBlock, super.s_label), RO}, {"fs", T_OBJECT, offsetof (SuperBlock, fs_obj), RO}, {0} }; #undef SUPER_U16_MEMBER #undef SUPER_U32_MEMBER static void super_dealloc (SuperBlock *self) { Py_DECREF (self->fs_obj); PyObject_DEL (self); } static PyObject * super_repr (SuperBlock *self) { return PyString_FromFormat ("", PyString_AS_STRING (self->fs_obj->device)); } static PyTypeObject SuperBlock_Type = { PyObject_HEAD_INIT(NULL) 0, /* ob_size */ "ocfs2.SuperBlock", /* tp_name */ sizeof(SuperBlock), /* tp_basicsize */ 0, /* tp_itemsize */ (destructor)super_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_compare */ (reprfunc)super_repr, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT, /* tp_flags */ NULL, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ 0, /* tp_methods */ super_members, /* tp_members */ super_getsets, /* tp_getset */ }; static PyObject * super_new (Filesystem *fs_obj, struct ocfs2_dinode *fs_super) { SuperBlock *self; self = PyObject_NEW (SuperBlock, &SuperBlock_Type); if (self == NULL) return NULL; Py_INCREF (fs_obj); self->fs_obj = fs_obj; memcpy (&self->super, &fs_super->id2.i_super, sizeof (self->super)); return (PyObject *) self; } static void dir_scan_iter_dealloc (DirScanIter *self) { if (self->scan) ocfs2_close_dir_scan (self->scan); Py_XDECREF (self->fs_obj); PyObject_DEL (self); } static PyObject * dir_scan_iter_getiter (DirScanIter *self) { Py_INCREF (self); return (PyObject *) self; } static PyObject * dir_scan_iter_next (DirScanIter *self) { errcode_t ret; struct ocfs2_dir_entry dirent; if (self->scan == NULL) { PyErr_SetNone (PyExc_StopIteration); return NULL; } CHECK_ERROR (ocfs2_get_next_dir_entry (self->scan, &dirent)); if (dirent.rec_len == 0) { ocfs2_close_dir_scan (self->scan); self->scan = NULL; Py_DECREF (self->fs_obj); self->fs_obj = NULL; PyErr_SetNone (PyExc_StopIteration); return NULL; } return dir_entry_new (self->fs_obj, &dirent); } static PyTypeObject DirScanIter_Type = { PyObject_HEAD_INIT(NULL) 0, /* ob_size */ "ocfs2.DirScanIter", /* tp_name */ sizeof(DirScanIter), /* tp_basicsize */ 0, /* tp_itemsize */ (destructor)dir_scan_iter_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_compare */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT|Py_TPFLAGS_HAVE_ITER, /* tp_flags */ NULL, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ (getiterfunc)dir_scan_iter_getiter, /* tp_iter */ (iternextfunc)dir_scan_iter_next, /* tp_iternext */ }; static PyObject * dir_scan_iter_new (Filesystem *fs_obj, ocfs2_dir_scan *scan) { DirScanIter *self; self = PyObject_NEW (DirScanIter, &DirScanIter_Type); if (self == NULL) { ocfs2_close_dir_scan (scan); return NULL; } Py_INCREF (fs_obj); self->fs_obj = fs_obj; self->scan = scan; return (PyObject *) self; } static PyObject * fs_flush (Filesystem *self) { errcode_t ret; CHECK_ERROR (ocfs2_flush (self->fs)); Py_INCREF(Py_None); return Py_None; } static PyObject * fs_clusters_to_blocks (Filesystem *self, PyObject *args, PyObject *kwargs) { unsigned int clusters; uint64_t blocks; static char *kwlist[] = { "clusters", NULL }; if (!PyArg_ParseTupleAndKeywords (args, kwargs, "I:clusters_to_blocks", kwlist, &clusters)) return NULL; blocks = ocfs2_clusters_to_blocks (self->fs, clusters); return PyLong_FromUnsignedLongLong (blocks); } static PyObject * fs_blocks_to_clusters (Filesystem *self, PyObject *args, PyObject *kwargs) { unsigned long long blocks; uint32_t clusters; static char *kwlist[] = { "blocks", NULL }; if (!PyArg_ParseTupleAndKeywords (args, kwargs, "K:blocks_to_clusters", kwlist, &blocks)) return NULL; clusters = ocfs2_clusters_to_blocks (self->fs, blocks); return PyInt_FromLong (clusters); } static PyObject * fs_blocks_in_bytes (Filesystem *self, PyObject *args, PyObject *kwargs) { unsigned long long bytes; uint64_t blocks; static char *kwlist[] = { "bytes", NULL }; if (!PyArg_ParseTupleAndKeywords (args, kwargs, "K:blocks_in_bytes", kwlist, &bytes)) return NULL; blocks = ocfs2_blocks_in_bytes (self->fs, bytes); return PyLong_FromUnsignedLongLong (blocks); } static PyObject * fs_clusters_in_blocks (Filesystem *self, PyObject *args, PyObject *kwargs) { unsigned long long blocks; uint32_t clusters; static char *kwlist[] = { "blocks", NULL }; if (!PyArg_ParseTupleAndKeywords (args, kwargs, "K:clusters_in_blocks", kwlist, &blocks)) return NULL; clusters = ocfs2_clusters_in_blocks (self->fs, blocks); return PyInt_FromLong (clusters); } static PyObject * fs_block_out_of_range (Filesystem *self, PyObject *args, PyObject *kwargs) { unsigned long long block; int ret; static char *kwlist[] = { "block", NULL }; if (!PyArg_ParseTupleAndKeywords (args, kwargs, "K:block_out_of_range", kwlist, &block)) return NULL; ret = ocfs2_block_out_of_range(self->fs, block); return PyBool_FromLong (ret); } static PyObject * fs_lookup_system_inode (Filesystem *self, PyObject *args, PyObject *kwargs) { errcode_t ret; int type, slot_num = OCFS2_INVALID_SLOT; uint64_t blkno; static char *kwlist[] = { "type", "slot_num", NULL }; if (!PyArg_ParseTupleAndKeywords (args, kwargs, "i|i:lookup_system_inode", kwlist, &type, &slot_num)) return NULL; CHECK_ERROR (ocfs2_lookup_system_inode(self->fs, type, slot_num, &blkno)); return PyLong_FromUnsignedLongLong (blkno); } static PyObject * fs_read_cached_inode (Filesystem *self, PyObject *args, PyObject *kwargs) { errcode_t ret; unsigned long long blkno; ocfs2_cached_inode *cinode; PyObject *dinode; static char *kwlist[] = { "blkno", NULL }; if (!PyArg_ParseTupleAndKeywords (args, kwargs, "K:read_cached_inode", kwlist, &blkno)) return NULL; CHECK_ERROR (ocfs2_read_cached_inode (self->fs, blkno, &cinode)); dinode = dinode_new (self, cinode->ci_inode); /* XXX: error check */ ocfs2_free_cached_inode (self->fs, cinode); return dinode; } typedef struct { PyObject *func; PyObject *data; Filesystem *fs; } WalkData; static int walk_dirs (struct ocfs2_dir_entry *dirent, uint64_t blocknr, int offset, int blocksize, char *buf, void *priv_data) { PyObject *de; WalkData *data = priv_data; de = dir_entry_new (data->fs, dirent); if (de == NULL) return OCFS2_DIRENT_ERROR; /* XXX: handle errors */ if (data->data) PyObject_CallFunction (data->func, "OiiO", de, offset, blocksize, data->data); else PyObject_CallFunction (data->func, "Oii", de, offset, blocksize); Py_DECREF (de); return 0; } static PyObject * fs_dir_iterate (Filesystem *self, PyObject *args, PyObject *kwargs) { errcode_t ret; WalkData wdata; PyObject *py_func, *py_data = NULL, *py_dir = NULL; uint64_t dir; int flags = OCFS2_DIRENT_FLAG_EXCLUDE_DOTS; static char *kwlist[] = { "callback", "data", "dir", "flags", NULL }; if (!PyArg_ParseTupleAndKeywords (args, kwargs, "O|OOi:dir_iterate", kwlist, &py_func, &py_data, &py_dir, &flags)) return NULL; if (!PyCallable_Check (py_func)) { PyErr_SetString (PyExc_TypeError, "callback must be a callable object"); return NULL; } if (py_dir == NULL || py_dir == Py_None) dir = self->fs->fs_root_blkno; else if (DirEntry_Check (py_dir)) dir = ((DirEntry *) py_dir)->dentry.inode; else if (PyInt_Check (py_dir)) dir = PyInt_AsUnsignedLongMask (py_dir); else if (PyLong_Check (py_dir)) dir = PyLong_AsUnsignedLongLongMask (py_dir); else { PyErr_SetString (PyExc_TypeError, "dir must be DirEntry or integer"); return NULL; } Py_INCREF (py_func); wdata.func = py_func; Py_XINCREF (py_data); wdata.data = py_data; wdata.fs = self; /* XXX: handle errors */ ret = ocfs2_dir_iterate (self->fs, dir, flags, NULL, walk_dirs, &wdata); Py_DECREF (py_func); Py_XDECREF (py_data); Py_INCREF (Py_None); return Py_None; } static PyObject * fs_dir_scan (Filesystem *self, PyObject *args, PyObject *kwargs) { errcode_t ret; PyObject *py_dir = NULL; uint64_t dir; int flags = OCFS2_DIR_SCAN_FLAG_EXCLUDE_DOTS; ocfs2_dir_scan *scan; static char *kwlist[] = { "dir", "flags", NULL }; if (!PyArg_ParseTupleAndKeywords (args, kwargs, "|Oi:dir_scan", kwlist, &py_dir, &flags)) return NULL; if (py_dir == NULL || py_dir == Py_None) dir = self->fs->fs_root_blkno; else if (DirEntry_Check (py_dir)) dir = ((DirEntry *) py_dir)->dentry.inode; else if (PyInt_Check (py_dir)) dir = PyInt_AsUnsignedLongMask (py_dir); else if (PyLong_Check (py_dir)) dir = PyLong_AsUnsignedLongLongMask (py_dir); else { PyErr_SetString (PyExc_TypeError, "dir must be DirEntry or integer"); return NULL; } CHECK_ERROR (ocfs2_open_dir_scan (self->fs, dir, flags, &scan)); return dir_scan_iter_new (self, scan); } static PyMethodDef fs_methods[] = { {"flush", (PyCFunction)fs_flush, METH_NOARGS}, {"clusters_to_blocks", (PyCFunction)fs_clusters_to_blocks, METH_KEYWORDS}, {"blocks_to_clusters", (PyCFunction)fs_blocks_to_clusters, METH_KEYWORDS}, {"blocks_in_bytes", (PyCFunction)fs_blocks_in_bytes, METH_KEYWORDS}, {"clusters_in_blocks", (PyCFunction)fs_clusters_in_blocks, METH_KEYWORDS}, {"block_out_of_range", (PyCFunction)fs_block_out_of_range, METH_KEYWORDS}, {"lookup_system_inode", (PyCFunction)fs_lookup_system_inode, METH_KEYWORDS}, {"read_cached_inode", (PyCFunction)fs_read_cached_inode, METH_KEYWORDS}, {"dir_iterate", (PyCFunction)fs_dir_iterate, METH_KEYWORDS}, {"iterdir", (PyCFunction)fs_dir_scan, METH_KEYWORDS}, {NULL, NULL} }; static PyObject * fs_super (Filesystem *self, void *closure) { return super_new (self, self->fs->fs_super); } static PyObject * fs_orig_super (Filesystem *self, void *closure) { return super_new (self, self->fs->fs_orig_super); } static PyObject * fs_uuid_str (Filesystem *self, void *closure) { return PyString_FromString (self->fs->uuid_str); } #define FS_U32_GETTER(name) \ static PyObject * \ fs_ ## name (Filesystem *self, void *closure) \ { \ return PyInt_FromLong (self->fs->fs_ ## name); \ } #define FS_UINT_GETTER FS_U32_GETTER #define FS_U64_GETTER(name) \ static PyObject * \ fs_ ## name (Filesystem *self, void *closure) \ { \ return PyLong_FromUnsignedLongLong (self->fs->fs_ ## name); \ } #define FS_GETTER_ENTRY(name) \ {"fs_" #name, (getter)fs_ ## name, (setter)0} FS_U32_GETTER (flags) FS_UINT_GETTER (blocksize) FS_UINT_GETTER (clustersize) FS_U32_GETTER (clusters) FS_U64_GETTER (blocks) FS_U32_GETTER (umask) FS_U64_GETTER (root_blkno) FS_U64_GETTER (sysdir_blkno) FS_U64_GETTER (first_cg_blkno) static PyGetSetDef fs_getsets[] = { FS_GETTER_ENTRY (flags), FS_GETTER_ENTRY (super), FS_GETTER_ENTRY (orig_super), FS_GETTER_ENTRY (blocksize), FS_GETTER_ENTRY (clustersize), FS_GETTER_ENTRY (clusters), FS_GETTER_ENTRY (blocks), FS_GETTER_ENTRY (umask), FS_GETTER_ENTRY (root_blkno), FS_GETTER_ENTRY (sysdir_blkno), FS_GETTER_ENTRY (first_cg_blkno), {"uuid_str", (getter)fs_uuid_str, (setter)0}, {NULL} }; #undef FS_UINT_GETTER #undef FS_U32_GETTER #undef FS_U64_GETTER #undef FS_GETTER_ENTRY static PyMemberDef fs_members[] = { {"device", T_OBJECT, offsetof (Filesystem, device), RO}, {"fs_devname", T_OBJECT, offsetof (Filesystem, device), RO}, {0} }; static void fs_dealloc (Filesystem *self) { if (self->fs) ocfs2_close (self->fs); Py_XDECREF (self->device); PyObject_DEL (self); } static PyObject * fs_repr (Filesystem *self) { return PyString_FromFormat("", PyString_AS_STRING (self->device)); } static int fs_init (Filesystem *self, PyObject *args, PyObject *kwargs) { errcode_t ret; char *device; int flags = OCFS2_FLAG_RO | OCFS2_FLAG_BUFFERED; unsigned int superblock = 0, blksize = 0; static char *kwlist[] = { "device", "flags", "superblock", "blocksize", NULL }; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|iII:ocfs2.Filesystem.__init__", kwlist, &device, &flags, &superblock, &blksize)) return -1; self->fs = NULL; self->device = PyString_FromString (device); if (self->device == NULL) return -1; ret = ocfs2_open (device, flags, superblock, blksize, &self->fs); if (ret) { Py_DECREF (self->device); self->device = NULL; PyErr_SetString (ocfs2_error, error_message (ret)); return -1; } return 0; } static PyTypeObject Filesystem_Type = { PyObject_HEAD_INIT(NULL) 0, /* ob_size */ "ocfs2.Filesystem", /* tp_name */ sizeof(Filesystem), /* tp_basicsize */ 0, /* tp_itemsize */ (destructor)fs_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_compare */ (reprfunc)fs_repr, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT, /* tp_flags */ NULL, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ fs_methods, /* tp_methods */ fs_members, /* tp_members */ fs_getsets, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ (initproc)fs_init, /* tp_init */ 0, /* tp_alloc */ 0, /* tp_new */ }; static PyMethodDef ocfs2_methods[] = { {NULL, NULL} /* sentinel */ }; static void add_constants (PyObject *m) { #define ADD_INT_CONSTANT(name) \ PyModule_AddIntConstant (m, #name, OCFS2_ ## name) #define ADD_STR_CONSTANT(name) \ PyModule_AddStringConstant (m, #name, OCFS2_ ## name) ADD_INT_CONSTANT (SUPER_BLOCK_BLKNO); ADD_INT_CONSTANT (MIN_CLUSTERSIZE); ADD_INT_CONSTANT (MAX_CLUSTERSIZE); ADD_INT_CONSTANT (MIN_BLOCKSIZE); ADD_INT_CONSTANT (MAX_BLOCKSIZE); ADD_INT_CONSTANT (SUPER_MAGIC); ADD_STR_CONSTANT (SUPER_BLOCK_SIGNATURE); ADD_STR_CONSTANT (INODE_SIGNATURE); ADD_STR_CONSTANT (EXTENT_BLOCK_SIGNATURE); ADD_STR_CONSTANT (GROUP_DESC_SIGNATURE); ADD_INT_CONSTANT (VALID_FL); ADD_INT_CONSTANT (ORPHANED_FL); ADD_INT_CONSTANT (SYSTEM_FL); ADD_INT_CONSTANT (SUPER_BLOCK_FL); ADD_INT_CONSTANT (LOCAL_ALLOC_FL); ADD_INT_CONSTANT (BITMAP_FL); ADD_INT_CONSTANT (JOURNAL_FL); ADD_INT_CONSTANT (HEARTBEAT_FL); ADD_INT_CONSTANT (CHAIN_FL); ADD_INT_CONSTANT (JOURNAL_DIRTY_FL); ADD_INT_CONSTANT (ERROR_FS); ADD_INT_CONSTANT (MAX_FILENAME_LEN); ADD_INT_CONSTANT (MAX_SLOTS); ADD_INT_CONSTANT (INVALID_SLOT); ADD_INT_CONSTANT (VOL_UUID_LEN); ADD_INT_CONSTANT (MAX_VOL_LABEL_LEN); ADD_INT_CONSTANT (MIN_JOURNAL_SIZE); ADD_INT_CONSTANT (FIRST_ONLINE_SYSTEM_INODE); ADD_INT_CONSTANT (LAST_GLOBAL_SYSTEM_INODE); ADD_INT_CONSTANT (FT_UNKNOWN); ADD_INT_CONSTANT (FT_REG_FILE); ADD_INT_CONSTANT (FT_DIR); ADD_INT_CONSTANT (FT_CHRDEV); ADD_INT_CONSTANT (FT_BLKDEV); ADD_INT_CONSTANT (FT_FIFO); ADD_INT_CONSTANT (FT_SOCK); ADD_INT_CONSTANT (FT_SYMLINK); ADD_INT_CONSTANT (FT_MAX); ADD_INT_CONSTANT (LINK_MAX); ADD_INT_CONSTANT (FLAG_RO); ADD_INT_CONSTANT (FLAG_RW); ADD_INT_CONSTANT (FLAG_CHANGED); ADD_INT_CONSTANT (FLAG_DIRTY); ADD_INT_CONSTANT (FLAG_SWAP_BYTES); ADD_INT_CONSTANT (FLAG_BUFFERED); ADD_INT_CONSTANT (FLAG_NO_REV_CHECK); ADD_INT_CONSTANT (DIRENT_CHANGED); ADD_INT_CONSTANT (DIRENT_ABORT); ADD_INT_CONSTANT (DIRENT_ERROR); ADD_INT_CONSTANT (DIRENT_FLAG_INCLUDE_EMPTY); ADD_INT_CONSTANT (DIRENT_FLAG_INCLUDE_REMOVED); ADD_INT_CONSTANT (DIRENT_FLAG_EXCLUDE_DOTS); #undef ADD_INT_CONSTANT #undef ADD_STR_CONSTANT #define ADD_INT_CONSTANT(name) \ PyModule_AddIntConstant (m, #name, name) ADD_INT_CONSTANT (BAD_BLOCK_SYSTEM_INODE); ADD_INT_CONSTANT (GLOBAL_INODE_ALLOC_SYSTEM_INODE); ADD_INT_CONSTANT (SLOT_MAP_SYSTEM_INODE); ADD_INT_CONSTANT (HEARTBEAT_SYSTEM_INODE); ADD_INT_CONSTANT (GLOBAL_BITMAP_SYSTEM_INODE); ADD_INT_CONSTANT (ORPHAN_DIR_SYSTEM_INODE); ADD_INT_CONSTANT (EXTENT_ALLOC_SYSTEM_INODE); ADD_INT_CONSTANT (INODE_ALLOC_SYSTEM_INODE); ADD_INT_CONSTANT (JOURNAL_SYSTEM_INODE); ADD_INT_CONSTANT (LOCAL_ALLOC_SYSTEM_INODE); ADD_INT_CONSTANT (NUM_SYSTEM_INODES); #undef ADD_INT_CONSTANT } void initocfs2 (void) { PyObject *m; if (PyType_Ready (&DInode_Type) < 0) return; if (PyType_Ready (&DirEntry_Type) < 0) return; if (PyType_Ready (&SuperBlock_Type) < 0) return; if (PyType_Ready (&DirScanIter_Type) < 0) return; Filesystem_Type.tp_new = PyType_GenericNew; if (PyType_Ready (&Filesystem_Type) < 0) return; initialize_ocfs_error_table (); m = Py_InitModule ("ocfs2", ocfs2_methods); ocfs2_error = PyErr_NewException ("ocfs2.error", PyExc_RuntimeError, NULL); if (ocfs2_error) { Py_INCREF (ocfs2_error); PyModule_AddObject (m, "error", ocfs2_error); } Py_INCREF (&DInode_Type); PyModule_AddObject (m, "DInode", (PyObject *) &DInode_Type); Py_INCREF (&DirEntry_Type); PyModule_AddObject (m, "DirEntry", (PyObject *) &DirEntry_Type); Py_INCREF (&SuperBlock_Type); PyModule_AddObject (m, "SuperBlock", (PyObject *) &SuperBlock_Type); Py_INCREF (&DirScanIter_Type); PyModule_AddObject (m, "DirScanIter", (PyObject *) &DirScanIter_Type); Py_INCREF (&Filesystem_Type); PyModule_AddObject (m, "Filesystem", (PyObject *) &Filesystem_Type); add_constants (m); if (PyErr_Occurred ()) Py_FatalError ("can't initialize module ocfs2"); } ocfs2-tools-ocfs2-tools-1.8.6/ocfs2console/ocfs2interface/ocfsplist.c000066400000000000000000000212261347147137200255320ustar00rootroot00000000000000/* * ocfsplist.c * * Walk the partition list on a system * * Copyright (C) 2002, 2005 Oracle Corporation. All rights reserved. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * * Author: Manish Singh */ #include #include #include #include #include #include #include #include #include "ocfs2/ocfs2.h" #include #include "ocfsplist.h" #define RAW_PARTITION_FSTYPE "partition table" #define UNKNOWN_FSTYPE "unknown" #define FILL_ASYNC_ITERATIONS 20 #define WALK_ASYNC_ITERATIONS 10 typedef struct _WalkData WalkData; struct _WalkData { OcfsPartitionListFunc func; gpointer data; GPatternSpec *filter; const gchar *fstype; gboolean unmounted; gboolean async; guint count; blkid_cache cache; }; static gboolean is_partition_data (const gchar *device); static gboolean used_unmounted (const gchar *fstype); static gchar *fstype_check (const gchar *device, WalkData *wdata); static gchar *get_device_fstype (const gchar *device, WalkData *wdata); static void partition_info_fill (GHashTable *info, gboolean async); static gboolean partition_walk (gpointer key, gpointer value, gpointer user_data); static inline void async_loop_run (gboolean async, guint *count, const guint num_iterations) { if (async) { (*count)++; if (*count % num_iterations == 0) while (g_main_context_iteration (NULL, FALSE)); } } static gboolean is_partition_data (const gchar *device) { guchar buf[512]; gint fd; gssize count; fd = open (device, O_RDWR); if (fd == -1) return FALSE; count = read (fd, buf, 512); close (fd); if (count != 512) return FALSE; return (buf[510] == 0x55) && (buf[511] == 0xaa); } static gboolean used_unmounted (const gchar *fstype) { return (strcmp (fstype, "oracleasm") == 0) || (strcmp (fstype, RAW_PARTITION_FSTYPE) == 0); } static gchar * fstype_check (const gchar *device, WalkData *wdata) { blkid_dev dev; gchar *fstype = NULL; dev = blkid_get_dev(wdata->cache, device, BLKID_DEV_NORMAL); if (dev) { blkid_tag_iterate iter; const gchar *type, *value; iter = blkid_tag_iterate_begin (dev); while (blkid_tag_next (iter, &type, &value) == 0) { if (strcmp (type, "TYPE") == 0) { if ((wdata->fstype == NULL) || (strcmp (value, wdata->fstype) == 0)) { fstype = g_strdup (value); break; } } } blkid_tag_iterate_end (iter); } if (fstype == NULL && wdata->fstype == NULL) { if (device && is_partition_data (device)) fstype = g_strdup (RAW_PARTITION_FSTYPE); else fstype = g_strdup (UNKNOWN_FSTYPE); } return fstype; } static gchar * get_device_fstype (const gchar *device, WalkData *wdata) { gboolean is_bad = FALSE; struct stat sbuf; FILE *f; gchar buf[100], *proc, *d; gint i, fd; if (wdata->filter && !g_pattern_match_string (wdata->filter, device)) return NULL; if ((stat (device, &sbuf) != 0) || (!S_ISBLK (sbuf.st_mode)) || ((sbuf.st_mode & 0222) == 0)) return NULL; if (strncmp ("/dev/hd", device, 7) == 0) { i = strlen (device) - 1; while (i > 5 && g_ascii_isdigit (device[i])) i--; d = g_strndup (device + 5, i + 1); proc = g_strconcat ("/proc/ide/", d, "/media", NULL); g_free (d); f = fopen (proc, "r"); g_free (proc); if (f != NULL && fgets (buf, sizeof (buf), f)) is_bad = ((strncmp (buf, "cdrom", 5) == 0) || (strncmp (buf, "tape", 4) == 0)); if (f) fclose (f); if (is_bad) return NULL; } fd = open (device, O_RDWR); if (fd == -1) return NULL; close (fd); return fstype_check (device, wdata); } static void partition_info_fill (GHashTable *info, gboolean async) { FILE *proc; gchar line[100], name[100], *device; GSList *list; gint i; gchar *p; guint count = 0; proc = fopen ("/proc/partitions", "r"); if (proc == NULL) return; while (fgets (line, sizeof(line), proc) != NULL) { if (sscanf(line, "%*d %*d %*d %99[^ \t\n]", name) != 1) continue; device = g_strconcat ("/dev/", name, NULL); i = strlen (device) - 1; if (g_ascii_isdigit (device[i])) { while (i > 0 && g_ascii_isdigit (device[i])) i--; p = g_strndup (device, i + 1); list = g_hash_table_lookup (info, p); if (list == NULL) { list = g_slist_prepend (NULL, device); g_hash_table_insert (info, p, list); } else { if (strcmp (p, list->data) == 0) { g_free (list->data); list->data = device; } else g_slist_append (list, device); g_free (p); } } else if (!g_hash_table_lookup (info, device)) { list = g_slist_prepend (NULL, g_strdup (device)); g_hash_table_insert (info, device, list); } else g_free (device); async_loop_run (async, &count, FILL_ASYNC_ITERATIONS); } fclose (proc); } static gboolean partition_walk (gpointer key, gpointer value, gpointer user_data) { WalkData *wdata; GSList *list, *last; gchar *device; gchar mountpoint[PATH_MAX]; OcfsPartitionInfo info; gint flags; errcode_t ret; wdata = user_data; list = value; while (list) { device = list->data; info.fstype = get_device_fstype (device, wdata); if (info.fstype) { info.device = device; ret = ocfs2_check_mount_point (device, &flags, mountpoint, PATH_MAX); if (ret == 0) { if (flags & OCFS2_MF_MOUNTED) info.mountpoint = mountpoint; else info.mountpoint = NULL; if (wdata->unmounted) { if ((info.mountpoint == NULL) && !used_unmounted (info.fstype) && !(flags & OCFS2_MF_BUSY)) wdata->func (&info, wdata->data); } else wdata->func (&info, wdata->data); } else info.mountpoint = NULL; g_free (info.fstype); } last = list; list = list->next; g_free (device); g_slist_free_1 (last); async_loop_run (wdata->async, &wdata->count, WALK_ASYNC_ITERATIONS); } g_free (key); return TRUE; } #ifdef LIST_TEST_HASH static void print_hash (gpointer key, gpointer value, gpointer user_data) { GSList *list; g_print ("Key: %s; Values:", (gchar *) key); for (list = value; list != NULL; list = list->next) g_print (" %s", (gchar *) list->data); g_print ("\n"); } #endif void ocfs_partition_list (OcfsPartitionListFunc func, gpointer data, const gchar *filter, const gchar *fstype, gboolean unmounted, gboolean async) { GHashTable *info; WalkData wdata = { func, data, NULL, fstype, unmounted, async, 0 }; if (blkid_get_cache (&wdata.cache, NULL) < 0) return; if (fstype && *fstype == '\0') wdata.fstype = NULL; if (filter && *filter) wdata.filter = g_pattern_spec_new (filter); info = g_hash_table_new (g_str_hash, g_str_equal); partition_info_fill (info, async); #ifdef LIST_TEST_HASH g_hash_table_foreach (info, print_hash, NULL); #endif g_hash_table_foreach_remove (info, partition_walk, &wdata); g_hash_table_destroy (info); blkid_put_cache (wdata.cache); } #ifdef LIST_TEST static void list_func (OcfsPartitionInfo *info, gpointer data) { g_print ("Device: %s; Mountpoint %s\n", info->device, info->mountpoint ? info->mountpoint : "N/A"); } int main (int argc, char **argv) { g_print ("All:\n"); ocfs_partition_list (list_func, NULL, "ocfs2", NULL, FALSE, FALSE); g_print ("Unmounted:\n"); ocfs_partition_list (list_func, NULL, NULL, NULL, TRUE, FALSE); return 0; } #endif ocfs2-tools-ocfs2-tools-1.8.6/ocfs2console/ocfs2interface/ocfsplist.h000066400000000000000000000027511347147137200255410ustar00rootroot00000000000000/* * ocfsplist.h * * Function prototypes for related 'C' file. * * Copyright (C) 2002 Oracle Corporation. All rights reserved. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * * Author: Manish Singh */ #ifndef __OCFS_PARTITION_LIST_H__ #define __OCFS_PARTITION_LIST_H__ #include typedef struct _OcfsPartitionInfo OcfsPartitionInfo; struct _OcfsPartitionInfo { gchar *device; gchar *mountpoint; gchar *fstype; }; typedef void (*OcfsPartitionListFunc) (OcfsPartitionInfo *info, gpointer data); void ocfs_partition_list (OcfsPartitionListFunc func, gpointer data, const gchar *filter, const gchar *fstype, gboolean unmounted, gboolean async); #endif /* __OCFS_PARTITION_LIST_H__ */ ocfs2-tools-ocfs2-tools-1.8.6/ocfs2console/ocfs2interface/partitionview.py000066400000000000000000000127401347147137200266370ustar00rootroot00000000000000# OCFS2Console - GUI frontend for OCFS2 management and debugging # Copyright (C) 2002, 2005 Oracle. All rights reserved. # # 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. import gtk from plist import partition_list COLUMN_DEVICE, COLUMN_MOUNTPOINT = range(2) class PartitionView(gtk.TreeView): def __init__(self, info_frames=()): store = gtk.ListStore(str, str, str) gtk.TreeView.__init__(self, store) self.insert_column_with_attributes(-1, 'Device', gtk.CellRendererText(), text=COLUMN_DEVICE) self.insert_column_with_attributes(-1, 'Mountpoint', gtk.CellRendererText(), text=COLUMN_MOUNTPOINT) sel = self.get_selection() sel.connect('changed', self.on_select) self.filter_entry = None self.sel_widgets = [] self.mount_widgets = [] self.unmount_widgets = [] self.info_frames = tuple(info_frames) def get_sel_values(self): sel = self.get_selection() store, iter = sel.get_selected() if store and iter: return store[iter] else: return (None, None) def get_device(self): selection = self.get_sel_values() return selection[COLUMN_DEVICE] def on_select(self, sel): self.selected = True self.set_widgets_sensitive(self.sel_widgets, True) device, mountpoint = self.get_sel_values() if mountpoint: self.set_widgets_sensitive(self.mount_widgets, True) self.set_widgets_sensitive(self.unmount_widgets, False) else: self.set_widgets_sensitive(self.mount_widgets, False) self.set_widgets_sensitive(self.unmount_widgets, True) self.update_info(device) def update_info(self, device): for frame, info in self.info_frames: if frame.child: frame.child.destroy() frame.add(info(device)) frame.show_all() def select_device(self, device): for row in self.get_model(): if row[COLUMN_DEVICE] == device: sel = self.get_selection() sel.select_iter(row.iter) def refresh_partitions(self): def list_compare(store, a, b): d1, m1 = store[a] d2, m2 = store[b] if m1 and not m2: return -1 elif not m1 and m2: return 1 else: return cmp(d1, d2) self.set_widgets_sensitive(self.sel_widgets, False) self.set_widgets_sensitive(self.mount_widgets, False) self.set_widgets_sensitive(self.unmount_widgets, False) if self.filter_entry: filter = self.filter_entry.get_text() else: filter = None old_device = self.get_device() store = gtk.ListStore(str, str) self.set_model(store) self.store = store self.sel = self.get_selection() self.selected = False store.set_sort_func(COLUMN_DEVICE, list_compare) store.set_sort_column_id(COLUMN_DEVICE, gtk.SORT_ASCENDING) partition_list(self.add_partition, old_device, filter=filter, fstype='ocfs2', async=True) if len(store): if not self.selected: self.sel.select_iter(store.get_iter_first()) else: self.update_info(None) def add_partition(self, device, mountpoint, fstype, old_device): iter = self.store.append((device, mountpoint)) if device == old_device: self.sel.select_iter(iter) self.selected = True def set_filter_entry(self, entry): self.filter_entry = entry def add_to_widget_list(self, widget_list, widgets): try: widget_list.extend(widgets) except TypeError: widget_list.append(widgets) def add_sel_widgets(self, widgets): self.add_to_widget_list(self.sel_widgets, widgets) def add_mount_widgets(self, widgets): self.add_to_widget_list(self.mount_widgets, widgets) def add_unmount_widgets(self, widgets): self.add_to_widget_list(self.unmount_widgets, widgets) def set_widgets_sensitive(self, widgets, sensitive=True): for widget in widgets: widget.set_sensitive(sensitive) def main(): def dummy(*args): gtk.main_quit() window = gtk.Window() window.set_size_request(300, 200) window.connect('delete_event', dummy) scrl_win = gtk.ScrolledWindow() scrl_win.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) window.add(scrl_win) pv = PartitionView() scrl_win.add(pv) window.show_all() pv.refresh_partitions() gtk.main() if __name__ == '__main__': main() ocfs2-tools-ocfs2-tools-1.8.6/ocfs2console/ocfs2interface/plistmodule.c000066400000000000000000000064611347147137200260710ustar00rootroot00000000000000/* * plistmodule.c * * Partition list python binding. * * Copyright (C) 2004, 2005 Oracle. All rights reserved. * * Author: Manish Singh * * 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 recieved a copy of the GNU General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. */ #include #include #include "ocfsplist.h" typedef struct { PyObject *func; PyObject *data; gboolean mountpoint; gboolean seen_error; } ProxyData; static void proxy (OcfsPartitionInfo *info, gpointer pdata) { ProxyData *data = pdata; PyObject *tuple, *val, *ret; gint len = 2, pos = 0; if (data->seen_error) return; if (data->mountpoint) len++; if (data->data) len++; tuple = PyTuple_New (len); PyTuple_SET_ITEM (tuple, pos, PyString_FromString (info->device)); pos++; if (data->mountpoint) { if (info->mountpoint == NULL) { Py_INCREF (Py_None); val = Py_None; } else val = PyString_FromString (info->mountpoint); PyTuple_SET_ITEM (tuple, pos, val); pos++; } PyTuple_SET_ITEM (tuple, pos, PyString_FromString (info->fstype)); pos++; if (data->data) { Py_INCREF (data->data); PyTuple_SET_ITEM (tuple, pos, data->data); pos++; } ret = PyObject_CallObject (data->func, tuple); if (ret == NULL) { PyErr_Print(); data->seen_error = TRUE; } Py_DECREF (tuple); } static PyObject * partition_list (PyObject *self, PyObject *args, PyObject *kwargs) { ProxyData pdata; PyObject *py_func, *py_data = NULL; gchar *filter = NULL, *fstype = NULL; gboolean unmounted = FALSE, async = FALSE; static gchar *kwlist[] = { "callback", "data", "filter", "fstype", "unmounted", "async", NULL }; if (!PyArg_ParseTupleAndKeywords (args, kwargs, "O|Ozzii:partition_list", kwlist, &py_func, &py_data, &filter, &fstype, &unmounted, &async)) return NULL; if (!PyCallable_Check (py_func)) { PyErr_SetString (PyExc_TypeError, "callback must be a callable object"); return NULL; } Py_INCREF (py_func); pdata.func = py_func; Py_XINCREF (py_data); pdata.data = py_data; pdata.mountpoint = !unmounted; pdata.seen_error = FALSE; ocfs_partition_list (proxy, &pdata, filter, fstype, unmounted, async); Py_DECREF (py_func); Py_XDECREF (py_data); Py_INCREF (Py_None); return Py_None; } static PyMethodDef plist_methods[] = { {"partition_list", (PyCFunction)partition_list, METH_KEYWORDS}, {NULL, NULL} /* sentinel */ }; void initplist (void) { PyObject *m; m = Py_InitModule ("plist", plist_methods); if (PyErr_Occurred ()) Py_FatalError ("can't initialize module plist"); } ocfs2-tools-ocfs2-tools-1.8.6/ocfs2console/ocfs2interface/process.py000066400000000000000000000106741347147137200254150ustar00rootroot00000000000000# OCFS2Console - GUI frontend for OCFS2 management and debugging # Copyright (C) 2002, 2005 Oracle. All rights reserved. # # 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. import os import fcntl import popen2 import gobject import gtk from guiutil import set_props INTERVAL = 100 TIMEOUT = 10000 class Process: def __init__(self, command, title, desc, parent=None, spin_now=False): if isinstance(command, basestring): if len(command.split(None, 1)) < 2: command = (command,) self.command = command self.title = title self.desc = desc self.parent = parent self.spin_now = spin_now self.pipe = popen2.Popen4(self.command) def reap(self): self.success = False self.killed = False self.count = TIMEOUT // INTERVAL self.threshold = self.count - INTERVAL * 10 self.dialog = None if self.spin_now: self.count = TIMEOUT * 60 self.make_progress_box() timeout_id = gobject.timeout_add(INTERVAL, self.timeout) fromchild = self.pipe.fromchild fileno = fromchild.fileno() flags = fcntl.fcntl(fileno, fcntl.F_GETFL, 0) flags = flags | os.O_NONBLOCK fcntl.fcntl(fileno, fcntl.F_SETFL, flags) self.output = '' output_id = gobject.io_add_watch(fromchild, gobject.IO_IN, self.read) gtk.main() if self.dialog: self.dialog.destroy() gobject.source_remove(output_id) gobject.source_remove(timeout_id) if not self.success: if self.killed: if self.output: self.output += '\n' self.output += 'Killed prematurely.' return self.success, self.output, self.killed def timeout(self): self.count = self.count - 1 ret = self.pipe.poll() if ret != -1: self.success = not os.WEXITSTATUS(ret) gtk.main_quit() return True if self.count < 1: self.kill() return True if self.count < self.threshold and not self.dialog: self.make_progress_box() if self.dialog: self.pbar.pulse() return True def kill(self): self.success = False self.killed = True os.kill(self.pipe.pid, 15) gobject.timeout_add(INTERVAL * 5, self.kill_timeout) gtk.main_quit() def kill_timeout(self): if self.pipe.poll() == -1: os.kill(self.pipe.pid, 9) self.kill_9 = True return False def make_progress_box(self): self.dialog = gtk.Window() set_props(self.dialog, title=self.title, resizable=False, modal=True, type_hint=gtk.gdk.WINDOW_TYPE_HINT_DIALOG) def ignore(w, e): return True self.dialog.connect('delete-event', ignore) self.dialog.set_transient_for(self.parent) vbox = gtk.VBox() set_props(vbox, spacing=0, homogeneous=False, border_width=4, parent=self.dialog) label = gtk.Label(self.desc + '...') vbox.pack_start(label, expand=False, fill=False) self.pbar = gtk.ProgressBar() vbox.pack_start(self.pbar, expand=False, fill=False) self.dialog.show_all() def read(self, fd, cond): if cond & gtk.gdk.INPUT_READ: try: self.output += fd.read(1024) except IOError: return False return True def main(): process = Process('echo Hello; sleep 10', 'Sleep', 'Sleeping', spin_now=True) print process.reap() if __name__ == '__main__': main() ocfs2-tools-ocfs2-tools-1.8.6/ocfs2console/ocfs2interface/pushconfig.py000066400000000000000000000067001347147137200260770ustar00rootroot00000000000000# OCFS2Console - GUI frontend for OCFS2 management and debugging # Copyright (C) 2005 Oracle. All rights reserved. # # 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. import socket import gtk import o2cb_ctl from guiutil import error_box from terminal import TerminalDialog, terminal_ok as pushconfig_ok CONFIG_FILE = '/etc/ocfs2/cluster.conf' command_template = '''set -e mkdir -p /etc/ocfs2 cat > /etc/ocfs2/cluster.conf <<\_______EOF %(cluster_config)s _______EOF #/etc/init.d/o2cb online %(cluster_name)s ''' def get_hosts(parent=None): hostname = socket.gethostname() cluster_name = o2cb_ctl.get_active_cluster_name(parent) nodes = o2cb_ctl.get_cluster_nodes(cluster_name, parent) remote_nodes = [node['name'] for node in nodes if node['name'] != hostname] return cluster_name, remote_nodes def generate_command(cluster_name): conf_file = open(CONFIG_FILE) config_data = conf_file.read() conf_file.close() if config_data.endswith('\n'): config_data = config_data[:-1] info = {'cluster_config' : config_data, 'cluster_name' : cluster_name} return command_template % info def propagate(terminal, dialog, remote_command, host_iter): try: host = host_iter.next() except StopIteration: terminal.feed('Finished!\r\n', -1) dialog.finished = True return command = ('ssh', 'root@%s' % host, remote_command) terminal.feed('Propagating cluster configuration to %s...\r\n' % host, -1) terminal.fork_command(command=command[0], argv=command) def push_config(parent=None): try: cluster_name, hosts = get_hosts(parent) except o2cb_ctl.CtlError, e: error_box(parent, str(e)) return try: command = generate_command(cluster_name) except IOError, e: error_box(parent, str(e)) return title = 'Propagate Cluster Configuration' dialog = TerminalDialog(parent=parent, title=title) terminal = dialog.terminal dialog.finished = False dialog.show_all() host_iter = iter(hosts) terminal.connect('child-exited', propagate, dialog, command, host_iter) propagate(terminal, dialog, command, host_iter) while 1: dialog.run() if dialog.finished: break msg = ('Cluster configuration propagation is still running. You ' 'should not close this window until it is finished') info = gtk.MessageDialog(parent=dialog, flags=gtk.DIALOG_DESTROY_WITH_PARENT, type=gtk.MESSAGE_WARNING, buttons=gtk.BUTTONS_CLOSE, message_format=msg) info.run() info.destroy() dialog.destroy() def main(): push_config() if __name__ == '__main__': main() ocfs2-tools-ocfs2-tools-1.8.6/ocfs2console/ocfs2interface/terminal.py000066400000000000000000000035701347147137200255470ustar00rootroot00000000000000# OCFS2Console - GUI frontend for OCFS2 management and debugging # Copyright (C) 2005 Oracle. All rights reserved. # # 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. import gtk from guiutil import set_props try: import vte except ImportError: terminal_ok = False else: terminal_ok = True class TerminalDialog(gtk.Dialog): def __init__(self, parent=None, title='Terminal'): gtk.Dialog.__init__(self, parent=parent, title=title, buttons=(gtk.STOCK_CLOSE, gtk.RESPONSE_CLOSE)) label = gtk.Label(title) label.set_alignment(xalign=0.0, yalign=0.5) self.vbox.pack_start(label) frame = gtk.Frame() frame.set_shadow_type(gtk.SHADOW_IN) self.vbox.pack_end(frame) hbox = gtk.HBox() frame.add(hbox) self.terminal = vte.Terminal() self.terminal.set_scrollback_lines(8192) #self.terminal.set_font_from_string('monospace 12') hbox.pack_start(self.terminal) scrollbar = gtk.VScrollbar() scrollbar.set_adjustment(self.terminal.get_adjustment()) hbox.pack_end(scrollbar) self.show_all() def main(): dialog = TerminalDialog() dialog.run() if __name__ == '__main__': main() ocfs2-tools-ocfs2-tools-1.8.6/ocfs2console/ocfs2interface/toolbar.py000066400000000000000000000045101347147137200253710ustar00rootroot00000000000000# OCFS2Console - GUI frontend for OCFS2 management and debugging # Copyright (C) 2005 Oracle. All rights reserved. # # 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. import gtk from guiutil import make_callback toolbar_data = ( ('Mount', gtk.STOCK_EXECUTE, 'mount', 'refresh'), ('Unmount', gtk.STOCK_STOP, 'unmount', 'refresh'), ('Refresh', gtk.STOCK_REFRESH, 'refresh', None) ) class Toolbar: def __init__(self, window): self.window = window def get_widgets(self): toolbar = gtk.Toolbar() items = {} for data in toolbar_data: label, stock_id, callback, sub_callback = data cb = make_callback(self.window, callback, sub_callback) icon = gtk.Image() icon.set_from_stock(stock_id, gtk.ICON_SIZE_BUTTON) items[callback] = toolbar.append_item(label, label, None, icon, cb) toolbar.append_space() filter_box, entry = self.get_filter_box() toolbar.append_widget(filter_box, 'Partition name filter', None) return toolbar, items, entry def get_filter_box(self): hbox = gtk.HBox(False, 4) label = gtk.Label('Filter:') hbox.pack_start(label, expand=False, fill=False) entry = gtk.Entry() hbox.pack_end(entry) return hbox, entry def main(): def dummy(*args): gtk.main_quit() window = gtk.Window() window.connect('delete_event', dummy) for i in toolbar_data: setattr(window, i[2], dummy) toolbar = Toolbar(window) vbox = gtk.VBox() window.add(vbox) vbox.add(toolbar.get_widgets()[0]) window.show_all() gtk.main() if __name__ == '__main__': main() ocfs2-tools-ocfs2-tools-1.8.6/ocfs2console/ocfs2interface/tune.py000066400000000000000000000107431347147137200247070ustar00rootroot00000000000000# OCFS2Console - GUI frontend for OCFS2 management and debugging # Copyright (C) 2005 Oracle. All rights reserved. # # 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. import gtk import ocfs2 from guiutil import Dialog, set_props, error_box, format_bytes from process import Process from fswidgets import NumSlots, VolumeLabel base_command = ('tunefs.ocfs2',) class TuneVolumeLabel(VolumeLabel): def __init__(self, device=None): VolumeLabel.__init__(self) try: fs = ocfs2.Filesystem(device) self.set_text(fs.fs_super.s_label) except ocfs2.error: pass title = 'Changing Label' action = 'Changing label' empty_ok = True class TuneNumSlots(NumSlots): def __init__(self, device=None): NumSlots.__init__(self) fs = ocfs2.Filesystem(device) self.set_range(fs.fs_super.s_max_slots, ocfs2.MAX_SLOTS) title = 'Edit Node Slot Count' action = 'Changing node slot count' empty_ok = False def tune_action(widget_type, parent, device): try: widget = widget_type(device) except ocfs2.error: desc = widget_type.label.replace('_', '') desc = desc.lower() error_box(parent, 'Could not get current %s for device %s' % (desc, device)) return False dialog = Dialog(parent=parent, title=widget_type.title, buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OK, gtk.RESPONSE_OK)) dialog.set_alternative_button_order((gtk.RESPONSE_OK, gtk.RESPONSE_CANCEL)) dialog.set_default_response(gtk.RESPONSE_OK) table = gtk.Table(rows=1, columns=2) set_props(table, row_spacing=6, column_spacing=6, border_width=6, parent=dialog.vbox) label = gtk.Label() label.set_text_with_mnemonic(widget_type.label + ':') set_props(label, xalign=0.0) table.attach(label, 0, 1, 0, 1) label.set_mnemonic_widget(widget) table.attach(widget, 1, 2, 0, 1) if isinstance(widget, gtk.Entry): widget.set_activates_default(True) widget.grab_focus() dialog.show_all() while 1: if dialog.run() != gtk.RESPONSE_OK: dialog.destroy() return False new_label = widget.get_text() if not new_label: if widget_type.empty_ok: msg = ('Are you sure you want to clear the %s on %s?' % (widget_type.lower(), device)) ask = gtk.MessageDialog(parent=dialog, flags=gtk.DIALOG_DESTROY_WITH_PARENT, type=gtk.MESSAGE_QUESTION, buttons=gtk.BUTTONS_YES_NO, message_format=msg) if ask.run() == gtk.RESPONSE_YES: break else: ask.destroy() else: error_box(dialog, '%s cannot be empty.' % widget_type.lower().ucfirst()) else: break command = list(base_command) command.extend(widget.get_arg()) command.append(device) dialog.destroy() tunefs = Process(command, widget_type.title, widget_type.action + '...', parent, spin_now=True) success, output, k = tunefs.reap() if not success: error_box(parent, 'File system tune error: %s' % output) return False return True def tune_label(parent, device): tune_action(TuneVolumeLabel, parent, device) def tune_slots(parent, device): tune_action(TuneNumSlots, parent, device) def main(): import sys device = sys.argv[1] tune_label(None, device) tune_slots(None, device) if __name__ == '__main__': main() ocfs2-tools-ocfs2-tools-1.8.6/patches/000077500000000000000000000000001347147137200175105ustar00rootroot00000000000000ocfs2-tools-ocfs2-tools-1.8.6/patches/Makefile000066400000000000000000000002271347147137200211510ustar00rootroot00000000000000TOPDIR = .. include $(TOPDIR)/Preamble.make DIST_FILES = README mountocfs2support.patch libblkidocfs2support.patch include $(TOPDIR)/Postamble.make ocfs2-tools-ocfs2-tools-1.8.6/patches/README000066400000000000000000000002071347147137200203670ustar00rootroot00000000000000mountocfs2support.patch patch for mount(8) to autodetect ocfs/ocfs2 and to mount ocfs2 by label and uuid ocfs2-tools-ocfs2-tools-1.8.6/patches/libblkidocfs2support.patch000066400000000000000000000041741347147137200247050ustar00rootroot00000000000000--- probe.h.ocfs2 2004-09-15 17:11:55.000000000 -0700 +++ probe.h 2004-09-15 17:12:53.000000000 -0700 @@ -234,6 +234,21 @@ #define OCFS_MAGIC "OracleCFS" +struct ocfs2_super_block { + u_char signature[8]; + u_char s_dummy1[184]; + u_char s_dummy2[80]; + u_char s_label[64]; + u_char s_uuid[16]; +}; + +#define OCFS2_MIN_BLOCKSIZE 512 +#define OCFS2_MAX_BLOCKSIZE 4096 + +#define OCFS2_SUPER_BLOCK_BLKNO 2 + +#define OCFS2_SUPER_BLOCK_SIGNATURE "OCFSV2" + #define ISODCL(from, to) (to - from + 1) struct iso_volume_descriptor { char type[ISODCL(1,1)]; /* 711 */ --- probe.c.ocfs2 2004-09-15 17:11:55.000000000 -0700 +++ probe.c 2004-09-15 17:12:53.000000000 -0700 @@ -327,9 +327,7 @@ major = ocfsmajor(ovh); if (major == 1) - blkid_set_tag(dev,"SEC_TYPE","ocfs1",sizeof("ocfs1")); - else if (major == 2) - blkid_set_tag(dev,"SEC_TYPE","ocfs2",sizeof("ocfs2")); + blkid_set_tag(dev,"SEC_TYPE","ocfs1",sizeof("ocfs")); else if (major >= 9) blkid_set_tag(dev,"SEC_TYPE","ntocfs",sizeof("ntocfs")); @@ -339,6 +337,21 @@ return 0; } +static int probe_ocfs2(int fd __BLKID_ATTR((unused)), + blkid_cache cache __BLKID_ATTR((unused)), + blkid_dev dev, + struct blkid_magic *id __BLKID_ATTR((unused)), + unsigned char *buf) +{ + struct ocfs2_super_block *osb; + + osb = (struct ocfs2_super_block *)buf; + + blkid_set_tag(dev, "LABEL", osb->s_label, sizeof(osb->s_label)); + set_uuid(dev, osb->s_uuid); + return 0; +} + /* * BLKID_BLK_OFFS is at least as large as the highest bim_kboff defined * in the type_array table below + bim_kbalign. @@ -398,7 +411,11 @@ { "swap", 0, 0x1ff6, 10, "SWAPSPACE2", 0 }, { "swap", 0, 0x3ff6, 10, "SWAP-SPACE", 0 }, { "swap", 0, 0x3ff6, 10, "SWAPSPACE2", 0 }, - { "ocfs", 0, 8, 9, "OracleCFS", probe_ocfs }, + { "ocfs", 0, 8, 9, "OracleCFS", probe_ocfs }, + { "ocfs2", 1, 0, 6, "OCFSV2", probe_ocfs2 }, + { "ocfs2", 2, 0, 6, "OCFSV2", probe_ocfs2 }, + { "ocfs2", 4, 0, 6, "OCFSV2", probe_ocfs2 }, + { "ocfs2", 8, 0, 6, "OCFSV2", probe_ocfs2 }, { NULL, 0, 0, 0, NULL, NULL } }; ocfs2-tools-ocfs2-tools-1.8.6/patches/mountocfs2support.patch000066400000000000000000000065401347147137200242720ustar00rootroot00000000000000--- util-linux-2.11y/mount/linux_fs.h.ocfs2 2004-09-15 15:51:45.000000000 -0700 +++ util-linux-2.11y/mount/linux_fs.h 2004-09-15 15:53:25.000000000 -0700 @@ -231,6 +231,21 @@ #define ocfslabellen(o) assemble2le(o.label_len) #define OCFS_MAGIC "OracleCFS" +struct ocfs2_super_block { + u_char signature[8]; + u_char s_dummy1[184]; + u_char s_dummy2[80]; + u_char s_label[64]; + u_char s_uuid[16]; +}; + +#define OCFS2_MIN_BLOCKSIZE 512 +#define OCFS2_MAX_BLOCKSIZE 4096 + +#define OCFS2_SUPER_BLOCK_BLKNO 2 + +#define OCFS2_SUPER_BLOCK_SIGNATURE "OCFSV2" + static inline int assemble2le(unsigned char *p) { return (p[0] | (p[1] << 8)); --- util-linux-2.11y/mount/get_label_uuid.c.ocfs2 2004-09-15 15:51:45.000000000 -0700 +++ util-linux-2.11y/mount/get_label_uuid.c 2004-09-15 15:53:25.000000000 -0700 @@ -90,6 +90,8 @@ struct jfs_super_block jfssb; struct ocfs_volume_header ovh; /* Oracle */ struct ocfs_volume_label olbl; + struct ocfs2_super_block osb; + int blksize, blkoff; fd = open(device, O_RDONLY); if (fd < 0) @@ -148,6 +150,27 @@ } rv = 0; } + else { + for (blksize = OCFS2_MIN_BLOCKSIZE; + blksize <= OCFS2_MAX_BLOCKSIZE; + blksize <<= 1) { + blkoff = blksize * OCFS2_SUPER_BLOCK_BLKNO; + if (lseek(fd, blkoff, SEEK_SET) == blkoff + && read(fd, (char *) &osb, + sizeof(osb)) == sizeof(osb) + && (strncmp(osb.signature, + OCFS2_SUPER_BLOCK_SIGNATURE, + sizeof(OCFS2_SUPER_BLOCK_SIGNATURE)) + == 0)) { + memcpy(uuid, osb.s_uuid, sizeof(osb.s_uuid)); + namesize = sizeof(osb.s_label); + if ((*label = calloc(namesize, 1)) != NULL) + memcpy(*label, osb.s_label, namesize); + rv = 0; + break; + } + } + } close(fd); return rv; --- util-linux-2.11y/mount/mount_guess_fstype.c.ocfs2 2002-11-25 03:42:21.000000000 -0800 +++ util-linux-2.11y/mount/mount_guess_fstype.c 2004-09-15 15:53:25.000000000 -0700 @@ -243,6 +243,7 @@ struct fat_super_block fatsb; struct xfs_super_block xfsb; struct cramfs_super_block cramfssb; + struct ocfs_volume_header ovh; } xsb; struct ufs_super_block ufssb; union { @@ -255,6 +256,7 @@ struct hpfs_super_block hpfssb; struct adfs_super_block adfssb; struct sysv_super_block svsb; + struct ocfs2_super_block osb; struct stat statbuf; /* opening and reading an arbitrary unknown path can have @@ -284,6 +286,8 @@ type = "romfs"; else if(!strncmp(xsb.xfsb.s_magic, XFS_SUPER_MAGIC, 4)) type = "xfs"; + else if(!strncmp(xsb.ovh.signature, OCFS_MAGIC, sizeof(OCFS_MAGIC))) + type = "ocfs"; else if(!strncmp(xsb.qnx4fs_magic+4, "QNX4FS", 6)) type = "qnx4"; else if(xsb.bfs_magic == 0x1badface) @@ -457,6 +461,21 @@ } if (!type) { + int blksize, blkoff; + for (blksize = OCFS2_MIN_BLOCKSIZE; + blksize <= OCFS2_MAX_BLOCKSIZE; + blksize <<= 1) { + blkoff = blksize * OCFS2_SUPER_BLOCK_BLKNO; + if (lseek(fd, blkoff, SEEK_SET) != blkoff + || read(fd, (char *) &osb, sizeof(osb)) != sizeof(osb)) + goto io_error; + if (strncmp(osb.signature, OCFS2_SUPER_BLOCK_SIGNATURE, + sizeof(OCFS2_SUPER_BLOCK_SIGNATURE)) == 0) + type = "ocfs2"; + } + } + + if (!type) { /* perhaps the user tries to mount the swap space on a new disk; warn her before she does mke2fs on it */ int pagesize = getpagesize(); ocfs2-tools-ocfs2-tools-1.8.6/pkg.m4000066400000000000000000000037531347147137200171140ustar00rootroot00000000000000 dnl PKG_CHECK_MODULES(GSTUFF, gtk+-2.0 >= 1.3 glib = 1.3.4, action-if, action-not) dnl defines GSTUFF_LIBS, GSTUFF_CFLAGS, see pkg-config man page dnl also defines GSTUFF_PKG_ERRORS on error AC_DEFUN([PKG_CHECK_MODULES], [ succeeded=no if test -z "$PKG_CONFIG"; then AC_PATH_PROG(PKG_CONFIG, pkg-config, no) fi if test "$PKG_CONFIG" = "no" ; then echo "*** The pkg-config script could not be found. Make sure it is" echo "*** in your path, or set the PKG_CONFIG environment variable" echo "*** to the full path to pkg-config." echo "*** Or see http://www.freedesktop.org/software/pkgconfig to get pkg-config." else PKG_CONFIG_MIN_VERSION=0.9.0 if $PKG_CONFIG --atleast-pkgconfig-version $PKG_CONFIG_MIN_VERSION; then AC_MSG_CHECKING(for $2) if $PKG_CONFIG --exists "$2" ; then AC_MSG_RESULT(yes) succeeded=yes AC_MSG_CHECKING($1_CFLAGS) $1_CFLAGS=`$PKG_CONFIG --cflags "$2"` AC_MSG_RESULT($$1_CFLAGS) AC_MSG_CHECKING($1_LIBS) $1_LIBS=`$PKG_CONFIG --libs "$2"` AC_MSG_RESULT($$1_LIBS) else $1_CFLAGS="" $1_LIBS="" ## If we have a custom action on failure, don't print errors, but ## do set a variable so people can do so. $1_PKG_ERRORS=`$PKG_CONFIG --errors-to-stdout --print-errors "$2"` ifelse([$4], ,echo $$1_PKG_ERRORS,) fi AC_SUBST($1_CFLAGS) AC_SUBST($1_LIBS) else echo "*** Your version of pkg-config is too old. You need version $PKG_CONFIG_MIN_VERSION or newer." echo "*** See http://www.freedesktop.org/software/pkgconfig" fi fi if test $succeeded = yes; then ifelse([$3], , :, [$3]) else ifelse([$4], , AC_MSG_ERROR([Library requirements ($2) not met; consider adjusting the PKG_CONFIG_PATH environment variable if your libraries are in a nonstandard prefix so pkg-config can find them.]), [$4]) fi ]) ocfs2-tools-ocfs2-tools-1.8.6/python.m4000066400000000000000000000167251347147137200176570ustar00rootroot00000000000000## ------------------------ ## Python file handling ## From Andrew Dalke ## Updated by James Henstridge ## ------------------------ # Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 # Free Software Foundation, Inc. # 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., 51 Franklin St, Fifth Floor, Boston, MA # 02110-1301, USA. # AM_PATH_PYTHON([MINIMUM-VERSION], [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) # Adds support for distributing Python modules and packages. To # install modules, copy them to $(pythondir), using the python_PYTHON # automake variable. To install a package with the same name as the # automake package, install to $(pkgpythondir), or use the # pkgpython_PYTHON automake variable. # The variables $(pyexecdir) and $(pkgpyexecdir) are provided as # locations to install python extension modules (shared libraries). # Another macro is required to find the appropriate flags to compile # extension modules. # If your package is configured with a different prefix to python, # users will have to add the install directory to the PYTHONPATH # environment variable, or create a .pth file (see the python # documentation for details). # If the MINIMUM-VERSION argument is passed, AM_PATH_PYTHON will # cause an error if the version of python installed on the system # doesn't meet the requirement. MINIMUM-VERSION should consist of # numbers and dots only. AC_DEFUN([AM_PATH_PYTHON], [ dnl Find a Python interpreter. Python versions prior to 1.5 are not dnl supported because the default installation locations changed from dnl $prefix/lib/site-python in 1.4 to $prefix/lib/python1.5/site-packages dnl in 1.5. m4_define_default([_AM_PYTHON_INTERPRETER_LIST], [python python2 python2.4 python2.3 python2.2 dnl python2.1 python2.0 python1.6 python1.5]) m4_if([$1],[],[ dnl No version check is needed. # Find any Python interpreter. if test -z "$PYTHON"; then AC_PATH_PROGS([PYTHON], _AM_PYTHON_INTERPRETER_LIST, :) fi am_display_PYTHON=python ], [ dnl A version check is needed. if test -n "$PYTHON"; then # If the user set $PYTHON, use it and don't search something else. AC_MSG_CHECKING([whether $PYTHON version >= $1]) AM_PYTHON_CHECK_VERSION([$PYTHON], [$1], [AC_MSG_RESULT(yes)], [AC_MSG_ERROR(too old)]) am_display_PYTHON=$PYTHON else # Otherwise, try each interpreter until we find one that satisfies # VERSION. AC_CACHE_CHECK([for a Python interpreter with version >= $1], [am_cv_pathless_PYTHON],[ for am_cv_pathless_PYTHON in _AM_PYTHON_INTERPRETER_LIST none; do test "$am_cv_pathless_PYTHON" = none && break AM_PYTHON_CHECK_VERSION([$am_cv_pathless_PYTHON], [$1], [break]) done]) # Set $PYTHON to the absolute path of $am_cv_pathless_PYTHON. if test "$am_cv_pathless_PYTHON" = none; then PYTHON=: else AC_PATH_PROG([PYTHON], [$am_cv_pathless_PYTHON]) fi am_display_PYTHON=$am_cv_pathless_PYTHON fi ]) if test "$PYTHON" = :; then dnl Run any user-specified action, or abort. m4_default([$3], [AC_MSG_ERROR([no suitable Python interpreter found])]) else dnl Query Python for its version number. Getting [:3] seems to be dnl the best way to do this; it's what "site.py" does in the standard dnl library. AC_CACHE_CHECK([for $am_display_PYTHON version], [am_cv_python_version], [am_cv_python_version=`$PYTHON -c "import sys; print sys.version[[:3]]"`]) AC_SUBST([PYTHON_VERSION], [$am_cv_python_version]) dnl Use the values of $prefix and $exec_prefix for the corresponding dnl values of PYTHON_PREFIX and PYTHON_EXEC_PREFIX. These are made dnl distinct variables so they can be overridden if need be. However, dnl general consensus is that you shouldn't need this ability. AC_SUBST([PYTHON_PREFIX], ['${prefix}']) AC_SUBST([PYTHON_EXEC_PREFIX], ['${exec_prefix}']) dnl At times (like when building shared libraries) you may want dnl to know which OS platform Python thinks this is. AC_CACHE_CHECK([for $am_display_PYTHON platform], [am_cv_python_platform], [am_cv_python_platform=`$PYTHON -c "import sys; print sys.platform"`]) AC_SUBST([PYTHON_PLATFORM], [$am_cv_python_platform]) dnl Set up 4 directories: dnl pythondir -- where to install python scripts. This is the dnl site-packages directory, not the python standard library dnl directory like in previous automake betas. This behavior dnl is more consistent with lispdir.m4 for example. dnl Query distutils for this directory. distutils does not exist in dnl Python 1.5, so we fall back to the hardcoded directory if it dnl doesn't work. AC_CACHE_CHECK([for $am_display_PYTHON script directory], [am_cv_python_pythondir], [am_cv_python_pythondir=`$PYTHON -c "from distutils import sysconfig; print sysconfig.get_python_lib(0,0,prefix='$PYTHON_PREFIX')" 2>/dev/null || echo "$PYTHON_PREFIX/lib/python$PYTHON_VERSION/site-packages"`]) AC_SUBST([pythondir], [$am_cv_python_pythondir]) dnl pkgpythondir -- $PACKAGE directory under pythondir. Was dnl PYTHON_SITE_PACKAGE in previous betas, but this naming is dnl more consistent with the rest of automake. AC_SUBST([pkgpythondir], [\${pythondir}/$PACKAGE]) dnl pyexecdir -- directory for installing python extension modules dnl (shared libraries) dnl Query distutils for this directory. distutils does not exist in dnl Python 1.5, so we fall back to the hardcoded directory if it dnl doesn't work. AC_CACHE_CHECK([for $am_display_PYTHON extension module directory], [am_cv_python_pyexecdir], [am_cv_python_pyexecdir=`$PYTHON -c "from distutils import sysconfig; print sysconfig.get_python_lib(1,0,prefix='$PYTHON_EXEC_PREFIX')" 2>/dev/null || echo "${PYTHON_EXEC_PREFIX}/lib/python${PYTHON_VERSION}/site-packages"`]) AC_SUBST([pyexecdir], [$am_cv_python_pyexecdir]) dnl pkgpyexecdir -- $(pyexecdir)/$(PACKAGE) AC_SUBST([pkgpyexecdir], [\${pyexecdir}/$PACKAGE]) dnl Run any user-specified action. $2 fi ]) # AM_PYTHON_CHECK_VERSION(PROG, VERSION, [ACTION-IF-TRUE], [ACTION-IF-FALSE]) # --------------------------------------------------------------------------- # Run ACTION-IF-TRUE if the Python interpreter PROG has version >= VERSION. # Run ACTION-IF-FALSE otherwise. # This test uses sys.hexversion instead of the string equivalent (first # word of sys.version), in order to cope with versions such as 2.2c1. # hexversion has been introduced in Python 1.5.2; it's probably not # worth to support older versions (1.5.1 was released on October 31, 1998). AC_DEFUN([AM_PYTHON_CHECK_VERSION], [prog="import sys, string # split strings by '.' and convert to numeric. Append some zeros # because we need at least 4 digits for the hex conversion. minver = map(int, string.split('$2', '.')) + [[0, 0, 0]] minverhex = 0 for i in xrange(0, 4): minverhex = (minverhex << 8) + minver[[i]] sys.exit(sys.hexversion < minverhex)" AS_IF([AM_RUN_LOG([$1 -c "$prog"])], [$3], [$4])]) ocfs2-tools-ocfs2-tools-1.8.6/pythondev.m4000066400000000000000000000017371347147137200203530ustar00rootroot00000000000000## Find the install dirs for the python installation. ## By James Henstridge dnl a macro to check for ability to create python extensions dnl AM_CHECK_PYTHON_HEADERS([ACTION-IF-POSSIBLE], [ACTION-IF-NOT-POSSIBLE]) dnl function also defines PYTHON_INCLUDES AC_DEFUN([AM_CHECK_PYTHON_HEADERS], [AC_REQUIRE([AM_PATH_PYTHON]) AC_MSG_CHECKING(for headers required to compile python extensions) dnl deduce PYTHON_INCLUDES py_prefix=`$PYTHON -c "import sys; print sys.prefix"` py_exec_prefix=`$PYTHON -c "import sys; print sys.exec_prefix"` PYTHON_INCLUDES="-I${py_prefix}/include/python${PYTHON_VERSION}" if test "$py_prefix" != "$py_exec_prefix"; then PYTHON_INCLUDES="$PYTHON_INCLUDES -I${py_exec_prefix}/include/python${PYTHON_VERSION}" fi AC_SUBST(PYTHON_INCLUDES) dnl check if the headers exist: save_CPPFLAGS="$CPPFLAGS" CPPFLAGS="$CPPFLAGS $PYTHON_INCLUDES" AC_TRY_CPP([#include ],dnl [AC_MSG_RESULT(found) $1],dnl [AC_MSG_RESULT(not found) $2]) CPPFLAGS="$save_CPPFLAGS" ]) ocfs2-tools-ocfs2-tools-1.8.6/rpmarch.guess000077500000000000000000000026221347147137200205720ustar00rootroot00000000000000#! /bin/sh mode="$1" srcdir="$2" host_cpu= QUERYFILE= if test -f /etc/redhat-release ; then QUERYFILE=/etc/redhat-release elif test -f /etc/SuSE-release ; then QUERYFILE=/etc/SuSE-release elif test -f /etc/UnitedLinux-release ; then QUERYFILE=/etc/UnitedLinux-release fi if test -n "$QUERYFILE"; then host_cpu="`rpm -qf $QUERYFILE --queryformat \"%{ARCH}\"`" fi if test -z "$host_cpu" -o "$host_cpu" = "noarch" ; then host_alias=`$srcdir/config.guess` host=`$srcdir/config.sub $host_alias` host_cpu=`echo $host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'` fi case "$host_cpu" in x86_64|ia64|s390x) TOOLSARCH="" ;; i386|i486|i586|i686|i786|k6|k7) TOOLSARCH="i386" ;; ppc|ppc64|ppciseries|ppcpseries|ppc64iseries|ppc64pseries|powerpc|powerpc64) TOOLSARCH="ppc" ;; *) echo "rpmarch.guess: Warning: unknown RPM CPU architecture: $host_cpu" >&2 TOOLSARCH="" ;; esac # Only a few of these need to be overwritten from RPM's default case "$host_cpu" in i586) MODULEARCH="$host_cpu" ;; i386) MODULEARCH="i686" ;; *) MODULEARCH="" ;; esac case "$mode" in module) if [ -n "$MODULEARCH" ] ; then echo "--target $MODULEARCH" fi ;; tools) if [ -n "$TOOLSARCH" ] ; then echo "--target $TOOLSARCH" fi ;; *) echo "rpmarch.guess: Invalid mode: $mode" >&2 echo "error" exit 1 ;; esac exit 0 ocfs2-tools-ocfs2-tools-1.8.6/runlog.m4000066400000000000000000000022101347147137200176240ustar00rootroot00000000000000# Copyright (C) 2001, 2003 Free Software Foundation, Inc. -*- Autoconf -*- # 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., 51 Franklin St, Fifth Floor, Boston, MA # 02110-1301, USA. # AM_RUN_LOG(COMMAND) # ------------------- # Run COMMAND, save the exit status in ac_status, and log it. # (This has been adapted from Autoconf's _AC_RUN_LOG macro.) AC_DEFUN([AM_RUN_LOG], [{ echo "$as_me:$LINENO: $1" >&AS_MESSAGE_LOG_FD ($1) >&AS_MESSAGE_LOG_FD 2>&AS_MESSAGE_LOG_FD ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD (exit $ac_status); }]) ocfs2-tools-ocfs2-tools-1.8.6/sizetest/000077500000000000000000000000001347147137200177335ustar00rootroot00000000000000ocfs2-tools-ocfs2-tools-1.8.6/sizetest/.gitignore000066400000000000000000000000651347147137200217240ustar00rootroot00000000000000cscope* stamp-md5 *.sw? *.cmd sizetest.ocfs2 *.8 *.d ocfs2-tools-ocfs2-tools-1.8.6/sizetest/Makefile000066400000000000000000000004641347147137200213770ustar00rootroot00000000000000TOPDIR = .. include $(TOPDIR)/Preamble.make UNINST_PROGRAMS = sizetest.ocfs2 INCLUDES = -I$(TOPDIR)/include DEFINES = -DVERSION=\"$(VERSION)\" CFILES = sizetest.c OBJS = $(subst .c,.o,$(CFILES)) DIST_FILES = $(CFILES) sizetest.ocfs2: $(OBJS) $(LIBOCFS2_DEPS) $(LINK) include $(TOPDIR)/Postamble.make ocfs2-tools-ocfs2-tools-1.8.6/sizetest/sizes.txt000066400000000000000000000101251347147137200216300ustar00rootroot00000000000000[off] ocfs2_extent_rec [size] 0x000 e_cpos +0x04 0x004 e_int_clusters +0x04 0x008 e_blkno +0x08 Total 0x010 [off] ocfs2_chain_rec [size] 0x000 c_free +0x04 0x004 c_total +0x04 0x008 c_blkno +0x08 Total 0x010 [off] ocfs2_extent_list [size] 0x000 l_tree_depth +0x02 0x002 l_count +0x02 0x004 l_next_free_rec +0x02 0x006 l_reserved1 +0x02 0x008 l_reserved2 +0x08 0x010 l_recs +0x00 Total 0x010 [off] ocfs2_chain_list [size] 0x000 cl_cpg +0x02 0x002 cl_bpc +0x02 0x004 cl_count +0x02 0x006 cl_next_free_rec +0x02 0x008 cl_reserved1 +0x08 0x010 cl_recs +0x00 Total 0x010 [off] ocfs2_extent_block [size] 0x000 h_signature +0x08 0x008 h_reserved1 +0x08 0x010 h_suballoc_slot +0x02 0x012 h_suballoc_bit +0x02 0x014 h_fs_generation +0x04 0x018 h_blkno +0x08 0x020 h_reserved3 +0x08 0x028 h_next_leaf_blk +0x08 0x030 h_list +0x10 Total 0x040 [off] ocfs2_super_block [size] 0x000 s_major_rev_level +0x02 0x002 s_minor_rev_level +0x02 0x004 s_mnt_count +0x02 0x006 s_max_mnt_count +0x02 0x008 s_state +0x02 0x00A s_errors +0x02 0x00C s_checkinterval +0x04 0x010 s_lastcheck +0x08 0x018 s_creator_os +0x04 0x01C s_feature_compat +0x04 0x020 s_feature_incompat +0x04 0x024 s_feature_ro_compat +0x04 0x028 s_root_blkno +0x08 0x030 s_system_dir_blkno +0x08 0x038 s_blocksize_bits +0x04 0x03C s_clustersize_bits +0x04 0x040 s_max_slots +0x02 0x042 s_tunefs_flag +0x02 0x044 s_reserved1 +0x04 0x048 s_first_cluster_group +0x08 0x050 s_label +0x40 0x090 s_uuid +0x10 Total 0x140 [off] ocfs2_local_alloc [size] 0x000 la_bm_off +0x04 0x004 la_size +0x02 0x006 la_reserved1 +0x02 0x008 la_reserved2 +0x08 0x010 la_bitmap +0x00 Total 0x010 [off] ocfs2_dinode [size] 0x000 i_signature +0x08 0x008 i_generation +0x04 0x00C i_suballoc_slot +0x02 0x00E i_suballoc_bit +0x02 0x010 i_reserved0 +0x04 0x014 i_clusters +0x04 0x018 i_uid +0x04 0x01C i_gid +0x04 0x020 i_size +0x08 0x028 i_mode +0x02 0x02A i_links_count +0x02 0x02C i_flags +0x04 0x030 i_atime +0x08 0x038 i_ctime +0x08 0x040 i_mtime +0x08 0x048 i_dtime +0x08 0x050 i_blkno +0x08 0x058 i_last_eb_blk +0x08 0x060 i_fs_generation +0x04 0x064 i_atime_nsec +0x04 0x068 i_ctime_nsec +0x04 0x06C i_mtime_nsec +0x04 0x070 i_attr +0x04 0x076 i_dyn_features +0x02 0x078 i_reserved2 +0x40 0x0B8 id1.i_pad1 +0x08 0x0B8 id1.dev1.i_rdev +0x08 0x0B8 id1.bitmap1.i_used +0x04 0x0BC id1.bitmap1.i_total +0x04 0x0B8 id1.journal1.ij_flags +0x04 0x0BC id1.journal1.ij_recovery_generation +0x04 0x0C0 id2.i_super +0x140 0x0C0 id2.i_lab +0x10 0x0C0 id2.i_chain +0x10 0x0C0 id2.i_list +0x10 0x0C0 id2.i_symlink +0x00 Total 0x208 [off] struct ocfs2_dir_entry [size] 0x000 inode +0x08 0x008 rec_len +0x02 0x00A name_len +0x01 0x00B file_type +0x01 0x00C name +0xFF Total 0x10B [off] ocfs2_group_desc [size] 0x000 bg_signature +0x08 0x008 bg_size +0x02 0x00A bg_bits +0x02 0x00C bg_free_bits_count +0x02 0x00E bg_chain +0x02 0x010 bg_generation +0x04 0x014 bg_reserved1 +0x04 0x018 bg_next_group +0x08 0x020 bg_parent_dinode +0x08 0x028 bg_blkno +0x08 0x030 bg_reserved2 +0x10 0x040 bg_bitmap +0x00 Total 0x040 ocfs2-tools-ocfs2-tools-1.8.6/sizetest/sizetest.c000066400000000000000000000225371347147137200217620ustar00rootroot00000000000000/* * sizetest.c * * ocfs2 utility to check structure sizing on various ports * * Copyright (C) 2004 Oracle Corporation. All rights reserved. * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA. * * Authors: Sunil Mushran */ #include "ocfs2/ocfs2.h" #undef offsetof #define offsetof(TYPE, MEMBER) ((unsigned int) &((TYPE *)0)->MEMBER) #define ssizeof(TYPE, MEMBER) ((unsigned int) sizeof(((TYPE *)0)->MEMBER)) #define START_TYPE(TYPE) do { \ printf("[off]\t%-20s\t[size]\n", #TYPE); \ } while (0) #define SHOW_OFFSET(TYPE, MEMBER) do { \ printf("0x%03X\t%-20s\t+0x%02X\n", offsetof(TYPE, MEMBER), #MEMBER, ssizeof(TYPE, MEMBER)); \ } while (0) #define END_TYPE(TYPE) do {\ printf("\t%-20s\t0x%03X\n", "Total", sizeof(*((TYPE *)0))); \ } while (0) static void print_ocfs2_extent_rec(void) { START_TYPE(ocfs2_extent_rec); SHOW_OFFSET(struct ocfs2_extent_rec, e_cpos); SHOW_OFFSET(struct ocfs2_extent_rec, e_int_clusters); SHOW_OFFSET(struct ocfs2_extent_rec, e_blkno); END_TYPE(struct ocfs2_extent_rec); printf("\n"); } static void print_ocfs2_chain_rec(void) { START_TYPE(ocfs2_chain_rec); SHOW_OFFSET(struct ocfs2_chain_rec, c_free); SHOW_OFFSET(struct ocfs2_chain_rec, c_total); SHOW_OFFSET(struct ocfs2_chain_rec, c_blkno); END_TYPE(struct ocfs2_chain_rec); printf("\n"); } static void print_ocfs2_extent_list(void) { START_TYPE(ocfs2_extent_list); SHOW_OFFSET(struct ocfs2_extent_list, l_tree_depth); SHOW_OFFSET(struct ocfs2_extent_list, l_count); SHOW_OFFSET(struct ocfs2_extent_list, l_next_free_rec); SHOW_OFFSET(struct ocfs2_extent_list, l_reserved1); SHOW_OFFSET(struct ocfs2_extent_list, l_reserved2); SHOW_OFFSET(struct ocfs2_extent_list, l_recs); END_TYPE(struct ocfs2_extent_list); printf("\n"); } static void print_ocfs2_chain_list(void) { START_TYPE(ocfs2_chain_list); SHOW_OFFSET(struct ocfs2_chain_list, cl_cpg); SHOW_OFFSET(struct ocfs2_chain_list, cl_bpc); SHOW_OFFSET(struct ocfs2_chain_list, cl_count); SHOW_OFFSET(struct ocfs2_chain_list, cl_next_free_rec); SHOW_OFFSET(struct ocfs2_chain_list, cl_reserved1); SHOW_OFFSET(struct ocfs2_chain_list, cl_recs); END_TYPE(struct ocfs2_chain_list); printf("\n"); } static void print_ocfs2_extent_block(void) { START_TYPE(ocfs2_extent_block); SHOW_OFFSET(struct ocfs2_extent_block, h_signature); SHOW_OFFSET(struct ocfs2_extent_block, h_check); SHOW_OFFSET(struct ocfs2_extent_block, h_suballoc_slot); SHOW_OFFSET(struct ocfs2_extent_block, h_suballoc_bit); SHOW_OFFSET(struct ocfs2_extent_block, h_fs_generation); SHOW_OFFSET(struct ocfs2_extent_block, h_blkno); SHOW_OFFSET(struct ocfs2_extent_block, h_suballoc_loc); SHOW_OFFSET(struct ocfs2_extent_block, h_next_leaf_blk); SHOW_OFFSET(struct ocfs2_extent_block, h_list); END_TYPE(struct ocfs2_extent_block); printf("\n"); } static void print_ocfs2_super_block(void) { START_TYPE(ocfs2_super_block); SHOW_OFFSET(struct ocfs2_super_block, s_major_rev_level); SHOW_OFFSET(struct ocfs2_super_block, s_minor_rev_level); SHOW_OFFSET(struct ocfs2_super_block, s_mnt_count); SHOW_OFFSET(struct ocfs2_super_block, s_max_mnt_count); SHOW_OFFSET(struct ocfs2_super_block, s_state); SHOW_OFFSET(struct ocfs2_super_block, s_errors); SHOW_OFFSET(struct ocfs2_super_block, s_checkinterval); SHOW_OFFSET(struct ocfs2_super_block, s_lastcheck); SHOW_OFFSET(struct ocfs2_super_block, s_creator_os); SHOW_OFFSET(struct ocfs2_super_block, s_feature_compat); SHOW_OFFSET(struct ocfs2_super_block, s_feature_incompat); SHOW_OFFSET(struct ocfs2_super_block, s_feature_ro_compat); SHOW_OFFSET(struct ocfs2_super_block, s_root_blkno); SHOW_OFFSET(struct ocfs2_super_block, s_system_dir_blkno); SHOW_OFFSET(struct ocfs2_super_block, s_blocksize_bits); SHOW_OFFSET(struct ocfs2_super_block, s_clustersize_bits); SHOW_OFFSET(struct ocfs2_super_block, s_max_slots); SHOW_OFFSET(struct ocfs2_super_block, s_tunefs_flag); SHOW_OFFSET(struct ocfs2_super_block, s_uuid_hash); SHOW_OFFSET(struct ocfs2_super_block, s_first_cluster_group); SHOW_OFFSET(struct ocfs2_super_block, s_label); SHOW_OFFSET(struct ocfs2_super_block, s_uuid); SHOW_OFFSET(struct ocfs2_super_block, s_cluster_info); SHOW_OFFSET(struct ocfs2_super_block, s_xattr_inline_size); SHOW_OFFSET(struct ocfs2_super_block, s_reserved0); SHOW_OFFSET(struct ocfs2_super_block, s_dx_seed); SHOW_OFFSET(struct ocfs2_super_block, s_reserved2); END_TYPE(struct ocfs2_super_block); printf("\n"); } static void print_ocfs2_local_alloc(void) { START_TYPE(ocfs2_local_alloc); SHOW_OFFSET(struct ocfs2_local_alloc, la_bm_off); SHOW_OFFSET(struct ocfs2_local_alloc, la_size); SHOW_OFFSET(struct ocfs2_local_alloc, la_reserved1); SHOW_OFFSET(struct ocfs2_local_alloc, la_reserved2); SHOW_OFFSET(struct ocfs2_local_alloc, la_bitmap); END_TYPE(struct ocfs2_local_alloc); printf("\n"); } static void print_ocfs2_dinode(void) { START_TYPE(ocfs2_dinode); SHOW_OFFSET(struct ocfs2_dinode, i_signature); SHOW_OFFSET(struct ocfs2_dinode, i_generation); SHOW_OFFSET(struct ocfs2_dinode, i_suballoc_slot); SHOW_OFFSET(struct ocfs2_dinode, i_suballoc_bit); SHOW_OFFSET(struct ocfs2_dinode, i_links_count_hi); SHOW_OFFSET(struct ocfs2_dinode, i_xattr_inline_size); SHOW_OFFSET(struct ocfs2_dinode, i_clusters); SHOW_OFFSET(struct ocfs2_dinode, i_uid); SHOW_OFFSET(struct ocfs2_dinode, i_gid); SHOW_OFFSET(struct ocfs2_dinode, i_size); SHOW_OFFSET(struct ocfs2_dinode, i_mode); SHOW_OFFSET(struct ocfs2_dinode, i_links_count); SHOW_OFFSET(struct ocfs2_dinode, i_flags); SHOW_OFFSET(struct ocfs2_dinode, i_atime); SHOW_OFFSET(struct ocfs2_dinode, i_ctime); SHOW_OFFSET(struct ocfs2_dinode, i_mtime); SHOW_OFFSET(struct ocfs2_dinode, i_dtime); SHOW_OFFSET(struct ocfs2_dinode, i_blkno); SHOW_OFFSET(struct ocfs2_dinode, i_last_eb_blk); SHOW_OFFSET(struct ocfs2_dinode, i_fs_generation); SHOW_OFFSET(struct ocfs2_dinode, i_atime_nsec); SHOW_OFFSET(struct ocfs2_dinode, i_ctime_nsec); SHOW_OFFSET(struct ocfs2_dinode, i_mtime_nsec); SHOW_OFFSET(struct ocfs2_dinode, i_attr); SHOW_OFFSET(struct ocfs2_dinode, i_orphaned_slot); SHOW_OFFSET(struct ocfs2_dinode, i_dyn_features); SHOW_OFFSET(struct ocfs2_dinode, i_xattr_loc); SHOW_OFFSET(struct ocfs2_dinode, i_check); SHOW_OFFSET(struct ocfs2_dinode, i_dx_root); SHOW_OFFSET(struct ocfs2_dinode, i_refcount_loc); SHOW_OFFSET(struct ocfs2_dinode, i_suballoc_loc); SHOW_OFFSET(struct ocfs2_dinode, i_reserved2); SHOW_OFFSET(struct ocfs2_dinode, id1.i_pad1); SHOW_OFFSET(struct ocfs2_dinode, id1.dev1.i_rdev); SHOW_OFFSET(struct ocfs2_dinode, id1.bitmap1.i_used); SHOW_OFFSET(struct ocfs2_dinode, id1.bitmap1.i_total); SHOW_OFFSET(struct ocfs2_dinode, id1.journal1.ij_flags); SHOW_OFFSET(struct ocfs2_dinode, id1.journal1.ij_recovery_generation); SHOW_OFFSET(struct ocfs2_dinode, id2.i_super); SHOW_OFFSET(struct ocfs2_dinode, id2.i_lab); SHOW_OFFSET(struct ocfs2_dinode, id2.i_chain); SHOW_OFFSET(struct ocfs2_dinode, id2.i_list); SHOW_OFFSET(struct ocfs2_dinode, id2.i_dealloc); SHOW_OFFSET(struct ocfs2_dinode, id2.i_data); SHOW_OFFSET(struct ocfs2_dinode, id2.i_symlink); END_TYPE(struct ocfs2_dinode); printf("\n"); } static void print_ocfs2_dir_entry(void) { START_TYPE(struct ocfs2_dir_entry); SHOW_OFFSET(struct ocfs2_dir_entry, inode); SHOW_OFFSET(struct ocfs2_dir_entry, rec_len); SHOW_OFFSET(struct ocfs2_dir_entry, name_len); SHOW_OFFSET(struct ocfs2_dir_entry, file_type); SHOW_OFFSET(struct ocfs2_dir_entry, name); END_TYPE(struct ocfs2_dir_entry); printf("\n"); } static void print_ocfs2_group_desc(void) { START_TYPE(ocfs2_group_desc); SHOW_OFFSET(struct ocfs2_group_desc, bg_signature); SHOW_OFFSET(struct ocfs2_group_desc, bg_size); SHOW_OFFSET(struct ocfs2_group_desc, bg_bits); SHOW_OFFSET(struct ocfs2_group_desc, bg_free_bits_count); SHOW_OFFSET(struct ocfs2_group_desc, bg_chain); SHOW_OFFSET(struct ocfs2_group_desc, bg_generation); SHOW_OFFSET(struct ocfs2_group_desc, bg_reserved1); SHOW_OFFSET(struct ocfs2_group_desc, bg_next_group); SHOW_OFFSET(struct ocfs2_group_desc, bg_parent_dinode); SHOW_OFFSET(struct ocfs2_group_desc, bg_blkno); SHOW_OFFSET(struct ocfs2_group_desc, bg_check); SHOW_OFFSET(struct ocfs2_group_desc, bg_reserved2); SHOW_OFFSET(struct ocfs2_group_desc, bg_bitmap); SHOW_OFFSET(struct ocfs2_group_desc, bg_bitmap_filler); SHOW_OFFSET(struct ocfs2_group_desc, bg_list); END_TYPE(struct ocfs2_group_desc); printf("\n"); } int main(int argc, char **argv) { print_ocfs2_extent_rec(); print_ocfs2_chain_rec(); print_ocfs2_extent_list(); print_ocfs2_chain_list(); print_ocfs2_extent_block(); print_ocfs2_super_block(); print_ocfs2_local_alloc(); print_ocfs2_dinode(); print_ocfs2_dir_entry(); print_ocfs2_group_desc(); return 0; } ocfs2-tools-ocfs2-tools-1.8.6/svnrev.guess000077500000000000000000000102561347147137200204630ustar00rootroot00000000000000#!/bin/sh # # This script creates a package version for packages. The package # version is the "release" or other version attached by the packaging # software (eg, RPM or Dpkg) after the upstream software version. # # This software must be invoked like so: # # svnrev.guess # # and it expects that release tags are of the form: # # /tags/- # # Note that release tag versioning expects a working connection to # the repository (for SVN log), but all other branches don't require it. # # If there is no .svn directory or it cannot discover the repository # version, the script sets a version of 0.local. # if [ $# -lt 1 ] then echo "Usage: svnrev.guess " >&2 exit 1 fi PACKAGE="$1" getstat() { if [ ! -d .svn ] then return fi svn stat -v | awk 'BEGIN{modified = "false";lastrev = 0} /Status against/{next} /^\?/{next} { col1=substr($0, 0, 1); if (col1 != " ") {modified = "true"} sub(/^......../, ""); if ($1 > lastrev) { lastrev=$1 } } END{print modified,lastrev}' } # # Branches that are not releases get a work-in-progress package version. # This is 0.[m]. The 0. ensures that an upcoming real # release, with an package version of 1, will supercede this package. # The "m" is added if the working tree has modifications to distinguish # it from a committed revision. # # If there is no repository or there is a problem getting the revision, # the package version is 0.local. # workingrev() { STATINFO="$(getstat)" MODIFIED=$(echo "$STATINFO" | cut -f1 -d" ") LASTREV=$(echo "$STATINFO" | cut -f2 -d" ") if [ -z "$LASTREV" ] then PKG_VERSION=0.local else PKG_VERSION=0.${LASTREV} if [ "$MODIFIED" = "true" ] then PKG_VERSION="${PKG_VERSION}m" fi fi echo "$PKG_VERSION" } # # If the branch is a tag (tags/project-x.y), it needs a release-based # package version (-1, -2, etc). Generally, it will be the tip of the # branch (package version 1). However, if there is a slight release # fixup, that warrants bumping the package version. # # The logic is pretty simple. Walk the history looking for the # creation of the tag (A /tags/project-x.y). For each revsion later # than the creation of the tag, bump the package version. # releaserev() { BRANCH="$1" BRANCH_SEARCH="`echo "$BRANCH" | sed -e 's/\//\\\\\//g'`" svn log -v 2>&1 | awk 'BEGIN{rev=0} /^r[0-9]+ \|/{rev += 1} /^ +A \/'"$BRANCH_SEARCH"'$/{exit} /^ +A \/'"$BRANCH_SEARCH"' (.*)$/{exit} END{print rev}' } # # Tag branches are releases, and need a release-style package version. # All other branches are non-release, and get an obviously # work-in-progress package version. # # guessbranch() expects the standard Subversion layout of # /trunk # /branches/ # /tags/ # guessbranch() { if [ ! -d .svn ] then return fi svn info | awk '/URL: .*\/trunk\/?$/{print "trunk"; exit 0} /URL: .*\/branches\/[^/]+\/?/{ gsub(/\/$/, "", $2); sub(/^.*\/branches\//, "", $2); print "branches/"$2; exit 0; } /URL: .*\/tags\/[^/]+\/?/{ gsub(/\/$/, "", $2); sub(/^.*\/tags\//, "", $2); print "tags/"$2; exit 0; }' } if ! which svn 1>/dev/null 2>&1 then echo "0.local" else BRANCH=$(guessbranch) case "$BRANCH" in tags/${PACKAGE}*) releaserev "$BRANCH" ;; *) workingrev ;; esac fi ocfs2-tools-ocfs2-tools-1.8.6/tests/000077500000000000000000000000001347147137200172235ustar00rootroot00000000000000ocfs2-tools-ocfs2-tools-1.8.6/tests/fsck-test.sh000077500000000000000000000117531347147137200214740ustar00rootroot00000000000000#!/bin/bash # # fsck-test.sh - Test fsck.ocfs2 # # This script tests fsck.ocfs2's ability to fix a corrupted volume. # It uses the fswreck utility to corrupt the volume. # # Copyright (C) 2011 Oracle. All rights reserved. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public # License, version 2, as published by the Free Software Foundation. # # 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. # ################################################################ APP=$(basename ${0}) PATH=$PATH:$(dirname ${0}):/sbin CHOWN=$(which chown) DATE=$(which date) ECHO=$(which echo) MKDIR=$(which mkdir) SEQ=$(which seq) SUDO="$(which sudo) -u root" WHOAMI=$(which whoami) USERID=$(${WHOAMI}) MKFS_BIN="${SUDO} `which mkfs.ocfs2`" FSCK_BIN="${SUDO} $(which fsck.ocfs2)" FSWRECK_BIN="${SUDO} $(which fswreck)" # log_message message log_message() { ${ECHO} "`${DATE} +\"%F %H:%M:%S\"` $@" ${ECHO} "`${DATE} +\"%F %H:%M:%S\"` $@" >> ${LOGFILE} } log_start() { log_message $@ START=$(date +%s) } # log_end $? log_end() { if [ "$#" -lt "1" ]; then ${ECHO} "Error in log_end()" exit 1 fi rc=$1 shift END=$(date +%s) DIFF=$(( ${END} - ${START} )) if [ $rc -ne 0 ]; then log_message "$@ (${DIFF} secs) ** FAIL **" else log_message "$@ (${DIFF} secs) succ" fi START=0 } get_bits() { if [ "$#" -lt "1" ]; then ${ECHO} "Error in get_bits()" exit 1 fi val=$1 for i in `${SEQ} 1 31` do if [ $[2 ** $i] -eq ${val} ]; then return $i fi done exit 1 } # do_format() code device outlog do_format() { if [ "$#" -lt "3" ]; then ${ECHO} "Error in do_format() $@" exit 1 fi code=$1 device=$2 outlog=$3 MKFSOPTS=$(${FSWRECK_BIN} -C ${code} -M 2>>${outlog}) RET=$? if [ $RET -ne 0 ] then ${ECHO} "ERROR: fswreck -C ${code} failed with ${RET}" >>${outlog} exit 1 fi cmd="${MKFS_BIN} -x ${MKFSOPTS} -L fswreck ${device}" $($cmd >>${outlog} 2>&1) RET=$? if [ $RET -ne 0 ]; then ${ECHO} "$cmd" >>${outlog} ${ECHO} "ERROR: Failed with ${RET}" >>${outlog} exit 1 fi } # do_fswreck() corruptcode device outlog do_fswreck() { if [ "$#" -lt "3" ]; then ${ECHO} "Error in do_fswreck() $@" exit 1 fi corruptcode=$1 device=$2 outlog=$3 cmd="${FSWRECK_BIN} -C ${corruptcode} ${device}" $($cmd >>${outlog} 2>&1) RET=$? if [ $RET -ne 0 ]; then ${ECHO} "$cmd" >>${outlog} ${ECHO} "ERROR: Failed with ${RET}" >>${outlog} return 1 fi return 0 } # do_fsck() device outlog do_fsck() { if [ "$#" -lt "2" ]; then ${ECHO} "Error in do_fsck() $@" exit 1 fi device=$1 outlog=$2 cmd="${FSCK_BIN} -fy ${device}" $($cmd >>${outlog} 2>&1) RET=$? if [ $RET -ne 0 ]; then ${ECHO} "$cmd" >>${outlog} ${ECHO} "ERROR: Failed with ${RET}" >>${outlog} return 1 fi return 0 } # do_mkdir DIR do_mkdir() { if [ "$#" -lt "1" ]; then ${ECHO} "Error in do_mkdir()" exit 1 fi ${SUDO} ${MKDIR} -p $1 if [ $? -ne 0 ]; then ${ECHO} "ERROR: mkdir $1" exit 1 fi ${SUDO} ${CHOWN} -R ${USERID} $1 } # # # MAIN # # usage() { ${ECHO} -n "usage: ${APP} -f path-to-fswreck -F path-to-fsck " ${ECHO} "-s startcode -e endcode -l logdir -d device" exit 1 } STARTCODE=-1 ENDCODE=-1 MAXCODE=500 while getopts "f:F:d:l:s:e:h?" args do case "$args" in f) FSWRECK_BIN="$OPTARG";; F) FSCK_BIN="$OPTARG";; d) DEVICE="$OPTARG";; l) OUTDIR="$OPTARG";; s) STARTCODE="$OPTARG";; e) ENDCODE="$OPTARG";; h) usage;; ?) usage;; esac done if [ -z ${DEVICE} ] ; then ${ECHO} "ERROR: No device" usage elif [ ! -b ${DEVICE} ] ; then ${ECHO} "ERROR: Invalid device ${DEVICE}" exit 1 fi if [ -z ${OUTDIR} ]; then ${ECHO} "ERROR: No logdir" usage fi if [ ${STARTCODE} -eq -1 ]; then STARTCODE=0 fi if [ ${ENDCODE} -eq -1 ]; then ENDCODE=${MAXCODE} fi if [ ${ENDCODE} -lt ${STARTCODE} ]; then ENDCODE=${STARTCODE} fi RUNDATE=`${DATE} +%F_%H:%M` LOGDIR=${OUTDIR}/${RUNDATE} LOGFILE=${LOGDIR}/fsck_test.log do_mkdir ${LOGDIR} ${ECHO} "Output log is ${LOGFILE}" STARTRUN=$(date +%s) log_message "*** Start fsck test ***" FAIL=0 PASS=0 for code in $(seq ${STARTCODE} ${ENDCODE}) do # Check code validity CODE=$(${FSWRECK_BIN} -L ${code} 2>>${LOGFILE}) rc=$? if [ $rc -ne 0 ] then break fi log_start "Corrupt code $code $CODE" OUTLOG=${LOGDIR}/corrupt_code_${code}.out # Format failure stops the test do_format ${code} ${DEVICE} ${OUTLOG} # Run fsck only if fswreck succeeds do_fswreck ${code} ${DEVICE} ${OUTLOG} rc=$? if [ $rc -eq 0 ] then do_fsck ${DEVICE} ${OUTLOG} rc=$? fi log_end $rc "Corrupt code $code $CODE" if [ $rc -eq 0 ] then PASS=$[$PASS + 1]; else FAIL=$[$FAIL + 1]; fi done ENDRUN=$(date +%s) DIFF=$(( ${ENDRUN} - ${STARTRUN} )) log_message "*** End Single Node test ***" log_message "Pass: $PASS Fail: $FAIL" log_message "Total Runtime ${DIFF} seconds" ocfs2-tools-ocfs2-tools-1.8.6/tests/mkfs-test.sh000077500000000000000000000170751347147137200215110ustar00rootroot00000000000000#!/bin/bash # # mkfs-test.sh - Test mkfs.ocfs2 # # Copyright (C) 2011, 2012 Oracle. All rights reserved. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public # License, version 2, as published by the Free Software Foundation. # # 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. # ################################################################ APP=$(basename ${0}) PATH=$PATH:$(dirname ${0}):/sbin CHOWN=$(which chown) DATE=$(which date) ECHO=$(which echo) MKDIR=$(which mkdir) SEQ=$(which seq) AWK=$(which awk) SUDO="$(which sudo) -u root" WHOAMI=$(which whoami) USERID=$(${WHOAMI}) MKFS_BIN="${SUDO} `which mkfs.ocfs2`" FSCK_BIN="${SUDO} $(which fsck.ocfs2)" TUNE_BIN="${SUDO} $(which tunefs.ocfs2)" DEBUG_BIN="${SUDO} $(which debugfs.ocfs2)" # log_message message log_message() { ${ECHO} "`${DATE} +\"%F %H:%M:%S\"` $@" ${ECHO} "`${DATE} +\"%F %H:%M:%S\"` $@" >> ${LOGFILE} } log_start() { log_message $@ START=$(date +%s) } # log_end $? log_end() { if [ "$#" -lt "1" ]; then ${ECHO} "Error in log_end()" exit 1 fi rc=$1 shift END=$(date +%s) DIFF=$(( ${END} - ${START} )) if [ $rc -ne 0 ]; then log_message "$@ (${DIFF} secs) ** FAIL **" else log_message "$@ (${DIFF} secs) succ" fi START=0 } # epilogue rc epilogue() { if [ "$#" -lt "1" ]; then ${ECHO} "Error in epilogue()" exit 1 fi rc=$1 if [ $rc -eq 0 ] then PASS=$[$PASS + 1]; else FAIL=$[$FAIL + 1]; fi TESTNUM=$[${TESTNUM} + 1] } # fsck_volume device outlog fsck_volume() { if [ "$#" -lt "2" ]; then ${ECHO} "Error in fsck_volume() $@" exit 1 fi device=$1 outlog=$2 cmd="${FSCK_BIN} -fy ${device}" $($cmd >>${outlog} 2>&1) RET=$? if [ $RET -ne 0 ]; then ${ECHO} "$cmd" >>${outlog} ${ECHO} "ERROR: Failed with ${RET}" >>${outlog} return 1 fi return 0 } # verify_block_cluster blksz cltsz device outlog verify_block_cluster() { if [ "$#" -lt "4" ]; then ${ECHO} "Error in verify_block_cluster() $@" exit 1 fi blksz=$1 cltsz=$2 device=$3 outlog=$4 out=$(${TUNE_BIN} -Q "B=%B;C=%T;" ${device} 2>&1) echo ${out} >> ${outlog} if [ "${out}" != "B=${blksz};C=${cltsz};" ] then ${ECHO} "ERROR: Incorrect block or cluster size" >>${outlog} return 1 fi return 0 } # verify_journal_size jrnlsz device outlog verify_journal_size() { if [ "$#" -lt "3" ]; then ${ECHO} "Error in verify_journal_size() $@" exit 1 fi jrnlsz=$1 device=$2 outlog=$3 ${DEBUG_BIN} -R "stat //journal:0000" ${device} >>${outlog} 2>&1 size=$(${AWK} '/User:/ {print $8}' $outlog) if [ "${size}" != "$[${jrnlsz} * 1024 * 1024]" ] then ${ECHO} "ERROR: Incorrect journal size" >>${outlog} return 1 fi return 0 } # verify_node_slots slots device outlog verify_node_slots() { if [ "$#" -lt "3" ]; then ${ECHO} "Error in verify_node_slots() $@" exit 1 fi slots=$1 device=$2 outlog=$3 out=$(${TUNE_BIN} -Q "N=%N;" ${device} 2>&1) echo ${out} >> ${outlog} if [ "${out}" != "N=${slots};" ] then ${ECHO} "ERROR: Incorrect slots" >>${outlog} return 1 fi return 0 } # verify_vol_label label device outlog verify_vol_label() { if [ "$#" -lt "3" ]; then ${ECHO} "Error in verify_vol_label() $@" exit 1 fi label=$1 device=$2 outlog=$3 out=$(${TUNE_BIN} -Q "V=%V;" ${device} 2>&1) echo ${out} >> ${outlog} if [ "${out}" != "V=${label};" ] then ${ECHO} "ERROR: Incorrect label" >>${outlog} return 1 fi return 0 } # do_mkdir dir do_mkdir() { if [ "$#" -lt "1" ]; then ${ECHO} "Error in do_mkdir()" exit 1 fi ${SUDO} ${MKDIR} -p $1 if [ $? -ne 0 ]; then ${ECHO} "ERROR: mkdir $1" exit 1 fi ${SUDO} ${CHOWN} -R ${USERID} $1 } # # # MAIN # # usage() { ${ECHO} "usage: ${APP} -m path-to-mkfs -l logdir -d device" exit 1 } while getopts "m:d:l:h?" args do case "$args" in m) MKFS_BIN="$OPTARG";; d) DEVICE="$OPTARG";; l) OUTDIR="$OPTARG";; h) usage;; ?) usage;; esac done if [ -z ${DEVICE} ] ; then ${ECHO} "ERROR: No device" usage elif [ ! -b ${DEVICE} ] ; then ${ECHO} "ERROR: Invalid device ${DEVICE}" exit 1 fi if [ -z ${OUTDIR} ]; then ${ECHO} "ERROR: No logdir" usage fi RUNDATE=`${DATE} +%F_%H:%M` LOGDIR=${OUTDIR}/${RUNDATE} LOGFILE=${LOGDIR}/mkfs_test.log do_mkdir ${LOGDIR} ${ECHO} "Output log is ${LOGFILE}" STARTRUN=$(date +%s) log_message "*** Start mkfs test ***" FAIL=0 PASS=0 TESTNUM=1 ### Test all combinations of blocksizes and clustersizes for blksz in 512 1024 2048 4096 do for cltsz in 4096 8192 16384 32768 65536 131072 262144 524288 1048576 do TEST="Test ${TESTNUM}: -b ${blksz} -C ${cltsz}" log_start "${TEST}" OUTLOG=${LOGDIR}/mkfs_test_${TESTNUM}.out # format ${MKFS_BIN} -x -M local -L mkfstest -b ${blksz} -C ${cltsz} ${DEVICE} >>${OUTLOG} 2>&1 rc=$? if [ $rc -eq 0 ] then # fsck fsck_volume "${DEVICE}" "${OUTLOG}" rc=$? if [ $rc -eq 0 ] then verify_block_cluster ${blksz} ${cltsz} "${DEVICE}" "${OUTLOG}" rc=$? fi fi log_end $rc "${TEST}" epilogue $rc done done ### Test -J size 4M, 64M, 128M, 256M for jrnlsz in 4 64 128 256 do TEST="Test ${TESTNUM}: -J size=${jrnlsz}M" log_start "${TEST}" OUTLOG=${LOGDIR}/mkfs_test_${TESTNUM}.out # format ${MKFS_BIN} -x -M local -L mkfstest -J size=${jrnlsz}M ${DEVICE} >>${OUTLOG} 2>&1 rc=$? if [ $rc -eq 0 ] then # fsck fsck_volume "${DEVICE}" "${OUTLOG}" rc=$? if [ $rc -eq 0 ] then verify_journal_size ${jrnlsz} "${DEVICE}" "${OUTLOG}" rc=$? fi fi log_end $rc "${TEST}" epilogue $rc done ### Test -N 2 8 16 32 64 128 255 for slots in 2 8 16 32 64 128 255 do TEST="Test ${TESTNUM}: -N ${slots}" log_start "${TEST}" OUTLOG=${LOGDIR}/mkfs_test_${TESTNUM}.out # format ${MKFS_BIN} -x -M local -L mkfstest -J size=4M -N ${slots} ${DEVICE} >>${OUTLOG} 2>&1 rc=$? if [ $rc -eq 0 ] then # fsck fsck_volume "${DEVICE}" "${OUTLOG}" rc=$? if [ $rc -eq 0 ] then verify_node_slots ${slots} "${DEVICE}" "${OUTLOG}" rc=$? fi fi log_end $rc "${TEST}" epilogue $rc done #### Test -T fstype for fstype in mail datafiles vmstore do TEST="Test ${TESTNUM}: -T ${fstype}" log_start "${TEST}" OUTLOG=${LOGDIR}/mkfs_test_${TESTNUM}.out # format ${MKFS_BIN} -x -M local -L mkfstest -J size=4M -T ${fstype} ${DEVICE} >>${OUTLOG} 2>&1 rc=$? if [ $rc -eq 0 ] then # fsck fsck_volume "${DEVICE}" "${OUTLOG}" rc=$? fi log_end $rc "${TEST}" epilogue $rc done #### Test -L label for label in mylabel label6789012345678901234567890123456789012345678901234567890123 do TEST="Test ${TESTNUM}: -L ${label}" log_start "${TEST}" OUTLOG=${LOGDIR}/mkfs_test_${TESTNUM}.out # format ${MKFS_BIN} -x -M local -L ${label} -J size=4M ${DEVICE} >>${OUTLOG} 2>&1 rc=$? if [ $rc -eq 0 ] then # fsck fsck_volume "${DEVICE}" "${OUTLOG}" rc=$? if [ $rc -eq 0 ] then verify_vol_label "${label}" "${DEVICE}" "${OUTLOG}" rc=$? fi fi log_end $rc "${TEST}" epilogue $rc done ##### Test -U uuid for uuid in 2A4D1C581FAA42A1A41D26EFC90C1315 2a4d1c58-1faa-42a1-a41d-26efc90c1315 do TEST="Test ${TESTNUM}: -U ${uuid}" log_start "${TEST}" OUTLOG=${LOGDIR}/mkfs_test_${TESTNUM}.out # format ${MKFS_BIN} -x -M local -L mkfstest -U ${uuid} -J size=4M ${DEVICE} >>${OUTLOG} 2>&1 rc=$? if [ $rc -eq 0 ] then # fsck fsck_volume "${DEVICE}" "${OUTLOG}" rc=$? fi log_end $rc "${TEST}" epilogue $rc done ENDRUN=$(date +%s) DIFF=$(( ${ENDRUN} - ${STARTRUN} )) log_message "Pass: $PASS Fail: $FAIL" log_message "Total Runtime ${DIFF} seconds" ocfs2-tools-ocfs2-tools-1.8.6/tunefs.ocfs2/000077500000000000000000000000001347147137200204005ustar00rootroot00000000000000ocfs2-tools-ocfs2-tools-1.8.6/tunefs.ocfs2/.gitignore000066400000000000000000000001541347147137200223700ustar00rootroot00000000000000cscope* tunefs.ocfs2 *.sw? stamp-md5 *.d tunefs.ocfs2.8 o2ne_err.[ch] debug_* ocfs2ne o2cluster o2cluster.8 ocfs2-tools-ocfs2-tools-1.8.6/tunefs.ocfs2/Cscope.make000066400000000000000000000012361347147137200224550ustar00rootroot00000000000000.PHONY: cscope cscope: rm -f cscope.* echo "-k" >> cscope.files echo "-I inc" >> cscope.files find . -maxdepth 2 -name '*.c' -print >>cscope.files find . -maxdepth 2 -name '*.h' -print >>cscope.files find ../libocfs2/ -maxdepth 2 -name '*.c' -print >>cscope.files find ../libocfs2/ -maxdepth 2 -name '*.h' -print >>cscope.files find ../libocfs2/ -maxdepth 2 -name '*.et' -print >>cscope.files find ../libo2cb/ -maxdepth 2 -name '*.c' -print >>cscope.files find ../libo2cb/ -maxdepth 2 -name '*.c' -print >>cscope.files find ../libo2dlm/ -maxdepth 2 -name '*.h' -print >>cscope.files find ../libo2dlm/ -maxdepth 2 -name '*.h' -print >>cscope.files cscope -b ocfs2-tools-ocfs2-tools-1.8.6/tunefs.ocfs2/Makefile000066400000000000000000000075701347147137200220510ustar00rootroot00000000000000TOPDIR = .. include $(TOPDIR)/Preamble.make LIBTOOLS_INTERNAL_LIBS = -L$(TOPDIR)/libtools-internal -ltools-internal LIBTOOLS_INTERNAL_DEPS = $(TOPDIR)/libtools-internal/libtools-internal.a LIBOCFS2_LIBS = -L$(TOPDIR)/libocfs2 -locfs2 LIBOCFS2_DEPS = $(TOPDIR)/libocfs2/libocfs2.a LIBO2DLM_LIBS = -L$(TOPDIR)/libo2dlm -lo2dlm $(DL_LIBS) LIBO2DLM_DEPS = $(TOPDIR)/libo2dlm/libo2dlm.a LIBO2CB_LIBS = -L$(TOPDIR)/libo2cb -lo2cb LIBO2CB_DEPS = $(TOPDIR)/libo2cb/libo2cb.a ifneq ($(BUILD_FSDLM_SUPPORT),) LIBO2CB_LIBS += -ldlm_lt endif ifneq ($(BUILD_CMAP_SUPPORT),) LIBO2CB_LIBS += -lcmap endif UNINST_LIBRARIES = libocfs2ne.a OCFS2NE_FEATURES = \ feature_backup_super \ feature_discontig_bg \ feature_extended_slotmap \ feature_inline_data \ feature_local \ feature_metaecc \ feature_refcount \ feature_sparse_files \ feature_unwritten_extents \ feature_xattr \ feature_indexed_dirs \ feature_quota \ feature_clusterinfo \ feature_append_dio OCFS2NE_OPERATIONS = \ op_cloned_volume \ op_features \ op_list_sparse_files \ op_query \ op_reset_uuid \ op_resize_volume \ op_set_label \ op_set_journal_size \ op_set_journal_block \ op_set_slot_count \ op_update_cluster_stack \ op_set_quota_sync_interval \ sbindir = $(root_sbindir) SBIN_PROGRAMS = tunefs.ocfs2 o2cluster INCLUDES = -I$(TOPDIR)/include -I. DEFINES = -DVERSION=\"$(VERSION)\" MANS = tunefs.ocfs2.8 o2cluster.8 ifneq ($(OCFS2_DEBUG_EXE),) DEBUG_EXE_FILES = $(shell awk '/DEBUG_EXE/{if (k[FILENAME] == 0) {print FILENAME; k[FILENAME] = 1;}}' $(CFILES)) DEBUG_EXE_PROGRAMS = $(addprefix debug_,$(subst .c,,$(DEBUG_EXE_FILES))) .SECONDARY: UNINST_PROGRAMS += $(filter-out debug_op_features,$(DEBUG_EXE_PROGRAMS)) debug_%.o : %.c $(CC) $(CFLAGS) $(LOCAL_CFLAGS) $(CPPFLAGS) $(LOCAL_CPPFLAGS) \ $(INCLUDES) $(DEFINES) \ -DDEBUG_EXE -o $@ -c $< debug_op_features: debug_op_features.o $(OCFS2NE_FEATURE_OBJS) libocfs2ne.a $(LIBOCFS2_DEPS) $(LIBO2DLM_DEPS) $(LIBO2CB_DEPS) $(LIBTOOLS_INTERNAL_DEPS) $(LINK) $(LIBOCFS2_LIBS) $(UUID_LIBS) $(LIBO2DLM_LIBS) \ $(LIBO2CB_LIBS) $(LIBTOOLS_INTERNAL_LIBS) $(COM_ERR_LIBS) $(AIO_LIBS) debug_%: debug_%.o libocfs2ne.a $(LIBOCFS2_DEPS) $(LIBO2DLM_DEPS) $(LIBO2CB_DEPS) $(LIBTOOLS_INTERNAL_DEPS) $(LINK) $(LIBOCFS2_LIBS) $(UUID_LIBS) $(LIBO2DLM_LIBS) \ $(LIBO2CB_LIBS) $(LIBTOOLS_INTERNAL_LIBS) $(COM_ERR_LIBS) $(AIO_LIBS) endif LIBOCFS2NE_CFILES = libocfs2ne.c O2CLUSTER_CFILES = o2cluster.c HFILES_GEN = o2ne_err.h o2ne_err.c o2ne_err.h: o2ne_err.et compile_et o2ne_err.et OCFS2NE_OPERATION_CFILES = $(patsubst %,%.c,$(OCFS2NE_OPERATIONS)) OCFS2NE_FEATURE_CFILES = $(patsubst %,%.c,$(OCFS2NE_FEATURES)) OCFS2NE_CFILES = \ ocfs2ne.c \ $(OCFS2NE_OPERATION_CFILES) \ $(OCFS2NE_FEATURE_CFILES) CFILES = $(LIBOCFS2NE_CFILES) $(OCFS2NE_CFILES) $(O2CLUSTER_CFILES) HFILES = libocfs2ne.h LIBOCFS2NE_OBJS = $(subst .c,.o,$(LIBOCFS2NE_CFILES)) o2ne_err.o OCFS2NE_OBJS = $(subst .c,.o,$(OCFS2NE_CFILES)) O2CLUSTER_OBJS = $(subst .c,.o,$(O2CLUSTER_CFILES)) $(LIBOCFS2NE_OBJS): $(HFILES_GEN) $(OCFS2NE_OBJS): $(HFILES_GEN) $(O2CLUSTER_OBJS): $(HFILES_GEN) DIST_FILES = $(CFILES) $(HFILES) tunefs.ocfs2.8.in o2cluster.8.in o2ne_err.et libocfs2ne.a: $(LIBOCFS2NE_OBJS) rm -f $@ $(AR) r $@ $^ $(RANLIB) $@ ocfs2ne: $(OCFS2NE_OBJS) libocfs2ne.a $(LIBOCFS2_DEPS) $(LIBO2DLM_DEPS) $(LIBO2CB_DEPS) $(LIBTOOLS_INTERNAL_DEPS) $(LINK) $(LIBOCFS2_LIBS) $(UUID_LIBS) $(LIBO2DLM_LIBS) \ $(LIBO2CB_LIBS) $(LIBTOOLS_INTERNAL_LIBS) $(COM_ERR_LIBS) $(AIO_LIBS) o2cluster: ${O2CLUSTER_OBJS} $(LIBOCFS2_DEPS) $(LIBO2CB_DEPS) $(LIBTOOLS_INTERNAL_DEPS) $(LIBO2DLM_DEPS) $(LINK) $(LIBOCFS2_LIBS) $(LIBO2CB_LIBS) $(LIBTOOLS_INTERNAL_LIBS) $(LIBO2DLM_LIBS) $(COM_ERR_LIBS) $(AIO_LIBS) tunefs.ocfs2: ocfs2ne ln -f ocfs2ne tunefs.ocfs2 CLEAN_RULES += clean-err CLEAN_RULES += ocfs2ne-clean ocfs2ne-clean: rm -f ocfs2ne clean-err: rm -f o2ne_err.c o2ne_err.h include $(TOPDIR)/Postamble.make ocfs2-tools-ocfs2-tools-1.8.6/tunefs.ocfs2/feature_append_dio.c000066400000000000000000000057651347147137200243760ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * feature_append_dio.c * * ocfs2 tune utility for enabling and disabling the append direct * io feature. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License version 2 as published by the Free Software Foundation. * * 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. */ #include #include #include #include #include #include #include #include "ocfs2/ocfs2.h" #include "libocfs2ne.h" static int enable_append_dio(ocfs2_filesys *fs, int flags) { errcode_t ret = 0; struct ocfs2_super_block *super = OCFS2_RAW_SB(fs->fs_super); struct tools_progress *prog; if (ocfs2_supports_append_dio(super)) { verbosef(VL_APP, "Append direct io feature is already enabled; " "nothing to enable\n"); goto out; } if (!tools_interact("Enable the append direct io feature on " "device \"%s\"? ", fs->fs_devname)) goto out; prog = tools_progress_start("Enable append direct io", "append-dio", 1); if (!prog) { ret = TUNEFS_ET_NO_MEMORY; tcom_err(ret, "while initializing the progress display"); goto out; } OCFS2_SET_INCOMPAT_FEATURE(super, OCFS2_FEATURE_INCOMPAT_APPEND_DIO); tunefs_block_signals(); ret = ocfs2_write_super(fs); tunefs_unblock_signals(); if (ret) tcom_err(ret, "while writing out the superblock"); tools_progress_step(prog, 1); tools_progress_stop(prog); out: return ret; } static int disable_append_dio(ocfs2_filesys *fs, int flags) { errcode_t ret = 0; struct ocfs2_super_block *super = OCFS2_RAW_SB(fs->fs_super); struct tools_progress *prog = NULL; if (!ocfs2_supports_append_dio(super)) { verbosef(VL_APP, "Append direct io feature is not enabled; " "nothing to disable\n"); goto out; } if (!tools_interact("Disable the append direct io feature on " "device \"%s\"? ", fs->fs_devname)) goto out; prog = tools_progress_start("Disabling append direct io", "noappend-dio", 0); if (!prog) { ret = TUNEFS_ET_NO_MEMORY; tcom_err(ret, "while initializing the progress display"); goto out; } OCFS2_CLEAR_INCOMPAT_FEATURE(super, OCFS2_FEATURE_INCOMPAT_APPEND_DIO); tunefs_block_signals(); ret = ocfs2_write_super(fs); tunefs_unblock_signals(); if (ret) tcom_err(ret, "while writing out the superblock"); tools_progress_step(prog, 1); out: if (prog) tools_progress_stop(prog); return ret; } DEFINE_TUNEFS_FEATURE_INCOMPAT(append_dio, OCFS2_FEATURE_INCOMPAT_APPEND_DIO, TUNEFS_FLAG_RW | TUNEFS_FLAG_ONLINE, enable_append_dio, disable_append_dio); #ifdef DEBUG_EXE int main(int argc, char *argv[]) { return tunefs_feature_main(argc, argv, &append_dio_feature); } #endif ocfs2-tools-ocfs2-tools-1.8.6/tunefs.ocfs2/feature_backup_super.c000066400000000000000000000134471347147137200247530ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * feature_backup_super.c * * ocfs2 tune utility to enable and disable the backup superblock feature. * * Copyright (C) 2004, 2008 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License version 2 as published by the Free Software Foundation. * * 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. */ #include #include #include #include #include "ocfs2/ocfs2.h" #include "libocfs2ne.h" static errcode_t empty_backup_supers(ocfs2_filesys *fs) { errcode_t ret; int num; uint64_t blocks[OCFS2_MAX_BACKUP_SUPERBLOCKS]; num = ocfs2_get_backup_super_offsets(fs, blocks, ARRAY_SIZE(blocks)); if (!num) return 0; ret = ocfs2_clear_backup_super_list(fs, blocks, num); if (ret) tcom_err(ret, "while freeing backup superblock locations"); return ret; } static errcode_t fill_backup_supers(ocfs2_filesys *fs) { errcode_t ret; int num; uint64_t blocks[OCFS2_MAX_BACKUP_SUPERBLOCKS]; num = ocfs2_get_backup_super_offsets(fs, blocks, ARRAY_SIZE(blocks)); ret = ocfs2_set_backup_super_list(fs, blocks, num); if (ret) tcom_err(ret, "while backing up the superblock"); return ret; } static int disable_backup_super(ocfs2_filesys *fs, int flags) { errcode_t err = 0; struct ocfs2_super_block *super = OCFS2_RAW_SB(fs->fs_super); struct tools_progress *prog; if (!OCFS2_HAS_COMPAT_FEATURE(super, OCFS2_FEATURE_COMPAT_BACKUP_SB)) { verbosef(VL_APP, "Backup superblock feature is not enabled; " "nothing to disable\n"); goto out; } if (!tools_interact("Disable the backup superblock feature on " "device \"%s\"? ", fs->fs_devname)) goto out; prog = tools_progress_start("Disable backup-super", "nobackup-super", 1); if (!prog) { err = TUNEFS_ET_NO_MEMORY; tcom_err(err, "while initializing the progress display"); goto out; } tunefs_block_signals(); err = empty_backup_supers(fs); if (!err) { super->s_feature_compat &= ~OCFS2_FEATURE_COMPAT_BACKUP_SB; err = ocfs2_write_super(fs); if (err) tcom_err(err, "while writing out the superblock\n" "Unable to disable the backup superblock " "feature on device \"%s\"", fs->fs_devname); } tunefs_unblock_signals(); tools_progress_step(prog, 1); tools_progress_stop(prog); out: return err; } static errcode_t load_global_bitmap(ocfs2_filesys *fs, ocfs2_cached_inode** inode) { errcode_t ret; uint64_t blkno; ret = ocfs2_lookup_system_inode(fs, GLOBAL_BITMAP_SYSTEM_INODE, 0, &blkno); if (ret) goto bail; ret = ocfs2_read_cached_inode(fs, blkno, inode); if (ret) goto bail; ret = ocfs2_load_chain_allocator(fs, *inode); bail: return ret; } static errcode_t check_backup_offsets(ocfs2_filesys *fs) { errcode_t ret; int i, num, val, failed = 0; ocfs2_cached_inode *chain_alloc = NULL; uint64_t blocks[OCFS2_MAX_BACKUP_SUPERBLOCKS]; num = ocfs2_get_backup_super_offsets(fs, blocks, ARRAY_SIZE(blocks)); if (!num) { ret = 1; errorf("Volume on device \"%s\" is too small to contain " "backup superblocks\n", fs->fs_devname); goto bail; } ret = load_global_bitmap(fs, &chain_alloc); if (ret) { tcom_err(ret, "while loading the global bitmap"); goto bail; } for (i = 0; i < num; i++) { ret = ocfs2_bitmap_test(chain_alloc->ci_chains, ocfs2_blocks_to_clusters(fs, blocks[i]), &val); if (ret) { tcom_err(ret, "looking up backup superblock locations " "in the global bitmap"); goto bail; } if (val) { verbosef(VL_APP, "Backup superblock location %d at block " "%"PRIu64" is in use\n", i, blocks[i]); /* in order to verify all the block in the 'blocks', * we don't stop the loop here. */ failed = 1; } } if (failed) { ret = 1; errorf("One or more backup superblock locations are " "already in use\n"); } else ret = 0; if (chain_alloc) ocfs2_free_cached_inode(fs, chain_alloc); bail: return ret; } static int enable_backup_super(ocfs2_filesys *fs, int flags) { errcode_t err = 0; struct ocfs2_super_block *super = OCFS2_RAW_SB(fs->fs_super); struct tools_progress *prog; if (OCFS2_HAS_COMPAT_FEATURE(super, OCFS2_FEATURE_COMPAT_BACKUP_SB)) { verbosef(VL_APP, "Backup superblock feature is already enabled; " "nothing to enable\n"); goto out; } if (!tools_interact("Enable the backup superblock feature on " "device \"%s\"? ", fs->fs_devname)) goto out; prog = tools_progress_start("Enable backup-super", "backup-super", 2); if (!prog) { err = TUNEFS_ET_NO_MEMORY; tcom_err(err, "while initializing the progress display"); goto out; } tunefs_block_signals(); err = check_backup_offsets(fs); tools_progress_step(prog, 1); if (!err) err = fill_backup_supers(fs); if (!err) { super->s_feature_compat |= OCFS2_FEATURE_COMPAT_BACKUP_SB; err = ocfs2_write_super(fs); if (err) tcom_err(err, "while writing out the superblock\n"); } tunefs_unblock_signals(); tools_progress_step(prog, 1); tools_progress_stop(prog); if (err) errorf("Unable to enable the backup superblock feature on " "device \"%s\"\n", fs->fs_devname); out: return err; } DEFINE_TUNEFS_FEATURE_COMPAT(backup_super, OCFS2_FEATURE_COMPAT_BACKUP_SB, TUNEFS_FLAG_RW | TUNEFS_FLAG_ALLOCATION, enable_backup_super, disable_backup_super); #ifdef DEBUG_EXE int main(int argc, char *argv[]) { return tunefs_feature_main(argc, argv, &backup_super_feature); } #endif ocfs2-tools-ocfs2-tools-1.8.6/tunefs.ocfs2/feature_clusterinfo.c000066400000000000000000000066411347147137200246230ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * feature_clusterinfo.c * * ocfs2 tune utility to enable and disable the clusterinfo feature flag * * Copyright (C) 2010 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License version 2 as published by the Free Software Foundation. * * 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. */ #include #include #include #include #include "ocfs2/ocfs2.h" #include "libocfs2ne.h" static int enable_clusterinfo(ocfs2_filesys *fs, int flags) { errcode_t err = 0; struct ocfs2_super_block *super = OCFS2_RAW_SB(fs->fs_super); struct tools_progress *prog; struct o2cb_cluster_desc desc; if (OCFS2_HAS_INCOMPAT_FEATURE(super, OCFS2_FEATURE_INCOMPAT_CLUSTERINFO)) { verbosef(VL_APP, "Clusterinfo feature is already enabled; " "nothing to enable\n"); goto out; } if (!tools_interact("Enable the clusterinfo feature on " "device \"%s\"? ", fs->fs_devname)) goto out; prog = tools_progress_start("Enable clusterinfo", "clusterinfo", 1); if (!prog) { err = TUNEFS_ET_NO_MEMORY; tcom_err(err, "while initializing the progress display"); goto out; } /* With clusterinfo set, userspace flag becomes superfluous */ OCFS2_SET_INCOMPAT_FEATURE(super, OCFS2_FEATURE_INCOMPAT_CLUSTERINFO); OCFS2_CLEAR_INCOMPAT_FEATURE(super, OCFS2_FEATURE_INCOMPAT_USERSPACE_STACK); err = o2cb_running_cluster_desc(&desc); if (!err) { tunefs_block_signals(); err = ocfs2_set_cluster_desc(fs, &desc); tunefs_unblock_signals(); o2cb_free_cluster_desc(&desc); } tools_progress_step(prog, 1); tools_progress_stop(prog); out: return err; } static int disable_clusterinfo(ocfs2_filesys *fs, int flags) { errcode_t err = 0; struct ocfs2_super_block *super = OCFS2_RAW_SB(fs->fs_super); struct tools_progress *prog; if (!OCFS2_HAS_INCOMPAT_FEATURE(super, OCFS2_FEATURE_INCOMPAT_CLUSTERINFO)) { verbosef(VL_APP, "Clusterinfo feature is already disabled; " "nothing to disable\n"); goto out; } if (!tools_interact("Disable the clusterinfo feature on " "device \"%s\"? ", fs->fs_devname)) goto out; prog = tools_progress_start("Disable clusterinfo", "noclusterinfo", 1); if (!prog) { err = TUNEFS_ET_NO_MEMORY; tcom_err(err, "while initializing the progress display"); goto out; } /* When clearing clusterinfo, set userspace if clusterstack != o2cb */ if (ocfs2_userspace_stack(super)) OCFS2_SET_INCOMPAT_FEATURE(super, OCFS2_FEATURE_INCOMPAT_USERSPACE_STACK); OCFS2_CLEAR_INCOMPAT_FEATURE(super, OCFS2_FEATURE_INCOMPAT_CLUSTERINFO); tunefs_block_signals(); err = ocfs2_write_super(fs); if (err) tcom_err(err, "while writing out the superblock"); tunefs_unblock_signals(); tools_progress_step(prog, 1); tools_progress_stop(prog); out: return err; } DEFINE_TUNEFS_FEATURE_INCOMPAT(clusterinfo, OCFS2_FEATURE_INCOMPAT_CLUSTERINFO, TUNEFS_FLAG_RW, enable_clusterinfo, disable_clusterinfo); #ifdef DEBUG_EXE int main(int argc, char *argv[]) { return tunefs_feature_main(argc, argv, &clusterinfo_feature); } #endif ocfs2-tools-ocfs2-tools-1.8.6/tunefs.ocfs2/feature_discontig_bg.c000066400000000000000000000176001347147137200247160ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * feature_discontig_alloc.c * * ocfs2 tune utility for enabling and disabling the discontig_alloc feature. * * Copyright (C) 2010 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License version 2 as published by the Free Software Foundation. * * 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. */ #include #include #include #include #include #include #include #include "ocfs2/ocfs2.h" #include "libocfs2ne.h" static int enable_discontig_bg(ocfs2_filesys *fs, int flags) { errcode_t ret = 0; struct ocfs2_super_block *super = OCFS2_RAW_SB(fs->fs_super); struct tools_progress *prog; if (ocfs2_supports_discontig_bg(super)) { verbosef(VL_APP, "Discontiguous block group feature is already enabled;" " nothing to enable\n"); goto out; } if (!tools_interact("Enable the discontiguous block group feature on " "device \"%s\"? ", fs->fs_devname)) goto out; prog = tools_progress_start("Enable discontig block group", "discontig bg", 1); if (!prog) { ret = TUNEFS_ET_NO_MEMORY; tcom_err(ret, "while initializing the progress display"); goto out; } OCFS2_SET_INCOMPAT_FEATURE(super, OCFS2_FEATURE_INCOMPAT_DISCONTIG_BG); tunefs_block_signals(); ret = ocfs2_write_super(fs); tunefs_unblock_signals(); if (ret) tcom_err(ret, "while writing out the superblock"); tools_progress_step(prog, 1); tools_progress_stop(prog); out: return ret; } struct discontig_bg { uint64_t bg_blkno; struct discontig_bg *next; }; struct no_discontig_bg_ctxt { ocfs2_filesys *fs; struct tools_progress *prog; char *bg_buf; errcode_t ret; int has_discontig; struct discontig_bg *bg_list; }; /* * Check whether the gd_blkno is a discontig block group, and * if yes set has_discontig and abort. * It also check whether bg_size is a new value, if yes, add * it to the list so that we can change it later. */ static int check_discontig_bg(ocfs2_filesys *fs, uint64_t gd_blkno, int chain_num, void *priv_data) { struct no_discontig_bg_ctxt *ctxt = priv_data; struct ocfs2_group_desc *gd; struct discontig_bg *bg; ctxt->ret = ocfs2_read_group_desc(fs, gd_blkno, ctxt->bg_buf); if (ctxt->ret) { tcom_err(ctxt->ret, "while reading group descriptor %"PRIu64, gd_blkno); return OCFS2_CHAIN_ERROR; } gd = (struct ocfs2_group_desc *)ctxt->bg_buf; if (ocfs2_gd_is_discontig(gd)) { ctxt->has_discontig = 1; return OCFS2_CHAIN_ABORT; } if (gd->bg_size == ocfs2_group_bitmap_size(fs->fs_blocksize, 0, OCFS2_RAW_SB(fs->fs_super)->s_feature_incompat)) return 0; /* * OK, now the gd isn't discontiguous while bg_size has * the new size. Record it so that we can change it later. */ ctxt->ret = ocfs2_malloc0(sizeof(struct discontig_bg), &bg); if (ctxt->ret) { tcom_err(ctxt->ret, "while allocating discontig_bg"); return OCFS2_CHAIN_ABORT; } bg->bg_blkno = gd_blkno; bg->next = ctxt->bg_list; ctxt->bg_list = bg; return 0; } static errcode_t find_discontig_bg(struct no_discontig_bg_ctxt *ctxt) { int i, iret; uint64_t blkno; ctxt->prog = tools_progress_start("Scanning suballocators", "scanning", 0); if (!ctxt->prog) { ctxt->ret = TUNEFS_ET_NO_MEMORY; tcom_err(ctxt->ret, "while initializing the progress display"); goto out; } /* iterate every inode_alloc first. */ for (i = 0; i < OCFS2_RAW_SB(ctxt->fs->fs_super)->s_max_slots; i++) { ctxt->ret = ocfs2_lookup_system_inode(ctxt->fs, INODE_ALLOC_SYSTEM_INODE, i, &blkno); if (ctxt->ret) { tcom_err(ctxt->ret, "while finding inode alloc %d", i); goto out; } iret = ocfs2_chain_iterate(ctxt->fs, blkno, check_discontig_bg, ctxt); if (ctxt->ret) { tcom_err(ctxt->ret, "while iterating inode alloc " "%d", i); goto out; } if (iret == OCFS2_CHAIN_ABORT || iret == OCFS2_CHAIN_ERROR) goto out; tools_progress_step(ctxt->prog, 1); } /* iterate every extent_alloc now. */ for (i = 0; i < OCFS2_RAW_SB(ctxt->fs->fs_super)->s_max_slots; i++) { ctxt->ret = ocfs2_lookup_system_inode(ctxt->fs, EXTENT_ALLOC_SYSTEM_INODE, i, &blkno); if (ctxt->ret) { tcom_err(ctxt->ret, "while finding extent alloc %d", i); goto out; } iret = ocfs2_chain_iterate(ctxt->fs, blkno, check_discontig_bg, ctxt); if (ctxt->ret) { tcom_err(ctxt->ret, "while iterating extent alloc " "%d", i); goto out; } if (iret == OCFS2_CHAIN_ABORT || iret == OCFS2_CHAIN_ERROR) goto out; tools_progress_step(ctxt->prog, 1); } out: if (ctxt->prog) { tools_progress_stop(ctxt->prog); ctxt->prog = NULL; } return ctxt->ret; } static errcode_t change_bg_size(struct no_discontig_bg_ctxt *ctxt) { errcode_t ret = 0; uint64_t bg_blkno; struct discontig_bg *bg; struct ocfs2_group_desc *gd; while (ctxt->bg_list) { bg = ctxt->bg_list; ctxt->bg_list = bg->next; bg_blkno = bg->bg_blkno; ocfs2_free(&bg); ret = ocfs2_read_group_desc(ctxt->fs, bg_blkno, ctxt->bg_buf); if (ret) { tcom_err(ctxt->ret, "while reading group descriptor " "%"PRIu64, bg_blkno); goto out; } gd = (struct ocfs2_group_desc *)ctxt->bg_buf; gd->bg_size = ocfs2_group_bitmap_size(ctxt->fs->fs_blocksize, 0, 0); ret = ocfs2_write_group_desc(ctxt->fs, bg_blkno, ctxt->bg_buf); if (ret) { tcom_err(ctxt->ret, "while writing group descriptor " "%"PRIu64, bg->bg_blkno); goto out; } } out: return ret; } static int disable_discontig_bg(ocfs2_filesys *fs, int flags) { errcode_t ret = 0; struct ocfs2_super_block *super = OCFS2_RAW_SB(fs->fs_super); struct tools_progress *prog = NULL; struct no_discontig_bg_ctxt ctxt; struct discontig_bg *tmp; memset(&ctxt, 0, sizeof(ctxt)); if (!ocfs2_supports_discontig_bg(super)) { verbosef(VL_APP, "Discontiguous block group feature is already " "disabled; nothing to disable\n"); goto out; } if (!tools_interact("Disable the discontiguous block group feature on " "device \"%s\"? ", fs->fs_devname)) goto out; prog = tools_progress_start("Enable discontig block group", "nodiscontig-bg", 4); if (!prog) { ret = TUNEFS_ET_NO_MEMORY; tcom_err(ret, "while initializing the progress display"); goto out; } ctxt.fs = fs; ret = ocfs2_malloc_block(fs->fs_io, &ctxt.bg_buf); if (ret) { tcom_err(ret, "while mallocing blocks for group read"); goto out; } ret = find_discontig_bg(&ctxt); if (ret) { tcom_err(ret, "while finding discontiguous block group"); goto out; } tools_progress_step(prog, 1); if (ctxt.has_discontig) { tcom_err(0, "We can't disable discontig feature while " "we have some discontiguous block groups"); goto out; } ret = change_bg_size(&ctxt); if (ret) { tcom_err(ret, "while changing bg size for block group"); goto out; } OCFS2_CLEAR_INCOMPAT_FEATURE(super, OCFS2_FEATURE_INCOMPAT_DISCONTIG_BG); tunefs_block_signals(); ret = ocfs2_write_super(fs); tunefs_unblock_signals(); if (ret) tcom_err(ret, "while writing out the superblock"); tools_progress_step(prog, 1); out: if (ctxt.bg_buf) ocfs2_free(&ctxt.bg_buf); while (ctxt.bg_list) { tmp = ctxt.bg_list; ctxt.bg_list = tmp->next; ocfs2_free(&tmp); } if (prog) tools_progress_stop(prog); return ret; } DEFINE_TUNEFS_FEATURE_INCOMPAT(discontig_bg, OCFS2_FEATURE_INCOMPAT_DISCONTIG_BG, TUNEFS_FLAG_RW | TUNEFS_FLAG_ALLOCATION | TUNEFS_FLAG_LARGECACHE, enable_discontig_bg, disable_discontig_bg); #ifdef DEBUG_EXE int main(int argc, char *argv[]) { return tunefs_feature_main(argc, argv, &discontig_bg_feature); } #endif ocfs2-tools-ocfs2-tools-1.8.6/tunefs.ocfs2/feature_extended_slotmap.c000066400000000000000000000065041347147137200256230ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * feature_extended_slotmap.c * * ocfs2 tune utility to enable and disable the extended slot map feature. * * Copyright (C) 2004, 2008 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License version 2 as published by the Free Software Foundation. * * 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. */ #include #include #include #include #include "ocfs2/ocfs2.h" #include "libocfs2ne.h" static int enable_extended_slotmap(ocfs2_filesys *fs, int flags) { errcode_t err = 0; struct ocfs2_super_block *super = OCFS2_RAW_SB(fs->fs_super); struct tools_progress *prog; if (ocfs2_uses_extended_slot_map(super)) { verbosef(VL_APP, "Extended slot map feature is already enabled; " "nothing to enable\n"); goto out; } if (!tools_interact("Enable the extended slot map feature on " "device \"%s\"? ", fs->fs_devname)) goto out; prog = tools_progress_start("Enable extended-slotmap", "extended-slotmap", 1); if (!prog) { err = TUNEFS_ET_NO_MEMORY; tcom_err(err, "while initializing the progress display"); goto out; } OCFS2_SET_INCOMPAT_FEATURE(super, OCFS2_FEATURE_INCOMPAT_EXTENDED_SLOT_MAP); tunefs_block_signals(); err = ocfs2_format_slot_map(fs); if (!err) { err = ocfs2_write_super(fs); if (err) tcom_err(err, "while writing out the superblock"); } else tcom_err(err, "while formatting the extended slot map"); tunefs_unblock_signals(); tools_progress_step(prog, 1); tools_progress_stop(prog); out: return err; } static int disable_extended_slotmap(ocfs2_filesys *fs, int flags) { errcode_t err = 0; struct ocfs2_super_block *super = OCFS2_RAW_SB(fs->fs_super); struct tools_progress *prog; if (!ocfs2_uses_extended_slot_map(super)) { verbosef(VL_APP, "Extended slot map feature is not enabled; " "nothing to disable\n"); goto out; } if (!tools_interact("Disable the extended slot map feature on " "device \"%s\"? ", fs->fs_devname)) goto out; prog = tools_progress_start("Disable extended-slotmap", "noextended-slotmap", 1); if (!prog) { err = TUNEFS_ET_NO_MEMORY; tcom_err(err, "while initializing the progress display"); goto out; } OCFS2_CLEAR_INCOMPAT_FEATURE(super, OCFS2_FEATURE_INCOMPAT_EXTENDED_SLOT_MAP); tunefs_block_signals(); err = ocfs2_format_slot_map(fs); if (!err) { err = ocfs2_write_super(fs); if (err) tcom_err(err, "while writing out the superblock"); } else tcom_err(err, "while formatting the old-style slot map"); tunefs_unblock_signals(); tools_progress_step(prog, 1); tools_progress_stop(prog); out: return err; } DEFINE_TUNEFS_FEATURE_INCOMPAT(extended_slotmap, OCFS2_FEATURE_INCOMPAT_EXTENDED_SLOT_MAP, TUNEFS_FLAG_RW | TUNEFS_FLAG_ALLOCATION, enable_extended_slotmap, disable_extended_slotmap); #ifdef DEBUG_EXE int main(int argc, char *argv[]) { return tunefs_feature_main(argc, argv, &extended_slotmap_feature); } #endif ocfs2-tools-ocfs2-tools-1.8.6/tunefs.ocfs2/feature_indexed_dirs.c000066400000000000000000000207311347147137200247230ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * feature_indexed_dirs.c * * ocfs2 tune utility for enabling and disabling the directory indexing * feature. * * Copyright (C) 2009, 2010 Novell. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License version 2 as published by the Free Software Foundation. * * 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. */ #include #include #include #include #include #include #include #include "ocfs2/ocfs2.h" #include "libocfs2ne.h" struct dx_dirs_inode { struct list_head list; uint64_t ino; }; struct dx_dirs_context { errcode_t ret; uint64_t dx_dirs_nr; struct list_head inodes; struct tools_progress *prog; }; /* * If an indexed-dirs disabled directory has an indexed tree, * this tree is unreliable. it must be truncated and rebuilt. */ static errcode_t build_dx_dir(ocfs2_filesys *fs, struct ocfs2_dinode *di, void *user_data) { errcode_t ret = 0; struct dx_dirs_context *ctxt = (struct dx_dirs_context *)user_data; if (!S_ISDIR(di->i_mode)) goto bail; if (di->i_dyn_features & OCFS2_INDEXED_DIR_FL) { verbosef(VL_APP, "Directory inode %llu already has an indexed tree, " "rebuild the indexed tree.\n", di->i_blkno); ret = ocfs2_dx_dir_truncate(fs, di->i_blkno); if (ret) { ret = TUNEFS_ET_DX_DIRS_TRUNCATE_FAILED; tcom_err(ret, "while rebuild indexed tree"); } } ret = tunefs_install_dir_trailer(fs, di, NULL); if (ret) { ret = TUNEFS_ET_INSTALL_DIR_TRAILER_FAILED; tcom_err(ret, "while enable indexed-dirs"); goto bail; } ret = ocfs2_dx_dir_build(fs, di->i_blkno); if (ret) { ret = TUNEFS_ET_DX_DIRS_BUILD_FAILED; tcom_err(ret, "while enable indexed-dirs"); } bail: tools_progress_step(ctxt->prog, 1); return ret; } static int enable_indexed_dirs(ocfs2_filesys *fs, int flags) { errcode_t ret = 0; struct ocfs2_super_block *super = OCFS2_RAW_SB(fs->fs_super); struct tools_progress *prog = NULL; struct dx_dirs_context ctxt; if (ocfs2_supports_indexed_dirs(super)) { verbosef(VL_APP, "Directory indexing feature is already enabled; " "nothing to enable\n"); goto out; } if (!tools_interact("Enable the directory indexing feature on " "device \"%s\"? ", fs->fs_devname)) goto out; prog = tools_progress_start("Enable directory indexing", "dir idx", 2); if (!prog) { ret = TUNEFS_ET_NO_MEMORY; tcom_err(ret, "while initializing the progress display"); goto out; } memset(&ctxt, 0, sizeof(struct dx_dirs_context)); ctxt.prog = tools_progress_start("Building indexed trees", "building", 0); if (!ctxt.prog) { ret = TUNEFS_ET_NO_MEMORY; goto out; } /* s_uuid_hash is also used by xattr */ if (!OCFS2_HAS_INCOMPAT_FEATURE(super, OCFS2_FEATURE_INCOMPAT_XATTR)) super->s_uuid_hash = ocfs2_xattr_uuid_hash((unsigned char *)super->s_uuid); /* set seed for indexed dir hash */ srand48(time(NULL)); super->s_dx_seed[0] = mrand48(); super->s_dx_seed[1] = mrand48(); super->s_dx_seed[2] = mrand48(); OCFS2_SET_INCOMPAT_FEATURE(super, OCFS2_FEATURE_INCOMPAT_INDEXED_DIRS); tunefs_block_signals(); ret = ocfs2_write_super(fs); if (ret) { ret = TUNEFS_ET_IO_WRITE_FAILED; tcom_err(ret, "while writing out the superblock"); goto unblock_out; } tools_progress_step(prog, 1); ret = tunefs_foreach_inode(fs, build_dx_dir, &ctxt); if (ret) tcom_err(ret, "while building indexed trees"); unblock_out: tunefs_unblock_signals(); tools_progress_step(prog, 1); if (ctxt.prog) tools_progress_stop(ctxt.prog); out: if (prog) tools_progress_stop(prog); return ret; } static errcode_t dx_dir_iterate(ocfs2_filesys *fs, struct ocfs2_dinode *di, void *user_data) { errcode_t ret = 0; struct dx_dirs_inode *dx_di = NULL; struct dx_dirs_context *ctxt= (struct dx_dirs_context *)user_data; if (!S_ISDIR(di->i_mode)) goto bail; if (!(di->i_dyn_features & OCFS2_INDEXED_DIR_FL)) goto bail; ret = ocfs2_malloc0(sizeof(struct dx_dirs_inode), &dx_di); if (ret) { ret = TUNEFS_ET_NO_MEMORY; goto bail; } dx_di->ino = di->i_blkno; ctxt->dx_dirs_nr ++; list_add_tail(&dx_di->list, &ctxt->inodes); tools_progress_step(ctxt->prog, 1); bail: return ret; } static errcode_t find_indexed_dirs(ocfs2_filesys *fs, struct dx_dirs_context *ctxt) { errcode_t ret; ctxt->prog = tools_progress_start("Scanning filesystem", "scanning", 0); if (!ctxt->prog) { ret = TUNEFS_ET_NO_MEMORY; goto bail; } ret = tunefs_foreach_inode(fs, dx_dir_iterate, ctxt); if (ret) { if (ret != TUNEFS_ET_NO_MEMORY) ret = TUNEFS_ET_DX_DIRS_SCAN_FAILED; goto bail; } verbosef(VL_APP, "We have %"PRIu64" indexed %s to truncate.\n", ctxt->dx_dirs_nr, (ctxt->dx_dirs_nr > 1)?"directories":"directory"); bail: if (ctxt->prog) tools_progress_stop(ctxt->prog); return ret; } static errcode_t clean_indexed_dirs(ocfs2_filesys *fs, struct dx_dirs_context *ctxt) { errcode_t ret = 0; struct list_head *pos; struct dx_dirs_inode *dx_di; struct tools_progress *prog; uint64_t dirs_truncated = 0; prog = tools_progress_start("Truncating indexed dirs", "truncating", ctxt->dx_dirs_nr); if (!prog) { ret = TUNEFS_ET_NO_MEMORY; goto bail; } list_for_each(pos, &ctxt->inodes) { dx_di = list_entry(pos, struct dx_dirs_inode, list); ret = ocfs2_dx_dir_truncate(fs, dx_di->ino); if (ret) { verbosef(VL_APP, "Truncate directory (ino \"%"PRIu64"\") failed.", dx_di->ino); ret = TUNEFS_ET_DX_DIRS_TRUNCATE_FAILED; goto bail; } dirs_truncated ++; tools_progress_step(prog, 1); } bail: tools_progress_stop(prog); verbosef(VL_APP, "\"%"PRIu64"\" from \"%"PRIu64"\" indexed %s truncated.", dirs_truncated, ctxt->dx_dirs_nr, (dirs_truncated <= 1) ? "directory is" : "directories are"); return ret; } static void release_dx_dirs_context(struct dx_dirs_context *ctxt) { struct list_head *pos, *n; struct dx_dirs_inode *dx_di; list_for_each_safe(pos, n, &ctxt->inodes) { dx_di = list_entry(pos, struct dx_dirs_inode, list); list_del(&dx_di->list); ocfs2_free(&dx_di); } } static int disable_indexed_dirs(ocfs2_filesys *fs, int flags) { errcode_t ret = 0; struct ocfs2_super_block *super = OCFS2_RAW_SB(fs->fs_super); struct dx_dirs_context ctxt; struct tools_progress *prog = NULL; if (!ocfs2_supports_indexed_dirs(super)) { verbosef(VL_APP, "Directory indexing feature is not enabled; " "nothing to disable\n"); goto out; } if (!tools_interact("Disabling the directory indexing feature on " "device \"%s\"? ", fs->fs_devname)) goto out; prog = tools_progress_start("Disable directory indexing", "no dir idx", 2); if (!prog) { ret = TUNEFS_ET_NO_MEMORY; tcom_err(ret, "while initializing the progress display"); goto out; } memset(&ctxt, 0, sizeof (struct dx_dirs_context)); INIT_LIST_HEAD(&ctxt.inodes); ret = find_indexed_dirs(fs, &ctxt); if (ret) { tcom_err(ret, "while scanning indexed directories"); goto out_cleanup; } tools_progress_step(prog, 1); tunefs_block_signals(); ret = clean_indexed_dirs(fs, &ctxt); if (ret) { tcom_err(ret, "while truncate indexed directories"); } /* We already touched file system, must disable dx dirs flag here. * fsck.ocfs2 will handle the orphan indexed trees. */ OCFS2_CLEAR_INCOMPAT_FEATURE(super, OCFS2_FEATURE_INCOMPAT_INDEXED_DIRS); /* s_uuid_hash is also used by xattr */ if (!OCFS2_HAS_INCOMPAT_FEATURE(super, OCFS2_FEATURE_INCOMPAT_XATTR)) super->s_uuid_hash = 0; super->s_dx_seed[0] = super->s_dx_seed[1] = super->s_dx_seed[2] = 0; ret = ocfs2_write_super(fs); tunefs_unblock_signals(); if (ret) { ret = TUNEFS_ET_IO_WRITE_FAILED; tcom_err(ret, "while writing super block"); } tools_progress_step(prog, 1); out_cleanup: release_dx_dirs_context(&ctxt); out: if (prog) tools_progress_stop(prog); return ret; } /* * TUNEFS_FLAG_ALLOCATION because disabling will want to dealloc * blocks. */ DEFINE_TUNEFS_FEATURE_INCOMPAT(indexed_dirs, OCFS2_FEATURE_INCOMPAT_INDEXED_DIRS, TUNEFS_FLAG_RW | TUNEFS_FLAG_ALLOCATION, enable_indexed_dirs, disable_indexed_dirs); #ifdef DEBUG_EXE int main(int argc, char *argv[]) { return tunefs_feature_main(argc, argv, &indexed_dirs_feature); } #endif ocfs2-tools-ocfs2-tools-1.8.6/tunefs.ocfs2/feature_inline_data.c000066400000000000000000000164351347147137200245370ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * feature_inline_data.c * * ocfs2 tune utility for enabling and disabling the inline data feature. * * Copyright (C) 2008 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License version 2 as published by the Free Software Foundation. * * 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. */ #include #include #include #include #include #include #include #include "ocfs2-kernel/kernel-list.h" #include "ocfs2/ocfs2.h" #include "libocfs2ne.h" /* * We scan up-front to find out how many files we have to expand. We keep * track of them so that we don't have to scan again to do the work. */ struct inline_data_inode { struct list_head list; uint64_t blkno; }; struct inline_data_context { errcode_t ret; uint32_t more_clusters; struct list_head inodes; struct tools_progress *prog; }; static int enable_inline_data(ocfs2_filesys *fs, int flags) { errcode_t ret = 0; struct ocfs2_super_block *super = OCFS2_RAW_SB(fs->fs_super); struct tools_progress *prog = NULL; if (ocfs2_support_inline_data(super)) { verbosef(VL_APP, "The inline data feature is already enabled; " "nothing to enable\n"); goto out; } if (!tools_interact("Enable the inline data feature on device " "\"%s\"? ", fs->fs_devname)) goto out; prog = tools_progress_start("Enabling inline-data", "inline-data", 1); if (!prog) { ret = TUNEFS_ET_NO_MEMORY; tcom_err(ret, "while initializing the progress display"); goto out; } OCFS2_SET_INCOMPAT_FEATURE(super, OCFS2_FEATURE_INCOMPAT_INLINE_DATA); tunefs_block_signals(); ret = ocfs2_write_super(fs); tunefs_unblock_signals(); if (ret) tcom_err(ret, "while writing out the superblock"); tools_progress_step(prog, 1); out: if (prog) tools_progress_stop(prog); return ret; } static errcode_t inline_iterate(ocfs2_filesys *fs, struct ocfs2_dinode *di, void *user_data) { errcode_t ret = 0; struct inline_data_inode *idi = NULL; struct inline_data_context *ctxt = user_data; if (!S_ISREG(di->i_mode) && !S_ISDIR(di->i_mode)) goto bail; if (!(di->i_dyn_features & OCFS2_INLINE_DATA_FL)) goto bail; ret = ocfs2_malloc0(sizeof(struct inline_data_inode), &idi); if (ret) goto bail; idi->blkno = di->i_blkno; ctxt->more_clusters++; list_add_tail(&idi->list, &ctxt->inodes); tools_progress_step(ctxt->prog, 1); return 0; bail: return ret; } static errcode_t find_inline_data(ocfs2_filesys *fs, struct inline_data_context *ctxt) { errcode_t ret; uint32_t free_clusters = 0; ctxt->prog = tools_progress_start("Scanning filesystem", "scanning", 0); if (!ctxt->prog) { ret = TUNEFS_ET_NO_MEMORY; goto bail; } ret = tunefs_foreach_inode(fs, inline_iterate, ctxt); if (ret) goto bail; ret = tunefs_get_free_clusters(fs, &free_clusters); if (ret) goto bail; verbosef(VL_APP, "We have %u clusters free, and need %u clusters to expand " "all inline data\n", free_clusters, ctxt->more_clusters); if (free_clusters < ctxt->more_clusters) ret = OCFS2_ET_NO_SPACE; bail: if (ctxt->prog) tools_progress_stop(ctxt->prog); return ret; } static void empty_inline_data_context(struct inline_data_context *ctxt) { struct list_head *pos, *n; struct inline_data_inode *idi; list_for_each_safe(pos, n, &ctxt->inodes) { idi = list_entry(pos, struct inline_data_inode, list); list_del(&idi->list); ocfs2_free(&idi); } } static errcode_t expand_inline_data(ocfs2_filesys *fs, struct inline_data_context *ctxt) { errcode_t ret = 0, err; struct list_head *pos; struct inline_data_inode *idi; ocfs2_cached_inode *ci = NULL; struct tools_progress *prog; struct ocfs2_super_block *super = OCFS2_RAW_SB(fs->fs_super); ocfs2_quota_hash *usrhash = NULL, *grphash = NULL; uint32_t uid, gid; long long change; prog = tools_progress_start("Expanding inline files", "expanding", ctxt->more_clusters); if (!ctxt->prog) return TUNEFS_ET_NO_MEMORY; ret = ocfs2_load_fs_quota_info(fs); if (ret) goto out; ret = ocfs2_init_quota_change(fs, &usrhash, &grphash); if (ret) goto out; list_for_each(pos, &ctxt->inodes) { idi = list_entry(pos, struct inline_data_inode, list); ret = ocfs2_read_cached_inode(fs, idi->blkno, &ci); if (ret) break; ret = ocfs2_convert_inline_data_to_extents(ci); if (ret) { ocfs2_free_cached_inode(fs, ci); break; } if (ci->ci_inode->i_flags & OCFS2_SYSTEM_FL && idi->blkno != super->s_root_blkno) { ocfs2_free_cached_inode(fs, ci); goto next; } change = ocfs2_clusters_to_bytes(fs, ci->ci_inode->i_clusters); uid = ci->ci_inode->i_uid; gid = ci->ci_inode->i_gid; ocfs2_free_cached_inode(fs, ci); ret = ocfs2_apply_quota_change(fs, usrhash, grphash, uid, gid, change, 0); if (ret) break; next: tools_progress_step(prog, 1); } out: err = ocfs2_finish_quota_change(fs, usrhash, grphash); if (!ret) ret = err; tools_progress_stop(prog); return ret; } static int disable_inline_data(ocfs2_filesys *fs, int flags) { errcode_t ret = 0; struct ocfs2_super_block *super = OCFS2_RAW_SB(fs->fs_super); struct inline_data_context ctxt; struct tools_progress *prog = NULL; if (!ocfs2_support_inline_data(super)) { verbosef(VL_APP, "The inline data feature is not enabled; " "nothing to disable\n"); goto out; } if (!tools_interact("Disable the inline data feature on device " "\"%s\"? ", fs->fs_devname)) goto out; prog = tools_progress_start("Disabling inline-data", "noinline-data", 3); if (!prog) { ret = TUNEFS_ET_NO_MEMORY; tcom_err(ret, "while initializing the progress display"); goto out; } memset(&ctxt, 0, sizeof(ctxt)); INIT_LIST_HEAD(&ctxt.inodes); ret = find_inline_data(fs, &ctxt); if (ret) { if (ret == OCFS2_ET_NO_SPACE) errorf("There is not enough space to expand all of " "the inline data on device \"%s\"\n", fs->fs_devname); else tcom_err(ret, "while trying to find files with inline data"); goto out_cleanup; } tools_progress_step(prog, 1); ret = expand_inline_data(fs, &ctxt); if (ret) { tcom_err(ret, "while trying to expand the inline data on device " "\"%s\"", fs->fs_devname); goto out_cleanup; } tools_progress_step(prog, 1); OCFS2_CLEAR_INCOMPAT_FEATURE(super, OCFS2_FEATURE_INCOMPAT_INLINE_DATA); tunefs_block_signals(); ret = ocfs2_write_super(fs); tunefs_unblock_signals(); if (ret) tcom_err(ret, "while writing out the superblock"); tools_progress_step(prog, 1); out_cleanup: empty_inline_data_context(&ctxt); out: if (prog) tools_progress_stop(prog); return ret; } DEFINE_TUNEFS_FEATURE_INCOMPAT(inline_data, OCFS2_FEATURE_INCOMPAT_INLINE_DATA, TUNEFS_FLAG_RW | TUNEFS_FLAG_ALLOCATION | TUNEFS_FLAG_LARGECACHE, enable_inline_data, disable_inline_data); #ifdef DEBUG_EXE int main(int argc, char *argv[]) { return tunefs_feature_main(argc, argv, &inline_data_feature); } #endif ocfs2-tools-ocfs2-tools-1.8.6/tunefs.ocfs2/feature_local.c000066400000000000000000000074721347147137200233630ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * feature_local.c * * ocfs2 tune utility for updating the mount type. * * Copyright (C) 2004, 2008 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License version 2 as published by the Free Software Foundation. * * 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. */ #include #include #include #include #include "ocfs2/ocfs2.h" #include "libocfs2ne.h" static int disable_local(ocfs2_filesys *fs, int flags) { errcode_t ret = 0; struct ocfs2_super_block *sb = OCFS2_RAW_SB(fs->fs_super); struct o2cb_cluster_desc desc; struct tools_progress *prog = NULL; if (!ocfs2_mount_local(fs)) { verbosef(VL_APP, "Device \"%s\" is already a cluster-aware " "filesystem; nothing to do\n", fs->fs_devname); goto out; } if (!tools_interact("Make device \"%s\" a cluster-aware " "filesystem? ", fs->fs_devname)) goto out; prog = tools_progress_start("Disable local", "nolocal", 3); if (!prog) { ret = TUNEFS_ET_NO_MEMORY; tcom_err(ret, "while initializing the progress display"); goto out; } /* * Since it was a local device, tunefs_open() will not * have connected to o2cb. */ ret = o2cb_init(); if (ret) { tcom_err(ret, "while connecting to the cluster stack"); goto out; } tools_progress_step(prog, 1); ret = o2cb_running_cluster_desc(&desc); if (ret) { tcom_err(ret, "while trying to determine the running cluster"); goto out; } if (desc.c_stack) verbosef(VL_APP, "Cluster stack: %s\n" "Cluster name: %s\n", desc.c_stack, desc.c_cluster); else verbosef(VL_APP, "Cluster stack: classic o2cb\n"); tools_progress_step(prog, 1); sb->s_feature_incompat &= ~OCFS2_FEATURE_INCOMPAT_LOCAL_MOUNT; tunefs_block_signals(); ret = ocfs2_set_cluster_desc(fs, &desc); tunefs_block_signals(); o2cb_free_cluster_desc(&desc); if (ret) tcom_err(ret, "while writing setting the cluster descriptor"); tools_progress_step(prog, 1); out: if (prog) tools_progress_stop(prog); if (ret) errorf("Unable to disable the local mount feature on " "device \"%s\"\n", fs->fs_devname); return ret; } static int enable_local(ocfs2_filesys *fs, int flags) { errcode_t ret = 0; struct ocfs2_super_block *sb = OCFS2_RAW_SB(fs->fs_super); struct tools_progress *prog; if (ocfs2_mount_local(fs)) { verbosef(VL_APP, "Device \"%s\" is already a single-node " "filesystem; nothing to do\n", fs->fs_devname); goto out; } if (!tools_interact("Make device \"%s\" a single-node " "(non-clustered) filesystem? ", fs->fs_devname)) goto out; prog = tools_progress_start("Enable local", "local", 1); if (!prog) { ret = TUNEFS_ET_NO_MEMORY; tcom_err(ret, "while initializing the progress display"); goto out; } sb->s_feature_incompat |= OCFS2_FEATURE_INCOMPAT_LOCAL_MOUNT; sb->s_feature_incompat &= ~OCFS2_FEATURE_INCOMPAT_USERSPACE_STACK; tunefs_block_signals(); ret = ocfs2_write_super(fs); tunefs_unblock_signals(); if (ret) tcom_err(ret, "while writing out the superblock; Unable to " "enable the local mount feature on device \"%s\"", fs->fs_devname); tools_progress_step(prog, 1); tools_progress_stop(prog); out: return ret; } DEFINE_TUNEFS_FEATURE_INCOMPAT(local, OCFS2_FEATURE_INCOMPAT_LOCAL_MOUNT, TUNEFS_FLAG_RW, enable_local, disable_local); #ifdef DEBUG_EXE int main(int argc, char *argv[]) { return tunefs_feature_main(argc, argv, &local_feature); } #endif ocfs2-tools-ocfs2-tools-1.8.6/tunefs.ocfs2/feature_metaecc.c000066400000000000000000000503541347147137200236670ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * feature_metaecc.c * * ocfs2 tune utility for enabling and disabling the metaecc feature. * * Copyright (C) 2004, 2008 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License version 2 as published by the Free Software Foundation. * * 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. */ #include #include #include #include #include #include #include #include "ocfs2-kernel/kernel-list.h" #include "ocfs2/kernel-rbtree.h" #include "ocfs2/ocfs2.h" #include "libocfs2ne.h" /* * Since we have to scan the inodes in our first pass to find directories * that need trailers, we might as well store them off and avoid reading * them again when its time to write ECC data. In fact, we'll do all the * scanning up-front, including extent blocks and group descriptors. The * only metadata block we don't store is the superblock, because we'll * write that last from fs->fs_super. * * We store all of this in an rb-tree of block_to_ecc structures. We can * look blocks back up if needed, and we have writeback functions attached. * * For directory inodes, we pass e_buf into tunefs_prepare_dir_trailer(), * which does not copy off the inode. Thus, when * tunefs_install_dir_trailer() modifies the inode, this is the one that * gets updated. * * For directory blocks, tunefs_prepare_dir_trailer() makes its own copies. * After we run tunefs_install_dir_trailer(), we'll have to copy the * changes back to our copy. */ struct block_to_ecc { struct rb_node e_node; uint64_t e_blkno; struct ocfs2_dinode *e_di; char *e_buf; errcode_t (*e_write)(ocfs2_filesys *fs, struct block_to_ecc *block); }; /* * We have to do chain allocators at the end, because we may use them * as we add dirblock trailers. Really, we only need the inode block * number. */ struct chain_to_ecc { struct list_head ce_list; uint64_t ce_blkno; }; struct add_ecc_context { errcode_t ae_ret; struct tools_progress *ae_prog; uint32_t ae_clusters; struct list_head ae_dirs; uint64_t ae_dircount; struct list_head ae_chains; uint64_t ae_chaincount; struct rb_root ae_blocks; uint64_t ae_blockcount; }; static void block_free(struct block_to_ecc *block) { if (block->e_buf) ocfs2_free(&block->e_buf); ocfs2_free(&block); } static struct block_to_ecc *block_lookup(struct add_ecc_context *ctxt, uint64_t blkno) { struct rb_node *p = ctxt->ae_blocks.rb_node; struct block_to_ecc *block; while (p) { block = rb_entry(p, struct block_to_ecc, e_node); if (blkno < block->e_blkno) { p = p->rb_left; } else if (blkno > block->e_blkno) { p = p->rb_right; } else return block; } return NULL; } static void dump_ecc_tree(struct add_ecc_context *ctxt) { struct rb_node *n; struct block_to_ecc *tmp; verbosef(VL_DEBUG, "Dumping ecc block tree\n"); n = rb_first(&ctxt->ae_blocks); while (n) { tmp = rb_entry(n, struct block_to_ecc, e_node); verbosef(VL_DEBUG, "Block %"PRIu64", struct %p, buf %p\n", tmp->e_blkno, tmp, tmp->e_buf); n = rb_next(n); } } static errcode_t block_insert(struct add_ecc_context *ctxt, struct block_to_ecc *block) { errcode_t ret; struct block_to_ecc *tmp; struct rb_node **p = &ctxt->ae_blocks.rb_node; struct rb_node *parent = NULL; while (*p) { parent = *p; tmp = rb_entry(parent, struct block_to_ecc, e_node); if (block->e_blkno < tmp->e_blkno) { p = &(*p)->rb_left; tmp = NULL; } else if (block->e_blkno > tmp->e_blkno) { p = &(*p)->rb_right; tmp = NULL; } else { dump_ecc_tree(ctxt); assert(0); /* We shouldn't find it. */ } } rb_link_node(&block->e_node, parent, p); rb_insert_color(&block->e_node, &ctxt->ae_blocks); ctxt->ae_blockcount++; ret = 0; return ret; } static errcode_t dinode_write_func(ocfs2_filesys *fs, struct block_to_ecc *block) { return ocfs2_write_inode(fs, block->e_blkno, block->e_buf); } static errcode_t block_insert_dinode(ocfs2_filesys *fs, struct add_ecc_context *ctxt, struct ocfs2_dinode *di) { errcode_t ret; struct block_to_ecc *block = NULL; ret = ocfs2_malloc0(sizeof(struct block_to_ecc), &block); if (ret) goto out; ret = ocfs2_malloc_block(fs->fs_io, &block->e_buf); if (ret) goto out; memcpy(block->e_buf, di, fs->fs_blocksize); block->e_di = (struct ocfs2_dinode *)block->e_buf; block->e_blkno = di->i_blkno; block->e_write = dinode_write_func; block_insert(ctxt, block); out: if (ret && block) block_free(block); return ret; } static errcode_t eb_write_func(ocfs2_filesys *fs, struct block_to_ecc *block) { return ocfs2_write_extent_block(fs, block->e_blkno, block->e_buf); } static errcode_t block_insert_eb(ocfs2_filesys *fs, struct add_ecc_context *ctxt, struct ocfs2_dinode *di, struct ocfs2_extent_block *eb) { errcode_t ret; struct block_to_ecc *block = NULL; ret = ocfs2_malloc0(sizeof(struct block_to_ecc), &block); if (ret) goto out; ret = ocfs2_malloc_block(fs->fs_io, &block->e_buf); if (ret) goto out; memcpy(block->e_buf, eb, fs->fs_blocksize); block->e_blkno = eb->h_blkno; block->e_write = eb_write_func; block_insert(ctxt, block); out: if (ret && block) block_free(block); return ret; } static errcode_t gd_write_func(ocfs2_filesys *fs, struct block_to_ecc *block) { return ocfs2_write_group_desc(fs, block->e_blkno, block->e_buf); } static errcode_t block_insert_gd(ocfs2_filesys *fs, struct add_ecc_context *ctxt, struct ocfs2_dinode *di, struct ocfs2_group_desc *gd) { errcode_t ret; struct block_to_ecc *block = NULL; ret = ocfs2_malloc0(sizeof(struct block_to_ecc), &block); if (ret) goto out; ret = ocfs2_malloc_block(fs->fs_io, &block->e_buf); if (ret) goto out; memcpy(block->e_buf, gd, fs->fs_blocksize); block->e_blkno = gd->bg_blkno; block->e_write = gd_write_func; block_insert(ctxt, block); out: if (ret && block) block_free(block); return ret; } static errcode_t dirblock_write_func(ocfs2_filesys *fs, struct block_to_ecc *block) { return ocfs2_write_dir_block(fs, block->e_di, block->e_blkno, block->e_buf); } static errcode_t block_insert_dirblock(ocfs2_filesys *fs, struct add_ecc_context *ctxt, struct ocfs2_dinode *di, uint64_t blkno, char *buf) { errcode_t ret; struct block_to_ecc *block = NULL; ret = ocfs2_malloc0(sizeof(struct block_to_ecc), &block); if (ret) goto out; ret = ocfs2_malloc_block(fs->fs_io, &block->e_buf); if (ret) goto out; memcpy(block->e_buf, buf, fs->fs_blocksize); block->e_di = di; block->e_blkno = blkno; block->e_write = dirblock_write_func; block_insert(ctxt, block); out: if (ret && block) block_free(block); return ret; } static void empty_ecc_blocks(struct add_ecc_context *ctxt) { struct block_to_ecc *block; struct rb_node *node; while ((node = rb_first(&ctxt->ae_blocks)) != NULL) { block = rb_entry(node, struct block_to_ecc, e_node); rb_erase(&block->e_node, &ctxt->ae_blocks); ocfs2_free(&block->e_buf); ocfs2_free(&block); } } static errcode_t add_ecc_chain(struct add_ecc_context *ctxt, uint64_t blkno) { errcode_t ret; struct chain_to_ecc *cte; ret = ocfs2_malloc0(sizeof(struct chain_to_ecc), &cte); if (!ret) { cte->ce_blkno = blkno; list_add_tail(&cte->ce_list, &ctxt->ae_chains); ctxt->ae_chaincount++; } return ret; } static void empty_add_ecc_context(struct add_ecc_context *ctxt) { struct tunefs_trailer_context *tc; struct chain_to_ecc *cte; struct list_head *n, *pos; list_for_each_safe(pos, n, &ctxt->ae_chains) { cte = list_entry(pos, struct chain_to_ecc, ce_list); list_del(&cte->ce_list); ocfs2_free(&cte); } list_for_each_safe(pos, n, &ctxt->ae_dirs) { tc = list_entry(pos, struct tunefs_trailer_context, d_list); tunefs_trailer_context_free(tc); } empty_ecc_blocks(ctxt); } struct add_ecc_iterate { struct add_ecc_context *ic_ctxt; struct ocfs2_dinode *ic_di; }; static int chain_iterate(ocfs2_filesys *fs, uint64_t gd_blkno, int chain_num, void *priv_data) { struct add_ecc_iterate *iter = priv_data; struct ocfs2_group_desc *gd = NULL; errcode_t ret; int iret = 0; ret = ocfs2_malloc_block(fs->fs_io, &gd); if (ret) goto out; verbosef(VL_DEBUG, "Reading group descriptor at %"PRIu64"\n", gd_blkno); ret = ocfs2_read_group_desc(fs, gd_blkno, (char *)gd); if (ret) goto out; ret = block_insert_gd(fs, iter->ic_ctxt, iter->ic_di, gd); out: if (gd) ocfs2_free(&gd); if (ret) { iter->ic_ctxt->ae_ret = ret; iret = OCFS2_CHAIN_ABORT; } return iret; } /* * Right now, this only handles directory data. Quota stuff will want * to genericize this or copy it. */ static int dirdata_iterate(ocfs2_filesys *fs, struct ocfs2_extent_rec *rec, int tree_depth, uint32_t ccount, uint64_t ref_blkno, int ref_recno, void *priv_data) { errcode_t ret = 0; struct add_ecc_iterate *iter = priv_data; char *buf = NULL; struct ocfs2_extent_block *eb; int iret = 0; uint64_t blocks, i; ret = ocfs2_malloc_block(fs->fs_io, &buf); if (ret) goto out; if (tree_depth) { verbosef(VL_DEBUG, "Reading extent block at %"PRIu64"\n", (uint64_t)rec->e_blkno); eb = (struct ocfs2_extent_block *)buf; ret = ocfs2_read_extent_block(fs, rec->e_blkno, (char *)eb); if (ret) goto out; ret = block_insert_eb(fs, iter->ic_ctxt, iter->ic_di, eb); } else { blocks = ocfs2_clusters_to_blocks(fs, rec->e_leaf_clusters); for (i = 0; i < blocks; i++) { ret = ocfs2_read_dir_block(fs, iter->ic_di, rec->e_blkno + i, buf); if (ret) break; ret = block_insert_dirblock(fs, iter->ic_ctxt, iter->ic_di, rec->e_blkno + i, buf); if (ret) break; } } out: if (buf) ocfs2_free(&buf); if (ret) { iter->ic_ctxt->ae_ret = ret; iret = OCFS2_EXTENT_ABORT; } return iret; } static int metadata_iterate(ocfs2_filesys *fs, struct ocfs2_extent_rec *rec, int tree_depth, uint32_t ccount, uint64_t ref_blkno, int ref_recno, void *priv_data) { errcode_t ret = 0; struct add_ecc_iterate *iter = priv_data; struct ocfs2_extent_block *eb = NULL; int iret = 0; if (!tree_depth) goto out; ret = ocfs2_malloc_block(fs->fs_io, &eb); if (ret) goto out; verbosef(VL_DEBUG, "Reading extent block at %"PRIu64"\n", (uint64_t)rec->e_blkno); ret = ocfs2_read_extent_block(fs, rec->e_blkno, (char *)eb); if (ret) goto out; ret = block_insert_eb(fs, iter->ic_ctxt, iter->ic_di, eb); out: if (eb) ocfs2_free(&eb); if (ret) { iter->ic_ctxt->ae_ret = ret; iret = OCFS2_EXTENT_ABORT; } return iret; } /* * This walks all the chain allocators we've stored off and adds their * blocks to the list. */ static errcode_t find_chain_blocks(ocfs2_filesys *fs, struct add_ecc_context *ctxt) { errcode_t ret; struct list_head *pos; struct chain_to_ecc *cte; struct ocfs2_dinode *di; struct block_to_ecc *block; char *buf = NULL; struct tools_progress *prog; struct add_ecc_iterate iter = { .ic_ctxt = ctxt, }; ret = ocfs2_malloc_block(fs->fs_io, &buf); if (ret) goto out; prog = tools_progress_start("Scanning allocators", "chains", ctxt->ae_chaincount); if (!prog) { ret = TUNEFS_ET_NO_MEMORY; goto out; } list_for_each(pos, &ctxt->ae_chains) { cte = list_entry(pos, struct chain_to_ecc, ce_list); ret = ocfs2_read_inode(fs, cte->ce_blkno, buf); if (ret) break; di = (struct ocfs2_dinode *)buf; ret = block_insert_dinode(fs, ctxt, di); if (ret) break; /* We need the inode, look it back up */ block = block_lookup(ctxt, di->i_blkno); if (!block) { ret = TUNEFS_ET_INTERNAL_FAILURE; break; } /* Now using our copy of the inode */ di = (struct ocfs2_dinode *)block->e_buf; assert(di->i_blkno == cte->ce_blkno); iter.ic_di = di; ret = ocfs2_chain_iterate(fs, di->i_blkno, chain_iterate, &iter); if (ret) break; tools_progress_step(prog, 1); } tools_progress_stop(prog); out: if (buf) ocfs2_free(&buf); return ret; } static errcode_t inode_iterate(ocfs2_filesys *fs, struct ocfs2_dinode *di, void *user_data) { errcode_t ret; struct block_to_ecc *block = NULL; struct tunefs_trailer_context *tc; struct add_ecc_context *ctxt = user_data; struct add_ecc_iterate iter = { .ic_ctxt = ctxt, }; /* * We have to handle chain allocators later, after the dir * trailer code has done any allocation it needs. */ if (di->i_flags & OCFS2_CHAIN_FL) { ret = add_ecc_chain(ctxt, di->i_blkno); goto out; } ret = block_insert_dinode(fs, ctxt, di); if (ret) goto out; /* These inodes have no other metadata on them */ if ((di->i_flags & (OCFS2_SUPER_BLOCK_FL | OCFS2_LOCAL_ALLOC_FL | OCFS2_DEALLOC_FL)) || (S_ISLNK(di->i_mode) && di->i_clusters == 0) || (di->i_dyn_features & OCFS2_INLINE_DATA_FL)) goto out; /* We need the inode, look it back up */ block = block_lookup(ctxt, di->i_blkno); if (!block) { ret = TUNEFS_ET_INTERNAL_FAILURE; goto out; } /* Now using our copy of the inode */ di = (struct ocfs2_dinode *)block->e_buf; iter.ic_di = di; /* * Ok, it's a regular file or directory. * If it's a regular file, gather extent blocks for this inode. * If it's a directory that has trailers, we need to gather all * of its blocks, data and metadata. * * We don't gather extent info for directories that need trailers * yet, because they might get modified as they gain trailers. * We'll add them after we insert their trailers. */ if (!S_ISDIR(di->i_mode)) ret = ocfs2_extent_iterate_inode(fs, di, 0, NULL, metadata_iterate, &iter); else if (ocfs2_dir_has_trailer(fs, di)) ret = ocfs2_extent_iterate_inode(fs, di, 0, NULL, dirdata_iterate, &iter); else { ret = tunefs_prepare_dir_trailer(fs, di, &tc); if (!ret) { verbosef(VL_DEBUG, "Directory %"PRIu64" needs %"PRIu64" " "more blocks\n", tc->d_blkno, tc->d_blocks_needed); list_add(&tc->d_list, &ctxt->ae_dirs); ctxt->ae_dircount++; ctxt->ae_clusters += ocfs2_clusters_in_blocks(fs, tc->d_blocks_needed); } } out: tools_progress_step(ctxt->ae_prog, 1); return ret; } static errcode_t find_blocks(ocfs2_filesys *fs, struct add_ecc_context *ctxt) { errcode_t ret; uint32_t free_clusters = 0; ctxt->ae_prog = tools_progress_start("Scanning filesystem", "scanning", 0); if (!ctxt->ae_prog) { ret = TUNEFS_ET_NO_MEMORY; goto bail; } ret = tunefs_foreach_inode(fs, inode_iterate, ctxt); if (ret) goto bail; tools_progress_stop(ctxt->ae_prog); ctxt->ae_prog = NULL; ret = tunefs_get_free_clusters(fs, &free_clusters); if (ret) goto bail; verbosef(VL_APP, "We have %u clusters free, and need %u clusters to add " "trailers to every directory\n", free_clusters, ctxt->ae_clusters); if (free_clusters < ctxt->ae_clusters) ret = OCFS2_ET_NO_SPACE; bail: if (ctxt->ae_prog) tools_progress_stop(ctxt->ae_prog); return ret; } static errcode_t install_trailers(ocfs2_filesys *fs, struct add_ecc_context *ctxt) { errcode_t ret = 0; struct tunefs_trailer_context *tc; struct list_head *n, *pos; struct tools_progress *prog; struct add_ecc_iterate iter = { .ic_ctxt = ctxt, }; prog = tools_progress_start("Installing dir trailers", "trailers", ctxt->ae_dircount); list_for_each_safe(pos, n, &ctxt->ae_dirs) { tc = list_entry(pos, struct tunefs_trailer_context, d_list); verbosef(VL_DEBUG, "Writing trailer for dinode %"PRIu64"\n", (uint64_t)tc->d_di->i_blkno); tunefs_block_signals(); ret = tunefs_install_dir_trailer(fs, tc->d_di, tc); tunefs_unblock_signals(); if (ret) break; iter.ic_di = tc->d_di; tunefs_trailer_context_free(tc); /* * Now that we've put trailers on the directory, we know * its allocation won't change. Add its blocks to the * block list. These will be cached, as installing the * trailer will have just touched them. */ ret = ocfs2_extent_iterate_inode(fs, tc->d_di, 0, NULL, dirdata_iterate, &iter); if (ret) break; tools_progress_step(prog, 1); } tools_progress_stop(prog); return ret; } static errcode_t write_ecc_blocks(ocfs2_filesys *fs, struct add_ecc_context *ctxt) { errcode_t ret = 0; struct rb_node *n; struct block_to_ecc *block; struct tools_progress *prog; prog = tools_progress_start("Writing blocks", "ECC", ctxt->ae_blockcount); if (!prog) return TUNEFS_ET_NO_MEMORY; n = rb_first(&ctxt->ae_blocks); while (n) { block = rb_entry(n, struct block_to_ecc, e_node); verbosef(VL_DEBUG, "Writing block %"PRIu64"\n", block->e_blkno); tools_progress_step(prog, 1); ret = block->e_write(fs, block); if (ret) break; n = rb_next(n); } tools_progress_stop(prog); return ret; } static int enable_metaecc(ocfs2_filesys *fs, int flags) { errcode_t ret = 0; struct ocfs2_super_block *super = OCFS2_RAW_SB(fs->fs_super); struct add_ecc_context ctxt; struct tools_progress *prog = NULL; if (ocfs2_meta_ecc(super)) { verbosef(VL_APP, "The metadata ECC feature is already enabled; " "nothing to enable\n"); goto out; } if (!tools_interact("Enable the metadata ECC feature on device " "\"%s\"? ", fs->fs_devname)) goto out; prog = tools_progress_start("Enabling metaecc", "metaecc", 5); if (!prog) { ret = TUNEFS_ET_NO_MEMORY; tcom_err(ret, "while initializing the progress display"); goto out; } memset(&ctxt, 0, sizeof(ctxt)); INIT_LIST_HEAD(&ctxt.ae_dirs); INIT_LIST_HEAD(&ctxt.ae_chains); ctxt.ae_blocks = RB_ROOT; ret = find_blocks(fs, &ctxt); if (ret) { if (ret == OCFS2_ET_NO_SPACE) errorf("There is not enough space to add directory " "trailers to the directories on device " "\"%s\"\n", fs->fs_devname); else tcom_err(ret, "while trying to find directory blocks"); goto out_cleanup; } tools_progress_step(prog, 1); ret = tunefs_set_in_progress(fs, OCFS2_TUNEFS_INPROG_DIR_TRAILER); if (ret) goto out_cleanup; ret = install_trailers(fs, &ctxt); if (ret) { tcom_err(ret, "while trying to install directory trailers on " "device \"%s\"", fs->fs_devname); goto out_cleanup; } ret = tunefs_clear_in_progress(fs, OCFS2_TUNEFS_INPROG_DIR_TRAILER); if (ret) goto out_cleanup; tools_progress_step(prog, 1); /* We're done with allocation, scan the chain allocators */ ret = find_chain_blocks(fs, &ctxt); if (ret) goto out_cleanup; tools_progress_step(prog, 1); /* Set the feature bit in-memory and rewrite all our blocks */ OCFS2_SET_INCOMPAT_FEATURE(super, OCFS2_FEATURE_INCOMPAT_META_ECC); ret = write_ecc_blocks(fs, &ctxt); if (ret) goto out_cleanup; tools_progress_step(prog, 1); tunefs_block_signals(); ret = ocfs2_write_super(fs); tunefs_unblock_signals(); if (ret) tcom_err(ret, "while writing out the superblock"); tools_progress_step(prog, 1); out_cleanup: empty_add_ecc_context(&ctxt); out: if (prog) tools_progress_stop(prog); return ret; } static int disable_metaecc(ocfs2_filesys *fs, int flags) { errcode_t ret = 0; struct ocfs2_super_block *super = OCFS2_RAW_SB(fs->fs_super); struct tools_progress *prog = NULL; if (!ocfs2_meta_ecc(super)) { verbosef(VL_APP, "The metadata ECC feature is not enabled; " "nothing to disable\n"); goto out; } if (!tools_interact("Disable the metadata ECC feature on device " "\"%s\"? ", fs->fs_devname)) goto out; prog = tools_progress_start("Disabling metaecc", "nometaecc", 1); if (!prog) { ret = TUNEFS_ET_NO_MEMORY; goto out; } OCFS2_CLEAR_INCOMPAT_FEATURE(super, OCFS2_FEATURE_INCOMPAT_META_ECC); tunefs_block_signals(); ret = ocfs2_write_super(fs); tunefs_unblock_signals(); if (ret) tcom_err(ret, "while writing out the superblock"); tools_progress_step(prog, 1); out: if (prog) tools_progress_stop(prog); return ret; } DEFINE_TUNEFS_FEATURE_INCOMPAT(metaecc, OCFS2_FEATURE_INCOMPAT_META_ECC, TUNEFS_FLAG_RW | TUNEFS_FLAG_ALLOCATION | TUNEFS_FLAG_LARGECACHE, enable_metaecc, disable_metaecc); #ifdef DEBUG_EXE int main(int argc, char *argv[]) { return tunefs_feature_main(argc, argv, &metaecc_feature); } #endif ocfs2-tools-ocfs2-tools-1.8.6/tunefs.ocfs2/feature_quota.c000066400000000000000000000251721347147137200234170ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * feature_quota.c * * ocfs2 tune utility for enabling and disabling quota support. * * Copyright (C) 2008 Novell. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License version 2 as published by the Free Software Foundation. * * 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. */ #include #include #include #include #include #include #include "ocfs2/ocfs2.h" #include "libocfs2ne.h" static char *type2name(int type) { if (type == USRQUOTA) return "user"; return "group"; } static errcode_t create_system_file(ocfs2_filesys *fs, int type, int node) { char fname[OCFS2_MAX_FILENAME_LEN]; uint64_t blkno; errcode_t ret; ocfs2_sprintf_system_inode_name(fname, sizeof(fname), type, node); ret = ocfs2_lookup(fs, fs->fs_sysdir_blkno, fname, strlen(fname), NULL, &blkno); if (!ret) { verbosef(VL_APP, "System file \"%s\" already exists!\n", fname); return 0; } ret = ocfs2_new_system_inode(fs, &blkno, ocfs2_system_inodes[type].si_mode, ocfs2_system_inodes[type].si_iflags); if (ret) { tcom_err(ret, "while creating system file \"%s\"", fname); return ret; } ret = ocfs2_link(fs, fs->fs_sysdir_blkno, fname, blkno, OCFS2_FT_REG_FILE); if (ret) { tcom_err(ret, "while linking file \"%s\" in the system " "directory", fname); return ret; } return 0; } static errcode_t create_quota_files(ocfs2_filesys *fs, int type, struct tools_progress *prog) { ocfs2_quota_hash *hash; errcode_t ret; int num_slots = OCFS2_RAW_SB(fs->fs_super)->s_max_slots; int i; int local_type = (type == USRQUOTA) ? LOCAL_USER_QUOTA_SYSTEM_INODE : LOCAL_GROUP_QUOTA_SYSTEM_INODE; int global_type = (type == USRQUOTA) ? USER_QUOTA_SYSTEM_INODE : GROUP_QUOTA_SYSTEM_INODE; verbosef(VL_APP, "Creating %s quota system files\n", type2name(type)); ret = create_system_file(fs, global_type, 0); if (ret) return ret; for (i = 0; i < num_slots; i++) { ret = create_system_file(fs, local_type, i); if (ret) return ret; } tools_progress_step(prog, 1); verbosef(VL_APP, "Initializing global %s quota file\n", type2name(type)); ret = ocfs2_init_fs_quota_info(fs, type); if (ret) { tcom_err(ret, "while looking up global %s quota file", type2name(type)); return ret; } fs->qinfo[type].flags = OCFS2_QF_INFO_LOADED; fs->qinfo[type].qi_info.dqi_syncms = OCFS2_DEF_QUOTA_SYNC; fs->qinfo[type].qi_info.dqi_bgrace = OCFS2_DEF_BLOCK_GRACE; fs->qinfo[type].qi_info.dqi_igrace = OCFS2_DEF_INODE_GRACE; ret = ocfs2_init_global_quota_file(fs, type); if (ret) { tcom_err(ret, "while initializing global %s quota files", type2name(type)); return ret; } tools_progress_step(prog, 1); verbosef(VL_APP, "Initializing local %s quota files\n", type2name(type)); ret = ocfs2_init_local_quota_files(fs, type); if (ret) { tcom_err(ret, "while initializing local %s quota files", type2name(type)); return ret; } tools_progress_step(prog, 1); verbosef(VL_APP, "Computing %s quota usage\n", type2name(type)); ret = ocfs2_new_quota_hash(&hash); if (ret) { tcom_err(ret, "while creating quota hash"); return ret; } if (type == USRQUOTA) ret = ocfs2_compute_quota_usage(fs, hash, NULL); else ret = ocfs2_compute_quota_usage(fs, NULL, hash); if (ret) { tcom_err(ret, "while scanning filesystem to gather " "quota usage"); return ret; } tools_progress_step(prog, 1); verbosef(VL_APP, "Write %s quotas to file\n", type2name(type)); ret = ocfs2_write_release_dquots(fs, type, hash); if (ret) { tcom_err(ret, "while writing %s quota usage to disk", type2name(type)); return ret; } tools_progress_step(prog, 1); ret = ocfs2_free_quota_hash(hash); if (ret) tcom_err(ret, "while freeing quota hash"); return ret; } struct remove_quota_files_ctxt { ocfs2_filesys *fs; errcode_t err; int type; }; static int remove_quota_files_iterate(struct ocfs2_dir_entry *dirent, uint64_t blocknr, int offset, int blocksize, char *buf, void *priv_data) { struct remove_quota_files_ctxt *ctxt = priv_data; char dname[OCFS2_MAX_FILENAME_LEN]; char wname[OCFS2_MAX_FILENAME_LEN]; errcode_t ret; int tail, i; int ret_flags = 0; strncpy(dname, dirent->name, dirent->name_len); dname[dirent->name_len] = 0; /* Check whether entry is quota file of type we want - i.e. matching * aquota.user:[0-9][0-9][0-9][0-9] or aquota.user for type == USRQUOTA * and similarly for type == GRPQUOTA */ strcpy(wname, "aquota."); strcat(wname, type2name(ctxt->type)); tail = strlen(wname); if (strncmp(dname, wname, tail)) return 0; if (dname[tail] == ':') { /* May be local file? */ tail++; for (i = 0; i < 4; i++) if (dname[tail + i] < '0' || dname[tail + i] > '9') return 0; if (dname[tail + i]) return 0; } else if (dname[tail]) /* May be global file? */ return 0; verbosef(VL_APP, "Deleting quota file %s\n", dname); ret = ocfs2_truncate(ctxt->fs, dirent->inode, 0); if (ret) { tcom_err(ret, "while truncating quota file \"%s\"", dname); ret_flags |= OCFS2_DIRENT_ERROR; ctxt->err = ret; goto out; } ret = ocfs2_delete_inode(ctxt->fs, dirent->inode); if (ret) { tcom_err(ret, "while deleting quota file \"%s\"", dname); ret_flags |= OCFS2_DIRENT_ERROR; ctxt->err = ret; } else { dirent->inode = 0; ret_flags |= OCFS2_DIRENT_CHANGED; } out: return ret_flags; } static errcode_t remove_quota_files(ocfs2_filesys *fs, int type, struct tools_progress *prog) { struct remove_quota_files_ctxt ctxt = { .fs = fs, .type = type, .err = 0, }; ocfs2_dir_iterate(fs, fs->fs_sysdir_blkno, OCFS2_DIRENT_FLAG_EXCLUDE_DOTS, NULL, remove_quota_files_iterate, &ctxt); tools_progress_step(prog, 1); return ctxt.err; } static int enable_usrquota(ocfs2_filesys *fs, int flags) { struct ocfs2_super_block *super = OCFS2_RAW_SB(fs->fs_super); errcode_t ret; struct tools_progress *prog = NULL; if (OCFS2_HAS_RO_COMPAT_FEATURE(super, OCFS2_FEATURE_RO_COMPAT_USRQUOTA)) { verbosef(VL_APP, "User quotas are already enabled; " "nothing to enable\n"); return 0; } if (!tools_interact("Enable user quota feature on device " "\"%s\"? ", fs->fs_devname)) return 0; prog = tools_progress_start("Enabling user quota", "usrquota", 6); if (!prog) { ret = TUNEFS_ET_NO_MEMORY; tcom_err(ret, "while initializing progress display"); return ret; } tunefs_block_signals(); ret = create_quota_files(fs, USRQUOTA, prog); if (ret) { tcom_err(ret, "while creating user quota files"); goto bail; } OCFS2_SET_RO_COMPAT_FEATURE(super, OCFS2_FEATURE_RO_COMPAT_USRQUOTA); ret = ocfs2_write_super(fs); tools_progress_step(prog, 1); bail: tunefs_unblock_signals(); tools_progress_stop(prog); return ret; } static int disable_usrquota(ocfs2_filesys *fs, int flags) { errcode_t ret; struct ocfs2_super_block *super = OCFS2_RAW_SB(fs->fs_super); struct tools_progress *prog = NULL; if (!OCFS2_HAS_RO_COMPAT_FEATURE(super, OCFS2_FEATURE_RO_COMPAT_USRQUOTA)) { verbosef(VL_APP, "User quotas are already disabled; " "nothing to disable\n"); return 0; } if (!tools_interact("Disable user quota feature on device " "\"%s\"? ", fs->fs_devname)) return 0; prog = tools_progress_start("Disabling user quota", "nousrquota", 2); if (!prog) { ret = TUNEFS_ET_NO_MEMORY; tcom_err(ret, "while initializing progress display"); return ret; } tunefs_block_signals(); ret = remove_quota_files(fs, USRQUOTA, prog); if (ret) { tcom_err(ret, "while removing user quota files"); goto bail; } OCFS2_CLEAR_RO_COMPAT_FEATURE(super, OCFS2_FEATURE_RO_COMPAT_USRQUOTA); ret = ocfs2_write_super(fs); tools_progress_step(prog, 1); bail: tunefs_unblock_signals(); tools_progress_stop(prog); return ret; } static int enable_grpquota(ocfs2_filesys *fs, int flags) { struct ocfs2_super_block *super = OCFS2_RAW_SB(fs->fs_super); errcode_t ret; struct tools_progress *prog = NULL; if (OCFS2_HAS_RO_COMPAT_FEATURE(super, OCFS2_FEATURE_RO_COMPAT_GRPQUOTA)) { verbosef(VL_APP, "Group quotas are already enabled; " "nothing to enable\n"); return 0; } if (!tools_interact("Enable group quota feature on device " "\"%s\"? ", fs->fs_devname)) return 0; prog = tools_progress_start("Enabling group quota", "grpquota", 6); if (!prog) { ret = TUNEFS_ET_NO_MEMORY; tcom_err(ret, "while initializing progress display"); return ret; } tunefs_block_signals(); ret = create_quota_files(fs, GRPQUOTA, prog); if (ret) { tcom_err(ret, "while creating group quota files"); goto bail; } OCFS2_SET_RO_COMPAT_FEATURE(super, OCFS2_FEATURE_RO_COMPAT_GRPQUOTA); ret = ocfs2_write_super(fs); tools_progress_step(prog, 1); bail: tools_progress_stop(prog); tunefs_unblock_signals(); return ret; } static int disable_grpquota(ocfs2_filesys *fs, int flags) { errcode_t ret; struct ocfs2_super_block *super = OCFS2_RAW_SB(fs->fs_super); struct tools_progress *prog = NULL; if (!OCFS2_HAS_RO_COMPAT_FEATURE(super, OCFS2_FEATURE_RO_COMPAT_GRPQUOTA)) { verbosef(VL_APP, "Group quotas are already disabled; " "nothing to disable\n"); return 0; } if (!tools_interact("Disable group quota feature on device " "\"%s\"? ", fs->fs_devname)) return 0; prog = tools_progress_start("Disabling user quota", "nousrquota", 2); if (!prog) { ret = TUNEFS_ET_NO_MEMORY; tcom_err(ret, "while initializing progress display"); return ret; } tunefs_block_signals(); ret = remove_quota_files(fs, GRPQUOTA, prog); if (ret) { tcom_err(ret, "while removing group quota files"); goto bail; } OCFS2_CLEAR_RO_COMPAT_FEATURE(super, OCFS2_FEATURE_RO_COMPAT_GRPQUOTA); ret = ocfs2_write_super(fs); tools_progress_step(prog, 1); bail: tools_progress_stop(prog); tunefs_unblock_signals(); return ret; } DEFINE_TUNEFS_FEATURE_RO_COMPAT(usrquota, OCFS2_FEATURE_RO_COMPAT_USRQUOTA, TUNEFS_FLAG_RW | TUNEFS_FLAG_ALLOCATION, enable_usrquota, disable_usrquota); DEFINE_TUNEFS_FEATURE_RO_COMPAT(grpquota, OCFS2_FEATURE_RO_COMPAT_GRPQUOTA, TUNEFS_FLAG_RW | TUNEFS_FLAG_ALLOCATION, enable_grpquota, disable_grpquota); #ifdef DEBUG_EXE int main(int argc, char *argv[]) { int ret; ret = tunefs_feature_main(argc, argv, &usrquota_feature); if (ret) return ret; return tunefs_feature_main(argc, argv, &grpquota_feature); } #endif ocfs2-tools-ocfs2-tools-1.8.6/tunefs.ocfs2/feature_refcount.c000066400000000000000000000322411347147137200241060ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * feature_refcount.c * * ocfs2 tune utility for enabling and disabling the refcount feature. * * Copyright (C) 2009 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License version 2 as published by the Free Software Foundation. * * 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. */ #include #include #include #include #include #include #include #include "ocfs2/kernel-rbtree.h" #include "ocfs2-kernel/kernel-list.h" #include "ocfs2/ocfs2.h" #include "libocfs2ne.h" struct refcount_file { struct list_head list; uint64_t blkno; }; struct refcount_block { struct rb_node ref_node; uint64_t blkno; struct list_head files_list; }; struct disable_refcount_ctxt { errcode_t ret; struct tools_progress *prog; uint32_t more_clusters; uint32_t more_ebs; struct rb_root ref_blknos; int files_count; }; /* See if the recount_file rbtree has the given ref_blkno. */ static struct refcount_block* refcount_block_lookup(struct disable_refcount_ctxt *ctxt, uint64_t ref_blkno) { struct rb_node *p = ctxt->ref_blknos.rb_node; struct refcount_block *ref_blk; while (p) { ref_blk = rb_entry(p, struct refcount_block, ref_node); if (ref_blkno < ref_blk->blkno) p = p->rb_left; else if (ref_blkno > ref_blk->blkno) p = p->rb_right; else return ref_blk; } return NULL; } static void refcount_block_insert(struct disable_refcount_ctxt *ctxt, struct refcount_block *insert_rb) { struct rb_node **p = &ctxt->ref_blknos.rb_node; struct rb_node *parent = NULL; struct refcount_block *ref_blk = NULL; while (*p) { parent = *p; ref_blk = rb_entry(parent, struct refcount_block, ref_node); if (insert_rb->blkno < ref_blk->blkno) p = &(*p)->rb_left; else if (insert_rb->blkno > ref_blk->blkno) p = &(*p)->rb_right; else assert(0); /* Caller checked */ } rb_link_node(&insert_rb->ref_node, parent, p); rb_insert_color(&insert_rb->ref_node, &ctxt->ref_blknos); } static void empty_refcount_file_context(struct disable_refcount_ctxt *ctxt) { struct refcount_block *ref_blk; struct refcount_file *file; struct rb_node *node; struct list_head *p, *next; while ((node = rb_first(&ctxt->ref_blknos)) != NULL) { ref_blk = rb_entry(node, struct refcount_block, ref_node); list_for_each_safe(p, next, &ref_blk->files_list) { file = list_entry(p, struct refcount_file, list); list_del(&file->list); ocfs2_free(&file); } rb_erase(&ref_blk->ref_node, &ctxt->ref_blknos); ocfs2_free(&ref_blk); } } static int ocfs2_xattr_get_refcount_clusters(ocfs2_cached_inode *ci, char *xe_buf, uint64_t xe_blkno, struct ocfs2_xattr_entry *xe, char *value_buf, uint64_t value_blkno, void *value, int in_bucket, void *priv_data) { errcode_t ret = 0; uint32_t *clusters = priv_data; uint32_t cpos = 0, len, p_cluster, num_clusters; uint16_t ext_flags; struct ocfs2_xattr_value_root *xv; if (ocfs2_xattr_is_local(xe)) return 0; xv = (struct ocfs2_xattr_value_root *)value; len = xv->xr_clusters; while (len) { ret = ocfs2_xattr_get_clusters(ci->ci_fs, &xv->xr_list, value_blkno, value_buf, cpos, &p_cluster, &num_clusters, &ext_flags); if (ret) break; if (ext_flags & OCFS2_EXT_REFCOUNTED) *clusters += num_clusters; len -= num_clusters; cpos += num_clusters; } if (ret) return OCFS2_XATTR_ERROR; return 0; } static errcode_t ocfs2_find_refcounted_clusters(ocfs2_filesys *fs, uint64_t blkno, uint32_t *clusters) { errcode_t ret; ocfs2_cached_inode *ci = NULL; uint32_t cpos, len, p_cluster, num_clusters; uint16_t ext_flags; ret = ocfs2_read_cached_inode(fs, blkno, &ci); if (ret) goto out; *clusters = 0; cpos = 0; if (!(ci->ci_inode->i_dyn_features & OCFS2_INLINE_DATA_FL)) { len = ocfs2_clusters_in_bytes(fs, ci->ci_inode->i_size); while (len) { ret = ocfs2_get_clusters(ci, cpos, &p_cluster, &num_clusters, &ext_flags); if (ret) break; if (ext_flags & OCFS2_EXT_REFCOUNTED) *clusters += num_clusters; len -= num_clusters; cpos += num_clusters; } } if (ci->ci_inode->i_dyn_features & OCFS2_HAS_XATTR_FL) ret = ocfs2_xattr_iterate(ci, ocfs2_xattr_get_refcount_clusters, clusters); out: if (ci) ocfs2_free_cached_inode(fs, ci); return ret; } static errcode_t refcount_iterate(ocfs2_filesys *fs, struct ocfs2_dinode *di, void *user_data) { errcode_t ret = 0; uint32_t clusters; uint64_t blk_num; struct refcount_file *file = NULL; struct refcount_block *ref_blk = NULL; struct disable_refcount_ctxt *ctxt = user_data; uint32_t recs_per_eb = ocfs2_extent_recs_per_eb(fs->fs_blocksize); if (!S_ISREG(di->i_mode)) goto bail; if (di->i_flags & OCFS2_SYSTEM_FL) goto bail; if (!(di->i_dyn_features & OCFS2_HAS_REFCOUNT_FL)) goto bail; ret = ocfs2_find_refcounted_clusters(fs, di->i_blkno, &clusters); if (ret) goto bail; ret = ocfs2_malloc0(sizeof(struct refcount_file), &file); if (ret) goto bail; file->blkno = di->i_blkno; INIT_LIST_HEAD(&file->list); ref_blk = refcount_block_lookup(ctxt, di->i_refcount_loc); if (!ref_blk) { ret = ocfs2_malloc0(sizeof(struct refcount_block), &ref_blk); if (ret) goto bail; ref_blk->blkno = di->i_refcount_loc; INIT_LIST_HEAD(&ref_blk->files_list); refcount_block_insert(ctxt, ref_blk); } list_add_tail(&file->list, &ref_blk->files_list); ctxt->more_clusters += clusters; blk_num = (clusters + recs_per_eb - 1) / recs_per_eb; ctxt->more_ebs += ocfs2_clusters_in_blocks(fs, blk_num); ctxt->files_count++; tools_progress_step(ctxt->prog, 1); return 0; bail: if (file) ocfs2_free(&file); return ret; } static errcode_t find_refcounted_files(ocfs2_filesys *fs, struct disable_refcount_ctxt *ctxt) { errcode_t ret; uint32_t free_clusters = 0; ctxt->prog = tools_progress_start("Scanning filesystem", "scanning", 0); if (!ctxt->prog) { ret = TUNEFS_ET_NO_MEMORY; goto bail; } ret = tunefs_foreach_inode(fs, refcount_iterate, ctxt); if (ret) goto bail; ret = tunefs_get_free_clusters(fs, &free_clusters); if (ret) goto bail; verbosef(VL_APP, "We have %u clusters free, and need %u clusters to fill " "every sparse file and %u clusters for more extent " "blocks\n", free_clusters, ctxt->more_clusters, ctxt->more_ebs); if (free_clusters < (ctxt->more_clusters + ctxt->more_ebs)) ret = OCFS2_ET_NO_SPACE; bail: if (ctxt->prog) { tools_progress_stop(ctxt->prog); ctxt->prog = NULL; } return ret; } static int ocfs2_xattr_cow_refcount_clusters(ocfs2_cached_inode *ci, char *xe_buf, uint64_t xe_blkno, struct ocfs2_xattr_entry *xe, char *value_buf, uint64_t value_blkno, void *value, int in_bucket, void *priv_data) { errcode_t ret = 0; uint32_t cpos = 0, len, p_cluster, num_clusters; uint16_t ext_flags; struct ocfs2_xattr_value_root *xv; if (ocfs2_xattr_is_local(xe)) return 0; xv = (struct ocfs2_xattr_value_root *)value; len = xv->xr_clusters; while (len) { ret = ocfs2_xattr_get_clusters(ci->ci_fs, &xv->xr_list, value_blkno, value_buf, cpos, &p_cluster, &num_clusters, &ext_flags); if (ret) break; if (ext_flags & OCFS2_EXT_REFCOUNTED) { ret = ocfs2_refcount_cow_xattr(ci, xe_buf, xe_blkno, value_buf, value_blkno, xv, 0, xv->xr_clusters); break; } len -= num_clusters; cpos += num_clusters; } if (ret) return OCFS2_XATTR_ERROR; return 0; } static errcode_t refcount_one_file(ocfs2_filesys *fs, struct refcount_file *file) { errcode_t ret; ocfs2_cached_inode *ci = NULL; uint32_t len; ret = ocfs2_read_cached_inode(fs, file->blkno, &ci); if (ret) goto out; if (!(ci->ci_inode->i_dyn_features & OCFS2_INLINE_DATA_FL)) { len = ocfs2_clusters_in_bytes(fs, ci->ci_inode->i_size); ret = ocfs2_refcount_cow(ci, 0, len, UINT_MAX); if (ret) goto out; } if (ci->ci_inode->i_dyn_features & OCFS2_HAS_XATTR_FL) { ret = ocfs2_xattr_iterate(ci, ocfs2_xattr_cow_refcount_clusters, NULL); if (ret) goto out; } ci->ci_inode->i_dyn_features &= ~OCFS2_HAS_REFCOUNT_FL; ci->ci_inode->i_refcount_loc = 0; ret = ocfs2_write_cached_inode(fs, ci); out: if (ci) ocfs2_free_cached_inode(fs, ci); return ret; } static errcode_t free_refcount_tree(ocfs2_filesys *fs, struct refcount_block *ref_blk) { errcode_t ret; char *buf = NULL; struct ocfs2_refcount_block *rb = NULL; ret = ocfs2_malloc_block(fs->fs_io, &buf); if (ret) goto out; ret = ocfs2_read_refcount_block(fs, ref_blk->blkno, buf); if (ret) goto out; rb = (struct ocfs2_refcount_block *)buf; /* Now the refcount tree should be an empty leaf one. */ assert(!(rb->rf_flags & OCFS2_REFCOUNT_TREE_FL)); assert(!rb->rf_records.rl_used); ret = ocfs2_delete_refcount_block(fs, ref_blk->blkno); out: if (buf) ocfs2_free(&buf); return ret; } static errcode_t replace_refcounted_files(ocfs2_filesys *fs, struct disable_refcount_ctxt *ctxt) { errcode_t ret = 0; struct tools_progress *prog; struct refcount_block *ref_blk; struct refcount_file *file; struct rb_node *node; struct list_head *p, *next; prog = tools_progress_start("Replacing files", "replacing", ctxt->files_count); if (!prog) { ret = TUNEFS_ET_NO_MEMORY; goto out; } while ((node = rb_first(&ctxt->ref_blknos)) != NULL) { ref_blk = rb_entry(node, struct refcount_block, ref_node); list_for_each_safe(p, next, &ref_blk->files_list) { file = list_entry(p, struct refcount_file, list); ret = refcount_one_file(fs, file); if (ret) goto out; tools_progress_step(prog, 1); } ret = free_refcount_tree(fs, ref_blk); if (ret) goto out; rb_erase(&ref_blk->ref_node, &ctxt->ref_blknos); ocfs2_free(&ref_blk); } out: if (prog) tools_progress_stop(prog); return ret; } static int disable_refcount(ocfs2_filesys *fs, int flags) { errcode_t ret = 0; struct ocfs2_super_block *super = OCFS2_RAW_SB(fs->fs_super); struct disable_refcount_ctxt ctxt; struct tools_progress *prog = NULL; if (!ocfs2_refcount_tree(super)) { verbosef(VL_APP, "Refcount feature is not enabled; " "nothing to disable\n"); goto out; } if (!tools_interact("Disable the refcount feature on device " "\"%s\"? ", fs->fs_devname)) goto out; prog = tools_progress_start("Disabling refcount", "norefcount", 3); if (!prog) { ret = TUNEFS_ET_NO_MEMORY; tcom_err(ret, "while initializing the progress display"); goto out; } memset(&ctxt, 0, sizeof(ctxt)); ctxt.ref_blknos = RB_ROOT; ret = find_refcounted_files(fs, &ctxt); if (ret) { if (ret == OCFS2_ET_NO_SPACE) errorf("There is not enough space to fill all of " "the refcounted files on device \"%s\"\n", fs->fs_devname); else tcom_err(ret, "while trying to find refcounted files"); goto out_cleanup; } tools_progress_step(prog, 1); ret = replace_refcounted_files(fs, &ctxt); if (ret) { tcom_err(ret, "while trying to replace refcounted files on device " "\"%s\"", fs->fs_devname); goto out_cleanup; } tools_progress_step(prog, 1); OCFS2_CLEAR_INCOMPAT_FEATURE(super, OCFS2_FEATURE_INCOMPAT_REFCOUNT_TREE); tunefs_block_signals(); ret = ocfs2_write_super(fs); tunefs_unblock_signals(); if (ret) tcom_err(ret, "while writing out the superblock"); tools_progress_step(prog, 1); out_cleanup: empty_refcount_file_context(&ctxt); out: if (prog) tools_progress_stop(prog); return ret; } static int enable_refcount(ocfs2_filesys *fs, int flags) { errcode_t ret = 0; struct ocfs2_super_block *super = OCFS2_RAW_SB(fs->fs_super); struct tools_progress *prog; if (ocfs2_refcount_tree(super)) { verbosef(VL_APP, "Refcount feature is already enabled; " "nothing to enable\n"); goto out; } if (!tools_interact("Enable the refcount feature on " "device \"%s\"? ", fs->fs_devname)) goto out; prog = tools_progress_start("Enable refcount", "refcount", 1); if (!prog) { ret = TUNEFS_ET_NO_MEMORY; tcom_err(ret, "while initializing the progress display"); goto out; } OCFS2_SET_INCOMPAT_FEATURE(super, OCFS2_FEATURE_INCOMPAT_REFCOUNT_TREE); tunefs_block_signals(); ret = ocfs2_write_super(fs); tunefs_unblock_signals(); if (ret) tcom_err(ret, "while writing out the superblock"); tools_progress_step(prog, 1); tools_progress_stop(prog); out: return ret; } DEFINE_TUNEFS_FEATURE_INCOMPAT(refcount, OCFS2_FEATURE_INCOMPAT_REFCOUNT_TREE, TUNEFS_FLAG_RW | TUNEFS_FLAG_ALLOCATION | TUNEFS_FLAG_LARGECACHE, enable_refcount, disable_refcount); #ifdef DEBUG_EXE int main(int argc, char *argv[]) { return tunefs_feature_main(argc, argv, &refcount_feature); } #endif ocfs2-tools-ocfs2-tools-1.8.6/tunefs.ocfs2/feature_sparse_files.c000066400000000000000000000332431347147137200247430ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * feature_sparse_files.c * * ocfs2 tune utility for enabling and disabling the sparse file feature. * * Copyright (C) 2004, 2008 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License version 2 as published by the Free Software Foundation. * * 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. */ #include #include #include #include #include #include #include #include "ocfs2-kernel/kernel-list.h" #include "ocfs2/ocfs2.h" #include "libocfs2ne.h" struct hole_list { struct list_head list; uint32_t start; uint32_t len; }; /* * A sparse file may have many holes. All the holes will be stored in * hole_list. Since filling up a hole may need a new extent record and * lead to some new extent block, the total hole number in the sparse file * will also be recorded. * * Some sparse file may also have some clusters which exceed the limit of * i_size, and they should be truncated. */ struct sparse_file { struct list_head list; uint64_t blkno; struct list_head holes; uint32_t holes_num; uint32_t hole_clusters; int truncate; uint32_t old_clusters; }; struct fill_hole_context { errcode_t ret; struct tools_progress *prog; uint32_t more_clusters; uint32_t more_ebs; struct list_head files; uint64_t holecount; }; /* * Truncate file according to i_size. * All allocated clusters which exceeds i_size will be released. */ static errcode_t truncate_to_i_size(ocfs2_filesys *fs, struct ocfs2_dinode *di, void *user_data) { errcode_t ret = 0; uint32_t new_clusters; ocfs2_cached_inode *ci = NULL; struct tools_progress *prog = user_data; if (!S_ISREG(di->i_mode)) goto out; if (di->i_flags & OCFS2_SYSTEM_FL) goto out; if (di->i_dyn_features & OCFS2_INLINE_DATA_FL) goto out; ret = ocfs2_read_cached_inode(fs, di->i_blkno, &ci); if (ret) goto out; tunefs_block_signals(); ret = ocfs2_zero_tail_and_truncate(fs, ci, di->i_size, &new_clusters); tunefs_unblock_signals(); if (ret) goto out; if (new_clusters != ci->ci_inode->i_clusters) { ci->ci_inode->i_clusters = new_clusters; tunefs_block_signals(); ret = ocfs2_write_cached_inode(fs, ci); tunefs_unblock_signals(); } out: if (ci) ocfs2_free_cached_inode(fs, ci); tools_progress_step(prog, 1); return ret; } static int enable_sparse_files(ocfs2_filesys *fs, int flags) { errcode_t ret = 0; struct ocfs2_super_block *super = OCFS2_RAW_SB(fs->fs_super); struct tools_progress *prog = NULL; if (ocfs2_sparse_alloc(super)) { verbosef(VL_APP, "Sparse file feature is already enabled; " "nothing to enable\n"); goto out; } if (!tools_interact("Enable the sparse file feature on device " "\"%s\"? ", fs->fs_devname)) goto out; prog = tools_progress_start("Enabling sparse", "sparse", 0); if (!prog) { ret = TUNEFS_ET_NO_MEMORY; tcom_err(ret, "while initializing the progress display"); goto out; } ret = tunefs_foreach_inode(fs, truncate_to_i_size, prog); if (ret) { tcom_err(ret, "while trying to remove any extraneous allocation"); goto out; } OCFS2_SET_INCOMPAT_FEATURE(super, OCFS2_FEATURE_INCOMPAT_SPARSE_ALLOC); tunefs_block_signals(); ret = ocfs2_write_super(fs); tunefs_unblock_signals(); if (ret) tcom_err(ret, "while writing out the superblock"); tools_progress_step(prog, 1); out: if (prog) tools_progress_stop(prog); return ret; } static errcode_t add_hole(struct sparse_file *file, uint32_t hole_start, uint32_t hole_len) { errcode_t ret; struct hole_list *hole = NULL; ret = ocfs2_malloc0(sizeof(struct hole_list), &hole); if (ret) return ret; hole->start = hole_start; hole->len = hole_len; list_add_tail(&hole->list, &file->holes); file->hole_clusters += hole_len; file->holes_num++; return 0; } static void free_sparse_file(struct sparse_file *file) { struct hole_list *hole; struct list_head *n, *pos; list_del(&file->list); list_for_each_safe(pos, n, &file->holes) { hole = list_entry(pos, struct hole_list, list); list_del(&hole->list); ocfs2_free(&hole); } ocfs2_free(&file); } static void empty_fill_hole_context(struct fill_hole_context *ctxt) { struct sparse_file *file; struct list_head *n, *pos; list_for_each_safe(pos, n, &ctxt->files) { file = list_entry(pos, struct sparse_file, list); free_sparse_file(file); } } /* * Walk the allocations of a file, filling in the struct sparse_file. */ static errcode_t find_holes_in_file(ocfs2_filesys *fs, struct ocfs2_dinode *di, struct sparse_file *file) { errcode_t ret; uint32_t clusters, v_cluster = 0, p_cluster, num_clusters; uint32_t last_v_cluster = 0; uint16_t extent_flags; ocfs2_cached_inode *ci = NULL; clusters = (di->i_size + fs->fs_clustersize -1 ) / fs->fs_clustersize; ret = ocfs2_read_cached_inode(fs, di->i_blkno, &ci); if (ret) goto bail; while (v_cluster < clusters) { ret = ocfs2_get_clusters(ci, v_cluster, &p_cluster, &num_clusters, &extent_flags); if (ret) goto bail; if (!p_cluster) { /* * If the tail of the file is a hole, let the * hole length only cover the last i_size. */ if (v_cluster + num_clusters == UINT32_MAX) num_clusters = clusters - v_cluster; ret = add_hole(file, v_cluster, num_clusters); if (ret) goto bail; } if (extent_flags & OCFS2_EXT_UNWRITTEN) { ret = TUNEFS_ET_UNWRITTEN_PRESENT; goto bail; } v_cluster += num_clusters; } /* * If the last allocated cluster's virtual offset is greater * than the clusters we calculated from i_size, this cluster * must exceed the limit of i_size. */ ret = ocfs2_get_last_cluster_offset(fs, di, &last_v_cluster); if (ret) goto bail; if (last_v_cluster >= clusters) file->truncate = 1; bail: if (ci) ocfs2_free_cached_inode(fs, ci); return ret; } static errcode_t hole_iterate(ocfs2_filesys *fs, struct ocfs2_dinode *di, void *user_data) { errcode_t ret = 0; uint64_t blk_num; uint32_t clusters; struct sparse_file *file = NULL; uint32_t recs_per_eb = ocfs2_extent_recs_per_eb(fs->fs_blocksize); struct fill_hole_context *ctxt = user_data; if (!S_ISREG(di->i_mode)) goto bail; if (di->i_flags & OCFS2_SYSTEM_FL) goto bail; if (di->i_dyn_features & OCFS2_INLINE_DATA_FL) goto bail; ret = ocfs2_malloc0(sizeof(struct sparse_file), &file); if (ret) goto bail; file->blkno = di->i_blkno; INIT_LIST_HEAD(&file->holes); file->old_clusters = di->i_clusters; ret = find_holes_in_file(fs, di, file); if (ret) goto bail; /* * If there is no hole or unwritten extents in the file and we don't * need to truncate it, just free it. */ if (list_empty(&file->holes) && !file->truncate) goto bail; /* * We have "hole_num" holes, so more extent records are needed, * and more extent blocks may needed here. * In order to simplify the estimation process, we take it for * granted that one hole need one extent record, so that we can * calculate the extent block we need roughly. */ blk_num = (file->holes_num + recs_per_eb - 1) / recs_per_eb; clusters = ocfs2_clusters_in_blocks(fs, blk_num); ctxt->more_ebs += clusters; list_add_tail(&file->list, &ctxt->files); ctxt->holecount += file->holes_num; ctxt->more_clusters += file->hole_clusters; tools_progress_step(ctxt->prog, 1); return 0; bail: if (file) ocfs2_free(&file); tools_progress_step(ctxt->prog, 1); return ret; } static errcode_t find_sparse_files(ocfs2_filesys *fs, struct fill_hole_context *ctxt) { errcode_t ret; uint32_t free_clusters = 0; ctxt->prog = tools_progress_start("Scanning filesystem", "scanning", 0); if (!ctxt->prog) { ret = TUNEFS_ET_NO_MEMORY; goto bail; } ret = tunefs_foreach_inode(fs, hole_iterate, ctxt); if (ret) goto bail; ret = tunefs_get_free_clusters(fs, &free_clusters); if (ret) goto bail; verbosef(VL_APP, "We have %u clusters free, and need %u clusters to fill " "every sparse file and %u clusters for more extent " "blocks\n", free_clusters, ctxt->more_clusters, ctxt->more_ebs); if (free_clusters < (ctxt->more_clusters + ctxt->more_ebs)) ret = OCFS2_ET_NO_SPACE; bail: if (ctxt->prog) { tools_progress_stop(ctxt->prog); ctxt->prog = NULL; } return ret; } static errcode_t fill_one_hole(ocfs2_filesys *fs, struct sparse_file *file, struct hole_list *hole) { errcode_t ret = 0; uint32_t start = hole->start; uint32_t len = hole->len; uint32_t n_clusters; uint64_t p_start; while (len) { tunefs_block_signals(); ret = ocfs2_new_clusters(fs, 1, len, &p_start, &n_clusters); if ((!ret && (n_clusters == 0)) || (ret == OCFS2_ET_BIT_NOT_FOUND)) ret = OCFS2_ET_NO_SPACE; if (ret) break; ret = tunefs_empty_clusters(fs, p_start, n_clusters); if (ret) break; ret = ocfs2_inode_insert_extent(fs, file->blkno, start, p_start, n_clusters, 0); if (ret) break; len -= n_clusters; start += n_clusters; tunefs_unblock_signals(); } if (ret) tunefs_unblock_signals(); return ret; } static errcode_t fill_one_file(ocfs2_filesys *fs, struct sparse_file *file, struct tools_progress *prog) { errcode_t ret = 0; struct hole_list *hole; struct list_head *pos; list_for_each(pos, &file->holes) { hole = list_entry(pos, struct hole_list, list); ret = fill_one_hole(fs, file, hole); if (ret) break; tools_progress_step(prog, 1); } return ret; } static errcode_t fill_sparse_files(ocfs2_filesys *fs, struct fill_hole_context *ctxt) { errcode_t ret = 0, err; char *buf = NULL; struct ocfs2_dinode *di = NULL; struct list_head *pos; struct sparse_file *file; struct tools_progress *prog; ocfs2_quota_hash *usrhash = NULL, *grphash = NULL; struct ocfs2_super_block *super = OCFS2_RAW_SB(fs->fs_super); prog = tools_progress_start("Filling holes", "filling", ctxt->holecount); if (!prog) { ret = TUNEFS_ET_NO_MEMORY; goto out; } ret = ocfs2_load_fs_quota_info(fs); if (ret) goto out; ret = ocfs2_init_quota_change(fs, &usrhash, &grphash); if (ret) goto out; ret = ocfs2_malloc_block(fs->fs_io, &buf); if (ret) goto out; /* Iterate all the holes and fill them. */ list_for_each(pos, &ctxt->files) { file = list_entry(pos, struct sparse_file, list); ret = fill_one_file(fs, file, prog); if (ret) break; if (!file->truncate && !usrhash && !grphash) continue; ret = ocfs2_read_inode(fs, file->blkno, buf); if (ret) break; di = (struct ocfs2_dinode *)buf; if (file->truncate) { ret = truncate_to_i_size(fs, di, prog); if (ret) break; } if (di->i_clusters != file->old_clusters && (!(di->i_flags & OCFS2_SYSTEM_FL) || file->blkno == super->s_root_blkno)) { long long change; if (di->i_clusters > file->old_clusters) { change = ocfs2_clusters_to_bytes(fs, di->i_clusters - file->old_clusters); } else { change = -ocfs2_clusters_to_bytes(fs, file->old_clusters - di->i_clusters); } ret = ocfs2_apply_quota_change(fs, usrhash, grphash, di->i_uid, di->i_gid, change, 0); if (ret) break; } } ocfs2_free(&buf); out: err = ocfs2_finish_quota_change(fs, usrhash, grphash); if (!ret) ret = err; if (prog) tools_progress_stop(prog); return ret; } static int disable_sparse_files(ocfs2_filesys *fs, int flags) { errcode_t ret = 0; struct ocfs2_super_block *super = OCFS2_RAW_SB(fs->fs_super); struct fill_hole_context ctxt; struct tools_progress *prog = NULL; if (!ocfs2_sparse_alloc(super)) { verbosef(VL_APP, "Sparse file feature is not enabled; " "nothing to disable\n"); goto out; } if (ocfs2_writes_unwritten_extents(super)) { errorf("Unwritten extents are enabled on device \"%s\"; " "sparse files cannot be disabled\n", fs->fs_devname); ret = TUNEFS_ET_UNWRITTEN_PRESENT; goto out; } if (!tools_interact("Disable the sparse file feature on device " "\"%s\"? ", fs->fs_devname)) goto out; prog = tools_progress_start("Disabling sparse", "nosparse", 3); if (!prog) { ret = TUNEFS_ET_NO_MEMORY; tcom_err(ret, "while initializing the progress display"); goto out; } memset(&ctxt, 0, sizeof(ctxt)); INIT_LIST_HEAD(&ctxt.files); ret = find_sparse_files(fs, &ctxt); if (ret) { if (ret == OCFS2_ET_NO_SPACE) errorf("There is not enough space to fill all of " "the sparse files on device \"%s\"\n", fs->fs_devname); else tcom_err(ret, "while trying to find sparse files"); goto out_cleanup; } tools_progress_step(prog, 1); ret = fill_sparse_files(fs, &ctxt); if (ret) { tcom_err(ret, "while trying to fill the sparse files on device " "\"%s\"", fs->fs_devname); goto out_cleanup; } tools_progress_step(prog, 1); OCFS2_CLEAR_INCOMPAT_FEATURE(super, OCFS2_FEATURE_INCOMPAT_SPARSE_ALLOC); tunefs_block_signals(); ret = ocfs2_write_super(fs); tunefs_unblock_signals(); if (ret) tcom_err(ret, "while writing out the superblock"); tools_progress_step(prog, 1); out_cleanup: empty_fill_hole_context(&ctxt); out: if (prog) tools_progress_stop(prog); return ret; } DEFINE_TUNEFS_FEATURE_INCOMPAT(sparse_files, OCFS2_FEATURE_INCOMPAT_SPARSE_ALLOC, TUNEFS_FLAG_RW | TUNEFS_FLAG_ALLOCATION | TUNEFS_FLAG_LARGECACHE, enable_sparse_files, disable_sparse_files); #ifdef DEBUG_EXE int main(int argc, char *argv[]) { return tunefs_feature_main(argc, argv, &sparse_files_feature); } #endif ocfs2-tools-ocfs2-tools-1.8.6/tunefs.ocfs2/feature_unwritten_extents.c000066400000000000000000000117531347147137200260770ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * feature_unwritten_extents.c * * ocfs2 tune utility for enabling and disabling the unwritten extents * feature. * * Copyright (C) 2004, 2008 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License version 2 as published by the Free Software Foundation. * * 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. */ #include #include #include #include #include #include #include #include "ocfs2/ocfs2.h" #include "libocfs2ne.h" static int enable_unwritten_extents(ocfs2_filesys *fs, int flags) { errcode_t ret = 0; struct ocfs2_super_block *super = OCFS2_RAW_SB(fs->fs_super); struct tools_progress *prog; if (ocfs2_writes_unwritten_extents(super)) { verbosef(VL_APP, "Unwritten extents feature is already enabled; " "nothing to enable\n"); goto out; } if (!ocfs2_sparse_alloc(super)) { errorf("Sparse files are not enabled on device \"%s\"; " "unwritten extents cannot be enabled\n", fs->fs_devname); ret = TUNEFS_ET_SPARSE_MISSING; goto out; } if (!tools_interact("Enable the unwritten extents feature on " "device \"%s\"? ", fs->fs_devname)) goto out; prog = tools_progress_start("Enable unwritten", "unwritten", 1); if (!prog) { ret = TUNEFS_ET_NO_MEMORY; tcom_err(ret, "while initializing the progress display"); goto out; } OCFS2_SET_RO_COMPAT_FEATURE(super, OCFS2_FEATURE_RO_COMPAT_UNWRITTEN); tunefs_block_signals(); ret = ocfs2_write_super(fs); tunefs_unblock_signals(); if (ret) tcom_err(ret, "while writing out the superblock"); tools_progress_step(prog, 1); tools_progress_stop(prog); out: return ret; } static errcode_t unwritten_iterate(ocfs2_filesys *fs, struct ocfs2_dinode *di, void *user_data) { errcode_t ret = 0; uint32_t clusters, v_cluster = 0, p_cluster, num_clusters; uint16_t extent_flags; uint64_t p_blkno; ocfs2_cached_inode *ci = NULL; struct tools_progress *prog = user_data; if (!S_ISREG(di->i_mode)) goto bail; if (di->i_flags & OCFS2_SYSTEM_FL) goto bail; if (di->i_dyn_features & OCFS2_INLINE_DATA_FL) goto bail; clusters = (di->i_size + fs->fs_clustersize -1 ) / fs->fs_clustersize; ret = ocfs2_read_cached_inode(fs, di->i_blkno, &ci); if (ret) goto bail; while (v_cluster < clusters) { ret = ocfs2_get_clusters(ci, v_cluster, &p_cluster, &num_clusters, &extent_flags); if (ret) break; if (extent_flags & OCFS2_EXT_UNWRITTEN) { p_blkno = ocfs2_clusters_to_blocks(fs, p_cluster); ret = tunefs_empty_clusters(fs, p_blkno, num_clusters); if (ret) break; tools_progress_step(prog, 1); tunefs_block_signals(); ret = ocfs2_mark_extent_written(fs, di, v_cluster, num_clusters, p_blkno); tunefs_unblock_signals(); if (ret) break; tools_progress_step(prog, 1); } v_cluster += num_clusters; } bail: if (ci) ocfs2_free_cached_inode(fs, ci); tools_progress_step(prog, 1); return ret; } static errcode_t clear_unwritten_extents(ocfs2_filesys *fs, struct tools_progress *prog) { return tunefs_foreach_inode(fs, unwritten_iterate, prog); } static int disable_unwritten_extents(ocfs2_filesys *fs, int flags) { errcode_t ret = 0; struct ocfs2_super_block *super = OCFS2_RAW_SB(fs->fs_super); struct tools_progress *prog = NULL; if (!ocfs2_writes_unwritten_extents(super)) { verbosef(VL_APP, "Unwritten extents feature is not enabled; " "nothing to disable\n"); goto out; } if (!tools_interact("Disable the unwritten extents feature on " "device \"%s\"? ", fs->fs_devname)) goto out; prog = tools_progress_start("Disabling unwritten", "nounwritten", 0); if (!prog) { ret = TUNEFS_ET_NO_MEMORY; tcom_err(ret, "while initializing the progress display"); goto out; } ret = clear_unwritten_extents(fs, prog); if (ret) { tcom_err(ret, "while trying to clear the unwritten extents on " "device \"%s\"", fs->fs_devname); goto out; } OCFS2_CLEAR_RO_COMPAT_FEATURE(super, OCFS2_FEATURE_RO_COMPAT_UNWRITTEN); tunefs_block_signals(); ret = ocfs2_write_super(fs); tunefs_unblock_signals(); if (ret) tcom_err(ret, "while writing out the superblock"); tools_progress_step(prog, 1); out: if (prog) tools_progress_stop(prog); return ret; } DEFINE_TUNEFS_FEATURE_RO_COMPAT(unwritten_extents, OCFS2_FEATURE_RO_COMPAT_UNWRITTEN, TUNEFS_FLAG_RW | TUNEFS_FLAG_ALLOCATION | TUNEFS_FLAG_LARGECACHE, enable_unwritten_extents, disable_unwritten_extents); #ifdef DEBUG_EXE int main(int argc, char *argv[]) { return tunefs_feature_main(argc, argv, &unwritten_extents_feature); } #endif ocfs2-tools-ocfs2-tools-1.8.6/tunefs.ocfs2/feature_xattr.c000066400000000000000000000254541347147137200234330ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * feature_xattr.c * * Copyright (C) 2004, 2008 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License version 2 as published by the Free Software Foundation. * * 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. */ #include #include #include #include #include #include #include #include "ocfs2-kernel/kernel-list.h" #include "ocfs2/ocfs2.h" #include "libocfs2ne.h" struct xattr_inode { struct list_head list; uint64_t blkno; }; struct xattr_context { errcode_t ret; struct list_head inodes; struct tools_progress *prog; uint64_t inode_count; }; static void empty_xattr_context(struct xattr_context *ctxt) { struct list_head *pos, *n; struct xattr_inode *xdi; list_for_each_safe(pos, n, &ctxt->inodes) { xdi = list_entry(pos, struct xattr_inode, list); list_del(&xdi->list); ocfs2_free(&xdi); } } static errcode_t remove_xattr_entry(ocfs2_filesys *fs, uint64_t ino, struct ocfs2_xattr_header *xh) { int i; errcode_t ret = 0; for (i = 0 ; i < xh->xh_count; i++) { struct ocfs2_xattr_entry *xe = &xh->xh_entries[i]; if (!ocfs2_xattr_is_local(xe)) { struct ocfs2_xattr_value_root *xv = (struct ocfs2_xattr_value_root *) ((void *)xh + xe->xe_name_offset + OCFS2_XATTR_SIZE(xe->xe_name_len)); ret = ocfs2_xattr_value_truncate(fs, ino, xv); if (ret) break; } } return ret; } static errcode_t remove_xattr_buckets(ocfs2_filesys *fs, uint64_t ino, uint64_t blkno, uint32_t clusters) { int i; errcode_t ret = 0; char *bucket = NULL; struct ocfs2_xattr_header *xh; int blk_per_bucket = ocfs2_blocks_per_xattr_bucket(fs); uint32_t bpc = ocfs2_xattr_buckets_per_cluster(fs); uint32_t num_buckets = clusters * bpc; ret = ocfs2_malloc_blocks(fs->fs_io, blk_per_bucket, &bucket); if (ret) { tcom_err(ret, "while allocating room to read" " bucket of extended attributes "); goto out; } for (i = 0; i < num_buckets; i++, blkno += blk_per_bucket) { ret = ocfs2_read_xattr_bucket(fs, blkno, bucket); if (ret) { tcom_err(ret, "while reading bucket of" " extended attributes "); goto out; } xh = (struct ocfs2_xattr_header *)bucket; /* * The real bucket num in this series of blocks is stored * in the 1st bucket. */ if (i == 0) num_buckets = xh->xh_num_buckets; ret = remove_xattr_entry(fs, ino, xh); if (ret) break; } out: ocfs2_free(&bucket); return ret; } static errcode_t remove_xattr_index_block(ocfs2_filesys *fs, uint64_t ino, struct ocfs2_xattr_block *xb) { struct ocfs2_extent_list *el = &xb->xb_attrs.xb_root.xt_list; errcode_t ret = 0; uint32_t name_hash = UINT_MAX, e_cpos = 0, num_clusters = 0; uint64_t p_blkno = 0; if (el->l_next_free_rec == 0) return 0; while (name_hash > 0) { ret = ocfs2_xattr_get_rec(fs, xb, name_hash, &p_blkno, &e_cpos, &num_clusters); if (ret) { tcom_err(ret, "while getting bucket record" " of extended attributes "); goto out; } ret = remove_xattr_buckets(fs, ino, p_blkno, num_clusters); if (ret) { tcom_err(ret, "while iterating bucket" " of extended attributes "); goto out; } if (e_cpos == 0) break; name_hash = e_cpos - 1; } out: return ret; } static errcode_t remove_xattr_block(ocfs2_filesys *fs, struct ocfs2_dinode *di) { errcode_t ret; char *blk = NULL; struct ocfs2_xattr_block *xb = NULL; ret = ocfs2_malloc_block(fs->fs_io, &blk); if (ret) { tcom_err(ret, "while allocating room to read block" " of extended attributes "); return ret; } ret = ocfs2_read_xattr_block(fs, di->i_xattr_loc, blk); if (ret) { tcom_err(ret, "while reading external block of" " extended attributes "); goto out; } xb = (struct ocfs2_xattr_block *)blk; if (!(xb->xb_flags & OCFS2_XATTR_INDEXED)) { struct ocfs2_xattr_header *xh = &xb->xb_attrs.xb_header; ret = remove_xattr_entry(fs, di->i_blkno, xh); if (ret) { tcom_err(ret, "while trying to remove extended" " attributes in external block "); goto out; } } else { ret = remove_xattr_index_block(fs, di->i_blkno, xb); if (ret) { tcom_err(ret, "while trying to remove extended" " attributes in index block "); goto out; } ret = ocfs2_xattr_tree_truncate(fs, &xb->xb_attrs.xb_root); if (ret) { tcom_err(ret, "while trying to remove extended" " attributes tree in index block "); goto out; } } /* release block*/ ret = ocfs2_delete_xattr_block(fs, di->i_xattr_loc); if (ret) goto out; /* clean extended attributes */ memset(blk, 0, fs->fs_blocksize); out: if (blk) ocfs2_free(&blk); return ret; } static errcode_t remove_xattr_ibody(ocfs2_filesys *fs, struct ocfs2_dinode *di) { errcode_t ret; struct ocfs2_xattr_header *xh = NULL; xh = (struct ocfs2_xattr_header *) ((void *)di + fs->fs_blocksize - di->i_xattr_inline_size); ret = remove_xattr_entry(fs, di->i_blkno, xh); if (ret) { tcom_err(ret, "while trying to remove extended" " attributes in ibody "); return ret; } /* clean inline extended attributs */ memset((char *)xh, 0, di->i_xattr_inline_size); if (di->i_dyn_features & OCFS2_INLINE_DATA_FL) { struct ocfs2_inline_data *idata = &di->id2.i_data; idata->id_count += di->i_xattr_inline_size; } else if (!(S_ISLNK(di->i_mode) && di->i_clusters == 0)) { struct ocfs2_extent_list *el = &di->id2.i_list; el->l_count += (di->i_xattr_inline_size / sizeof(struct ocfs2_extent_rec)); } di->i_xattr_inline_size = 0; return ret; } static errcode_t remove_xattr(ocfs2_filesys *fs, struct xattr_context *ctxt) { errcode_t ret = 0; struct list_head *pos; struct xattr_inode *xdi; struct ocfs2_dinode *di = NULL; ocfs2_cached_inode *ci = NULL; struct tools_progress *prog = NULL; prog = tools_progress_start("Removing extended attributes", "removing", ctxt->inode_count); if (!prog) return TUNEFS_ET_NO_MEMORY; list_for_each(pos, &ctxt->inodes) { xdi = list_entry(pos, struct xattr_inode, list); ret = ocfs2_read_cached_inode(fs, xdi->blkno, &ci); if (ret) break; di = ci->ci_inode; if (di->i_dyn_features & OCFS2_INLINE_XATTR_FL) { ret = remove_xattr_ibody(fs, di); if (ret) break; } if (di->i_xattr_loc) ret = remove_xattr_block(fs, di); di->i_xattr_loc = 0; di->i_dyn_features &= ~(OCFS2_INLINE_XATTR_FL | OCFS2_HAS_XATTR_FL); ret = ocfs2_write_cached_inode(fs, ci); ocfs2_free_cached_inode(fs, ci); if (ret) break; tools_progress_step(prog, 1); } tools_progress_stop(prog); return ret; } static errcode_t xattr_iterate(ocfs2_filesys *fs, struct ocfs2_dinode *di, void *user_data) { errcode_t ret = 0; struct xattr_inode *xdi = NULL; struct xattr_context *ctxt = (struct xattr_context *)user_data; if (!S_ISREG(di->i_mode) && !S_ISDIR(di->i_mode) && !S_ISLNK(di->i_mode)) goto bail; if (!(di->i_dyn_features & OCFS2_HAS_XATTR_FL)) goto bail; ret = ocfs2_malloc0(sizeof(struct xattr_inode), &xdi); if (ret) goto bail; xdi->blkno = di->i_blkno; list_add_tail(&xdi->list, &ctxt->inodes); ctxt->inode_count++; tools_progress_step(ctxt->prog, 1); return 0; bail: return ret; } static int enable_xattr(ocfs2_filesys *fs, int flag) { errcode_t ret = 0; struct ocfs2_super_block *super = OCFS2_RAW_SB(fs->fs_super); struct tools_progress *prog = NULL; if (ocfs2_support_xattr(super)) { verbosef(VL_APP, "The extended attribute feature is already enabled; " "nothing to enable\n"); goto out; } if (!tools_interact("Enable the extended attribute feature on device " "\"%s\"? ", fs->fs_devname)) goto out; prog = tools_progress_start("Enabling extended attribute", "xattr", 1); if (!prog) { ret = TUNEFS_ET_NO_MEMORY; tcom_err(ret, "while initializing the progress display"); goto out; } /* s_uuid_hash is also used by Indexed Dirs */ if (!OCFS2_HAS_INCOMPAT_FEATURE(super, OCFS2_FEATURE_INCOMPAT_INDEXED_DIRS)) super->s_uuid_hash = ocfs2_xattr_uuid_hash((unsigned char *)super->s_uuid); super->s_xattr_inline_size = OCFS2_MIN_XATTR_INLINE_SIZE; OCFS2_SET_INCOMPAT_FEATURE(super, OCFS2_FEATURE_INCOMPAT_XATTR); tunefs_block_signals(); ret = ocfs2_write_super(fs); tunefs_unblock_signals(); if (ret) tcom_err(ret, "while writing out the superblock"); tools_progress_step(prog, 1); out: if (prog) tools_progress_stop(prog); return ret; } static int disable_xattr(ocfs2_filesys *fs, int flag) { errcode_t ret = 0; struct ocfs2_super_block *super = OCFS2_RAW_SB(fs->fs_super); struct xattr_context ctxt = { .prog = NULL, .inode_count = 0, }; struct tools_progress *prog = NULL; if (!ocfs2_support_xattr(super)) { verbosef(VL_APP, "The extended attribute feature is not enabled; " "nothing to disable\n"); goto out; } if (!tools_interact("Disable the extended attribute feature on device" " \"%s\"? ", fs->fs_devname)) goto out; prog = tools_progress_start("Disabling extended attribute", "noxattr", 3); if (!prog) { ret = TUNEFS_ET_NO_MEMORY; tcom_err(ret, "while initializing the progress display"); goto out; } ctxt.prog = tools_progress_start("Scanning filesystem", "scanning", 0); if (!ctxt.prog) { ret = TUNEFS_ET_NO_MEMORY; goto out; } INIT_LIST_HEAD(&ctxt.inodes); ret = tunefs_foreach_inode(fs, xattr_iterate, &ctxt); tools_progress_stop(ctxt.prog); if (ret) { tcom_err(ret, "while trying to find files with" " extended attributes "); goto out_cleanup; } tools_progress_step(prog, 1); ret = remove_xattr(fs, &ctxt); if (ret) { tcom_err(ret, "while trying to remove extended attributes"); goto out_cleanup; } tools_progress_step(prog, 1); /* s_uuid_hash is also used by Indexed Dirs */ if (!OCFS2_HAS_INCOMPAT_FEATURE(super, OCFS2_FEATURE_INCOMPAT_INDEXED_DIRS)) super->s_uuid_hash = 0; super->s_xattr_inline_size = 0; OCFS2_CLEAR_INCOMPAT_FEATURE(super, OCFS2_FEATURE_INCOMPAT_XATTR); tunefs_block_signals(); ret = ocfs2_write_super(fs); tunefs_unblock_signals(); if (ret) tcom_err(ret, "while writing out the superblock"); tools_progress_step(prog, 1); out_cleanup: empty_xattr_context(&ctxt); out: if (prog) tools_progress_stop(prog); return ret; } DEFINE_TUNEFS_FEATURE_INCOMPAT(xattr, OCFS2_FEATURE_INCOMPAT_XATTR, TUNEFS_FLAG_RW | TUNEFS_FLAG_ALLOCATION | TUNEFS_FLAG_LARGECACHE, enable_xattr, disable_xattr); #ifdef DEBUG_EXE int main(int argc, char *argv[]) { return tunefs_feature_main(argc, argv, &xattr_feature); } #endif ocfs2-tools-ocfs2-tools-1.8.6/tunefs.ocfs2/libocfs2ne.c000066400000000000000000001673351347147137200226110ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * libocfs2ne.c * * Shared routines for the ocfs2 tunefs utility * * Copyright (C) 2004, 2008 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License version 2 as published by the Free Software Foundation. * * 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. */ #define _LARGEFILE64_SOURCE #define _GNU_SOURCE /* for getopt_long and O_DIRECT */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ocfs2/ocfs2.h" #include "ocfs2/bitops.h" #include "libocfs2ne.h" #define WHOAMI "tunefs.ocfs2" /* * Keeps track of how ocfs2ne sees the filesystem. This structure is * filled in by the master ocfs2_filesys (the first caller to * tunefs_open()). Every other ocfs2_filesys refers to it. */ struct tunefs_filesystem_state { /* The master ocfs2_filesys (first tunefs_open()) */ ocfs2_filesys *ts_master; /* * When a single-node (local) filesystem is opened, we prevent * concurrent mount(2) by opening the device O_EXCL. This is the * fd we used. The value is -1 for cluster-aware filesystems. */ int ts_local_fd; /* * Already-mounted filesystems can only do online operations. * This is the fd we send ioctl(2)s to. If the filesystem isn't * in use, this is -1. */ int ts_online_fd; /* * Do we have the cluster locked? This can be zero if we're a * local filesystem. If it is non-zero, ts_master->fs_dlm_ctxt * must be valid. */ int ts_cluster_locked; /* Non-zero if we've ever mucked with the allocator */ int ts_allocation; /* * Number of clusters in the filesystem. If changed by a * resized filesystem, it is tracked here and used at final * close. */ uint32_t ts_fs_clusters; /* Size of the largest journal seen in tunefs_journal_check() */ uint32_t ts_journal_clusters; /* Journal feature bits found during tunefs_journal_check() */ ocfs2_fs_options ts_journal_features; }; struct tunefs_private { struct list_head tp_list; ocfs2_filesys *tp_fs; /* All tunefs_privates point to the master state. */ struct tunefs_filesystem_state *tp_state; /* Flags passed to tunefs_open() for this ocfs2_filesys */ int tp_open_flags; }; /* List of all ocfs2_filesys objects opened by tunefs_open() */ static LIST_HEAD(fs_list); /* Refcount for calls to tunefs_[un]block_signals() */ static unsigned int blocked_signals_count; /* For DEBUG_EXE programs */ static const char *usage_string; /* * Code to manage the fs_private state. */ static inline struct tunefs_private *to_private(ocfs2_filesys *fs) { return fs->fs_private; } static struct tunefs_filesystem_state *tunefs_get_master_state(void) { struct tunefs_filesystem_state *s = NULL; struct tunefs_private *tp; if (!list_empty(&fs_list)) { tp = list_entry(fs_list.prev, struct tunefs_private, tp_list); s = tp->tp_state; } return s; } static struct tunefs_filesystem_state *tunefs_get_state(ocfs2_filesys *fs) { struct tunefs_private *tp = to_private(fs); return tp->tp_state; } static errcode_t tunefs_set_state(ocfs2_filesys *fs) { errcode_t err = 0; struct tunefs_private *tp = to_private(fs); struct tunefs_filesystem_state *s = tunefs_get_master_state(); if (!s) { err = ocfs2_malloc0(sizeof(struct tunefs_filesystem_state), &s); if (!err) { s->ts_local_fd = -1; s->ts_online_fd = -1; s->ts_master = fs; s->ts_fs_clusters = fs->fs_clusters; } else s = NULL; } tp->tp_state = s; return err; } /* * Functions for use by operations. */ /* Call this with SIG_BLOCK to block and SIG_UNBLOCK to unblock */ static void block_signals(int how) { sigset_t sigs; sigfillset(&sigs); sigdelset(&sigs, SIGTRAP); sigdelset(&sigs, SIGSEGV); sigprocmask(how, &sigs, NULL); } void tunefs_block_signals(void) { if (!blocked_signals_count) block_signals(SIG_BLOCK); blocked_signals_count++; } void tunefs_unblock_signals(void) { if (blocked_signals_count) { blocked_signals_count--; if (!blocked_signals_count) block_signals(SIG_UNBLOCK); } else errorf("Trying to unblock signals, but signals were not " "blocked\n"); } errcode_t tunefs_dlm_lock(ocfs2_filesys *fs, const char *lockid, int flags, enum o2dlm_lock_level level) { struct tunefs_filesystem_state *state = tunefs_get_state(fs); if (ocfs2_mount_local(fs)) return 0; return o2dlm_lock(state->ts_master->fs_dlm_ctxt, lockid, flags, level); } errcode_t tunefs_dlm_unlock(ocfs2_filesys *fs, char *lockid) { struct tunefs_filesystem_state *state = tunefs_get_state(fs); if (ocfs2_mount_local(fs)) return 0; return o2dlm_unlock(state->ts_master->fs_dlm_ctxt, lockid); } errcode_t tunefs_online_ioctl(ocfs2_filesys *fs, int op, void *arg) { int rc; struct tunefs_filesystem_state *state = tunefs_get_state(fs); if (state->ts_online_fd < 0) return TUNEFS_ET_INTERNAL_FAILURE; rc = ioctl(state->ts_online_fd, op, arg); if (rc) { switch (errno) { case EBADF: case EFAULT: return TUNEFS_ET_INTERNAL_FAILURE; break; case ENOTTY: return TUNEFS_ET_ONLINE_NOT_SUPPORTED; break; default: return TUNEFS_ET_ONLINE_FAILED; break; } } return 0; } errcode_t tunefs_get_number(char *arg, uint64_t *res) { char *ptr = NULL; uint64_t num; num = strtoull(arg, &ptr, 0); if ((ptr == arg) || (num == UINT64_MAX)) return TUNEFS_ET_INVALID_NUMBER; switch (*ptr) { case '\0': break; case 'p': case 'P': num *= 1024; /* FALL THROUGH */ case 't': case 'T': num *= 1024; /* FALL THROUGH */ case 'g': case 'G': num *= 1024; /* FALL THROUGH */ case 'm': case 'M': num *= 1024; /* FALL THROUGH */ case 'k': case 'K': num *= 1024; /* FALL THROUGH */ case 'b': case 'B': break; default: return TUNEFS_ET_INVALID_NUMBER; } *res = num; return 0; } errcode_t tunefs_set_in_progress(ocfs2_filesys *fs, int flag) { /* RESIZE is a special case due for historical reasons */ if (flag == OCFS2_FEATURE_INCOMPAT_RESIZE_INPROG) { OCFS2_RAW_SB(fs->fs_super)->s_feature_incompat |= OCFS2_FEATURE_INCOMPAT_RESIZE_INPROG; } else { OCFS2_RAW_SB(fs->fs_super)->s_feature_incompat |= OCFS2_FEATURE_INCOMPAT_TUNEFS_INPROG; OCFS2_RAW_SB(fs->fs_super)->s_tunefs_flag |= flag; } return ocfs2_write_primary_super(fs); } errcode_t tunefs_clear_in_progress(ocfs2_filesys *fs, int flag) { /* RESIZE is a special case due for historical reasons */ if (flag == OCFS2_FEATURE_INCOMPAT_RESIZE_INPROG) { OCFS2_RAW_SB(fs->fs_super)->s_feature_incompat &= ~OCFS2_FEATURE_INCOMPAT_RESIZE_INPROG; } else { OCFS2_RAW_SB(fs->fs_super)->s_tunefs_flag &= ~flag; if (OCFS2_RAW_SB(fs->fs_super)->s_tunefs_flag == 0) OCFS2_RAW_SB(fs->fs_super)->s_feature_incompat &= ~OCFS2_FEATURE_INCOMPAT_TUNEFS_INPROG; } return ocfs2_write_primary_super(fs); } errcode_t tunefs_set_journal_size(ocfs2_filesys *fs, uint64_t new_size, ocfs2_fs_options mask, ocfs2_fs_options options) { errcode_t ret = 0; char jrnl_file[OCFS2_MAX_FILENAME_LEN]; uint64_t blkno; int i; int max_slots = OCFS2_RAW_SB(fs->fs_super)->s_max_slots; uint32_t num_clusters; char *buf = NULL; struct ocfs2_dinode *di; struct tunefs_filesystem_state *state = tunefs_get_state(fs); struct tools_progress *prog; ocfs2_fs_options new_features, *newfeat = &new_features, *curfeat; int features_change; num_clusters = ocfs2_clusters_in_blocks(fs, ocfs2_blocks_in_bytes(fs, new_size)); /* If no size was passed in, use the size we found at open() */ if (!num_clusters) num_clusters = state->ts_journal_clusters; /* * This can't come from a NOCLUSTER operation, so we'd better * have a size in ts_journal_clusters */ assert(num_clusters); ret = ocfs2_malloc_block(fs->fs_io, &buf); if (ret) { verbosef(VL_LIB, "%s while allocating inode buffer for journal " "resize\n", error_message(ret)); return ret; } prog = tools_progress_start("Setting journal size", "jsize", max_slots); if (!prog) { ret = TUNEFS_ET_NO_MEMORY; verbosef(VL_LIB, "%s while initializing progress display for " "journal resize\n", error_message(ret)); return ret; } curfeat = &state->ts_journal_features; newfeat->opt_compat = (curfeat->opt_compat & ~mask.opt_compat) | (options.opt_compat & mask.opt_compat); newfeat->opt_incompat = (curfeat->opt_incompat & ~mask.opt_incompat) | (options.opt_incompat & mask.opt_incompat); newfeat->opt_ro_compat = (curfeat->opt_ro_compat & ~mask.opt_ro_compat) | (options.opt_ro_compat & mask.opt_ro_compat); features_change = (newfeat->opt_compat ^ curfeat->opt_compat) || (newfeat->opt_incompat ^ curfeat->opt_incompat) || (newfeat->opt_ro_compat ^ curfeat->opt_ro_compat); for (i = 0; i < max_slots; ++i) { ocfs2_sprintf_system_inode_name(jrnl_file, OCFS2_MAX_FILENAME_LEN, JOURNAL_SYSTEM_INODE, i); ret = ocfs2_lookup_system_inode(fs, JOURNAL_SYSTEM_INODE, i, &blkno); if (ret) { verbosef(VL_LIB, "%s while looking up \"%s\" during " "journal resize\n", error_message(ret), jrnl_file); goto bail; } ret = ocfs2_read_inode(fs, blkno, buf); if (ret) { verbosef(VL_LIB, "%s while reading journal inode " "%"PRIu64" for resizing\n", error_message(ret), blkno); goto bail; } di = (struct ocfs2_dinode *)buf; if (num_clusters == di->i_clusters && !features_change) { tools_progress_step(prog, 1); continue; } verbosef(VL_LIB, "Resizing journal \"%s\" to %"PRIu32" clusters\n", jrnl_file, num_clusters); ret = ocfs2_make_journal(fs, blkno, num_clusters, newfeat); if (ret) { verbosef(VL_LIB, "%s while resizing \"%s\" at block " "%"PRIu64" to %"PRIu32" clusters\n", error_message(ret), jrnl_file, blkno, num_clusters); goto bail; } verbosef(VL_LIB, "Successfully resized journal \"%s\"\n", jrnl_file); tools_progress_step(prog, 1); } bail: tools_progress_stop(prog); if (buf) ocfs2_free(&buf); return ret; } errcode_t tunefs_empty_clusters(ocfs2_filesys *fs, uint64_t start_blk, uint32_t num_clusters) { errcode_t ret; char *buf = NULL; uint64_t bpc = ocfs2_clusters_to_blocks(fs, 1); uint64_t total_blocks = ocfs2_clusters_to_blocks(fs, num_clusters); uint64_t io_blocks = total_blocks; ret = ocfs2_malloc_blocks(fs->fs_io, io_blocks, &buf); if (ret == OCFS2_ET_NO_MEMORY) { io_blocks = bpc; ret = ocfs2_malloc_blocks(fs->fs_io, io_blocks, &buf); } if (ret) goto bail; memset(buf, 0, io_blocks * fs->fs_blocksize); while (total_blocks) { ret = io_write_block_nocache(fs->fs_io, start_blk, io_blocks, buf); if (ret) goto bail; total_blocks -= io_blocks; start_blk += io_blocks; } bail: if (buf) ocfs2_free(&buf); return ret; } errcode_t tunefs_get_free_clusters(ocfs2_filesys *fs, uint32_t *clusters) { errcode_t ret; uint64_t blkno; char *buf = NULL; struct ocfs2_dinode *di = NULL; ret = ocfs2_malloc_block(fs->fs_io, &buf); if (ret) goto bail; ret = ocfs2_lookup_system_inode(fs, GLOBAL_BITMAP_SYSTEM_INODE, 0, &blkno); if (ret) goto bail; ret = ocfs2_read_inode(fs, blkno, buf); if (ret) goto bail; di = (struct ocfs2_dinode *)buf; if (clusters) *clusters = di->id1.bitmap1.i_total - di->id1.bitmap1.i_used; bail: if (buf) ocfs2_free(&buf); return ret; } static errcode_t tunefs_validate_inode(ocfs2_filesys *fs, struct ocfs2_dinode *di) { if (memcmp(di->i_signature, OCFS2_INODE_SIGNATURE, strlen(OCFS2_INODE_SIGNATURE))) return OCFS2_ET_BAD_INODE_MAGIC; ocfs2_swap_inode_to_cpu(fs, di); if (di->i_fs_generation != fs->fs_super->i_fs_generation) return OCFS2_ET_INODE_NOT_VALID; if (!(di->i_flags & OCFS2_VALID_FL)) return OCFS2_ET_INODE_NOT_VALID; return 0; } errcode_t tunefs_foreach_inode(ocfs2_filesys *fs, errcode_t (*func)(ocfs2_filesys *fs, struct ocfs2_dinode *di, void *user_data), void *user_data) { errcode_t ret; uint64_t blkno; char *buf; struct ocfs2_dinode *di; ocfs2_inode_scan *scan; ret = ocfs2_malloc_block(fs->fs_io, &buf); if (ret) { verbosef(VL_LIB, "%s while allocating a buffer for inode scanning\n", error_message(ret)); goto out; } di = (struct ocfs2_dinode *)buf; ret = ocfs2_open_inode_scan(fs, &scan); if (ret) { verbosef(VL_LIB, "%s while opening inode scan\n", error_message(ret)); goto out_free; } for(;;) { ret = ocfs2_get_next_inode(scan, &blkno, buf); if (ret) { verbosef(VL_LIB, "%s while getting next inode\n", error_message(ret)); break; } if (blkno == 0) break; ret = tunefs_validate_inode(fs, di); if (ret) continue; if (func) { ret = func(fs, di, user_data); if (ret) break; } } ocfs2_close_inode_scan(scan); out_free: ocfs2_free(&buf); out: return ret; } /* A dirblock we have to add a trailer to */ struct tunefs_trailer_dirblock { struct list_head db_list; uint64_t db_blkno; char *db_buf; /* * These require a little explanation. They point to * ocfs2_dir_entry structures inside db_buf. * * db_last is the entry we're going to *keep*. If the last * entry in the dirblock has enough extra rec_len to allow the * trailer, db_last points to it. We will shorten its rec_len * and insert the trailer. * * However, if the last entry in the dirblock cannot be * truncated, db_last points to the entry before that - the * last entry we're keeping in this dirblock. * * Examples: * * - The last entry in the dirblock has a name_len of 1 and a * rec_len of 128. We can easily change the rec_len to 64 and * insert the trailer. db_last points to this entry. * * - The last entry in the dirblock has a name_len of 1 and a * rec_len of 48. The previous entry has a name_len of 1 and a * rec_len of 32. We have to move the last entry out. The * second-to-last entry can have its rec_len truncated to 16, so * we put it in db_last. */ struct ocfs2_dir_entry *db_last; }; void tunefs_trailer_context_free(struct tunefs_trailer_context *tc) { struct tunefs_trailer_dirblock *db; struct list_head *n, *pos; if (!list_empty(&tc->d_list)) list_del(&tc->d_list); list_for_each_safe(pos, n, &tc->d_dirblocks) { db = list_entry(pos, struct tunefs_trailer_dirblock, db_list); list_del(&db->db_list); ocfs2_free(&db->db_buf); ocfs2_free(&db); } ocfs2_free(&tc); } /* * We're calculating how many bytes we need to add to make space for * the dir trailers. But we need to make sure that the added directory * blocks also have room for a trailer. */ static void add_bytes_needed(ocfs2_filesys *fs, struct tunefs_trailer_context *tc, unsigned int rec_len) { unsigned int toff = ocfs2_dir_trailer_blk_off(fs); unsigned int block_offset = tc->d_bytes_needed % fs->fs_blocksize; /* * If the current byte offset would put us into a trailer, push * it out to the start of the next block. Remember, dirents have * to be at least 16 bytes, which is why we check against the * smallest rec_len. */ if ((block_offset + rec_len) > (toff - OCFS2_DIR_REC_LEN(1))) tc->d_bytes_needed += fs->fs_blocksize - block_offset; tc->d_bytes_needed += rec_len; tc->d_blocks_needed = ocfs2_blocks_in_bytes(fs, tc->d_bytes_needed); } static errcode_t walk_dirblock(ocfs2_filesys *fs, struct tunefs_trailer_context *tc, struct tunefs_trailer_dirblock *db) { errcode_t ret = 0; struct ocfs2_dir_entry *dirent, *prev = NULL; unsigned int real_rec_len; unsigned int offset = 0; unsigned int toff = ocfs2_dir_trailer_blk_off(fs); while (offset < fs->fs_blocksize) { dirent = (struct ocfs2_dir_entry *) (db->db_buf + offset); if (((offset + dirent->rec_len) > fs->fs_blocksize) || (dirent->rec_len < 8) || ((dirent->rec_len % 4) != 0) || (((dirent->name_len & 0xFF)+8) > dirent->rec_len)) { ret = OCFS2_ET_DIR_CORRUPTED; break; } real_rec_len = dirent->inode ? OCFS2_DIR_REC_LEN(dirent->name_len) : OCFS2_DIR_REC_LEN(1); if ((offset + real_rec_len) <= toff) goto next; /* * The first time through, we store off the last dirent * before the trailer. */ if (!db->db_last) db->db_last = prev; /* Only live dirents need to be moved */ if (dirent->inode) { verbosef(VL_DEBUG, "Will move dirent %.*s out of " "directory block %"PRIu64" to make way " "for the trailer\n", dirent->name_len, dirent->name, db->db_blkno); add_bytes_needed(fs, tc, real_rec_len); } next: prev = dirent; offset += dirent->rec_len; } /* There were no dirents across the boundary */ if (!db->db_last) db->db_last = prev; return ret; } static int dirblock_scan_iterate(ocfs2_filesys *fs, uint64_t blkno, uint64_t bcount, uint16_t ext_flags, void *priv_data) { errcode_t ret = 0; struct tunefs_trailer_dirblock *db = NULL; struct tunefs_trailer_context *tc = priv_data; ret = ocfs2_malloc0(sizeof(struct tunefs_trailer_dirblock), &db); if (ret) goto out; ret = ocfs2_malloc_block(fs->fs_io, &db->db_buf); if (ret) goto out; db->db_blkno = blkno; verbosef(VL_DEBUG, "Reading dinode %"PRIu64" dirblock %"PRIu64" at block " "%"PRIu64"\n", (uint64_t)tc->d_di->i_blkno, bcount, blkno); ret = ocfs2_read_dir_block(fs, tc->d_di, blkno, db->db_buf); if (ret) goto out; ret = walk_dirblock(fs, tc, db); if (ret) goto out; list_add_tail(&db->db_list, &tc->d_dirblocks); db = NULL; out: if (db) { if (db->db_buf) ocfs2_free(&db->db_buf); ocfs2_free(&db); } if (ret) { tc->d_err = ret; return OCFS2_BLOCK_ABORT; } return 0; } errcode_t tunefs_prepare_dir_trailer(ocfs2_filesys *fs, struct ocfs2_dinode *di, struct tunefs_trailer_context **tc_ret) { errcode_t ret = 0; struct tunefs_trailer_context *tc = NULL; if (ocfs2_dir_has_trailer(fs, di)) goto out; ret = ocfs2_malloc0(sizeof(struct tunefs_trailer_context), &tc); if (ret) goto out; tc->d_blkno = di->i_blkno; tc->d_di = di; INIT_LIST_HEAD(&tc->d_list); INIT_LIST_HEAD(&tc->d_dirblocks); ret = ocfs2_block_iterate_inode(fs, tc->d_di, 0, dirblock_scan_iterate, tc); if (!ret) ret = tc->d_err; if (ret) goto out; *tc_ret = tc; tc = NULL; out: if (tc) tunefs_trailer_context_free(tc); return ret; } /* * We are hand-coding the directory expansion because we're going to * build the new directory blocks ourselves. We can't just use * ocfs2_expand_dir() and ocfs2_link(), because we're moving around * entries. */ static errcode_t expand_dir_if_needed(ocfs2_filesys *fs, struct ocfs2_dinode *di, uint64_t blocks_needed) { errcode_t ret = 0; uint64_t used_blocks, total_blocks; uint32_t clusters_needed; /* This relies on the fact that i_size of a directory is a * multiple of blocksize */ used_blocks = ocfs2_blocks_in_bytes(fs, di->i_size); total_blocks = ocfs2_clusters_to_blocks(fs, di->i_clusters); if ((used_blocks + blocks_needed) <= total_blocks) goto out; clusters_needed = ocfs2_clusters_in_blocks(fs, (used_blocks + blocks_needed) - total_blocks); ret = ocfs2_extend_allocation(fs, di->i_blkno, clusters_needed); if (ret) goto out; /* Pick up changes to the inode */ ret = ocfs2_read_inode(fs, di->i_blkno, (char *)di); out: return ret; } static void shift_dirent(ocfs2_filesys *fs, struct tunefs_trailer_context *tc, struct ocfs2_dir_entry *dirent) { /* Using the real rec_len */ unsigned int rec_len = OCFS2_DIR_REC_LEN(dirent->name_len); unsigned int offset, remain; /* * If the current byte offset would put us into a trailer, push * it out to the start of the next block. Remember, dirents have * to be at least 16 bytes, which is why we check against the * smallest rec_len. */ if (rec_len > (tc->d_next_dirent->rec_len - OCFS2_DIR_REC_LEN(1))) { tc->d_cur_block += fs->fs_blocksize; tc->d_next_dirent = (struct ocfs2_dir_entry *)tc->d_cur_block; } assert(ocfs2_blocks_in_bytes(fs, tc->d_cur_block - tc->d_new_blocks) < tc->d_blocks_needed); offset = (char *)(tc->d_next_dirent) - tc->d_cur_block; remain = tc->d_next_dirent->rec_len - rec_len; memcpy(tc->d_cur_block + offset, dirent, rec_len); tc->d_next_dirent->rec_len = rec_len; verbosef(VL_DEBUG, "Installed dirent %.*s at offset %u of new block " "%"PRIu64", rec_len %u\n", tc->d_next_dirent->name_len, tc->d_next_dirent->name, offset, ocfs2_blocks_in_bytes(fs, tc->d_cur_block - tc->d_new_blocks), rec_len); offset += rec_len; tc->d_next_dirent = (struct ocfs2_dir_entry *)(tc->d_cur_block + offset); tc->d_next_dirent->rec_len = remain; verbosef(VL_DEBUG, "New block %"PRIu64" has its last dirent at %u, with %u " "bytes left\n", ocfs2_blocks_in_bytes(fs, tc->d_cur_block - tc->d_new_blocks), offset, remain); } static errcode_t fixup_dirblock(ocfs2_filesys *fs, struct tunefs_trailer_context *tc, struct tunefs_trailer_dirblock *db) { errcode_t ret = 0; struct ocfs2_dir_entry *dirent; unsigned int real_rec_len; unsigned int offset; unsigned int toff = ocfs2_dir_trailer_blk_off(fs); /* * db_last is the last dirent we're *keeping*. So we need to * move out every valid dirent *after* db_last. * * tunefs_prepare_dir_trailer() should have calculated this * correctly. */ offset = ((char *)db->db_last) - db->db_buf; offset += db->db_last->rec_len; while (offset < fs->fs_blocksize) { dirent = (struct ocfs2_dir_entry *) (db->db_buf + offset); if (((offset + dirent->rec_len) > fs->fs_blocksize) || (dirent->rec_len < 8) || ((dirent->rec_len % 4) != 0) || (((dirent->name_len & 0xFF)+8) > dirent->rec_len)) { ret = OCFS2_ET_DIR_CORRUPTED; break; } real_rec_len = dirent->inode ? OCFS2_DIR_REC_LEN(dirent->name_len) : OCFS2_DIR_REC_LEN(1); assert((offset + real_rec_len) > toff); /* Only live dirents need to be moved */ if (dirent->inode) { verbosef(VL_DEBUG, "Moving dirent %.*s out of directory " "block %"PRIu64" to make way for the " "trailer\n", dirent->name_len, dirent->name, db->db_blkno); shift_dirent(fs, tc, dirent); } offset += dirent->rec_len; } /* * Now that we've moved any dirents out of the way, we need to * fix up db_last and install the trailer. */ offset = ((char *)db->db_last) - db->db_buf; verbosef(VL_DEBUG, "Last valid dirent of directory block %"PRIu64" " "(\"%.*s\") is %u bytes in. Setting rec_len to %u and " "installing the trailer\n", db->db_blkno, db->db_last->name_len, db->db_last->name, offset, toff - offset); db->db_last->rec_len = toff - offset; ocfs2_init_dir_trailer(fs, tc->d_di, db->db_blkno, db->db_buf); return ret; } static errcode_t run_dirblocks(ocfs2_filesys *fs, struct tunefs_trailer_context *tc) { errcode_t ret = 0; struct list_head *pos; struct tunefs_trailer_dirblock *db; list_for_each(pos, &tc->d_dirblocks) { db = list_entry(pos, struct tunefs_trailer_dirblock, db_list); ret = fixup_dirblock(fs, tc, db); if (ret) break; } return ret; } static errcode_t write_dirblocks(ocfs2_filesys *fs, struct tunefs_trailer_context *tc) { errcode_t ret = 0; struct list_head *pos; struct tunefs_trailer_dirblock *db; list_for_each(pos, &tc->d_dirblocks) { db = list_entry(pos, struct tunefs_trailer_dirblock, db_list); ret = ocfs2_write_dir_block(fs, tc->d_di, db->db_blkno, db->db_buf); if (ret) { verbosef(VL_DEBUG, "Error writing dirblock %"PRIu64"\n", db->db_blkno); break; } } return ret; } static errcode_t init_new_dirblocks(ocfs2_filesys *fs, struct tunefs_trailer_context *tc) { int i; errcode_t ret; uint64_t blkno; uint64_t orig_block = ocfs2_blocks_in_bytes(fs, tc->d_di->i_size); ocfs2_cached_inode *cinode; char *blockptr; struct ocfs2_dir_entry *first; ret = ocfs2_read_cached_inode(fs, tc->d_blkno, &cinode); if (ret) goto out; assert(!memcmp(tc->d_di, cinode->ci_inode, fs->fs_blocksize)); for (i = 0; i < tc->d_blocks_needed; i++) { ret = ocfs2_extent_map_get_blocks(cinode, orig_block + i, 1, &blkno, NULL, NULL); if (ret) goto out; blockptr = tc->d_new_blocks + (i * fs->fs_blocksize); memset(blockptr, 0, fs->fs_blocksize); first = (struct ocfs2_dir_entry *)blockptr; first->rec_len = ocfs2_dir_trailer_blk_off(fs); ocfs2_init_dir_trailer(fs, tc->d_di, blkno, blockptr); } out: return ret; } static errcode_t write_new_dirblocks(ocfs2_filesys *fs, struct tunefs_trailer_context *tc) { int i; errcode_t ret; uint64_t blkno; uint64_t orig_block = ocfs2_blocks_in_bytes(fs, tc->d_di->i_size); ocfs2_cached_inode *cinode; char *blockptr; ret = ocfs2_read_cached_inode(fs, tc->d_blkno, &cinode); if (ret) goto out; assert(!memcmp(tc->d_di, cinode->ci_inode, fs->fs_blocksize)); for (i = 0; i < tc->d_blocks_needed; i++) { ret = ocfs2_extent_map_get_blocks(cinode, orig_block + i, 1, &blkno, NULL, NULL); if (ret) goto out; blockptr = tc->d_new_blocks + (i * fs->fs_blocksize); ret = ocfs2_write_dir_block(fs, tc->d_di, blkno, blockptr); if (ret) { verbosef(VL_DEBUG, "Error writing dirblock %"PRIu64"\n", blkno); goto out; } } out: return ret; } errcode_t tunefs_install_dir_trailer(ocfs2_filesys *fs, struct ocfs2_dinode *di, struct tunefs_trailer_context *tc) { errcode_t ret = 0; struct tunefs_trailer_context *our_tc = NULL; if ((di->i_dyn_features & OCFS2_INLINE_DATA_FL) || ocfs2_dir_has_trailer(fs, di)) goto out; if (!tc) { ret = tunefs_prepare_dir_trailer(fs, di, &our_tc); if (ret) goto out; tc = our_tc; } if (tc->d_di != di) { ret = OCFS2_ET_INVALID_ARGUMENT; goto out; } if (tc->d_blocks_needed) { ret = ocfs2_malloc_blocks(fs->fs_io, tc->d_blocks_needed, &tc->d_new_blocks); if (ret) goto out; tc->d_cur_block = tc->d_new_blocks; ret = expand_dir_if_needed(fs, di, tc->d_blocks_needed); if (ret) goto out; ret = init_new_dirblocks(fs, tc); if (ret) goto out; tc->d_next_dirent = (struct ocfs2_dir_entry *)tc->d_cur_block; verbosef(VL_DEBUG, "t_next_dirent has rec_len of %u\n", tc->d_next_dirent->rec_len); } ret = run_dirblocks(fs, tc); if (ret) goto out; /* * We write in a specific order. We write any new dirblocks first * so that they are on disk. Then we write the new i_size in the * inode. If we crash at this point, the directory has duplicate * entries but no lost entries. fsck can clean it up. Finally, we * write the modified dirblocks with trailers. */ if (tc->d_blocks_needed) { ret = write_new_dirblocks(fs, tc); if (ret) goto out; di->i_size += ocfs2_blocks_to_bytes(fs, tc->d_blocks_needed); ret = ocfs2_write_inode(fs, di->i_blkno, (char *)di); if (ret) goto out; } ret = write_dirblocks(fs, tc); out: if (our_tc) tunefs_trailer_context_free(our_tc); return ret; } /* * Starting, opening, closing, and exiting. */ static void tunefs_close_all(void) { struct list_head *pos, *n; struct tunefs_private *tp; list_for_each_safe(pos, n, &fs_list) { tp = list_entry(pos, struct tunefs_private, tp_list); tunefs_close(tp->tp_fs); } } static void handle_signal(int caught_sig) { int exitp = 0, abortp = 0; static int segv_already = 0; switch (caught_sig) { case SIGQUIT: abortp = 1; /* FALL THROUGH */ case SIGTERM: case SIGINT: case SIGHUP: errorf("Caught signal %d, exiting\n", caught_sig); exitp = 1; break; case SIGSEGV: errorf("Segmentation fault, exiting\n"); exitp = 1; if (segv_already) { errorf("Segmentation fault loop detected\n"); abortp = 1; } else segv_already = 1; break; default: errorf("Caught signal %d, ignoring\n", caught_sig); break; } if (!exitp) return; if (abortp) abort(); tunefs_close_all(); exit(1); } static int setup_signals(void) { int rc = 0; struct sigaction act; act.sa_sigaction = NULL; sigemptyset(&act.sa_mask); act.sa_handler = handle_signal; #ifdef SA_INTERRUPT act.sa_flags = SA_INTERRUPT; #endif rc += sigaction(SIGTERM, &act, NULL); rc += sigaction(SIGINT, &act, NULL); rc += sigaction(SIGHUP, &act, NULL); rc += sigaction(SIGQUIT, &act, NULL); rc += sigaction(SIGSEGV, &act, NULL); act.sa_handler = SIG_IGN; rc += sigaction(SIGPIPE, &act, NULL); /* Get EPIPE instead */ return rc; } void tunefs_init(const char *argv0) { initialize_o2ne_error_table(); initialize_ocfs_error_table(); initialize_o2dl_error_table(); initialize_o2cb_error_table(); tools_setup_argv0(argv0); setbuf(stdout, NULL); setbuf(stderr, NULL); if (setup_signals()) { errorf("%s\n", error_message(TUNEFS_ET_SIGNALS_FAILED)); exit(1); } } /* * Single-node filesystems need to prevent mount(8) from happening * while tunefs.ocfs2 is running. bd_claim does this for us when we * open O_EXCL. */ static errcode_t tunefs_lock_local(ocfs2_filesys *fs, int flags) { errcode_t err = 0; int mount_flags; int rc; struct tunefs_filesystem_state *state = tunefs_get_state(fs); if (state->ts_local_fd > -1) return 0; rc = open64(fs->fs_devname, O_RDWR | O_EXCL); if (rc < 0) { if (errno == EBUSY) { /* bd_claim has a hold, let's see if it's ocfs2 */ err = ocfs2_check_if_mounted(fs->fs_devname, &mount_flags); if (!err) { if (!(mount_flags & OCFS2_MF_MOUNTED) || (mount_flags & OCFS2_MF_READONLY) || (mount_flags & OCFS2_MF_SWAP) || !(flags & TUNEFS_FLAG_ONLINE)) err = TUNEFS_ET_DEVICE_BUSY; else err = TUNEFS_ET_PERFORM_ONLINE; } } else if (errno == ENOENT) err = OCFS2_ET_NAMED_DEVICE_NOT_FOUND; else err = OCFS2_ET_IO; } else state->ts_local_fd = rc; return err; } static void tunefs_unlock_local(ocfs2_filesys *fs) { struct tunefs_filesystem_state *state = tunefs_get_state(fs); assert(state->ts_master == fs); if (state->ts_local_fd > -1) { close(state->ts_local_fd); /* Don't care about errors */ state->ts_local_fd = -1; } } static errcode_t tunefs_unlock_cluster(ocfs2_filesys *fs) { errcode_t tmp, err = 0; struct tunefs_filesystem_state *state = tunefs_get_state(fs); struct tools_progress *prog = NULL; if (fs->fs_dlm_ctxt) prog = tools_progress_start("Unlocking filesystem", "unlocking", 2); /* * We continue even with no progress, because we're unlocking * and probably exiting. */ assert(state->ts_master == fs); if (state->ts_cluster_locked) { assert(fs->fs_dlm_ctxt); tunefs_block_signals(); err = ocfs2_release_cluster(fs); tunefs_unblock_signals(); state->ts_cluster_locked = 0; } if (prog) tools_progress_step(prog, 1); /* We shut down the dlm regardless of err */ if (fs->fs_dlm_ctxt) { tmp = ocfs2_shutdown_dlm(fs, WHOAMI); if (!err) err = tmp; } if (prog) { tools_progress_step(prog, 1); tools_progress_stop(prog); } return err; } /* * We only unlock if we're closing the master filesystem. We unlock * both local and cluster locks, because we may have started as a local * filesystem, then switched to a cluster filesystem in the middle. */ static errcode_t tunefs_unlock_filesystem(ocfs2_filesys *fs) { errcode_t err = 0; struct tunefs_filesystem_state *state = tunefs_get_state(fs); if (state->ts_master == fs) { tunefs_unlock_local(fs); err = tunefs_unlock_cluster(fs); } return err; } static errcode_t tunefs_lock_cluster(ocfs2_filesys *fs, int flags) { errcode_t err = 0; struct tunefs_filesystem_state *state = tunefs_get_state(fs); ocfs2_filesys *master_fs = state->ts_master; struct tools_progress *prog = NULL; if (state->ts_cluster_locked) goto out; if (flags & TUNEFS_FLAG_SKIPCLUSTER) { err = TUNEFS_ET_CLUSTER_SKIPPED; goto out; } prog = tools_progress_start("Locking filesystem", "locking", 2); if (!prog) { err = TUNEFS_ET_NO_MEMORY; goto out; } if (!master_fs->fs_dlm_ctxt) { err = o2cb_init(); if (err) goto out; err = ocfs2_initialize_dlm(master_fs, WHOAMI); if (flags & TUNEFS_FLAG_NOCLUSTER) { if (err == O2CB_ET_INVALID_STACK_NAME || err == O2CB_ET_INVALID_CLUSTER_NAME || err == O2CB_ET_INVALID_HEARTBEAT_MODE) { /* * We expected this - why else ask for * TUNEFS_FLAG_NOCLUSTER? * * Note that this is distinct from the O2CB * error, as that is a real error when * TUNEFS_FLAG_NOCLUSTER is not specified. */ err = TUNEFS_ET_INVALID_STACK_NAME; } /* * Success means do nothing, any other error * propagates up. */ goto out; } else if (err) goto out; } tools_progress_step(prog, 1); tunefs_block_signals(); err = ocfs2_lock_down_cluster(master_fs); tunefs_unblock_signals(); if (!err) state->ts_cluster_locked = 1; else if ((err == O2DLM_ET_TRYLOCK_FAILED) && (flags & TUNEFS_FLAG_ONLINE)) err = TUNEFS_ET_PERFORM_ONLINE; else ocfs2_shutdown_dlm(fs, WHOAMI); tools_progress_step(prog, 1); out: if (prog) tools_progress_stop(prog); return err; } /* * We try to lock the filesystem in *this* ocfs2_filesys. We get the * state off of the master, but the filesystem may have changed since * the master opened its ocfs2_filesys. It might have been switched to * LOCAL or something. We trust the current status in order to make our * decision. * * Inside the underlying lock functions, they check the state to see if * they actually need to do anything. If they don't have it locked, they * will always retry the lock. The filesystem may have gotten unmounted * right after we ran our latest online operation. */ static errcode_t tunefs_lock_filesystem(ocfs2_filesys *fs, int flags) { errcode_t err = 0; if (ocfs2_mount_local(fs)) err = tunefs_lock_local(fs, flags); else err = tunefs_lock_cluster(fs, flags); return err; } static int tunefs_count_free_bits(struct ocfs2_group_desc *gd) { int end = 0; int start; int bits = 0; while (end < gd->bg_bits) { start = ocfs2_find_next_bit_clear(gd->bg_bitmap, gd->bg_bits, end); if (start >= gd->bg_bits) break; end = ocfs2_find_next_bit_set(gd->bg_bitmap, gd->bg_bits, start); bits += (end - start); } return bits; } static errcode_t tunefs_validate_chain_group(ocfs2_filesys *fs, struct ocfs2_dinode *di, int chain) { errcode_t ret = 0; uint64_t blkno; char *buf = NULL; struct ocfs2_group_desc *gd; struct ocfs2_chain_list *cl; struct ocfs2_chain_rec *cr; uint32_t total = 0; uint32_t free = 0; uint16_t bits; ret = ocfs2_malloc_block(fs->fs_io, &buf); if (ret) { verbosef(VL_LIB, "%s while allocating a buffer for chain group " "validation\n", error_message(ret)); goto bail; } total = 0; free = 0; cl = &(di->id2.i_chain); cr = &(cl->cl_recs[chain]); blkno = cr->c_blkno; while (blkno) { ret = ocfs2_read_group_desc(fs, blkno, buf); if (ret) { verbosef(VL_LIB, "%s while reading chain group descriptor " "at block %"PRIu64"\n", error_message(ret), blkno); goto bail; } gd = (struct ocfs2_group_desc *)buf; if (gd->bg_parent_dinode != di->i_blkno) { ret = OCFS2_ET_CORRUPT_CHAIN; verbosef(VL_LIB, "Chain allocator at block %"PRIu64" is " "corrupt. It contains group descriptor " "at %"PRIu64", but that descriptor says " "it belongs to allocator %"PRIu64"\n", (uint64_t)di->i_blkno, blkno, (uint64_t)gd->bg_parent_dinode); goto bail; } if (gd->bg_chain != chain) { ret = OCFS2_ET_CORRUPT_CHAIN; verbosef(VL_LIB, "Chain allocator at block %"PRIu64" is " "corrupt. Group descriptor at %"PRIu64" " "was found on chain %u, but it says it " "belongs to chain %u\n", (uint64_t)di->i_blkno, blkno, chain, gd->bg_chain); goto bail; } bits = tunefs_count_free_bits(gd); if (bits != gd->bg_free_bits_count) { ret = OCFS2_ET_CORRUPT_CHAIN; verbosef(VL_LIB, "Chain allocator at block %"PRIu64" is " "corrupt. Group descriptor at %"PRIu64" " "has %u free bits but says it has %u\n", (uint64_t)di->i_blkno, (uint64_t)blkno, bits, gd->bg_free_bits_count); goto bail; } if (gd->bg_bits > gd->bg_size * 8) { ret = OCFS2_ET_CORRUPT_CHAIN; verbosef(VL_LIB, "Chain allocator at block %"PRIu64" is " "corrupt. Group descriptor at %"PRIu64" " "can only hold %u bits, but it claims to " "have %u\n", (uint64_t)di->i_blkno, (uint64_t)blkno, gd->bg_size * 8, gd->bg_bits); goto bail; } if (gd->bg_free_bits_count >= gd->bg_bits) { ret = OCFS2_ET_CORRUPT_CHAIN; verbosef(VL_LIB, "Chain allocator at block %"PRIu64" is " "corrupt. Group descriptor at %"PRIu64" " "claims to have more free bits than " "total bits\n", (uint64_t)di->i_blkno, (uint64_t)blkno); goto bail; } total += gd->bg_bits; free += gd->bg_free_bits_count; blkno = gd->bg_next_group; } if (cr->c_total != total) { ret = OCFS2_ET_CORRUPT_CHAIN; verbosef(VL_LIB, "Chain allocator at block %"PRIu64" is corrupt. " "It contains %u total bits, but it says it has " "%u\n", (uint64_t)di->i_blkno, total, cr->c_total); goto bail; } if (cr->c_free != free) { ret = OCFS2_ET_CORRUPT_CHAIN; verbosef(VL_LIB, "Chain allocator at block %"PRIu64" is corrupt. " "It contains %u free bits, but it says it has " "%u\n", (uint64_t)di->i_blkno, free, cr->c_free); goto bail; } bail: if (buf) ocfs2_free(&buf); return ret; } static errcode_t tunefs_global_bitmap_check(ocfs2_filesys *fs) { errcode_t ret = 0; uint64_t bm_blkno = 0; char *buf = NULL; struct ocfs2_chain_list *cl; struct ocfs2_dinode *di; int i; verbosef(VL_LIB, "Verifying the global allocator\n"); ret = ocfs2_malloc_block(fs->fs_io, &buf); if (ret) { verbosef(VL_LIB, "%s while allocating an inode buffer to validate " "the global bitmap\n", error_message(ret)); goto bail; } ret = ocfs2_lookup_system_inode(fs, GLOBAL_BITMAP_SYSTEM_INODE, 0, &bm_blkno); if (ret) { verbosef(VL_LIB, "%s while looking up the global bitmap inode\n", error_message(ret)); goto bail; } ret = ocfs2_read_inode(fs, bm_blkno, buf); if (ret) { verbosef(VL_LIB, "%s while reading the global bitmap inode at " "block %"PRIu64"", error_message(ret), bm_blkno); goto bail; } di = (struct ocfs2_dinode *)buf; cl = &(di->id2.i_chain); /* Warm up the cache with the groups */ ret = ocfs2_cache_chain_allocator_blocks(fs, di); if (ret) verbosef(VL_LIB, "Caching global bitmap failed, err %d\n", (int)ret); ret = 0; for (i = 0; i < cl->cl_next_free_rec; ++i) { ret = tunefs_validate_chain_group(fs, di, i); if (ret) goto bail; } bail: if (buf) ocfs2_free(&buf); return ret; } static errcode_t tunefs_open_bitmap_check(ocfs2_filesys *fs) { struct tunefs_private *tp = to_private(fs); struct tunefs_filesystem_state *state = tunefs_get_state(fs); if (!(tp->tp_open_flags & TUNEFS_FLAG_ALLOCATION)) return 0; state->ts_allocation = 1; return tunefs_global_bitmap_check(fs); } void tunefs_update_fs_clusters(ocfs2_filesys *fs) { struct tunefs_private *tp = to_private(fs); struct tunefs_filesystem_state *state = tunefs_get_state(fs); if (!(tp->tp_open_flags & TUNEFS_FLAG_ALLOCATION)) { verbosef(VL_LIB, "Operation that claimed it would do no allocation " "just attempted to update the filesystem size\n"); return; } state->ts_fs_clusters = fs->fs_clusters; } static errcode_t tunefs_close_bitmap_check(ocfs2_filesys *fs) { errcode_t ret; uint32_t old_clusters; struct tunefs_filesystem_state *state = tunefs_get_state(fs); if (!state->ts_allocation) return 0; if (state->ts_master != fs) return 0; /* * An operation that resized the filesystem will have called * tunefs_update_fs_clusters(). The bitmap check needs this * new value, so we swap it in for the call. */ old_clusters = fs->fs_clusters; fs->fs_clusters = state->ts_fs_clusters; fs->fs_blocks = ocfs2_clusters_to_blocks(fs, fs->fs_clusters); ret = tunefs_global_bitmap_check(fs); fs->fs_clusters = old_clusters; fs->fs_blocks = ocfs2_clusters_to_blocks(fs, fs->fs_clusters); return ret; } static errcode_t tunefs_journal_check(ocfs2_filesys *fs, bool offline) { errcode_t ret; char *jsb_buf = NULL; ocfs2_cached_inode *ci = NULL; uint64_t blkno, contig; journal_superblock_t *jsb; int i, dirty = 0; uint16_t max_slots = OCFS2_RAW_SB(fs->fs_super)->s_max_slots; struct tunefs_private *tp = to_private(fs); struct tunefs_filesystem_state *state = tunefs_get_state(fs); /* We only need to check the journal once */ if (state->ts_journal_clusters) return 0; if (offline) verbosef(VL_LIB, "Checking for dirty journals\n"); ret = ocfs2_malloc_block(fs->fs_io, &jsb_buf); if (ret) { verbosef(VL_LIB, "%s while allocating a block during journal " "check\n", error_message(ret)); goto bail; } for (i = 0; i < max_slots; ++i) { ret = ocfs2_lookup_system_inode(fs, JOURNAL_SYSTEM_INODE, i, &blkno); if (ret) { verbosef(VL_LIB, "%s while looking up journal inode for " "slot %u during journal check\n", error_message(ret), i); goto bail; } ret = ocfs2_read_cached_inode(fs, blkno, &ci); if (ret) { verbosef(VL_LIB, "%s while reading inode %"PRIu64" during " " journal check", error_message(ret), blkno); goto bail; } state->ts_journal_clusters = ocfs2_max(state->ts_journal_clusters, ci->ci_inode->i_clusters); if (offline) { dirty = (ci->ci_inode->id1.journal1.ij_flags & OCFS2_JOURNAL_DIRTY_FL); if (dirty) { ret = TUNEFS_ET_JOURNAL_DIRTY; verbosef(VL_LIB, "Node slot %d's journal is dirty." "Run fsck.ocfs2 to replay all " "dirty journals.", i); break; } } ret = ocfs2_extent_map_get_blocks(ci, 0, 1, &blkno, &contig, NULL); if (!ret) ret = ocfs2_read_journal_superblock(fs, blkno, jsb_buf); if (ret) { verbosef(VL_LIB, "%s while reading journal superblock " "for inode %"PRIu64" during journal " "check", error_message(ret), ci->ci_blkno); goto bail; } jsb = (journal_superblock_t *)jsb_buf; state->ts_journal_features.opt_compat |= jsb->s_feature_compat; state->ts_journal_features.opt_ro_compat |= jsb->s_feature_ro_compat; state->ts_journal_features.opt_incompat |= jsb->s_feature_incompat; ocfs2_free_cached_inode(fs, ci); ci = NULL; } /* * If anything follows a NOCLUSTER operation, it will have * closed and reopened the filesystem. It must recheck the * journals. */ if (tp->tp_open_flags & TUNEFS_FLAG_NOCLUSTER) state->ts_journal_clusters = 0; bail: if (ci) ocfs2_free_cached_inode(fs, ci); if (jsb_buf) ocfs2_free(&jsb_buf); return ret; } static errcode_t tunefs_open_online_descriptor(ocfs2_filesys *fs) { int rc, flags = 0; errcode_t ret = 0; char mnt_dir[PATH_MAX]; struct tunefs_filesystem_state *state = tunefs_get_state(fs); if (state->ts_online_fd > -1) goto out; memset(mnt_dir, 0, sizeof(mnt_dir)); ret = ocfs2_check_mount_point(fs->fs_devname, &flags, mnt_dir, sizeof(mnt_dir)); if (ret) goto out; if (!(flags & OCFS2_MF_MOUNTED) || (flags & OCFS2_MF_READONLY) || (flags & OCFS2_MF_SWAP)) { ret = TUNEFS_ET_NOT_MOUNTED; goto out; } rc = open64(mnt_dir, O_RDONLY); if (rc < 0) { if (errno == EBUSY) ret = TUNEFS_ET_DEVICE_BUSY; else if (errno == ENOENT) ret = TUNEFS_ET_NOT_MOUNTED; else ret = OCFS2_ET_IO; } else state->ts_online_fd = rc; out: return ret; } static void tunefs_close_online_descriptor(ocfs2_filesys *fs) { struct tunefs_filesystem_state *state = tunefs_get_state(fs); if ((state->ts_master == fs) && (state->ts_online_fd > -1)) { close(state->ts_online_fd); /* Don't care about errors */ state->ts_online_fd = -1; } } /* * If io_init_cache fails, we will go do the work without the * io_cache, so there is no check for failure here. */ static void tunefs_init_cache(ocfs2_filesys *fs) { errcode_t err; struct tunefs_private *tp = to_private(fs); struct tunefs_filesystem_state *state = tunefs_get_state(fs); uint64_t blocks_wanted; int scale_down; /* * We have one I/O cache for all ocfs2_filesys structures. This * guarantees a consistent view of the disk. The master filesys * allocates it, child filesyses just use it. */ if (state->ts_master != fs) { io_share_cache(state->ts_master->fs_io, fs->fs_io); return; } /* * Operations needing a large cache really want enough to * hold the whole filesystem in memory. The rest of the * operations don't need much at all. A cache big enough to * hold a chain allocator group should be enough. Our largest * chain allocator is 4MB, so let's do 8MB and allow for * incidental blocks. */ if (tp->tp_open_flags & TUNEFS_FLAG_LARGECACHE) blocks_wanted = fs->fs_blocks; else blocks_wanted = ocfs2_blocks_in_bytes(fs, 8 * 1024 * 1024); /* * We don't want to exhaust memory, so we start with twice our * actual need. When we find out how much we can get, we actually * get half that. */ blocks_wanted <<= 1; scale_down = 1; while (blocks_wanted > 0) { io_destroy_cache(fs->fs_io); verbosef(VL_LIB, "Asking for %"PRIu64" blocks of I/O cache\n", blocks_wanted); err = io_init_cache(fs->fs_io, blocks_wanted); if (!err) { /* * We want to pin our cache; there's no point in * having a large cache if half of it is in swap. * However, some callers may not be privileged * enough, so once we get down to a small enough * number (512 blocks), we'll stop caring. */ err = io_mlock_cache(fs->fs_io); if (err && (blocks_wanted <= 512)) err = 0; } if (!err) { verbosef(VL_LIB, "Got %"PRIu64" blocks\n", blocks_wanted); /* If we've already scaled down, we're done. */ if (!scale_down) break; scale_down = 0; } blocks_wanted >>= 1; } } static errcode_t tunefs_add_fs(ocfs2_filesys *fs, int flags) { errcode_t err; struct tunefs_private *tp; err = ocfs2_malloc0(sizeof(struct tunefs_private), &tp); if (err) goto out; tp->tp_open_flags = flags; fs->fs_private = tp; tp->tp_fs = fs; err = tunefs_set_state(fs); if (err) { fs->fs_private = NULL; ocfs2_free(&tp); goto out; } /* * This is purposely a push. The first open of the filesystem * will be the one holding the locks, so we want it to be the last * close (a FILO stack). When signals happen, tunefs_close_all() * pops each off in turn, finishing with the lock holder. */ list_add(&tp->tp_list, &fs_list); out: return err; } static void tunefs_remove_fs(ocfs2_filesys *fs) { struct tunefs_private *tp = to_private(fs); struct tunefs_filesystem_state *s = NULL; if (tp) { s = tp->tp_state; list_del(&tp->tp_list); tp->tp_fs = NULL; fs->fs_private = NULL; ocfs2_free(&tp); } if (s && (s->ts_master == fs)) { assert(list_empty(&fs_list)); ocfs2_free(&s); } } /* * Return true if this error code is a special (non-fatal) ocfs2ne * error code. */ static int tunefs_special_errorp(errcode_t err) { if (err == TUNEFS_ET_CLUSTER_SKIPPED) return 1; if (err == TUNEFS_ET_INVALID_STACK_NAME) return 1; if (err == TUNEFS_ET_PERFORM_ONLINE) return 1; return 0; } errcode_t tunefs_open(const char *device, int flags, ocfs2_filesys **ret_fs) { int rw = flags & TUNEFS_FLAG_RW; errcode_t err, tmp; int open_flags; ocfs2_filesys *fs = NULL; bool offline; verbosef(VL_LIB, "Opening device \"%s\"\n", device); open_flags = OCFS2_FLAG_HEARTBEAT_DEV_OK; if (rw) open_flags |= OCFS2_FLAG_RW | OCFS2_FLAG_STRICT_COMPAT_CHECK; else open_flags |= OCFS2_FLAG_RO; err = ocfs2_open(device, open_flags, 0, 0, &fs); if (err) goto out; err = tunefs_add_fs(fs, flags); if (err) goto out; if (!rw) goto out; if (OCFS2_RAW_SB(fs->fs_super)->s_feature_incompat & OCFS2_FEATURE_INCOMPAT_HEARTBEAT_DEV) { err = TUNEFS_ET_HEARTBEAT_DEV; goto out; } if (OCFS2_RAW_SB(fs->fs_super)->s_feature_incompat & OCFS2_FEATURE_INCOMPAT_RESIZE_INPROG) { err = TUNEFS_ET_RESIZE_IN_PROGRESS; goto out; } if (OCFS2_RAW_SB(fs->fs_super)->s_feature_incompat & OCFS2_FEATURE_INCOMPAT_TUNEFS_INPROG) { err = TUNEFS_ET_TUNEFS_IN_PROGRESS; goto out; } err = tunefs_lock_filesystem(fs, flags); if (err && !tunefs_special_errorp(err)) goto out; /* * We will use block cache in io. Now, whether the cluster is * locked or the volume is mount local, in both situation we can * safely use cache. If we're not locked * (tunefs_special_errorp(err) != 0), we can't safely use it. * If this tunefs run has both special and regular operations, * ocfs2ne will retry with the regular arguments and will get * the cache for the regular operations. */ if (!err) tunefs_init_cache(fs); /* * SKIPCLUSTER operations don't check the journals - they couldn't * replay them anyway. */ if (err == TUNEFS_ET_CLUSTER_SKIPPED) goto out; offline = (err != TUNEFS_ET_PERFORM_ONLINE); /* Offline operations need clean journals. For online operations, * only read the journal super block. Check bitmap and dirty * journals only for offline operations. */ tmp = tunefs_journal_check(fs, offline); if (!tmp && offline) tmp = tunefs_open_bitmap_check(fs); if (tmp) { err = tmp; tunefs_unlock_filesystem(fs); goto out; } if (!offline) { tmp = tunefs_open_online_descriptor(fs); if (tmp) { err = tmp; tunefs_unlock_filesystem(fs); } } out: if (err && !tunefs_special_errorp(err)) { if (fs) { tunefs_remove_fs(fs); ocfs2_close(fs); fs = NULL; } verbosef(VL_LIB, "Open of device \"%s\" failed\n", device); } else { verbosef(VL_LIB, "Device \"%s\" opened\n", device); *ret_fs = fs; } return err; } int tunefs_is_journal64(ocfs2_filesys *fs) { struct tunefs_filesystem_state *state = tunefs_get_state(fs); if (state->ts_journal_features.opt_incompat & JBD2_FEATURE_INCOMPAT_64BIT) return 1; return 0; } errcode_t tunefs_close(ocfs2_filesys *fs) { errcode_t tmp, err = 0; /* * We want to clean up everything we can even if there * are errors, but we preserve the first error we get. */ if (fs) { verbosef(VL_LIB, "Closing device \"%s\"\n", fs->fs_devname); tunefs_close_online_descriptor(fs); err = tunefs_close_bitmap_check(fs); tmp = tunefs_unlock_filesystem(fs); if (!err) err = tmp; tunefs_remove_fs(fs); tmp = ocfs2_close(fs); if (!err) err = tmp; if (!err) verbosef(VL_LIB, "Device closed\n"); else verbosef(VL_LIB, "Close of device failed\n"); fs = NULL; } return err; } /* * Helper functions for the main code. */ errcode_t tunefs_feature_run(ocfs2_filesys *master_fs, struct tunefs_feature *feat) { int rc = 0; errcode_t err, tmp; ocfs2_filesys *fs; int flags; verbosef(VL_DEBUG, "Running feature \"%s\"\n", feat->tf_name); flags = feat->tf_open_flags & ~(TUNEFS_FLAG_ONLINE | TUNEFS_FLAG_NOCLUSTER); err = tunefs_open(master_fs->fs_devname, feat->tf_open_flags, &fs); if (err == TUNEFS_ET_PERFORM_ONLINE) flags |= TUNEFS_FLAG_ONLINE; else if (err == TUNEFS_ET_INVALID_STACK_NAME) flags |= TUNEFS_FLAG_NOCLUSTER; else if (err) goto out; err = 0; switch (feat->tf_action) { case FEATURE_ENABLE: rc = feat->tf_enable(fs, flags); break; case FEATURE_DISABLE: rc = feat->tf_disable(fs, flags); break; case FEATURE_NOOP: verbosef(VL_APP, "Ran NOOP for feature \"%s\" - how'd " "that happen?\n", feat->tf_name); break; default: errorf("Unknown action %d called against feature " "\"%s\"\n", feat->tf_action, feat->tf_name); err = TUNEFS_ET_INTERNAL_FAILURE; break; } if (rc) err = TUNEFS_ET_OPERATION_FAILED; tmp = tunefs_close(fs); if (!err) err = tmp; out: return err; } errcode_t tunefs_op_run(ocfs2_filesys *master_fs, struct tunefs_operation *op) { errcode_t err, tmp; ocfs2_filesys *fs; int flags; verbosef(VL_DEBUG, "Running operation \"%s\"\n", op->to_name); flags = op->to_open_flags & ~(TUNEFS_FLAG_ONLINE | TUNEFS_FLAG_NOCLUSTER); err = tunefs_open(master_fs->fs_devname, op->to_open_flags, &fs); if (err == TUNEFS_ET_PERFORM_ONLINE) flags |= TUNEFS_FLAG_ONLINE; else if (err == TUNEFS_ET_INVALID_STACK_NAME) flags |= TUNEFS_FLAG_NOCLUSTER; else if (err == TUNEFS_ET_CLUSTER_SKIPPED) flags |= TUNEFS_FLAG_SKIPCLUSTER; else if (err) goto out; err = 0; if (op->to_run(op, fs, flags)) err = TUNEFS_ET_OPERATION_FAILED; tmp = tunefs_close(fs); if (!err) err = tmp; out: return err; } /* * Helper calls for operation and feature DEBUG_EXE code */ static errcode_t copy_argv(char **argv, char ***new_argv) { int i; char **t_argv; for (i = 0; argv[i]; i++) ; /* Count argv */ /* This is intentionally leaked */ t_argv = malloc(sizeof(char *) * (i + 1)); if (!t_argv) return TUNEFS_ET_NO_MEMORY; for (i = 0; argv[i]; i++) t_argv[i] = (char *)argv[i]; t_argv[i] = NULL; *new_argv = t_argv; return 0; } /* All the +1 are to leave argv[0] in place */ static void shuffle_argv(int *argc, int optind, char **argv) { int src, dst; int new_argc = *argc - optind + 1; for (src = optind, dst = 1; src < *argc; src++, dst++) argv[dst] = argv[src]; if (dst != new_argc) verbosef(VL_DEBUG, "dst is not new_argc %d %d\n", dst, new_argc); argv[dst] = NULL; *argc = new_argc; } static void tunefs_debug_usage(int error) { enum tools_verbosity_level level = VL_ERR; if (!error) level = VL_OUT; verbosef(level, "%s", usage_string ? usage_string : "(null)"); verbosef(level, "[opts] can be any mix of:\n" "\t-i|--interactive\n" "\t-v|--verbose (more than one increases verbosity)\n" "\t-q|--quiet (more than one decreases verbosity)\n" "\t-h|--help\n" "\t-V|--version\n"); } extern int optind, opterr, optopt; extern char *optarg; static void tunefs_parse_core_options(int *argc, char ***argv, char *usage) { errcode_t err; int c; char **new_argv; int print_usage = 0, print_version = 0; char error[PATH_MAX]; static struct option long_options[] = { { "help", 0, NULL, 'h' }, { "version", 0, NULL, 'V' }, { "verbose", 0, NULL, 'v' }, { "quiet", 0, NULL, 'q' }, { "interactive", 0, NULL, 'i'}, { 0, 0, 0, 0} }; usage_string = usage; err = copy_argv(*argv, &new_argv); if (err) { tcom_err(err, "while processing command-line arguments"); exit(1); } opterr = 0; error[0] = '\0'; while ((c = getopt_long(*argc, new_argv, ":hVvqi", long_options, NULL)) != EOF) { switch (c) { case 'h': print_usage = 1; break; case 'V': print_version = 1; break; case 'v': tools_verbose(); break; case 'q': tools_quiet(); break; case 'i': tools_interactive(); break; case '?': snprintf(error, PATH_MAX, "Invalid option: \'-%c\'", optopt); print_usage = 1; break; case ':': snprintf(error, PATH_MAX, "Option \'-%c\' requires an argument", optopt); print_usage = 1; break; default: snprintf(error, PATH_MAX, "Shouldn't get here %c %c", optopt, c); break; } if (*error) break; } if (*error) errorf("%s\n", error); if (print_version) tools_version(); if (print_usage) tunefs_debug_usage(*error != '\0'); if (print_usage || print_version) exit(0); if (*error) exit(1); shuffle_argv(argc, optind, new_argv); *argv = new_argv; } static int single_feature_parse_option(struct tunefs_operation *op, char *arg) { int rc = 0; struct tunefs_feature *feat = op->to_private; if (!arg) { errorf("No action specified\n"); rc = 1; } else if (!strcmp(arg, "enable")) feat->tf_action = FEATURE_ENABLE; else if (!strcmp(arg, "disable")) feat->tf_action = FEATURE_DISABLE; else { errorf("Invalid action: \"%s\"\n", arg); rc = 1; } return rc; } static int single_feature_run(struct tunefs_operation *op, ocfs2_filesys *fs, int flags) { errcode_t err; struct tunefs_feature *feat = op->to_private; err = tunefs_feature_run(fs, feat); if (err && (err != TUNEFS_ET_OPERATION_FAILED)) tcom_err(err, "while toggling feature \"%s\"", feat->tf_name); return err; } DEFINE_TUNEFS_OP(single_feature, NULL, 0, single_feature_parse_option, single_feature_run); int tunefs_feature_main(int argc, char *argv[], struct tunefs_feature *feat) { char usage[PATH_MAX]; snprintf(usage, PATH_MAX, "Usage: ocfs2ne_feature_%s [opts] " "{enable|disable}\n", feat->tf_name); single_feature_op.to_debug_usage = usage; single_feature_op.to_open_flags = feat->tf_open_flags; single_feature_op.to_private = feat; return tunefs_op_main(argc, argv, &single_feature_op); } int tunefs_op_main(int argc, char *argv[], struct tunefs_operation *op) { errcode_t err; int rc = 1; ocfs2_filesys *fs; char *arg = NULL; tunefs_init(argv[0]); tunefs_parse_core_options(&argc, &argv, op->to_debug_usage); if (argc < 2) { errorf("No device specified\n"); tunefs_debug_usage(1); goto out; } if (op->to_parse_option) { if (argc > 3) { errorf("Too many arguments\n"); tunefs_debug_usage(1); goto out; } if (argc == 3) arg = argv[2]; rc = op->to_parse_option(op, arg); if (rc) { tunefs_debug_usage(1); goto out; } } else if (argc > 2) { errorf("Too many arguments\n"); tunefs_debug_usage(1); goto out; } err = tunefs_open(argv[1], op->to_open_flags, &fs); if (err && !tunefs_special_errorp(err)) { tcom_err(err, "- Unable to open device \"%s\" read-write.", argv[1]); goto out; } err = tunefs_op_run(fs, op); if (!err) rc = 0; else if (err != TUNEFS_ET_OPERATION_FAILED) tcom_err(err, "while running operation \"%s\"", op->to_name); err = tunefs_close(fs); if (err) { tcom_err(err, "while closing device \"%s\"", argv[1]); rc = 1; } out: return rc; } #ifdef DEBUG_EXE int parent = 0; static void closeup(ocfs2_filesys *fs, const char *device) { errcode_t err; verbosef(VL_OUT, "success\n"); err = tunefs_close(fs); if (err) { tcom_err(err, "- Unable to close device \"%s\".", device); } } int main(int argc, char *argv[]) { errcode_t err; const char *device; ocfs2_filesys *fs; tunefs_init(argv[0]); tunefs_parse_core_options(&argc, &argv, "Usage: debug_libocfs2ne [-p] \n"); if (argc > 3) { errorf("Too many arguments\n"); tunefs_debug_usage(1); return 1; } if (argc == 3) { if (strcmp(argv[1], "-p")) { errorf("Invalid argument: \'%s\'\n", argv[1]); tunefs_debug_usage(1); return 1; } parent = 1; device = argv[2]; } else if ((argc == 2) && strcmp(argv[1], "-p")) { device = argv[1]; } else { errorf("Device must be specified\n"); tunefs_debug_usage(1); return 1; } verbosef(VL_OUT, "Opening device \"%s\" read-only... ", device); err = tunefs_open(device, TUNEFS_FLAG_RO, &fs); if (err) { verbosef(VL_OUT, "failed\n"); tcom_err(err, "- Unable to open device \"%s\" read-only.", device); } else closeup(fs, device); verbosef(VL_OUT, "Opening device \"%s\" read-write... ", device); err = tunefs_open(device, TUNEFS_FLAG_RW, &fs); if (err) { verbosef(VL_OUT, "failed\n"); tcom_err(err, "- Unable to open device \"%s\" read-write.", device); } else closeup(fs, device); verbosef(VL_OUT, "Opening device \"%s\" for an online operation... ", device); err = tunefs_open(device, TUNEFS_FLAG_RW | TUNEFS_FLAG_ONLINE, &fs); if (err == TUNEFS_ET_PERFORM_ONLINE) { closeup(fs, device); verbosef(VL_OUT, "Operation would have been online\n"); } else if (!err) { closeup(fs, device); verbosef(VL_OUT, "Operation would have been offline\n"); } else { verbosef(VL_OUT, "failed\n"); tcom_err(err, "- Unable to open device \"%s\" read-write.", device); } verbosef(VL_OUT, "Opening device \"%s\" for a stackless operation... ", device); err = tunefs_open(device, TUNEFS_FLAG_RW | TUNEFS_FLAG_NOCLUSTER, &fs); if (err == TUNEFS_ET_INVALID_STACK_NAME) { closeup(fs, device); verbosef(VL_OUT, "Expected cluster stack mismatch found\n"); } else if (!err) { closeup(fs, device); verbosef(VL_OUT, "Cluster stacks already match\n"); } else { verbosef(VL_OUT, "failed\n"); tcom_err(err, "- Unable to open device \"%s\" read-write.", device); } return 0; } #endif /* DEBUG_EXE */ ocfs2-tools-ocfs2-tools-1.8.6/tunefs.ocfs2/libocfs2ne.h000066400000000000000000000275361347147137200226140ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * libocfs2ne.h * * tunefs helper library prototypes. * * Copyright (C) 2004, 2008 Oracle. All rights reserved. * * 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. * * 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. */ #ifndef _LIBTUNEFS_H #define _LIBTUNEFS_H #include "o2ne_err.h" #include "tools-internal/verbose.h" #include "tools-internal/progress.h" /* * Adding a capability to ocfs2ne is pretty simple. You create a source * file for the method, define the method structure, and then link that * method structure in the appropriate place. This comment provides some * generic details shared by all method types. * * If you are setting or clearing a filesystem feature, you want to create * a struct tunefs_feature with DEFINE_TUNEFS_FEATURE_*(). See those * comments for more details on feature methods. * * If you are creating a more generic operation, you create a struct * tunefs_operation with DEFINE_TUNEFS_OP(). See those comments for more * details as well. * * A method should have the following characteristics: * * - It must be idempotent. If filesystem is already in the correct * state, the method should do nothing and return success. * * - It must use tools_interact() before writing any changes. If the * user specified -i, tools_interact() will ask the user before * proceeding. Otherwise, it always returns "go ahead", so you can * always call it safely. * * - It must use the verbosef() APIs unless output is the point of the * operation. Errors are reported with tcom_err() or errorf(). Verbose * output uses verbosef(). This way, all output honors the -v/-q * options. Operations that create output are the exception, because * the user asked for output. For example, list_sparse really makes no * sense without output. * * - It should be silent under normal operation. The user can specify -v * if they want more detail. Operations that create output don't count * here, of course. If something may take a long time, a progress * indicator of some sort is OK, but it should use verbosef(VL_OUT) so * that it honors '-q'. */ /* Flags for tunefs_open() */ #define TUNEFS_FLAG_RO 0x00 #define TUNEFS_FLAG_RW 0x01 #define TUNEFS_FLAG_ONLINE 0x02 /* Operation can run online */ #define TUNEFS_FLAG_NOCLUSTER 0x04 /* Operation does not need the cluster stack */ #define TUNEFS_FLAG_ALLOCATION 0x08 /* Operation will use the allocator */ #define TUNEFS_FLAG_SKIPCLUSTER 0x10 /* Operation cannot start the cluster stack */ #define TUNEFS_FLAG_LARGECACHE 0x20 /* Operation needs a large I/O cache */ /* What to do with a feature */ enum tunefs_feature_action { FEATURE_NOOP = 0, FEATURE_ENABLE = 1, FEATURE_DISABLE = 2, }; struct tunefs_feature { char *tf_name; ocfs2_fs_options tf_feature; /* The feature bit is set in the appropriate field */ int tf_open_flags; /* Flags for tunefs_open(). Like operations, the ones that mattered are passed to the enable and disable functions */ int (*tf_enable)(ocfs2_filesys *fs, int flags); int (*tf_disable)(ocfs2_filesys *fs, int flags); enum tunefs_feature_action tf_action; }; #define __TUNEFS_FEATURE(_name, _flags, _compat, _ro_compat, _incompat, \ _enable, _disable) \ { \ .tf_name = #_name, \ .tf_open_flags = _flags, \ .tf_feature = { \ .opt_compat = _compat, \ .opt_ro_compat = _ro_compat, \ .opt_incompat = _incompat, \ }, \ .tf_enable = _enable, \ .tf_disable = _disable, \ } /* * Creating a method to enable or disable a filesystem feature is pretty * simple. The source file only needs two functions - one to enable the * feature, one to disable it. It is legal to provide NULL for one of the * functions. ocfs2ne will report "unsupported". Each function should be * idempotent and use tunefs_interact() before writing to the filesystem. * * A feature should check the flags it needs set or cleared before it can * enable or disable. While ocfs2ne will make sure to run features in * order, the user may skip a feature in interactive mode. * * DEFINE_TUNEFS_FEATURE_COMPAT(), DEFINE_TUNEFS_FEATURE_RO_COMPAT(), or * DEFINE_TUNEFS_FEATURE_INCOMPAT() are used as appropriate to define the * tunefs_feature structure. This links the enable and disable functions * to the appropriate feature bit. * * Finally, the feature structure needs to be added to ocfs2ne_features.c. * * For debugging, a DEBUG_EXE section can be added at the bottom of the * source file. It just needs to pass the struct tunefs_feature to * tunefs_feature_main(). */ #define DEFINE_TUNEFS_FEATURE_COMPAT(_name, _bit, _flags, _enable, \ _disable) \ struct tunefs_feature _name##_feature = \ __TUNEFS_FEATURE(_name, _flags, _bit, 0, 0, _enable, _disable) #define DEFINE_TUNEFS_FEATURE_RO_COMPAT(_name, _bit, _flags, _enable, \ _disable) \ struct tunefs_feature _name##_feature = \ __TUNEFS_FEATURE(_name, _flags, 0, _bit, 0, _enable, _disable) #define DEFINE_TUNEFS_FEATURE_INCOMPAT(_name, _bit, _flags, _enable, \ _disable) \ struct tunefs_feature _name##_feature = \ __TUNEFS_FEATURE(_name, _flags, 0, 0, _bit, _enable, _disable) struct tunefs_operation { char *to_name; int to_open_flags; /* Flags for tunefs_open() */ int (*to_parse_option)(struct tunefs_operation *op, char *arg); int (*to_run)(struct tunefs_operation *op, ocfs2_filesys *fs, int flags); /* The tunefs_open() flags that mattered */ void *to_private; char *to_debug_usage; /* DEBUG_EXEC usage string */ }; #define __TUNEFS_OP(_name, _usage, _flags, _parse, _run) \ { \ .to_name = #_name, \ .to_open_flags = _flags, \ .to_parse_option = _parse, \ .to_run = _run, \ .to_debug_usage = _usage, \ } /* * Creating a tunefs operation is only a little more complex then a * feature. The source file only requires the run() function to perform * the operation. If the operation needs an argument, it can provide a * parse_option() function. The parse_option() is called while ocfs2ne * is processing options, and should provide any sanity checking of the * argument (eg, converting a string to a number). The operation can use * op->to_private to pass the option value to run(). Again, the run() * function should be idempotent and use tunefs_interact() before writing * to the filesystem. * * DEFINE_TUNEFS_OP() is used to define the tunefs_operation structure. * * A command-line option needs to be added to ocfs2ne.c for the new * operation. This just means adding a struct tunefs_option that * references the struct tunefs_operation. New operations should be able * to use generic_handle_arg() for their option's handle() function. * * For debugging, a DEBUG_EXE section can be added at the bottom of the * source file. It just needs to pass the struct tunefs_operation to * tunefs_op_main(). If a usage string was specified in DEFINE_TUNEFS_OP(), * the debugging program will use that usage string. */ #define DEFINE_TUNEFS_OP(_name, _usage, _flags, _parse, _run) \ struct tunefs_operation _name##_op = \ __TUNEFS_OP(_name, _usage, _flags, _parse, _run) /* Sets up argv0, signals, and output buffering */ void tunefs_init(const char *argv0); /* * Filesystem changes that are sensitive to interruption should be wrapped * with a block/unblock pair. The scope should be as narrow as possible, * so that a user can interrupt the process without it hanging. */ void tunefs_block_signals(void); void tunefs_unblock_signals(void); /* Set and clear the various tunefs in-progress bits */ errcode_t tunefs_set_in_progress(ocfs2_filesys *fs, int flag); errcode_t tunefs_clear_in_progress(ocfs2_filesys *fs, int flag); /* Turn a string into a number. Supports K/M/G/T/P suffixes */ errcode_t tunefs_get_number(char *arg, uint64_t *res); /* * Set all journals to new_size. If new_size is 0, it will set all * journals to the size of the largest existing journal. */ errcode_t tunefs_set_journal_size(ocfs2_filesys *fs, uint64_t new_size, ocfs2_fs_options mask, ocfs2_fs_options options); /* Determine how many clusters the filesystem has free */ errcode_t tunefs_get_free_clusters(ocfs2_filesys *fs, uint32_t *clusters); /* Zero out an extent at start_blk */ errcode_t tunefs_empty_clusters(ocfs2_filesys *fs, uint64_t start_blk, uint32_t num_clusters); /* Tell tunefs that you updated the filesystem size */ void tunefs_update_fs_clusters(ocfs2_filesys *fs); /* * Send an ioctl() to a live filesystem for online operation. If the * filesystem is mounted and an operation needs to be performed online, * tunefs_open() will have already connected to the filesystem. */ errcode_t tunefs_online_ioctl(ocfs2_filesys *fs, int op, void *arg); /* * Online operations need to make locks, but the ocfs2_filesys an * operation gets may not be the one we used to access the dlm. */ errcode_t tunefs_dlm_lock(ocfs2_filesys *fs, const char *lockid, int flags, enum o2dlm_lock_level level); errcode_t tunefs_dlm_unlock(ocfs2_filesys *fs, char *lockid); /* * A wrapper for inode scanning. Calls func() for each valid inode. */ errcode_t tunefs_foreach_inode(ocfs2_filesys *fs, errcode_t (*func)(ocfs2_filesys *fs, struct ocfs2_dinode *di, void *user_data), void *user_data); /* Functions used by the core program sources */ /* Open and cloee a filesystem */ errcode_t tunefs_open(const char *device, int flags, ocfs2_filesys **ret_fs); errcode_t tunefs_close(ocfs2_filesys *fs); /* * Run a tunefs_operation with its own ocfs2_filesys. The special * error TUNEFS_ET_OPERATION_FAILED means the operation itself failed. * It will have handled any error output. Any other errors are from * tunefs_op_run() itself. */ errcode_t tunefs_op_run(ocfs2_filesys *master_fs, struct tunefs_operation *op); /* The same, but for tunefs_feature */ errcode_t tunefs_feature_run(ocfs2_filesys *master_fs, struct tunefs_feature *feat); /* * For debugging programs. These open a filesystem and call * tunefs_*_run() as appropriate. */ int tunefs_feature_main(int argc, char *argv[], struct tunefs_feature *feat); int tunefs_op_main(int argc, char *argv[], struct tunefs_operation *op); /* A directory inode we're adding trailers to */ struct tunefs_trailer_context { struct list_head d_list; uint64_t d_blkno; /* block number of the dir */ struct ocfs2_dinode *d_di; /* The directory's inode */ struct list_head d_dirblocks; /* List of its dirblocks */ uint64_t d_bytes_needed; /* How many new bytes will cover the dirents we are moving to make way for trailers */ uint64_t d_blocks_needed; /* How many blocks covers d_bytes_needed */ char *d_new_blocks; /* Buffer of new blocks to fill */ char *d_cur_block; /* Which block we're filling in d_new_blocks */ struct ocfs2_dir_entry *d_next_dirent; /* Next dentry to use */ errcode_t d_err; /* Any processing error during iteration of the directory */ }; /* * called from feature_metaecc.c and feature_indexed_dirs.c * to install dir trailers */ errcode_t tunefs_prepare_dir_trailer(ocfs2_filesys *fs, struct ocfs2_dinode *di, struct tunefs_trailer_context **tc_ret); errcode_t tunefs_install_dir_trailer(ocfs2_filesys *fs, struct ocfs2_dinode *di, struct tunefs_trailer_context *tc); void tunefs_trailer_context_free(struct tunefs_trailer_context *tc); int tunefs_is_journal64(ocfs2_filesys *fs); #endif /* _LIBTUNEFS_H */ ocfs2-tools-ocfs2-tools-1.8.6/tunefs.ocfs2/o2cluster.8.in000066400000000000000000000072701347147137200230260ustar00rootroot00000000000000.TH "o2cluster" "8" "January 2012" "Version @VERSION@" "OCFS2 Manual Pages" .SH "NAME" o2cluster \- Change cluster stack stamped on an \fIOCFS2\fR file system. .SH "SYNOPSIS" \fBo2cluster\fR [\fB\-o\fR|\fB\-\-show\-ondisk\fR] [\fB\-r\fR|\fB\-\-show\-running\fR] [\fB\-u\fR|\fB\-\-update\fR[=]] [\fB\-hvVyn\fR] [device] .SH "DESCRIPTION" .PP \fBo2cluster\fR is used to change the cluster stack stamped on an \fBOCFS2\fR file system. It also used to list the active cluster stack and the one stamped on-disk. This utility does not expect the cluster to be online. It only updates the file system if it is reasonably assured that it is not in-use on any other node. Clean journals implies the file system is not in-use. This utility aborts if it detects even one dirty journal. \fIBefore using this utility, the user should use other means to ensure that the volume is not in-use, and more importantly, not about to be put in-use\fR. While clean journals implies the file system is not in-use, there is a tiny window after the check and before the update during which another node could mount the file system using the older cluster stack. If a dirty journal is detected, it implies one of two scenarios. Either the file system is mounted on another node, or, the last node to have it mounted, crashed. There is no way, short of joining the cluster, that the utility can use to differentiate between the two. Considering this utility is targeted to be used in scenarios when the user is looking to change the on-disk cluster stack, it becomes a chicken-and-egg problem. If one were to run into this scenario, the user should manually re-confirm that the file system is not in-use on another node and then run \fBfsck.ocfs2(8)\fR. It will update the on-disk cluster stack to the active cluster stack, and, do a complete file system check. .SH "SPECIFYING CLUSTER STACK .PP The cluster stack can be specified in one of two forms. The first as \fBdefault\fR, denoting the original \fIclassic o2cb\fR cluster stack with local heartbeat. The second as a triplet with the stack name, the cluster name and the cluster flags separated by commas. Like \fBo2cb,mycluster,global\fR. The valid stack names are \fBo2cb\fR, \fBpcmk\fR, and \fBcman\fR. The cluster name can be up to 16 characters. The \fIo2cb\fR stack further restricts the names to contain only alphanumeric characters. The valid flags for the \fIo2cb\fR stack are \fBlocal\fR and \fBglobal\fR, denoting the two heartbeat modes. The only valid flag for the other stacks is \fBnone\fR. .SH "OPTIONS" .TP \fB\-o\fR|\fB\-\-show\-ondisk\fR Shows the cluster stack stamped on-disk. .TP \fB\-r\fR|\fB\-\-show\-running\fR Shows the active cluster stack. .TP \fB\-u\fR|\fB\-\-update\fR[=\fI\fR] Updates the on-disk cluster stack with the one provided. If no cluster stack is provided, the utility detects the active cluster stack and stamps it on-disk. .TP \fB\-v, \-\-verbose\fR Verbose mode. .TP \fB\-V, \-\-version\fR Show version and exit. .TP \fB\-y, \-\-yes\fR Always answer Yes in interactive command line. .TP \fB\-n, \-\-no\fR Always answer No in interactive command line. .SH "EXAMPLES" .nf .ps 9 .ft 6 # o2cluster -r o2cb,myactivecluster,global # o2cluster -o /dev/sda1 o2cb,mycluster,global # o2cluster --update=o2cb,yourcluster,global /dev/sdb1 Changing the clusterstack from o2cb,mycluster,global to o2cb,yourcluster,global. Continue? y Updated successfully. .ft .ps .fi .SH "SEE ALSO" .BR debugfs.ocfs2(8) .BR fsck.ocfs2(8) .BR fsck.ocfs2.checks(8) .BR mkfs.ocfs2(8) .BR mount.ocfs2(8) .BR mounted.ocfs2(8) .BR o2image(8) .BR o2info(1) .BR tunefs.ocfs2(8) .SH "AUTHORS" Oracle Corporation .SH "COPYRIGHT" Copyright \(co 2011, 2012 Oracle. All rights reserved. ocfs2-tools-ocfs2-tools-1.8.6/tunefs.ocfs2/o2cluster.c000066400000000000000000000365071347147137200225010ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * o2cluster.c * * ocfs2 update cluster stack. * * Copyright (C) 2011, 2012 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License version 2 as published by the Free Software Foundation. * * 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. */ #define _GNU_SOURCE /* for getopt_long and O_DIRECT */ #include #include #include #include #include #include #include #include "ocfs2/ocfs2.h" #include "ocfs2/bitops.h" #include "o2ne_err.h" #include "tools-internal/verbose.h" #include "tools-internal/progress.h" #define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d)) #define BITS_PER_BYTE 8 #define BITS_TO_LONGS(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(long)) enum { CL_UNKNOWN = 0, CL_LIST_RUNNING = 1, CL_LIST_ONDISK = 2, CL_UPDATE_DISK = 3, }; static void usage(int rc) { verbosef(VL_OUT, "Usage: %s [options] \n", tools_progname()); verbosef(VL_OUT, " %s -r|--show-running (currently active clusterstack)\n", tools_progname()); verbosef(VL_OUT, " %s -h|--help\n", tools_progname()); verbosef(VL_OUT, " %s -V|--version\n", tools_progname()); verbosef(VL_OUT, "[options] can be:\n"); verbosef(VL_OUT, "\t-u|--update[=]\n"); verbosef(VL_OUT, "\t-o|--show-ondisk (shows clusterstack as stamped ondisk)\n"); verbosef(VL_OUT, "\t-v|--verbose (increases verbosity; more than one permitted)\n"); verbosef(VL_OUT, "\t-y|--yes\n"); verbosef(VL_OUT, "\t-n|--no\n\n"); verbosef(VL_OUT, "Updates and lists the cluster stack stamped on an OCFS2 file system.\n\n"); verbosef(VL_OUT, "The clusterstack may be specified in one of two forms. The first as \"default\"\n"); verbosef(VL_OUT, "denoting the original classic o2cb cluster stack with local heartbeat.\n"); verbosef(VL_OUT, "The second as a triplet with the stack name, the cluster name and the cluster\n"); verbosef(VL_OUT, "flags separated by commas. Like \"o2cb,mycluster,global\".\n\n"); verbosef(VL_OUT, "Valid stack names are \"o2cb\", \"pcmk\" and \"cman\".\n\n"); verbosef(VL_OUT, "Cluster names can be up to 16 characters. The o2cb stack further restricts\n"); verbosef(VL_OUT, "the names to contain only alphanumeric characters.\n\n"); verbosef(VL_OUT, "For the o2cb stack, valid flags are \"local\" and \"global\" denoting the two\n"); verbosef(VL_OUT, "heartbeat modes. Use \"none\" for the other cluster stacks.\n"); exit(rc); } static char * cluster_flags_in_string(struct o2cb_cluster_desc *desc) { if (!desc->c_stack) return O2CB_LOCAL_HEARTBEAT_TAG; if (!strcmp(desc->c_stack, OCFS2_CLASSIC_CLUSTER_STACK)) { if (desc->c_flags == OCFS2_CLUSTER_O2CB_GLOBAL_HEARTBEAT) return O2CB_GLOBAL_HEARTBEAT_TAG; else return O2CB_LOCAL_HEARTBEAT_TAG; } else return "none"; } static void cluster_desc_in_string(struct o2cb_cluster_desc *desc, char *str, int len) { if (desc->c_stack) snprintf(str, len, "%s,%s,%s", desc->c_stack, desc->c_cluster, cluster_flags_in_string(desc)); else snprintf(str, len, "%s", "default"); } static errcode_t get_running_cluster(struct o2cb_cluster_desc *desc) { errcode_t ret; ret = o2cb_init(); if (!ret) ret = o2cb_running_cluster_desc(desc); return ret; } static int parse_cluster_info(char *csinfo, struct o2cb_cluster_desc *desc) { char *tok, *tokens = NULL; int ret = 1; int i; #define MAX_VALS 3 char *val[MAX_VALS]; tokens = strdup(csinfo); if (!tokens) { tcom_err(OCFS2_ET_NO_MEMORY, "while parsing cluster options"); goto bail; } for (i = 0, tok = strtok(tokens, ","); i < MAX_VALS && tok; tok = strtok(NULL, ","), ++i) val[i] = tok; if (i == 1 && !strcmp(val[0], "default")) { desc->c_stack = NULL; desc->c_cluster = NULL; desc->c_flags = 0; ret = 0; goto bail; } if (i != MAX_VALS) { errorf("Cluster details should be in the format " "\",,\"\n"); goto bail; } #undef MAX_VALS desc->c_stack = strdup(val[0]); desc->c_cluster = strdup(val[1]); if (!desc->c_stack || !desc->c_cluster) { tcom_err(OCFS2_ET_NO_MEMORY, "while parsing cluster details"); goto bail; } if (!o2cb_valid_stack_name(desc->c_stack)) { tcom_err(O2CB_ET_INVALID_STACK_NAME, "; unknown cluster stack '%s'", desc->c_stack); goto bail; } if (!strcmp(desc->c_stack, OCFS2_CLASSIC_CLUSTER_STACK)) { if (!o2cb_valid_o2cb_cluster_name(desc->c_cluster)) { tcom_err(O2CB_ET_INVALID_CLUSTER_NAME, "; max %d alpha-numeric characters", OCFS2_CLUSTER_NAME_LEN); goto bail; } if (!o2cb_valid_heartbeat_mode(val[2])) { tcom_err(O2CB_ET_INVALID_HEARTBEAT_MODE, "; unknown heartbeat mode '%s'", val[2]); goto bail; } if (!strcmp(val[2], O2CB_GLOBAL_HEARTBEAT_TAG)) desc->c_flags = OCFS2_CLUSTER_O2CB_GLOBAL_HEARTBEAT; } else { if (!o2cb_valid_cluster_name(desc->c_cluster)) { tcom_err(O2CB_ET_INVALID_CLUSTER_NAME, "; max %d characters", OCFS2_CLUSTER_NAME_LEN); goto bail; } if (strcasecmp(val[2], "none")) { tcom_err(O2CB_ET_INVALID_HEARTBEAT_MODE, "; heartbeat " "mode must be 'none' for this cluster stack"); goto bail; } } ret = 0; bail: free(tokens); return ret; } static errcode_t parse_options(int argc, char *argv[], char **device, int *task, struct o2cb_cluster_desc *desc) { int ret = 1, c; static struct option long_options[] = { { "update", 2, 0, 'u' }, { "show-ondisk", 0, 0, 'o' }, { "show-running", 0, 0, 'r' }, { "help", 0, 0, 'h' }, { "verbose", 0, 0, 'v' }, { "version", 0, 0, 'V' }, { 0, 0, 0, 0 }, }; while (1) { c = getopt_long(argc, argv, "+hvVynrou::", long_options, NULL); if (c == -1) break; switch (c) { case 'h': usage(1); break; case 'n': tools_interactive_no(); break; case 'y': tools_interactive_yes(); break; case 'o': if (*task != CL_UNKNOWN) usage(1); *task = CL_LIST_ONDISK; break; case 'r': if (*task != CL_UNKNOWN) usage(1); *task = CL_LIST_RUNNING; break; case 'v': tools_verbose(); break; case 'V': tools_version(); exit(1); break; case 'u': if (*task != CL_UNKNOWN) usage(1); *task = CL_UPDATE_DISK; if (optarg) { if (parse_cluster_info(optarg, desc)) goto out; break; } ret = get_running_cluster(desc); if (ret) { tcom_err(ret, "while discovering running " "cluster stack"); goto out; } break; default: usage(1); break; } } if (*task == CL_UNKNOWN) usage(1); if (*task == CL_LIST_RUNNING) { ret = 0; goto out; } if (optind >= argc) { errorf("No device specified\n"); usage(1); } *device = strdup(argv[optind]); if (!*device) { tcom_err(OCFS2_ET_NO_MEMORY, "while parsing parameters"); goto out; } optind++; if (optind < argc) { errorf("Too many arguments\n"); usage(1); } ret = 0; out: return ret; } static void handle_signal(int caught_sig) { int exitp = 0, abortp = 0; static int segv_already = 0; switch (caught_sig) { case SIGQUIT: abortp = 1; /* FALL THROUGH */ case SIGTERM: case SIGINT: case SIGHUP: errorf("Caught signal %d, exiting\n", caught_sig); exitp = 1; break; case SIGSEGV: errorf("Segmentation fault, exiting\n"); exitp = 1; if (segv_already) { errorf("Segmentation fault loop detected\n"); abortp = 1; } else segv_already = 1; break; default: errorf("Caught signal %d, ignoring\n", caught_sig); break; } if (!exitp) return; if (abortp) abort(); // tunefs_close_all(); exit(1); } static int setup_signals(void) { int rc = 0; struct sigaction act; act.sa_sigaction = NULL; sigemptyset(&act.sa_mask); act.sa_handler = handle_signal; #ifdef SA_INTERRUPT act.sa_flags = SA_INTERRUPT; #endif rc += sigaction(SIGTERM, &act, NULL); rc += sigaction(SIGINT, &act, NULL); rc += sigaction(SIGHUP, &act, NULL); rc += sigaction(SIGQUIT, &act, NULL); rc += sigaction(SIGSEGV, &act, NULL); act.sa_handler = SIG_IGN; rc += sigaction(SIGPIPE, &act, NULL); /* Get EPIPE instead */ return rc; } static void tool_init(const char *argv0) { initialize_ocfs_error_table(); initialize_o2cb_error_table(); initialize_o2dl_error_table(); tools_setup_argv0(argv0); tools_interactive(); setbuf(stdout, NULL); setbuf(stderr, NULL); if (setup_signals()) { errorf("Unable to setup signal handling\n"); exit(1); } } static errcode_t scan_journals(ocfs2_filesys *fs, unsigned long *dirty_map) { errcode_t ret = 0; char *buf = NULL; ocfs2_cached_inode *ci = NULL; uint64_t blkno; int i; uint16_t max_slots = OCFS2_RAW_SB(fs->fs_super)->s_max_slots; if (OCFS2_RAW_SB(fs->fs_super)->s_feature_incompat & OCFS2_FEATURE_INCOMPAT_HEARTBEAT_DEV) { verbosef(VL_LIB, "Heartbeat device; No need to check for " "dirty journals\n"); goto bail; } verbosef(VL_LIB, "Checking for dirty journals\n"); ret = ocfs2_malloc_block(fs->fs_io, &buf); if (ret) { verbosef(VL_LIB, "%s while allocating a block during journal " "check\n", error_message(ret)); goto bail; } for (i = 0; i < max_slots; ++i) { ret = ocfs2_lookup_system_inode(fs, JOURNAL_SYSTEM_INODE, i, &blkno); if (ret) { verbosef(VL_LIB, "%s while looking up journal inode " "for slot %u during journal check\n", error_message(ret), i); goto bail; } ret = ocfs2_read_cached_inode(fs, blkno, &ci); if (ret) { verbosef(VL_LIB, "%s while reading inode %"PRIu64" " "during journal check", error_message(ret), blkno); goto bail; } if (ci->ci_inode->id1.journal1.ij_flags & OCFS2_JOURNAL_DIRTY_FL) ocfs2_set_bit(i, dirty_map); } bail: if (ci) ocfs2_free_cached_inode(fs, ci); ocfs2_free(&buf); return ret; } #define DIRTY_JOURNAL_WARNING \ "Dirty journals could indicate that the volume is in use on " \ "one or more nodes.\nIf so, then this operation should not be " \ "performed. However, it could also be\nthat the last node " \ "using the filesystem crashed leaving a dirty journal.\n" \ "In the normal course, this journal would have been recovered " \ "during mount.\nDANGER: YOU MUST BE ABSOLUTELY SURE THAT NO " \ "OTHER NODE IS USING THIS FILESYSTEM\nBEFORE MODIFYING ITS " \ "CLUSTER CONFIGURATION.\nCONTINUE? " static errcode_t journal_check(ocfs2_filesys *fs) { int ret = 0; int i, out, inuse, slot, size; unsigned long dirty_map[BITS_TO_LONGS(O2NM_MAX_NODES)]; char warning[1024] = "Device \"%s\" has dirty journals in slots "; memset(dirty_map, 0, sizeof(dirty_map)); ret = scan_journals(fs, dirty_map); if (ret) goto out; inuse = ocfs2_get_bits_set(dirty_map, sizeof(dirty_map), 0); if (!inuse) goto out; out = strlen(warning); slot = -1; size = BITS_PER_BYTE * sizeof(dirty_map); for (i = 0; i < O2NM_MAX_NODES; ++i) { slot = ocfs2_find_next_bit_set(dirty_map, size, slot + 1); if (slot >= size) break; if (i) out += sprintf(warning + out, ", %d", slot); else out += sprintf(warning + out, "%d", slot); } out += sprintf(warning + out, ".\n"); verbosef(VL_OUT, warning, fs->fs_devname); #if 0 if (tools_interact_critical(DIRTY_JOURNAL_WARNING)) goto out; #endif ret = -1; out: return ret; } static errcode_t fs_open(const char *device, ocfs2_filesys **ret_fs) { errcode_t err; int open_flags; verbosef(VL_LIB, "Opening device '%s'\n", device); *ret_fs = NULL; open_flags = OCFS2_FLAG_HEARTBEAT_DEV_OK; open_flags |= OCFS2_FLAG_RW | OCFS2_FLAG_STRICT_COMPAT_CHECK; err = ocfs2_open(device, open_flags, 0, 0, ret_fs); if (err) { tcom_err(err, "while opening device '%s'", device); goto out; } /*TODO Review all features */ out: if (!err) verbosef(VL_LIB, "Device \"%s\" opened\n", device); else if (*ret_fs) ocfs2_close(*ret_fs); return err; } static void fs_close(ocfs2_filesys *fs) { errcode_t err; if (!fs) return; verbosef(VL_LIB, "Closing device \"%s\"\n", fs->fs_devname); err = ocfs2_close(fs); if (!err) verbosef(VL_LIB, "Device closed\n"); else verbosef(VL_LIB, "Device close failed (%s)\n", error_message(err)); } static int do_update(char *device, struct o2cb_cluster_desc *newcl) { int ret; ocfs2_filesys *fs = NULL; struct o2cb_cluster_desc ondisk = { NULL, NULL, 0 }; struct o2cb_cluster_desc *diskcl = &ondisk; char fromcs[64], tocs[64]; ret = fs_open(device, &fs); if (ret) goto out; if (OCFS2_RAW_SB(fs->fs_super)->s_feature_incompat & OCFS2_FEATURE_INCOMPAT_LOCAL_MOUNT) { ret = -1; errorf("Device not clustered. Use tunefs.ocfs2(8) to enable " "clustered mode.\n"); goto out; } ret = journal_check(fs); if (ret) goto out; ret = ocfs2_fill_cluster_desc(fs, diskcl); if (ret) { tcom_err(ret, "while discovering ondisk cluster stack"); goto out; } /* Abort if ondisk cluster matches requested cluster */ if ((!newcl->c_stack && !diskcl->c_stack) || (newcl->c_stack && newcl->c_cluster && diskcl->c_stack && diskcl->c_cluster && !strcmp(newcl->c_stack, diskcl->c_stack) && !strcmp(newcl->c_cluster, diskcl->c_cluster) && newcl->c_flags == diskcl->c_flags)) { verbosef(VL_OUT, "New cluster stack is already on disk.\n"); goto out; } cluster_desc_in_string(diskcl, fromcs, sizeof(fromcs)); cluster_desc_in_string(newcl, tocs, sizeof(tocs)); if (!tools_interact("Changing the clusterstack from %s to %s. " "Continue? ", fromcs, tocs)) goto out; ret = ocfs2_set_cluster_desc(fs, newcl); if (ret) tcom_err(ret, "while updating the cluster stack ondisk"); else verbosef(VL_OUT, "Updated successfully.\n"); out: o2cb_free_cluster_desc(diskcl); fs_close(fs); return ret; } static errcode_t do_list_ondisk(char *device) { int ret; ocfs2_filesys *fs = NULL; struct o2cb_cluster_desc desc = { NULL, NULL, 0 }; char cldesc[64]; ret = fs_open(device, &fs); if (ret) goto out; if (OCFS2_RAW_SB(fs->fs_super)->s_feature_incompat & OCFS2_FEATURE_INCOMPAT_LOCAL_MOUNT) { ret = 1; errorf("Clustering is not enabled on device '%s'.\n", device); goto out; } ret = ocfs2_fill_cluster_desc(fs, &desc); if (ret) { tcom_err(ret, "while discovering ondisk cluster stack"); goto out; } cluster_desc_in_string(&desc, cldesc, sizeof(cldesc)); verbosef(VL_OUT, "%s\n", cldesc); out: o2cb_free_cluster_desc(&desc); fs_close(fs); return ret; } static errcode_t do_list_active(void) { int ret; struct o2cb_cluster_desc desc = { NULL, NULL, 0 }; char cldesc[64]; ret = get_running_cluster(&desc); if (ret) { tcom_err(ret, "while discovering running cluster stack"); goto out; } cluster_desc_in_string(&desc, cldesc, sizeof(cldesc)); verbosef(VL_OUT, "%s\n", cldesc); out: return ret; } int main(int argc, char *argv[]) { errcode_t ret; int task = CL_UNKNOWN; char *device = NULL; struct o2cb_cluster_desc desc = { NULL, NULL, 0 }; tool_init(argv[0]); ret = parse_options(argc, argv, &device, &task, &desc); if (ret) goto out; switch (task) { case CL_LIST_RUNNING: ret = do_list_active(); break; case CL_LIST_ONDISK: ret = do_list_ondisk(device); break; case CL_UPDATE_DISK: ret = do_update(device, &desc); break; default: break; } out: if (ret) verbosef(VL_OUT, "Aborting.\n"); o2cb_free_cluster_desc(&desc); free(device); return ret ? 1 : 0; } ocfs2-tools-ocfs2-tools-1.8.6/tunefs.ocfs2/o2ne_err.et000066400000000000000000000054631347147137200224550ustar00rootroot00000000000000# # libtunefs_err.et # # Error codes for the OCFS2 tunefs internal library. # # Copyright (C) 2008 Oracle. All rights reserved. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public # License, version 2, as published by the Free Software Foundation. # # 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. # error_table o2ne ec TUNEFS_ET_SIGNALS_FAILED, "Unable to setup signal handling" ec TUNEFS_ET_HEARTBEAT_DEV, "Heartbeat devices cannot be tuned, they can only be re-formatted with mkfs.ocfs2" ec TUNEFS_ET_RESIZE_IN_PROGRESS, "This device was in the process of resizing. Run \"fsck.ocfs2 -f \" to clean up the aborted operation, then try again" ec TUNEFS_ET_TUNEFS_IN_PROGRESS, "This device was in the process of another tunefs.ocfs2 operation. Run \"fsck.ocfs2 -f \" to clean up the aborted operation, then try " "again" ec TUNEFS_ET_INVALID_STACK_NAME, "Cluster stack specified does not match the one currently running" ec TUNEFS_ET_PERFORM_ONLINE, "Operation needs to be performed online" ec TUNEFS_ET_DEVICE_BUSY, "Device is busy" ec TUNEFS_ET_NOT_MOUNTED, "Mountpoint not found for online operation" ec TUNEFS_ET_ONLINE_FAILED, "Online operation request failed (see dmesg)" ec TUNEFS_ET_INTERNAL_FAILURE, "Internal logic failure" ec TUNEFS_ET_NO_MEMORY, "Unable to allocate memory" ec TUNEFS_ET_JOURNAL_DIRTY, "At least one journal has uncommitted changes. Run fsck.ocfs2 to replay all dirty journals" ec TUNEFS_ET_INVALID_NUMBER, "Invalid number" ec TUNEFS_ET_TOO_MANY_SLOTS_OLD, "Filesystem cannot support more than 255 slots" ec TUNEFS_ET_TOO_MANY_SLOTS_EXTENDED, "Filesystem cannot support more than 32767 slots" ec TUNEFS_ET_ORPHAN_DIR_NOT_EMPTY, "Orphan dir is not empty" ec TUNEFS_ET_TRUNCATE_LOG_NOT_EMPTY, "Truncate log is not empty" ec TUNEFS_ET_LOCAL_ALLOC_NOT_EMPTY, "Local allocation file is not empty" ec TUNEFS_ET_UNWRITTEN_PRESENT, "Unwritten extents are present" ec TUNEFS_ET_SPARSE_MISSING, "Sparse file support is missing" ec TUNEFS_ET_OPERATION_FAILED, "Operation failed" ec TUNEFS_ET_ONLINE_NOT_SUPPORTED, "This online operation is not available" ec TUNEFS_ET_CLUSTER_SKIPPED, "Cluster stack initialization was skipped" ec TUNEFS_ET_DX_DIRS_SCAN_FAILED, "Scanning inodes for directory indexing failed" ec TUNEFS_ET_IO_WRITE_FAILED, "Write I/O failed" ec TUNEFS_ET_DX_DIRS_TRUNCATE_FAILED, "Truncate directory indexed tree failed" ec TUNEFS_ET_DX_DIRS_BUILD_FAILED, "Build directory indexed tree failed" ec TUNEFS_ET_INSTALL_DIR_TRAILER_FAILED, "Install directory trailer failed" end ocfs2-tools-ocfs2-tools-1.8.6/tunefs.ocfs2/ocfs2ne.c000066400000000000000000000641111347147137200221060ustar00rootroot00000000000000/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * ocfs2ne.c * * ocfs2 tune utility. * * Copyright (C) 2004, 2008 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License version 2 as published by the Free Software Foundation. * * 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. */ #define _GNU_SOURCE /* for getopt_long and O_DIRECT */ #include #include #include #include #include #include #include "ocfs2/ocfs2.h" #include "libocfs2ne.h" /* * Why do we have a list of option structures will callbacks instead of * a simple switch() statement? Because the ocfs2ne option set has grown * over time, and there are a few operations that can be triggered by * more than one option. For example, -M {cluster|local} is really just * clearing or setting the fs feature 'local'. * * For most argument-free operations, they'll just specify their name and * val. Options with arguments will mostly use generic_handle_arg() as * their ->opt_handle(). * * If you are adding a new feature flag, do not add an option here. It * should be handled by --fs-features. Just write a tunefs_feature in * ocfs2ne_feature_.c and add it to the list ocfs2ne_features.c. * If you are adding an operation, make its option something that stands on * its own and can use generic_handle_arg() if it needs an argument. */ struct tunefs_option { struct option opt_option; /* For getopt_long(). If there is no short option, set .val to CHAR_MAX. A unique value will be inserted by the code. */ struct tunefs_operation *opt_op; /* Operation associated with this option. This needs to be set if the option has no ->opt_handle() or is using generic_handle_arg(). If set, opt_op will be added to the run_list when this option is seen. */ char *opt_help; /* Help string */ int opt_set; /* Was this option seen */ int (*opt_handle)(struct tunefs_option *opt, char *arg); void *opt_private; }; /* * ocfs2ne lumps all journal options as name[=value] arguments underneath * '-J'. They end up being tunefs_operations, and we link them up here. */ struct tunefs_journal_option { char *jo_name; char *jo_help; struct tunefs_operation *jo_op; }; /* Things to run */ struct tunefs_run { struct list_head tr_list; struct tunefs_operation *tr_op; }; extern struct tunefs_operation list_sparse_op; extern struct tunefs_operation query_op; extern struct tunefs_operation reset_uuid_op; extern struct tunefs_operation features_op; extern struct tunefs_operation resize_volume_op; extern struct tunefs_operation set_journal_size_op; extern struct tunefs_operation set_journal_block32_op; extern struct tunefs_operation set_journal_block64_op; extern struct tunefs_operation set_label_op; extern struct tunefs_operation set_slot_count_op; extern struct tunefs_operation update_cluster_stack_op; extern struct tunefs_operation cloned_volume_op; extern struct tunefs_operation set_usrquota_sync_interval_op; extern struct tunefs_operation set_grpquota_sync_interval_op; /* List of operations we're going to run */ static LIST_HEAD(tunefs_run_list); /* Number of operations we're going to run */ static int tunefs_op_count; /* Progress display for tunefs operations */ static struct tools_progress *tunefs_op_progress; static struct tunefs_journal_option set_journal_size_option = { .jo_name = "size", .jo_help = "size=", .jo_op = &set_journal_size_op, }; static struct tunefs_journal_option set_journal_block64_option = { .jo_name = "block64", .jo_help = "block64", .jo_op = &set_journal_block64_op, }; static struct tunefs_journal_option set_journal_block32_option = { .jo_name = "block32", .jo_help = "block32", .jo_op = &set_journal_block32_op, }; static struct tunefs_journal_option set_journal_noblock64_option = { .jo_name = "noblock64", .jo_help = "noblock64", .jo_op = &set_journal_block32_op, }; static struct tunefs_journal_option set_journal_noblock32_option = { .jo_name = "noblock32", .jo_help = "noblock32", .jo_op = &set_journal_block64_op, }; /* The list of all supported journal options */ static struct tunefs_journal_option *tunefs_journal_options[] = { &set_journal_size_option, &set_journal_block64_option, &set_journal_block32_option, &set_journal_noblock64_option, &set_journal_noblock32_option, NULL, }; /* * Operations are intended to run in the order we see them in the * command-line arguments. As each option is seen, the operation is * added with tunefs_append_operation(). * * There are two exceptions. First, special-cased options (pretty much * the feature) will end up at the end because we can't process them * until we've seen all command-line arguments. * * Second, resize is the only user of tunefs_prepend_operation(). We want * to grow the filesystem *before* we do anything that might require space! */ static errcode_t tunefs_append_operation(struct tunefs_operation *op) { errcode_t err; struct tunefs_run *run; err = ocfs2_malloc0(sizeof(struct tunefs_run), &run); if (!err) { run->tr_op = op; list_add_tail(&run->tr_list, &tunefs_run_list); tunefs_op_count++; } return err; } static errcode_t tunefs_prepend_operation(struct tunefs_operation *op) { errcode_t err; struct tunefs_run *run; err = ocfs2_malloc0(sizeof(struct tunefs_run), &run); if (!err) { run->tr_op = op; list_add(&run->tr_list, &tunefs_run_list); tunefs_op_count++; } return err; } static void print_usage(int rc); static int handle_help(struct tunefs_option *opt, char *arg) { print_usage(0); return 1; } static int handle_version(struct tunefs_option *opt, char *arg) { tools_version(); exit(0); return 1; } static int handle_verbosity(struct tunefs_option *opt, char *arg) { int rc = 0; switch (opt->opt_option.val) { case 'v': tools_verbose(); break; case 'q': tools_quiet(); break; default: errorf("Invalid option to handle_verbosity: %c\n", opt->opt_option.val); rc = 1; break; } /* More than one -v or -q is valid */ opt->opt_set = 0; return rc; } static int handle_interactive(struct tunefs_option *opt, char *arg) { tools_interactive(); return 0; } static int handle_progress(struct tunefs_option *opt, char *arg) { tools_progress_enable(); return 0; } static int handle_answer(struct tunefs_option *opt, char *arg) { int rc = 0; switch (opt->opt_option.val) { case 'y': tools_interactive_yes(); break; case 'n': tools_interactive_no(); break; default: errorf("Invalid option to handle_answer: %c\n", opt->opt_option.val); rc = 1; break; } return rc; } /* * Plain operations just want to have their ->to_parse_option() called. * Their tunefs_option can use this function if they set opt_op to the * tunefs_operation. */ static int generic_handle_arg(struct tunefs_option *opt, char *arg) { struct tunefs_operation *op = opt->opt_op; if (!op->to_parse_option) { errorf("Option \"%s\" claims it has an argument, but " "operation \"%s\" isn't expecting one\n", opt->opt_option.name, op->to_name); return 1; } return op->to_parse_option(op, arg); } /* * Store a copy of the argument on opt_private. * * For example, the multiple options setting fs_features want to save off * their feature string. They use this function directly or indirectly. */ static int strdup_handle_arg(struct tunefs_option *opt, char *arg) { char *ptr = NULL; if (arg) { ptr = strdup(arg); if (!ptr) { errorf("Unable to allocate memory while processing " "options\n"); return 1; } } opt->opt_private = ptr; return 0; } static int mount_type_handle_arg(struct tunefs_option *opt, char *arg) { int rc = 0; if (!arg) { errorf("No mount type specified\n"); rc = 1; } else if (!strcmp(arg, "local")) rc = strdup_handle_arg(opt, "local"); else if (!strcmp(arg, "cluster")) rc = strdup_handle_arg(opt, "nolocal"); else { errorf("Invalid mount type: \"%s\"\n", arg); rc = 1; } return rc; } static int backup_super_handle_arg(struct tunefs_option *opt, char *arg) { return strdup_handle_arg(opt, "backup-super"); } static struct tunefs_journal_option *find_journal_option(char *name) { int i; struct tunefs_journal_option *jopt; for (i = 0; tunefs_journal_options[i]; i++) { jopt = tunefs_journal_options[i]; if (!strcmp(name, jopt->jo_name)) return jopt; } return NULL; } /* derived from e2fsprogs */ static int handle_journal_arg(struct tunefs_option *opt, char *arg) { errcode_t err; int i, rc = 0; char *options, *token, *next, *p, *val; int journal_usage = 0; struct tunefs_journal_option *jopt; if (arg) { options = strdup(arg); if (!options) { tcom_err(TUNEFS_ET_NO_MEMORY, "while processing journal options"); return 1; } } else options = NULL; for (token = options; token && *token; token = next) { p = strchr(token, ','); next = NULL; if (p) { *p = '\0'; next = p + 1; } val = strchr(token, '='); if (val) { *val = '\0'; val++; } jopt = find_journal_option(token); if (!jopt) { errorf("Unknown journal option: \"%s\"\n", token); journal_usage++; continue; } if (jopt->jo_op->to_parse_option) { if (jopt->jo_op->to_parse_option(jopt->jo_op, val)) { journal_usage++; continue; } } else if (val) { errorf("Journal option \"%s\" does not accept " "arguments\n", token); journal_usage++; continue; } err = tunefs_append_operation(jopt->jo_op); if (err) { tcom_err(err, "while processing journal options"); rc = 1; break; } } if (journal_usage) { verbosef(VL_ERR, "Valid journal options are:\n"); for (i = 0; tunefs_journal_options[i]; i++) verbosef(VL_ERR, "\t%s\n", tunefs_journal_options[i]->jo_help); rc = 1; } free(options); return rc; } static struct tunefs_option help_option = { .opt_option = { .name = "help", .val = 'h', }, .opt_handle = handle_help, }; static struct tunefs_option version_option = { .opt_option = { .name = "version", .val = 'V', }, .opt_handle = handle_version, }; static struct tunefs_option verbose_option = { .opt_option = { .name = "verbose", .val = 'v', }, .opt_help = "-v|--verbose (increases verbosity; more than one permitted)", .opt_handle = handle_verbosity, }; static struct tunefs_option quiet_option = { .opt_option = { .name = "quiet", .val = 'q', }, .opt_help = "-q|--quiet (decreases verbosity; more than one permitted)", .opt_handle = handle_verbosity, }; static struct tunefs_option interactive_option = { .opt_option = { .name = "interactive", .val = 'i', }, .opt_help = "-i|--interactive", .opt_handle = handle_interactive, }; static struct tunefs_option progress_option = { .opt_option = { .name = "progress", .val = 'p', }, .opt_help = "-p|--progress", .opt_handle = handle_progress, }; static struct tunefs_option yes_option = { .opt_option = { .name = "yes", .val = 'y', }, .opt_help = "-y|--yes", .opt_handle = handle_answer, }; static struct tunefs_option no_option = { .opt_option = { .name = "no", .val = 'n', }, .opt_help = "-n|--no", .opt_handle = handle_answer, }; static struct tunefs_option query_option = { .opt_option = { .name = "query", .val = 'Q', .has_arg = 1, }, .opt_help = "-Q|--query ", .opt_handle = &generic_handle_arg, .opt_op = &query_op, }; static struct tunefs_option list_sparse_option = { .opt_option = { .name = "list-sparse", .val = CHAR_MAX, }, .opt_help = " --list-sparse", .opt_op = &list_sparse_op, }; static struct tunefs_option reset_uuid_option = { .opt_option = { .name = "uuid-reset", .val = 'U', .has_arg = 2, }, .opt_help = "-U|--uuid-reset[=new-uuid]", .opt_handle = &generic_handle_arg, .opt_op = &reset_uuid_op, }; static struct tunefs_option update_cluster_stack_option = { .opt_option = { .name = "update-cluster-stack", .val = CHAR_MAX, }, .opt_help = " --update-cluster-stack", .opt_op = &update_cluster_stack_op, }; static struct tunefs_option cloned_volume_option = { .opt_option = { .name = "cloned-volume", .val = CHAR_MAX, .has_arg = 2, }, .opt_help = " --cloned-volume[=new-label]", .opt_op = &cloned_volume_op, }; static struct tunefs_option set_slot_count_option = { .opt_option = { .name = "node-slots", .val = 'N', .has_arg = 1, }, .opt_help = "-N|--node-slots ", .opt_handle = generic_handle_arg, .opt_op = &set_slot_count_op, }; static struct tunefs_option set_label_option = { .opt_option = { .name = "label", .val = 'L', .has_arg = 1, }, .opt_help = "-L|--label