From 769abb4e813f017ed2c5d77c4d2a67c83f0c1098 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E7=90=A6?= <mpiglet@outlook.com> Date: Sun, 26 Sep 2021 02:03:58 +0000 Subject: [PATCH] erofs-utils: introduce dump.erofs to erofs-utils --- .gitignore | 30 ++ AUTHORS | 9 + COPYING | 359 +++++++++++++++++++++++ ChangeLog | 61 ++++ README | 233 +++++++++++++++ dump/Makefile.am | 9 + dump/main.c | 743 +++++++++++++++++++++++++++++++++++++++++++++++ lib/inode.c | 2 +- 8 files changed, 1445 insertions(+), 1 deletion(-) create mode 100644 .gitignore create mode 100644 AUTHORS create mode 100644 COPYING create mode 100644 ChangeLog create mode 100644 README create mode 100644 dump/Makefile.am create mode 100644 dump/main.c diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8bdd505 --- /dev/null +++ b/.gitignore @@ -0,0 +1,30 @@ +.* +*~ +*.[ao] +*.diff +*.la +*.lo +*.mod.c +*.orig +*.patch +*.rej +*.so +*.so.dbg +*.tar.* + +# +# Generated files +# +aclocal.m4 +autom4te.cache +config.* +Makefile +Makefile.in +config/ +m4/ +configure +configure.scan +libtool +stamp-h +stamp-h1 + diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..3487d44 --- /dev/null +++ b/AUTHORS @@ -0,0 +1,9 @@ +EROFS USERSPACE UTILITIES +M: Li Guifu <bluce.liguifu@huawei.com> +M: Miao Xie <miaoxie@huawei.com> +M: Fang Wei <fangwei1@huawei.com> +R: Gao Xiang <xiang@kernel.org> +R: Chao Yu <yuchao0@huawei.com> +S: Maintained +L: linux-erofs@lists.ozlabs.org + diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..b7eaaf4 --- /dev/null +++ b/COPYING @@ -0,0 +1,359 @@ +Valid-License-Identifier: GPL-2.0 +Valid-License-Identifier: GPL-2.0-only +Valid-License-Identifier: GPL-2.0+ +Valid-License-Identifier: GPL-2.0-or-later +SPDX-URL: https://spdx.org/licenses/GPL-2.0.html +Usage-Guide: + To use this license in source code, put one of the following SPDX + tag/value pairs into a comment according to the placement + guidelines in the licensing rules documentation. + For 'GNU General Public License (GPL) version 2 only' use: + SPDX-License-Identifier: GPL-2.0 + or + SPDX-License-Identifier: GPL-2.0-only + For 'GNU General Public License (GPL) version 2 or any later version' use: + SPDX-License-Identifier: GPL-2.0+ + or + SPDX-License-Identifier: GPL-2.0-or-later +License-Text: + + 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. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 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. + + <signature of Ty Coon>, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..6637bc3 --- /dev/null +++ b/ChangeLog @@ -0,0 +1,61 @@ +erofs-utils 1.3 + + * This release includes the following updates: + - support new big pcluster feature together with Linux 5.13+; + - optimize buffer allocation logic (Hu Weiwen); + - optimize build performance for large directories (Hu Weiwen); + - add support to override uid / gid (Hu Weiwen); + - add support to adjust lz4 history window size (Huang Jianan); + - add a manual for erofsfuse; + - add support to limit max decompressed extent size; + - various bugfixes and cleanups; + + -- Gao Xiang <xiang@kernel.org> Tue, 01 Jun 2021 00:00:00 +0800 + +erofs-utils (1.2.1-1) unstable; urgency=medium + + * A quick maintenance release includes the following updates: + - fix reported build issues due to different configurations; + - (mkfs.erofs, AOSP) fix sub-directory prefix for canned fs_config; + - update some obsoleted email address; + + -- Gao Xiang <xiang@kernel.org> Sun, 10 Jan 2021 00:00:00 +0800 + +erofs-utils (1.2-1) unstable; urgency=medium + + * This release includes the following features and bugfixes: + - (mkfs.erofs) support selinux file contexts; + - (mkfs.erofs) support $SOURCE_DATE_EPOCH; + - (mkfs.erofs) support a pre-defined UUID; + - (mkfs.erofs) fix random padding for reproducable builds; + - (mkfs.erofs) several fixes around hard links; + - (mkfs.erofs) minor code cleanups; + - (mkfs.erofs, AOSP) support Android fs_config; + - (experimental, disabled by default) add erofsfuse approach; + + -- Gao Xiang <xiang@kernel.org> Sun, 06 Dec 2020 00:00:00 +0800 + +erofs-utils (1.1-1) unstable; urgency=low + + * a maintenance release includes the following updates: + - (mkfs.erofs) add a manual for mkfs.erofs; + - (mkfs.erofs) add superblock checksum support; + - (mkfs.erofs) add filesystem UUID support; + - (mkfs.erofs) add exclude files support; + - (mkfs.erofs) fix compiling issues under specific conditions; + - (mkfs.erofs) minor code cleanups; + + -- Gao Xiang <xiang@kernel.org> Tue, 14 Apr 2020 00:00:00 +0800 + +erofs-utils (1.0-1) unstable; urgency=low + + * first release with the following new features: + - (mkfs.erofs) uncompressed file support; + - (mkfs.erofs) uncompressed tail-end packing inline data support; + - (mkfs.erofs) lz4 / lz4HC compressed file support; + - (mkfs.erofs) special file support; + - (mkfs.erofs) inline / shared xattrs support; + - (mkfs.erofs) Posix ACL support; + + -- Gao Xiang <xiang@kernel.org> Thu, 24 Oct 2019 00:00:00 +0800 + diff --git a/README b/README new file mode 100644 index 0000000..af9cdf1 --- /dev/null +++ b/README @@ -0,0 +1,233 @@ +erofs-utils +=========== + +erofs-utils includes user-space tools for EROFS filesystem. +Currently mkfs.erofs and erofsfuse (experimental) are available. + +Dependencies & build +-------------------- + + lz4 1.8.0+ for lz4 enabled [2], lz4 1.9.3+ highly recommended [4][5]. + + libfuse 2.6+ for erofsfuse enabled as a plus. + +How to build for lz4-1.9.0 or above +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To build, you can run the following commands in order: + +:: + + $ ./autogen.sh + $ ./configure + $ make + +mkfs.erofs binary will be generated under mkfs folder. + +* For lz4 < 1.9.2, there are some stability issues about + LZ4_compress_destSize(). (lz4hc isn't impacted) [3]. + +** For lz4 = 1.9.2, there is a noticeable regression about + LZ4_decompress_safe_partial() [5], which impacts erofsfuse + functionality for legacy images (without 0PADDING). + +How to build for lz4-1.8.0~1.8.3 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +For these old lz4 versions, lz4hc algorithm cannot be supported +without lz4-static installed due to LZ4_compress_HC_destSize() +unstable api usage, which means lz4 will only be available if +lz4-static isn't found. + +On Fedora, lz4-static can be installed by using: + + yum install lz4-static.x86_64 + +However, it's still not recommended using those versions directly +since there are serious bugs in these compressors, see [2] [3] [4] +as well. + +mkfs.erofs +---------- + +two main kinds of EROFS images can be generated: (un)compressed. + + - For uncompressed images, there will be none of compression + files in these images. However, it can decide whether the tail + block of a file should be inlined or not properly [1]. + + - For compressed images, it will try to use lz4(hc) algorithm + first for each regular file and see if storage space can be + saved with compression. If not, fallback to an uncompressed + file. + +How to generate EROFS images (Linux 5.3+) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Currently lz4 and lz4hc are available for compression, e.g. + $ mkfs.erofs -zlz4hc foo.erofs.img foo/ + +Or leave all files uncompressed as an option: + $ mkfs.erofs foo.erofs.img foo/ + +In addition, you could specify a higher compression level to get a +(slightly) better compression ratio than the default level, e.g. + $ mkfs.erofs -zlz4hc,12 foo.erofs.img foo/ + +How to generate EROFS big pcluster images (Linux 5.13+) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In order to get much better compression ratios (thus better sequential +read performance for common storage devices), big pluster feature has +been introduced since linux-5.13, which is not forward-compatible with +old kernels. + +In details, -C is used to specify the maximum size of each big pcluster +in bytes, e.g. + $ mkfs.erofs -zlz4hc -C65536 foo.erofs.img foo/ + +So in that case, pcluster size can be 64KiB at most. + +Note that large pcluster size can cause bad random performance, so +please evaluate carefully in advance. Or make your own per-(sub)file +compression strategies according to file access patterns if needed. + +How to generate legacy EROFS images (Linux 4.19+) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Decompression inplace and compacted indexes have been introduced in +linux-5.3, which are not forward-compatible with older kernels. + +In order to generate _legacy_ EROFS images for old kernels, +consider adding "-E legacy-compress" to the command line, e.g. + + $ mkfs.erofs -E legacy-compress -zlz4hc foo.erofs.img foo/ + +For Linux kernel >= 5.3, legacy EROFS images are _NOT recommended_ +due to runtime performance loss compared with non-legacy images. + +Obsoleted erofs.mkfs +~~~~~~~~~~~~~~~~~~~~ + +There is an original erofs.mkfs version developed by Li Guifu, +which was replaced by the new erofs-utils implementation. + +git://git.kernel.org/pub/scm/linux/kernel/git/xiang/erofs-utils.git -b obsoleted_mkfs + +PLEASE NOTE: This version is highly _NOT recommended_ now. + +erofsfuse (experimental, unstable) +---------------------------------- + +erofsfuse is introduced to support EROFS format for various platforms +(including older linux kernels) and new on-disk features iteration. +It can also be used as an unpacking tool for unprivileged users. + +It supports fixed-sized output decompression *without* any in-place +I/O or in-place decompression optimization. Also like the other FUSE +implementations, it suffers from most common performance issues (e.g. +significant I/O overhead, double caching, etc.) + +Therefore, NEVER use it if performance is the top concern. + +Note that xattr & ACL aren't implemented yet due to the current Android +use-case vs limited time. If you have some interest, contribution is, +as always, welcome. + +How to build erofsfuse +~~~~~~~~~~~~~~~~~~~~~~ + +It's disabled by default as an experimental feature for now, to enable +and build it manually: + + $ ./configure --enable-fuse + $ make + +erofsfuse binary will be generated under fuse folder. + +How to mount an EROFS image with erofsfuse +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +As the other FUSE implementations, it's quite simple to mount with +erofsfuse, e.g.: + $ erofsfuse foo.erofs.img foo/ + +Alternatively, to make it run in foreground (with debugging level 3): + $ erofsfuse -f --dbglevel=3 foo.erofs.img foo/ + +To debug erofsfuse (also automatically run in foreground): + $ erofsfuse -d foo.erofs.img foo/ + +To unmount an erofsfuse mountpoint as a non-root user: + $ fusermount -u foo/ + +Contribution +------------ + +erofs-utils is under GPLv2+ as a part of EROFS filesystem project, +feel free to send patches or feedback to us. + +To: + linux-erofs mailing list <linux-erofs@lists.ozlabs.org> + Li Guifu <bluce.liguifu@huawei.com> + Miao Xie <miaoxie@huawei.com> + Fang Wei <fangwei1@huawei.com> + +Cc: + Gao Xiang <xiang@kernel.org> + Chao Yu <yuchao0@huawei.com> + +Comments +-------- + +[1] According to the EROFS on-disk format, the tail block of files + could be inlined aggressively with its metadata in order to reduce + the I/O overhead and save the storage space (called tail-packing). + +[2] There was a bug until lz4-1.8.3, which can crash erofs-utils + randomly. Fortunately bugfix by our colleague Qiuyang Sun was + merged in lz4-1.9.0. + + For more details, please refer to + https://github.com/lz4/lz4/commit/660d21272e4c8a0f49db5fc1e6853f08713dff82 + +[3] There were many bugfixes merged into lz4-1.9.2 for + LZ4_compress_destSize(), and I once ran into some crashs due to + those issues. * Again lz4hc is not affected. * + + [LZ4_compress_destSize] Allow 2 more bytes of match length + https://github.com/lz4/lz4/commit/690009e2c2f9e5dcb0d40e7c0c40610ce6006eda + + [LZ4_compress_destSize] Fix rare data corruption bug + https://github.com/lz4/lz4/commit/6bc6f836a18d1f8fd05c8fc2b42f1d800bc25de1 + + [LZ4_compress_destSize] Fix overflow condition + https://github.com/lz4/lz4/commit/13a2d9e34ffc4170720ce417c73e396d0ac1471a + + [LZ4_compress_destSize] Fix off-by-one error in fix + https://github.com/lz4/lz4/commit/7c32101c655d93b61fc212dcd512b87119dd7333 + + [LZ4_compress_destSize] Fix off-by-one error + https://github.com/lz4/lz4/commit/d7cad81093cd805110291f84d64d385557d0ffba + + since upstream lz4 doesn't have stable branch for old versions, it's + preferred to use latest upstream lz4 library (although some regressions + could happen since new features are also introduced to latest upstream + version as well) or backport all stable bugfixes to old stable versions, + e.g. our unofficial lz4 fork: https://github.com/erofs/lz4 + +[4] LZ4HC didn't compress long zeroed buffer properly with + LZ4_compress_HC_destSize() + https://github.com/lz4/lz4/issues/784 + + which has been resolved in + https://github.com/lz4/lz4/commit/e7fe105ac6ed02019d34731d2ba3aceb11b51bb1 + + and already included in lz4-1.9.3, see: + https://github.com/lz4/lz4/releases/tag/v1.9.3 + +[5] LZ4_decompress_safe_partial is broken in 1.9.2 + https://github.com/lz4/lz4/issues/783 + + which is also resolved in lz4-1.9.3. + diff --git a/dump/Makefile.am b/dump/Makefile.am new file mode 100644 index 0000000..d7b2873 --- /dev/null +++ b/dump/Makefile.am @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Makefile.am + +AUTOMAKE_OPTIONS = foreign +bin_PROGRAMS = dump.erofs +AM_CPPFLAGS = ${libuuid_CFLAGS} +dump_erofs_SOURCES = main.c +dump_erofs_CFLAGS = -Wall -Werror -I$(top_srcdir)/include +dump_erofs_LDADD = $(top_builddir)/lib/liberofs.la ${libuuid_LIBS} ${liblz4_LIBS} \ No newline at end of file diff --git a/dump/main.c b/dump/main.c new file mode 100644 index 0000000..34a17f9 --- /dev/null +++ b/dump/main.c @@ -0,0 +1,743 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2021-2022 HUAWEI, Inc. + * http://www.huawei.com/ + * Created by Wang Qi <mpiglet@outlook.com> + * Guo Xuenan <guoxuenan@huawei.com> + */ + +#include <stdlib.h> +#include <getopt.h> +#include <sys/sysmacros.h> +#include <time.h> +#include "erofs/print.h" +#include "erofs/io.h" + +#ifdef HAVE_LIBUUID +#include <uuid.h> +#endif + +struct dumpcfg { + bool print_superblock; + bool print_inode; + bool print_statistic; + bool print_inode_phy; + bool print_version; + u64 ino; + u64 ino_phy; +}; +static struct dumpcfg dumpcfg; + +static const char chart_format[] = "%-16s %-11d %8.2f%% |%-50s|\n"; +static const char header_format[] = "%-16s %11s %16s |%-50s|\n"; +static char *file_types[] = { + ".so", ".png", ".jpg", ".xml", ".html", ".odex", + ".vdex", ".apk", ".ttf", ".jar", ".json", ".ogg", + ".oat", ".art", ".rc", ".otf", ".txt", "others", +}; +#define OTHERFILETYPE ARRAY_SIZE(file_types) +/* (1 << FILE_MAX_SIZE_BITS)KB */ +#define FILE_MAX_SIZE_BITS 16 + +static const char * const file_category_types[] = { + [EROFS_FT_UNKNOWN] = "unknown type", + [EROFS_FT_REG_FILE] = "regular file", + [EROFS_FT_DIR] = "directory", + [EROFS_FT_CHRDEV] = "char dev", + [EROFS_FT_BLKDEV] = "block dev", + [EROFS_FT_FIFO] = "FIFO file", + [EROFS_FT_SOCK] = "SOCK file", + [EROFS_FT_SYMLINK] = "symlink file", +}; + +struct statistics { + unsigned long files; + unsigned long compressed_files; + unsigned long uncompressed_files; + unsigned long files_total_size; + unsigned long files_total_origin_size; + double compress_rate; + + /* statistics the number of files based on inode_info->flags */ + unsigned long file_category_stat[EROFS_FT_MAX]; + /* statistics the number of files based on file name extensions */ + unsigned int file_type_stat[OTHERFILETYPE]; + /* statistics the number of files based on file orignial size */ + unsigned int file_original_size[FILE_MAX_SIZE_BITS + 1]; + /* statistics the number of files based on the compressed + * size of file + */ + unsigned int file_comp_size[FILE_MAX_SIZE_BITS + 1]; +}; +static struct statistics stats; + +static struct option long_options[] = { + {"help", no_argument, 0, 1}, + {0, 0, 0, 0}, +}; + +struct feature { + bool compat; + __le32 flag; + const char *name; +}; + +static struct feature feature_lists[] = { + { true, EROFS_FEATURE_COMPAT_SB_CHKSUM, "sb_csum" }, + { false, EROFS_FEATURE_INCOMPAT_LZ4_0PADDING, "0padding" }, + { false, EROFS_FEATURE_INCOMPAT_BIG_PCLUSTER, "bigpcluster" }, +}; + +static void usage(void) +{ + fputs("usage: [options] IMAGE\n\n" + "Dump erofs layout from IMAGE, and [options] are:\n" + "-s print information about superblock\n" + "-i # print target # inode info\n" + "-I # print target # inode on-disk info\n" + "-S print statistic information of the erofs-image\n" + "-V print the version number of dump.erofs and exit.\n" + "--help display this help and exit.\n", + stderr); +} +static void dumpfs_print_version(void) +{ + fprintf(stderr, "dump.erofs %s\n", cfg.c_version); +} + +static int dumpfs_parse_options_cfg(int argc, char **argv) +{ + int opt; + u64 i; + + while ((opt = getopt_long(argc, argv, "i:I:sSV", + long_options, NULL)) != -1) { + switch (opt) { + case 'i': + i = atoll(optarg); + dumpcfg.print_inode = true; + dumpcfg.ino = i; + break; + case 'I': + i = atoll(optarg); + dumpcfg.print_inode_phy = true; + dumpcfg.ino_phy = i; + break; + case 's': + dumpcfg.print_superblock = true; + break; + case 'S': + dumpcfg.print_statistic = true; + break; + case 'V': + dumpfs_print_version(); + exit(0); + case 1: + usage(); + exit(0); + default: + return -EINVAL; + } + } + + if (optind >= argc) + return -EINVAL; + + cfg.c_img_path = strdup(argv[optind++]); + if (!cfg.c_img_path) + return -ENOMEM; + + if (optind < argc) { + erofs_err("unexpected argument: %s\n", argv[optind]); + return -EINVAL; + } + return 0; +} + +static int get_file_compressed_size(struct erofs_inode *inode, + erofs_off_t *size) +{ + *size = 0; + switch (inode->datalayout) { + case EROFS_INODE_FLAT_INLINE: + case EROFS_INODE_FLAT_PLAIN: + stats.uncompressed_files++; + *size = inode->i_size; + break; + case EROFS_INODE_FLAT_COMPRESSION_LEGACY: + case EROFS_INODE_FLAT_COMPRESSION: + stats.compressed_files++; + *size = inode->u.i_blocks * EROFS_BLKSIZ; + break; + default: + erofs_err("unknown datalayout"); + return -1; + } + return 0; +} + +static int get_path_by_nid(erofs_nid_t nid, erofs_nid_t parent_nid, + erofs_nid_t target, char *path, unsigned int pos) +{ + int err; + struct erofs_inode inode = {.nid = nid}; + erofs_off_t offset; + char buf[EROFS_BLKSIZ]; + + path[pos++] = '/'; + if (target == sbi.root_nid) + return 0; + + err = erofs_read_inode_from_disk(&inode); + if (err) { + erofs_err("read inode %lu failed", nid); + return err; + } + + offset = 0; + while (offset < inode.i_size) { + erofs_off_t maxsize = min_t(erofs_off_t, + inode.i_size - offset, EROFS_BLKSIZ); + struct erofs_dirent *de = (void *)buf; + struct erofs_dirent *end; + unsigned int nameoff; + + err = erofs_pread(&inode, buf, maxsize, offset); + if (err) + return err; + + nameoff = le16_to_cpu(de->nameoff); + if (nameoff < sizeof(struct erofs_dirent) || + nameoff >= PAGE_SIZE) { + erofs_err("invalid de[0].nameoff %u @ nid %llu", + nameoff, nid | 0ULL); + return -EFSCORRUPTED; + } + + end = (void *)buf + nameoff; + while (de < end) { + const char *dname; + unsigned int dname_len; + + nameoff = le16_to_cpu(de->nameoff); + dname = (char *)buf + nameoff; + if (de + 1 >= end) + dname_len = strnlen(dname, maxsize - nameoff); + else + dname_len = le16_to_cpu(de[1].nameoff) + - nameoff; + + /* a corrupted entry is found */ + if (nameoff + dname_len > maxsize || + dname_len > EROFS_NAME_LEN) { + erofs_err("bogus dirent @ nid %llu", + le64_to_cpu(de->nid) | 0ULL); + DBG_BUGON(1); + return -EFSCORRUPTED; + } + + if (de->nid == target) { + memcpy(path + pos, dname, dname_len); + return 0; + } + + if (de->file_type == EROFS_FT_DIR && + de->nid != parent_nid && + de->nid != nid) { + memcpy(path + pos, dname, dname_len); + err = get_path_by_nid(de->nid, nid, + target, path, pos + dname_len); + if (!err) + return 0; + memset(path + pos, 0, dname_len); + } + ++de; + } + offset += maxsize; + } + return -1; +} + +static void dumpfs_print_inode_phy(void) +{ + int err; + erofs_nid_t nid = dumpcfg.ino_phy; + struct erofs_inode inode = {.nid = nid}; + char path[PATH_MAX + 1] = {0}; + + err = erofs_read_inode_from_disk(&inode); + if (err < 0) { + erofs_err("read inode %lu from disk failed", nid); + return; + } + + const erofs_off_t ibase = iloc(inode.nid); + const erofs_off_t pos = Z_EROFS_VLE_LEGACY_INDEX_ALIGN( + ibase + inode.inode_isize + inode.xattr_isize); + erofs_blk_t blocks = inode.u.i_blocks; + erofs_blk_t start = 0; + erofs_blk_t end = 0; + unsigned int extent_count; + struct erofs_map_blocks map = { + .index = UINT_MAX, + .m_la = 0, + }; + + fprintf(stdout, "Inode %lu on-disk info:\n", nid); + err = get_path_by_nid(sbi.root_nid, sbi.root_nid, nid, path, 0); + if (!err) + fprintf(stderr, "File path: %s\n", path); + else + erofs_err("path not found"); + fprintf(stdout, "File size: %lu\n", inode.i_size); + + switch (inode.datalayout) { + case EROFS_INODE_FLAT_INLINE: + case EROFS_INODE_FLAT_PLAIN: + if (inode.u.i_blkaddr == NULL_ADDR) + start = end = erofs_blknr(pos); + else { + start = inode.u.i_blkaddr; + end = start + BLK_ROUND_UP(inode.i_size) - 1; + } + fprintf(stdout, "Plain blknr: %u - %u\n", start, end); + break; + + case EROFS_INODE_FLAT_COMPRESSION_LEGACY: + case EROFS_INODE_FLAT_COMPRESSION: + err = z_erofs_map_blocks_iter(&inode, &map, 0); + if (err) + erofs_err("get file blocks range failed"); + + start = erofs_blknr(map.m_pa); + end = start - 1 + blocks; + fprintf(stdout, + "Compressed blknr: %u - %u\n", start, end); + extent_count = 0; + map.m_la = 0; + while (map.m_la < inode.i_size) { + err = z_erofs_map_blocks_iter(&inode, &map, + EROFS_GET_BLOCKS_FIEMAP); + fprintf(stdout, "Extent %u:\n", extent_count++); + fprintf(stdout, "on-disk blkaddr/length: 0x%08lx/0x%04lx\n", + map.m_pa, map.m_plen); + fprintf(stdout, "logical offset/length: 0x%08lx/0x%04lx\n", + map.m_la, map.m_llen); + map.m_la += map.m_llen; + } + + break; + } +} + +static void dumpfs_print_inode(void) +{ + int err; + erofs_off_t size; + u16 access_mode; + time_t t; + erofs_nid_t nid = dumpcfg.ino; + struct erofs_inode inode = {.nid = nid}; + char path[PATH_MAX + 1] = {0}; + char access_mode_str[] = "rwxrwxrwx"; + + err = erofs_read_inode_from_disk(&inode); + if (err) { + erofs_err("read inode %lu from disk failed", nid); + return; + } + + err = get_file_compressed_size(&inode, &size); + if (err) { + erofs_err("get file size failed\n"); + return; + } + + fprintf(stdout, "Inode %lu on-disk info:\n", dumpcfg.ino); + err = get_path_by_nid(sbi.root_nid, sbi.root_nid, nid, path, 0); + + fprintf(stdout, "File path: %s\n", + !err ? path : "path not found"); + fprintf(stdout, "File nid: %lu\n", inode.nid); + fprintf(stdout, "File inode core size: %d\n", inode.inode_isize); + fprintf(stdout, "File original size: %lu\n", inode.i_size); + fprintf(stdout, "File on-disk size: %lu\n", size); + fprintf(stdout, "File compress rate: %.2f%%\n", + (double)(100 * size) / (double)(inode.i_size)); + fprintf(stdout, "File extent size: %u\n", inode.extent_isize); + fprintf(stdout, "File xattr size: %u\n", inode.xattr_isize); + fprintf(stdout, "File type: "); + switch (inode.i_mode & S_IFMT) { + case S_IFBLK: fprintf(stdout, "block device\n"); break; + case S_IFCHR: fprintf(stdout, "character device\n"); break; + case S_IFDIR: fprintf(stdout, "directory\n"); break; + case S_IFIFO: fprintf(stdout, "FIFO/pipe\n"); break; + case S_IFLNK: fprintf(stdout, "symlink\n"); break; + case S_IFREG: fprintf(stdout, "regular file\n"); break; + case S_IFSOCK: fprintf(stdout, "socket\n"); break; + default: fprintf(stdout, "unknown?\n"); break; + } + + access_mode = inode.i_mode & 0777; + t = inode.i_ctime; + for (int i = 8; i >= 0; i--) + if (((access_mode >> i) & 1) == 0) + access_mode_str[8 - i] = '-'; + fprintf(stdout, "File access: %04o/%s\n", + access_mode, access_mode_str); + fprintf(stdout, "File uid: %u\n", inode.i_uid); + fprintf(stdout, "File gid: %u\n", inode.i_gid); + fprintf(stdout, "File datalayout: %d\n", inode.datalayout); + fprintf(stdout, "File nlink: %u\n", inode.i_nlink); + fprintf(stdout, "File create time: %s", ctime(&t)); + fprintf(stdout, "File access time: %s", ctime(&t)); + fprintf(stdout, "File modify time: %s", ctime(&t)); +} + +static int get_file_type(const char *filename) +{ + char *postfix = strrchr(filename, '.'); + int type = 0; + + if (postfix == NULL) + return OTHERFILETYPE - 1; + while (type < OTHERFILETYPE - 1) { + if (strcmp(postfix, file_types[type]) == 0) + break; + type++; + } + return type; +} + +static void update_file_size_statatics(erofs_off_t occupied_size, + erofs_off_t original_size) +{ + int occupied_size_mark; + int original_size_mark; + + original_size_mark = 0; + occupied_size_mark = 0; + occupied_size >>= 10; + original_size >>= 10; + + while (occupied_size || original_size) { + if (occupied_size) { + occupied_size >>= 1; + occupied_size_mark++; + } + if (original_size) { + original_size >>= 1; + original_size_mark++; + } + } + + if (original_size_mark >= FILE_MAX_SIZE_BITS) + stats.file_original_size[FILE_MAX_SIZE_BITS]++; + else + stats.file_original_size[original_size_mark]++; + + if (occupied_size_mark >= FILE_MAX_SIZE_BITS) + stats.file_comp_size[FILE_MAX_SIZE_BITS]++; + else + stats.file_comp_size[occupied_size_mark]++; +} + +static int erofs_read_dir(erofs_nid_t nid, erofs_nid_t parent_nid) +{ + struct erofs_inode vi = { .nid = nid}; + int err; + char buf[EROFS_BLKSIZ]; + erofs_off_t offset; + + err = erofs_read_inode_from_disk(&vi); + if (err) + return err; + + offset = 0; + while (offset < vi.i_size) { + erofs_off_t maxsize = min_t(erofs_off_t, + vi.i_size - offset, EROFS_BLKSIZ); + struct erofs_dirent *de = (void *)buf; + struct erofs_dirent *end; + unsigned int nameoff; + + err = erofs_pread(&vi, buf, maxsize, offset); + if (err) + return err; + + nameoff = le16_to_cpu(de->nameoff); + + if (nameoff < sizeof(struct erofs_dirent) || + nameoff >= PAGE_SIZE) { + erofs_err("invalid de[0].nameoff %u @ nid %llu", + nameoff, nid | 0ULL); + return -EFSCORRUPTED; + } + end = (void *)buf + nameoff; + while (de < end) { + const char *dname; + unsigned int dname_len; + struct erofs_inode inode = { .nid = de->nid }; + erofs_off_t occupied_size = 0; + /* skip "." and ".." dentry */ + if (de->nid == nid || de->nid == parent_nid) { + de++; + continue; + } + + nameoff = le16_to_cpu(de->nameoff); + dname = (char *)buf + nameoff; + + if (de + 1 >= end) + dname_len = strnlen(dname, maxsize - nameoff); + else + dname_len = le16_to_cpu(de[1].nameoff) - nameoff; + + /* a corrupted entry is found */ + if (nameoff + dname_len > maxsize || + dname_len > EROFS_NAME_LEN) { + erofs_err("bogus dirent @ nid %llu", + le64_to_cpu(de->nid) | 0ULL); + DBG_BUGON(1); + return -EFSCORRUPTED; + } + + if (de->file_type >= EROFS_FT_MAX) { + erofs_err("invalid file type %llu", de->nid); + de++; + continue; + } + if (de->file_type != EROFS_FT_DIR) + stats.file_category_stat[de->file_type]++; + + err = erofs_read_inode_from_disk(&inode); + if (err) { + erofs_err("read file inode from disk failed!"); + return err; + } + + stats.files++; + err = get_file_compressed_size(&inode, &occupied_size); + if (err) { + erofs_err("get file size failed\n"); + return err; + } + + switch (de->file_type) { + case EROFS_FT_REG_FILE: + stats.files_total_origin_size += inode.i_size; + stats.file_type_stat[get_file_type(dname)]++; + stats.files_total_size += occupied_size; + update_file_size_statatics(occupied_size, inode.i_size); + break; + case EROFS_FT_DIR: + if (de->nid != nid && de->nid != parent_nid) { + err = erofs_read_dir(de->nid, nid); + if (err) { + fprintf(stdout, + "parse dir nid %llu error occurred\n", + de->nid); + return err; + } + stats.file_category_stat[EROFS_FT_DIR]++; + } + break; + case EROFS_FT_UNKNOWN: + case EROFS_FT_CHRDEV: + case EROFS_FT_BLKDEV: + case EROFS_FT_FIFO: + case EROFS_FT_SOCK: + case EROFS_FT_SYMLINK: + break; + default: + erofs_err("%d file type not exists", de->file_type); + } + ++de; + } + offset += maxsize; + } + return 0; +} + +static void dumpfs_print_statistic_of_filetype(void) +{ + fprintf(stdout, "Filesystem total file count: %lu\n", + stats.files); + for (int i = 0; i < EROFS_FT_MAX; i++) + fprintf(stdout, "Filesystem %s count: %lu\n", + file_category_types[i], stats.file_category_stat[i]); +} + +static void dumpfs_print_chart_row(char *col1, unsigned int col2, + double col3, char *col4) +{ + char row[500] = {0}; + + sprintf(row, chart_format, col1, col2, col3, col4); + fprintf(stdout, row); +} + +static void dumpfs_print_chart_of_file(unsigned int *file_counts, + unsigned int len) +{ + char col1[30]; + unsigned int col2; + double col3; + char col4[400]; + unsigned int lowerbound = 0; + unsigned int upperbound = 1; + + fprintf(stdout, header_format, ">=(KB) .. <(KB) ", "count", + "ratio", "distribution"); + for (int i = 0; i < len; i++) { + memset(col1, 0, sizeof(col1)); + memset(col4, 0, sizeof(col4)); + if (i == len - 1) + sprintf(col1, "%6d ..", lowerbound); + else if (i <= 6) + sprintf(col1, "%6d .. %-6d", lowerbound, upperbound); + else + + sprintf(col1, "%6d .. %-6d", lowerbound, upperbound); + col2 = file_counts[i]; + col3 = (double)(100 * col2) / (double)stats.file_category_stat[EROFS_FT_REG_FILE]; + memset(col4, '#', col3 / 2); + dumpfs_print_chart_row(col1, col2, col3, col4); + lowerbound = upperbound; + upperbound <<= 1; + } +} + +static void dumpfs_print_chart_of_file_type(char **file_types, unsigned int len) +{ + char col1[30]; + unsigned int col2; + double col3; + char col4[401]; + + fprintf(stdout, header_format, "type", "count", "ratio", + "distribution"); + for (int i = 0; i < len; i++) { + memset(col1, 0, sizeof(col1)); + memset(col4, 0, sizeof(col4)); + sprintf(col1, "%-17s", file_types[i]); + col2 = stats.file_type_stat[i]; + col3 = (double)(100 * col2) / (double)stats.file_category_stat[EROFS_FT_REG_FILE]; + memset(col4, '#', col3 / 2); + dumpfs_print_chart_row(col1, col2, col3, col4); + } +} + +static void dumpfs_print_statistic_of_compression(void) +{ + stats.compress_rate = (double)(100 * stats.files_total_size) / + (double)(stats.files_total_origin_size); + fprintf(stdout, "Filesystem compressed files: %lu\n", + stats.compressed_files); + fprintf(stdout, "Filesystem uncompressed files: %lu\n", + stats.uncompressed_files); + fprintf(stdout, "Filesystem total original file size: %lu Bytes\n", + stats.files_total_origin_size); + fprintf(stdout, "Filesystem total file size: %lu Bytes\n", + stats.files_total_size); + fprintf(stdout, "Filesystem compress rate: %.2f%%\n", + stats.compress_rate); +} + +static void dumpfs_print_statistic(void) +{ + int err; + + err = erofs_read_dir(sbi.root_nid, sbi.root_nid); + if (err) { + erofs_err("read dir failed"); + return; + } + + dumpfs_print_statistic_of_filetype(); + dumpfs_print_statistic_of_compression(); + + fprintf(stdout, "\nOriginal file size distribution:\n"); + dumpfs_print_chart_of_file(stats.file_original_size, + ARRAY_SIZE(stats.file_original_size)); + fprintf(stdout, "\nOn-Disk file size distribution:\n"); + dumpfs_print_chart_of_file(stats.file_comp_size, + ARRAY_SIZE(stats.file_comp_size)); + fprintf(stdout, "\nFile type distribution:\n"); + dumpfs_print_chart_of_file_type(file_types, OTHERFILETYPE); +} + +static void dumpfs_print_superblock(void) +{ + time_t time = sbi.build_time; + char uuid_str[37] = "not available"; + int i = 0; + + fprintf(stdout, "Filesystem magic number: 0x%04X\n", + EROFS_SUPER_MAGIC_V1); + fprintf(stdout, "Filesystem blocks: %lu\n", + sbi.blocks); + fprintf(stdout, "Filesystem inode metadata start block: %u\n", + sbi.meta_blkaddr); + fprintf(stdout, "Filesystem shared xattr metadata start block: %u\n", + sbi.xattr_blkaddr); + fprintf(stdout, "Filesystem root nid: %ld\n", + sbi.root_nid); + fprintf(stdout, "Filesystem inode count: %lu\n", + sbi.inos); + fprintf(stdout, "Filesystem created: %s", + ctime(&time)); + fprintf(stdout, "Filesystem features: "); + for (; i < ARRAY_SIZE(feature_lists); i++) { + __le32 feature = feature_lists[i].compat ? + sbi.feature_compat : sbi.feature_incompat; + if (feature & feature_lists[i].flag) + fprintf(stdout, "%s ", feature_lists[i].name); + } +#ifdef HAVE_LIBUUID + uuid_unparse_lower(sbi.uuid, uuid_str); +#endif + fprintf(stdout, "\nFilesystem UUID: %s\n", + uuid_str); +} + +int main(int argc, char **argv) +{ + int err = 0; + + erofs_init_configure(); + err = dumpfs_parse_options_cfg(argc, argv); + if (err) { + if (err == -EINVAL) + usage(); + goto exit; + } + + err = dev_open_ro(cfg.c_img_path); + if (err) { + erofs_err("open image file failed"); + goto exit; + } + + err = erofs_read_superblock(); + if (err) { + erofs_err("read superblock failed"); + goto exit; + } + + if (dumpcfg.print_superblock) + dumpfs_print_superblock(); + + if (dumpcfg.print_statistic) + dumpfs_print_statistic(); + + if (dumpcfg.print_inode) + dumpfs_print_inode(); + + if (dumpcfg.print_inode_phy) + dumpfs_print_inode_phy(); + +exit: + erofs_exit_configure(); + return err; +} diff --git a/lib/inode.c b/lib/inode.c index 10dcc11..f4aa94f 100644 --- a/lib/inode.c +++ b/lib/inode.c @@ -1214,4 +1214,4 @@ struct erofs_inode *erofs_dumpfs_build_tree_from_path(struct erofs_inode *parent struct erofs_inode *inode = erofs_iget_from_img_path(path); -} \ No newline at end of file +} -- GitLab