pax_global_header00006660000000000000000000000064132341714360014516gustar00rootroot0000000000000052 comment=254cdad2ee035883e68053c066d6aac1cce500f9 f2fs-tools-1.10.0/000077500000000000000000000000001323417143600135735ustar00rootroot00000000000000f2fs-tools-1.10.0/.gitignore000066400000000000000000000007411323417143600155650ustar00rootroot00000000000000.* *~ *.[ao] *.orig *.tar.* *.s *.lo *.la *.so *.so.dbg *.mod.c *.lst *.orig *.rej CVS !.gitignore tags TAGS GPATH GRTAGS GSYMS GTAGS # # Generated files # compile install-sh missing depcomp INSTALL aclocal.m4 autoconf-[0-9].* autom4te.cache build-aux Makefile Makefile.in config.* configure configure.scan ltmain.sh libtool stamp-h stamp-h1 /mkfs/mkfs.f2fs /fsck/fsck.f2fs /tools/f2fstat /tools/fibmap.f2fs /tools/parse.f2fs /tools/f2fscrypt # cscope files cscope.* ncscope.* f2fs-tools-1.10.0/AUTHORS000066400000000000000000000001211323417143600146350ustar00rootroot00000000000000This package has been developed by Praesto Team at Samsung Electronics Co., Ltd. f2fs-tools-1.10.0/COPYING000066400000000000000000001305111323417143600146270ustar00rootroot00000000000000The tools for F2FS are covered by GNU Public License version 2. Exceptionally, the following files are also covered by the GNU Lesser General Public License Version 2.1 as the dual licenses. - include/f2fs_fs.h - lib/libf2fs.c - lib/libf2fs_io.c - mkfs/f2fs_format.c - mkfs/f2fs_format_main.c - mkfs/f2fs_format_utils.c - mkfs/f2fs_format_utils.h ================================================================================ Copyright (c) 2012 Samsung Electronics Co., Ltd. http://www.samsung.com/ 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. ================================================================================ GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 51 Franklin Street, 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 Street, 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. ================================================================================ GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 51 Franklin Street, 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. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, 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 and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, 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 library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete 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 distribute a copy of this License along with the Library. 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 Library or any portion of it, thus forming a work based on the Library, 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) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, 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 Library, 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 Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you 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. If distribution of 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 satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be 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. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library 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. 9. 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 Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library 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 with this License. 11. 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 Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library 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 Library. 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. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library 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. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser 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 Library 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 Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, 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 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "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 LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. 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 LIBRARY 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 LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), 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 Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. 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 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.1 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 Street, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. , 1 April 1990 Ty Coon, President of Vice That's all there is to it! f2fs-tools-1.10.0/ChangeLog000066400000000000000000000002351323417143600153450ustar00rootroot00000000000000f2fs-tools-1.0.0 Tue Aug 14, 2012 KST * The first release of f2fs-tools. f2fs-tools-1.0.1 Tue Jun 04, 2013 KST * Added fsck and dump for f2fs-tools. f2fs-tools-1.10.0/Makefile.am000066400000000000000000000001131323417143600156220ustar00rootroot00000000000000## Makefile.am ACLOCAL_AMFLAGS = -I m4 SUBDIRS = man lib mkfs fsck tools f2fs-tools-1.10.0/README000066400000000000000000000017471323417143600144640ustar00rootroot00000000000000F2FS format utilility --------------------- To use f2fs filesystem, you should format the storage partition with this utilility. Otherwise, you cannot mount f2fs. Before compilation ------------------ You should install the following packages. - libuuid-devel or uuid-dev - pkg-config - autoconf - libtool - libselinux1-dev Initial compilation ------------------- Before compilation initially, autoconf/automake tools should be run. # ./autogen.sh How to compile -------------- # ./configure # make # make install How to cross-compile (e.g., for ARM) ------------------------------------ 1. Add the below line into mkfs/Makefile.am: mkfs_f2fs_LDFLAGS = -all-static 2. Add the below line into fsck/Makefile.am: fsck_f2fs_LDFLAGS = -all-static 3. then, do: # LDFLAGS=--static ./configure \ --host=arm-none-linux-gnueabi --target=arm-none-linux-gnueabi # make How to run by default --------------------- $ mkfs.f2fs -l [LABEL] $DEV For more mkfs options, see man page. f2fs-tools-1.10.0/VERSION000066400000000000000000000000221323417143600146350ustar00rootroot000000000000001.10.0 2018-01-30 f2fs-tools-1.10.0/autogen.sh000077500000000000000000000001251323417143600155720ustar00rootroot00000000000000#!/bin/sh aclocal && \ autoheader && \ autoconf && \ libtoolize && \ automake -a -c f2fs-tools-1.10.0/configure.ac000066400000000000000000000121271323417143600160640ustar00rootroot00000000000000# -*- Autoconf -*- # Process this file with autoconf to produce a configure script. AC_PREREQ([2.68]) # Get version from file VERSION m4_define([f2fs_tools_version], m4_esyscmd([sed -n '1p' VERSION | tr -d '\n'])) m4_define([f2fs_tools_date], m4_esyscmd([sed -n '2p' VERSION | tr -d '\n'])) m4_define([f2fs_tools_gitdate], m4_esyscmd([git log -1 --pretty=format:%ci 2> /dev/null])) AC_INIT([F2FS tools], [f2fs_tools_version], [linux-f2fs-devel@lists.sourceforge.net]) AC_DEFINE([F2FS_TOOLS_VERSION], "f2fs_tools_version", [f2fs-tools version]) AC_DEFINE([F2FS_MAJOR_VERSION], m4_bpatsubst(f2fs_tools_version, [\([0-9]*\)\(\w\|\W\)*], [\1]), [Major version for f2fs-tools]) AC_DEFINE([F2FS_MINOR_VERSION], m4_bpatsubst(f2fs_tools_version, [\([0-9]*\).\([0-9]*\)\(\w\|\W\)*], [\2]), [Minor version for f2fs-tools]) AC_CHECK_FILE(.git, AC_DEFINE([F2FS_TOOLS_DATE], "m4_bpatsubst(f2fs_tools_gitdate, [\([0-9-]*\)\(\w\|\W\)*], [\1])", [f2fs-tools date based on Git commits]), AC_DEFINE([F2FS_TOOLS_DATE], "f2fs_tools_date", [f2fs-tools date based on Source releases])) AC_CONFIG_SRCDIR([config.h.in]) AC_CONFIG_HEADER([config.h]) AC_CONFIG_MACRO_DIR([m4]) AC_CONFIG_AUX_DIR([build-aux]) AM_INIT_AUTOMAKE([foreign tar-pax dist-xz]) # Test configure options. AC_ARG_WITH([selinux], AS_HELP_STRING([--without-selinux], [Ignore presence of libselinux and disable selinux support])) AC_ARG_WITH([blkid], AS_HELP_STRING([--without-blkid], [Ignore presence of libblkid and disable blkid support])) # Checks for programs. AC_PROG_CC AC_PROG_LIBTOOL AC_PATH_PROG([LDCONFIG], [ldconfig], [AC_MSG_ERROR([ldconfig not found])], [$PATH:/sbin]) # Checks for libraries. PKG_CHECK_MODULES([libuuid], [uuid]) AS_IF([test "x$with_selinux" != "xno"], [PKG_CHECK_MODULES([libselinux], [libselinux], [have_selinux=yes], [have_selinux=no])], [have_selinux=no] ) AS_IF([test "x$have_selinux" = "xyes"], [AC_DEFINE([HAVE_LIBSELINUX], [1], [Use libselinux])], [AS_IF([test "x$with_selinux" = "xyes"], [AC_MSG_ERROR([selinux support requested but libselinux not found])] )] ) AS_IF([test "x$with_blkid" != "xno"], [PKG_CHECK_MODULES([libblkid], [blkid], [have_blkid=yes], [have_blkid=no])], [have_blkid=no] ) AS_IF([test "x$have_blkid" = "xyes"], [AC_DEFINE([HAVE_LIBBLKID], [1], [Use blkid])], [AS_IF([test "x$with_blkid" = "xyes"], [AC_MSG_ERROR([blkid support requested but libblkid not found])] )] ) # Checks for header files. AC_CHECK_HEADERS(m4_flatten([ attr/xattr.h byteswap.h fcntl.h linux/blkzoned.h linux/falloc.h linux/fs.h linux/hdreg.h linux/limits.h linux/posix_acl.h linux/types.h linux/xattr.h mntent.h scsi/sg.h stdlib.h string.h sys/acl.h sys/ioctl.h sys/syscall.h sys/mount.h sys/sysmacros.h sys/xattr.h unistd.h ])) # Checks for typedefs, structures, and compiler characteristics. AC_C_INLINE AC_TYPE_INT32_T AC_TYPE_INT8_T AC_TYPE_SIZE_T # Checks for library functions. AC_FUNC_GETMNTENT AC_CHECK_FUNCS_ONCE([ add_key fallocate fsetxattr fstat fstat64 getmntent keyctl llseek lseek64 memset setmntent ]) AS_IF([test "$ac_cv_header_byteswap_h" = "yes"], [AC_CHECK_DECLS([bswap_64],,,[#include ])]) dnl dnl Check to see if llseek() is declared in unistd.h. On some libc's dnl it is, and on others it isn't..... Thank you glibc developers.... dnl AC_CHECK_DECL(llseek,[AC_DEFINE(HAVE_LLSEEK_PROTOTYPE, 1, [Define to 1 if llseek declared in unistd.h])],, [#include ]) dnl dnl Check to see if lseek64() is declared in unistd.h. Glibc's header files dnl are so convoluted that I can't tell whether it will always be defined, dnl and if it isn't defined while lseek64 is defined in the library, dnl disaster will strike. dnl dnl Warning! Use of --enable-gcc-wall may throw off this test. dnl dnl AC_CHECK_DECL(lseek64,[AC_DEFINE(HAVE_LSEEK64_PROTOTYPE, 1, [Define to 1 if lseek64 declared in unistd.h])],, [#define _LARGEFILE_SOURCE #define _LARGEFILE64_SOURCE #include ]) dnl dnl Word sizes... dnl # AC_CANONICAL_HOST is needed to access the 'host_os' variable AC_CANONICAL_HOST build_linux=no build_windows=no build_mac=no # Detect the target system case "${host_os}" in linux*) build_linux=yes ;; cygwin*|mingw*) build_windows=yes ;; darwin*) build_mac=yes ;; *) AC_MSG_ERROR(["OS $host_os is not supported"]) ;; esac # Pass the conditionals to automake AM_CONDITIONAL([LINUX], [test "$build_linux" = "yes"]) AM_CONDITIONAL([WINDOWS], [test "$build_windows" = "yes"]) AM_CONDITIONAL([OSX], [test "$build_mac" = "yes"]) # Install directories #AC_PREFIX_DEFAULT([/usr]) #AC_SUBST([sbindir], [/sbin]) #AC_SUBST([sysconfdir], [/etc]) #AC_SUBST([localstatedir], [/var]) AC_CONFIG_FILES([ Makefile man/Makefile lib/Makefile mkfs/Makefile fsck/Makefile tools/Makefile ]) # export library version info for mkfs/libf2fs_format_la AC_SUBST(FMT_CURRENT, 3) AC_SUBST(FMT_REVISION, 0) AC_SUBST(FMT_AGE, 0) # export library version info for lib/libf2fs_la AC_SUBST(LIBF2FS_CURRENT, 4) AC_SUBST(LIBF2FS_REVISION, 0) AC_SUBST(LIBF2FS_AGE, 0) AC_OUTPUT f2fs-tools-1.10.0/fsck/000077500000000000000000000000001323417143600145215ustar00rootroot00000000000000f2fs-tools-1.10.0/fsck/Makefile.am000066400000000000000000000013611323417143600165560ustar00rootroot00000000000000## Makefile.am AM_CPPFLAGS = ${libuuid_CFLAGS} -I$(top_srcdir)/include AM_CFLAGS = -Wall sbin_PROGRAMS = fsck.f2fs noinst_HEADERS = common.h dict.h dqblk_v2.h f2fs.h fsck.h node.h quotaio.h quotaio_tree.h quotaio_v2.h xattr.h include_HEADERS = $(top_srcdir)/include/quota.h fsck_f2fs_SOURCES = main.c fsck.c dump.c mount.c defrag.c resize.c \ node.c segment.c dir.c sload.c xattr.c \ dict.c mkquota.c quotaio.c quotaio_tree.c quotaio_v2.c fsck_f2fs_LDADD = ${libselinux_LIBS} ${libuuid_LIBS} $(top_builddir)/lib/libf2fs.la install-data-hook: ln -sf fsck.f2fs $(DESTDIR)/$(sbindir)/dump.f2fs ln -sf fsck.f2fs $(DESTDIR)/$(sbindir)/defrag.f2fs ln -sf fsck.f2fs $(DESTDIR)/$(sbindir)/resize.f2fs ln -sf fsck.f2fs $(DESTDIR)/$(sbindir)/sload.f2fs f2fs-tools-1.10.0/fsck/common.h000066400000000000000000000012221323417143600161570ustar00rootroot00000000000000/** * * Various things common for all utilities * */ #ifndef __QUOTA_COMMON_H__ #define __QUOTA_COMMON_H__ #undef DEBUG_QUOTA #ifndef __attribute__ # if !defined __GNUC__ || __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 8) || __STRICT_ANSI__ # define __attribute__(x) # endif #endif #define log_err(format, arg ...) \ fprintf(stderr, "[ERROR] %s:%d:%s:: " format "\n", \ __FILE__, __LINE__, __func__, ## arg) #ifdef DEBUG_QUOTA # define log_debug(format, arg ...) \ fprintf(stderr, "[DEBUG] %s:%d:%s:: " format "\n", \ __FILE__, __LINE__, __func__, ## arg) #else # define log_debug(...) #endif #endif /* __QUOTA_COMMON_H__ */ f2fs-tools-1.10.0/fsck/defrag.c000066400000000000000000000045531323417143600161240ustar00rootroot00000000000000/** * defrag.c * * Copyright (c) 2015 Jaegeuk Kim * * 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. */ #include "fsck.h" static int migrate_block(struct f2fs_sb_info *sbi, u64 from, u64 to) { void *raw = calloc(BLOCK_SZ, 1); struct seg_entry *se; struct f2fs_summary sum; u64 offset; int ret, type; ASSERT(raw != NULL); /* read from */ ret = dev_read_block(raw, from); ASSERT(ret >= 0); /* write to */ ret = dev_write_block(raw, to); ASSERT(ret >= 0); /* update sit bitmap & valid_blocks && se->type */ se = get_seg_entry(sbi, GET_SEGNO(sbi, from)); offset = OFFSET_IN_SEG(sbi, from); type = se->type; se->valid_blocks--; f2fs_clear_bit(offset, (char *)se->cur_valid_map); se->dirty = 1; se = get_seg_entry(sbi, GET_SEGNO(sbi, to)); offset = OFFSET_IN_SEG(sbi, to); se->type = type; se->valid_blocks++; f2fs_set_bit(offset, (char *)se->cur_valid_map); se->dirty = 1; /* read/write SSA */ get_sum_entry(sbi, from, &sum); update_sum_entry(sbi, to, &sum); /* if data block, read node and update node block */ if (IS_DATASEG(type)) update_data_blkaddr(sbi, le32_to_cpu(sum.nid), le16_to_cpu(sum.ofs_in_node), to); else update_nat_blkaddr(sbi, 0, le32_to_cpu(sum.nid), to); DBG(0, "Migrate %s block %"PRIx64" -> %"PRIx64"\n", IS_DATASEG(type) ? "data" : "node", from, to); free(raw); return 0; } int f2fs_defragment(struct f2fs_sb_info *sbi, u64 from, u64 len, u64 to, int left) { struct seg_entry *se; u64 idx, offset; /* flush NAT/SIT journal entries */ flush_journal_entries(sbi); for (idx = from; idx < from + len; idx++) { u64 target = to; se = get_seg_entry(sbi, GET_SEGNO(sbi, idx)); offset = OFFSET_IN_SEG(sbi, idx); if (!f2fs_test_bit(offset, (const char *)se->cur_valid_map)) continue; if (find_next_free_block(sbi, &target, left, se->type)) { MSG(0, "Not enough space to migrate blocks"); return -1; } if (migrate_block(sbi, idx, target)) { ASSERT_MSG("Found inconsistency: please run FSCK"); return -1; } } /* update curseg info; can update sit->types */ move_curseg_info(sbi, to); zero_journal_entries(sbi); write_curseg_info(sbi); /* flush dirty sit entries */ flush_sit_entries(sbi); write_checkpoint(sbi); return 0; } f2fs-tools-1.10.0/fsck/dict.c000066400000000000000000001052701323417143600156150ustar00rootroot00000000000000/* * Dictionary Abstract Data Type * Copyright (C) 1997 Kaz Kylheku * * Free Software License: * * All rights are reserved by the author, with the following exceptions: * Permission is granted to freely reproduce and distribute this software, * possibly in exchange for a fee, provided that this copyright notice appears * intact. Permission is also granted to adapt this software to produce * derivative works, as long as the modified versions carry this copyright * notice and additional notices stating that the work has been modified. * This source code may be translated into executable form and incorporated * into proprietary software; there is no requirement for such software to * contain a copyright notice related to this source. * * $Id: dict.c,v 1.40.2.7 2000/11/13 01:36:44 kaz Exp $ * $Name: kazlib_1_20 $ */ #define DICT_NODEBUG #include "config.h" #include #include #ifdef DICT_NODEBUG #define dict_assert(x) #else #include #define dict_assert(x) assert(x) #endif #define DICT_IMPLEMENTATION #include "dict.h" #include #ifdef KAZLIB_RCSID static const char rcsid[] = "$Id: dict.c,v 1.40.2.7 2000/11/13 01:36:44 kaz Exp $"; #endif /* * These macros provide short convenient names for structure members, * which are embellished with dict_ prefixes so that they are * properly confined to the documented namespace. It's legal for a * program which uses dict to define, for instance, a macro called ``parent''. * Such a macro would interfere with the dnode_t struct definition. * In general, highly portable and reusable C modules which expose their * structures need to confine structure member names to well-defined spaces. * The resulting identifiers aren't necessarily convenient to use, nor * readable, in the implementation, however! */ #define left dict_left #define right dict_right #define parent dict_parent #define color dict_color #define key dict_key #define data dict_data #define nilnode dict_nilnode #define nodecount dict_nodecount #define maxcount dict_maxcount #define compare dict_compare #define allocnode dict_allocnode #define freenode dict_freenode #define context dict_context #define dupes dict_dupes #define dictptr dict_dictptr #define dict_root(D) ((D)->nilnode.left) #define dict_nil(D) (&(D)->nilnode) #define DICT_DEPTH_MAX 64 static dnode_t *dnode_alloc(void *context); static void dnode_free(dnode_t *node, void *context); /* * Perform a ``left rotation'' adjustment on the tree. The given node P and * its right child C are rearranged so that the P instead becomes the left * child of C. The left subtree of C is inherited as the new right subtree * for P. The ordering of the keys within the tree is thus preserved. */ static void rotate_left(dnode_t *upper) { dnode_t *lower, *lowleft, *upparent; lower = upper->right; upper->right = lowleft = lower->left; lowleft->parent = upper; lower->parent = upparent = upper->parent; /* don't need to check for root node here because root->parent is the sentinel nil node, and root->parent->left points back to root */ if (upper == upparent->left) { upparent->left = lower; } else { dict_assert(upper == upparent->right); upparent->right = lower; } lower->left = upper; upper->parent = lower; } /* * This operation is the ``mirror'' image of rotate_left. It is * the same procedure, but with left and right interchanged. */ static void rotate_right(dnode_t *upper) { dnode_t *lower, *lowright, *upparent; lower = upper->left; upper->left = lowright = lower->right; lowright->parent = upper; lower->parent = upparent = upper->parent; if (upper == upparent->right) { upparent->right = lower; } else { dict_assert(upper == upparent->left); upparent->left = lower; } lower->right = upper; upper->parent = lower; } /* * Do a postorder traversal of the tree rooted at the specified * node and free everything under it. Used by dict_free(). */ static void free_nodes(dict_t *dict, dnode_t *node, dnode_t *nil) { if (node == nil) return; free_nodes(dict, node->left, nil); free_nodes(dict, node->right, nil); dict->freenode(node, dict->context); } /* * This procedure performs a verification that the given subtree is a binary * search tree. It performs an inorder traversal of the tree using the * dict_next() successor function, verifying that the key of each node is * strictly lower than that of its successor, if duplicates are not allowed, * or lower or equal if duplicates are allowed. This function is used for * debugging purposes. */ #ifndef DICT_NODEBUG static int verify_bintree(dict_t *dict) { dnode_t *first, *next; first = dict_first(dict); if (dict->dupes) { while (first && (next = dict_next(dict, first))) { if (dict->compare(first->key, next->key) > 0) return 0; first = next; } } else { while (first && (next = dict_next(dict, first))) { if (dict->compare(first->key, next->key) >= 0) return 0; first = next; } } return 1; } /* * This function recursively verifies that the given binary subtree satisfies * three of the red black properties. It checks that every red node has only * black children. It makes sure that each node is either red or black. And it * checks that every path has the same count of black nodes from root to leaf. * It returns the blackheight of the given subtree; this allows blackheights to * be computed recursively and compared for left and right siblings for * mismatches. It does not check for every nil node being black, because there * is only one sentinel nil node. The return value of this function is the * black height of the subtree rooted at the node ``root'', or zero if the * subtree is not red-black. */ static unsigned int verify_redblack(dnode_t *nil, dnode_t *root) { unsigned height_left, height_right; if (root != nil) { height_left = verify_redblack(nil, root->left); height_right = verify_redblack(nil, root->right); if (height_left == 0 || height_right == 0) return 0; if (height_left != height_right) return 0; if (root->color == dnode_red) { if (root->left->color != dnode_black) return 0; if (root->right->color != dnode_black) return 0; return height_left; } if (root->color != dnode_black) return 0; return height_left + 1; } return 1; } /* * Compute the actual count of nodes by traversing the tree and * return it. This could be compared against the stored count to * detect a mismatch. */ static dictcount_t verify_node_count(dnode_t *nil, dnode_t *root) { if (root == nil) return 0; else return 1 + verify_node_count(nil, root->left) + verify_node_count(nil, root->right); } #endif /* * Verify that the tree contains the given node. This is done by * traversing all of the nodes and comparing their pointers to the * given pointer. Returns 1 if the node is found, otherwise * returns zero. It is intended for debugging purposes. */ static int verify_dict_has_node(dnode_t *nil, dnode_t *root, dnode_t *node) { if (root != nil) { return root == node || verify_dict_has_node(nil, root->left, node) || verify_dict_has_node(nil, root->right, node); } return 0; } #ifdef FSCK_NOTUSED /* * Dynamically allocate and initialize a dictionary object. */ dict_t *dict_create(dictcount_t maxcount, dict_comp_t comp) { dict_t *new = malloc(sizeof *new); if (new) { new->compare = comp; new->allocnode = dnode_alloc; new->freenode = dnode_free; new->context = NULL; new->nodecount = 0; new->maxcount = maxcount; new->nilnode.left = &new->nilnode; new->nilnode.right = &new->nilnode; new->nilnode.parent = &new->nilnode; new->nilnode.color = dnode_black; new->dupes = 0; } return new; } #endif /* FSCK_NOTUSED */ /* * Select a different set of node allocator routines. */ void dict_set_allocator(dict_t *dict, dnode_alloc_t al, dnode_free_t fr, void *context) { dict_assert(dict_count(dict) == 0); dict_assert((al == NULL && fr == NULL) || (al != NULL && fr != NULL)); dict->allocnode = al ? al : dnode_alloc; dict->freenode = fr ? fr : dnode_free; dict->context = context; } #ifdef FSCK_NOTUSED /* * Free a dynamically allocated dictionary object. Removing the nodes * from the tree before deleting it is required. */ void dict_destroy(dict_t *dict) { dict_assert(dict_isempty(dict)); free(dict); } #endif /* * Free all the nodes in the dictionary by using the dictionary's * installed free routine. The dictionary is emptied. */ void dict_free_nodes(dict_t *dict) { dnode_t *nil = dict_nil(dict), *root = dict_root(dict); free_nodes(dict, root, nil); dict->nodecount = 0; dict->nilnode.left = &dict->nilnode; dict->nilnode.right = &dict->nilnode; } #ifdef FSCK_NOTUSED /* * Obsolescent function, equivalent to dict_free_nodes */ void dict_free(dict_t *dict) { #ifdef KAZLIB_OBSOLESCENT_DEBUG dict_assert("call to obsolescent function dict_free()" && 0); #endif dict_free_nodes(dict); } #endif /* * Initialize a user-supplied dictionary object. */ dict_t *dict_init(dict_t *dict, dictcount_t maxcount, dict_comp_t comp) { dict->compare = comp; dict->allocnode = dnode_alloc; dict->freenode = dnode_free; dict->context = NULL; dict->nodecount = 0; dict->maxcount = maxcount; dict->nilnode.left = &dict->nilnode; dict->nilnode.right = &dict->nilnode; dict->nilnode.parent = &dict->nilnode; dict->nilnode.color = dnode_black; dict->dupes = 0; return dict; } #ifdef FSCK_NOTUSED /* * Initialize a dictionary in the likeness of another dictionary */ void dict_init_like(dict_t *dict, const dict_t *template) { dict->compare = template->compare; dict->allocnode = template->allocnode; dict->freenode = template->freenode; dict->context = template->context; dict->nodecount = 0; dict->maxcount = template->maxcount; dict->nilnode.left = &dict->nilnode; dict->nilnode.right = &dict->nilnode; dict->nilnode.parent = &dict->nilnode; dict->nilnode.color = dnode_black; dict->dupes = template->dupes; dict_assert(dict_similar(dict, template)); } /* * Remove all nodes from the dictionary (without freeing them in any way). */ static void dict_clear(dict_t *dict) { dict->nodecount = 0; dict->nilnode.left = &dict->nilnode; dict->nilnode.right = &dict->nilnode; dict->nilnode.parent = &dict->nilnode; dict_assert(dict->nilnode.color == dnode_black); } #endif /* FSCK_NOTUSED */ /* * Verify the integrity of the dictionary structure. This is provided for * debugging purposes, and should be placed in assert statements. Just because * this function succeeds doesn't mean that the tree is not corrupt. Certain * corruptions in the tree may simply cause undefined behavior. */ #ifndef DICT_NODEBUG int dict_verify(dict_t *dict) { dnode_t *nil = dict_nil(dict), *root = dict_root(dict); /* check that the sentinel node and root node are black */ if (root->color != dnode_black) return 0; if (nil->color != dnode_black) return 0; if (nil->right != nil) return 0; /* nil->left is the root node; check that its parent pointer is nil */ if (nil->left->parent != nil) return 0; /* perform a weak test that the tree is a binary search tree */ if (!verify_bintree(dict)) return 0; /* verify that the tree is a red-black tree */ if (!verify_redblack(nil, root)) return 0; if (verify_node_count(nil, root) != dict_count(dict)) return 0; return 1; } #endif /* DICT_NODEBUG */ #ifdef FSCK_NOTUSED /* * Determine whether two dictionaries are similar: have the same comparison and * allocator functions, and same status as to whether duplicates are allowed. */ int dict_similar(const dict_t *left, const dict_t *right) { if (left->compare != right->compare) return 0; if (left->allocnode != right->allocnode) return 0; if (left->freenode != right->freenode) return 0; if (left->context != right->context) return 0; if (left->dupes != right->dupes) return 0; return 1; } #endif /* FSCK_NOTUSED */ /* * Locate a node in the dictionary having the given key. * If the node is not found, a null a pointer is returned (rather than * a pointer that dictionary's nil sentinel node), otherwise a pointer to the * located node is returned. */ dnode_t *dict_lookup(dict_t *dict, const void *key) { dnode_t *root = dict_root(dict); dnode_t *nil = dict_nil(dict); dnode_t *saved; int result; /* simple binary search adapted for trees that contain duplicate keys */ while (root != nil) { result = dict->compare(key, root->key); if (result < 0) root = root->left; else if (result > 0) root = root->right; else { if (!dict->dupes) { /* no duplicates, return match */ return root; } else { /* could be dupes, find leftmost one */ do { saved = root; root = root->left; while (root != nil && dict->compare(key, root->key)) root = root->right; } while (root != nil); return saved; } } } return NULL; } #ifdef FSCK_NOTUSED /* * Look for the node corresponding to the lowest key that is equal to or * greater than the given key. If there is no such node, return null. */ dnode_t *dict_lower_bound(dict_t *dict, const void *key) { dnode_t *root = dict_root(dict); dnode_t *nil = dict_nil(dict); dnode_t *tentative = 0; while (root != nil) { int result = dict->compare(key, root->key); if (result > 0) { root = root->right; } else if (result < 0) { tentative = root; root = root->left; } else { if (!dict->dupes) { return root; } else { tentative = root; root = root->left; } } } return tentative; } /* * Look for the node corresponding to the greatest key that is equal to or * lower than the given key. If there is no such node, return null. */ dnode_t *dict_upper_bound(dict_t *dict, const void *key) { dnode_t *root = dict_root(dict); dnode_t *nil = dict_nil(dict); dnode_t *tentative = 0; while (root != nil) { int result = dict->compare(key, root->key); if (result < 0) { root = root->left; } else if (result > 0) { tentative = root; root = root->right; } else { if (!dict->dupes) { return root; } else { tentative = root; root = root->right; } } } return tentative; } #endif /* * Insert a node into the dictionary. The node should have been * initialized with a data field. All other fields are ignored. * The behavior is undefined if the user attempts to insert into * a dictionary that is already full (for which the dict_isfull() * function returns true). */ void dict_insert(dict_t *dict, dnode_t *node, const void *key) { dnode_t *where = dict_root(dict), *nil = dict_nil(dict); dnode_t *parent = nil, *uncle, *grandpa; int result = -1; node->key = key; dict_assert(!dict_isfull(dict)); dict_assert(!dict_contains(dict, node)); dict_assert(!dnode_is_in_a_dict(node)); /* basic binary tree insert */ while (where != nil) { parent = where; result = dict->compare(key, where->key); /* trap attempts at duplicate key insertion unless it's explicitly allowed */ dict_assert(dict->dupes || result != 0); if (result < 0) where = where->left; else where = where->right; } dict_assert(where == nil); if (result < 0) parent->left = node; else parent->right = node; node->parent = parent; node->left = nil; node->right = nil; dict->nodecount++; /* red black adjustments */ node->color = dnode_red; while (parent->color == dnode_red) { grandpa = parent->parent; if (parent == grandpa->left) { uncle = grandpa->right; if (uncle->color == dnode_red) { /* red parent, red uncle */ parent->color = dnode_black; uncle->color = dnode_black; grandpa->color = dnode_red; node = grandpa; parent = grandpa->parent; } else { /* red parent, black uncle */ if (node == parent->right) { rotate_left(parent); parent = node; dict_assert(grandpa == parent->parent); /* rotation between parent and child preserves grandpa */ } parent->color = dnode_black; grandpa->color = dnode_red; rotate_right(grandpa); break; } } else { /* symmetric cases: parent == parent->parent->right */ uncle = grandpa->left; if (uncle->color == dnode_red) { parent->color = dnode_black; uncle->color = dnode_black; grandpa->color = dnode_red; node = grandpa; parent = grandpa->parent; } else { if (node == parent->left) { rotate_right(parent); parent = node; dict_assert(grandpa == parent->parent); } parent->color = dnode_black; grandpa->color = dnode_red; rotate_left(grandpa); break; } } } dict_root(dict)->color = dnode_black; dict_assert(dict_verify(dict)); } #ifdef FSCK_NOTUSED /* * Delete the given node from the dictionary. If the given node does not belong * to the given dictionary, undefined behavior results. A pointer to the * deleted node is returned. */ dnode_t *dict_delete(dict_t *dict, dnode_t *delete) { dnode_t *nil = dict_nil(dict), *child, *delparent = delete->parent; /* basic deletion */ dict_assert(!dict_isempty(dict)); dict_assert(dict_contains(dict, delete)); /* * If the node being deleted has two children, then we replace it with its * successor (i.e. the leftmost node in the right subtree.) By doing this, * we avoid the traditional algorithm under which the successor's key and * value *only* move to the deleted node and the successor is spliced out * from the tree. We cannot use this approach because the user may hold * pointers to the successor, or nodes may be inextricably tied to some * other structures by way of embedding, etc. So we must splice out the * node we are given, not some other node, and must not move contents from * one node to another behind the user's back. */ if (delete->left != nil && delete->right != nil) { dnode_t *next = dict_next(dict, delete); dnode_t *nextparent = next->parent; dnode_color_t nextcolor = next->color; dict_assert(next != nil); dict_assert(next->parent != nil); dict_assert(next->left == nil); /* * First, splice out the successor from the tree completely, by * moving up its right child into its place. */ child = next->right; child->parent = nextparent; if (nextparent->left == next) { nextparent->left = child; } else { dict_assert(nextparent->right == next); nextparent->right = child; } /* * Now that the successor has been extricated from the tree, install it * in place of the node that we want deleted. */ next->parent = delparent; next->left = delete->left; next->right = delete->right; next->left->parent = next; next->right->parent = next; next->color = delete->color; delete->color = nextcolor; if (delparent->left == delete) { delparent->left = next; } else { dict_assert(delparent->right == delete); delparent->right = next; } } else { dict_assert(delete != nil); dict_assert(delete->left == nil || delete->right == nil); child = (delete->left != nil) ? delete->left : delete->right; child->parent = delparent = delete->parent; if (delete == delparent->left) { delparent->left = child; } else { dict_assert(delete == delparent->right); delparent->right = child; } } delete->parent = NULL; delete->right = NULL; delete->left = NULL; dict->nodecount--; dict_assert(verify_bintree(dict)); /* red-black adjustments */ if (delete->color == dnode_black) { dnode_t *parent, *sister; dict_root(dict)->color = dnode_red; while (child->color == dnode_black) { parent = child->parent; if (child == parent->left) { sister = parent->right; dict_assert(sister != nil); if (sister->color == dnode_red) { sister->color = dnode_black; parent->color = dnode_red; rotate_left(parent); sister = parent->right; dict_assert(sister != nil); } if (sister->left->color == dnode_black && sister->right->color == dnode_black) { sister->color = dnode_red; child = parent; } else { if (sister->right->color == dnode_black) { dict_assert(sister->left->color == dnode_red); sister->left->color = dnode_black; sister->color = dnode_red; rotate_right(sister); sister = parent->right; dict_assert(sister != nil); } sister->color = parent->color; sister->right->color = dnode_black; parent->color = dnode_black; rotate_left(parent); break; } } else { /* symmetric case: child == child->parent->right */ dict_assert(child == parent->right); sister = parent->left; dict_assert(sister != nil); if (sister->color == dnode_red) { sister->color = dnode_black; parent->color = dnode_red; rotate_right(parent); sister = parent->left; dict_assert(sister != nil); } if (sister->right->color == dnode_black && sister->left->color == dnode_black) { sister->color = dnode_red; child = parent; } else { if (sister->left->color == dnode_black) { dict_assert(sister->right->color == dnode_red); sister->right->color = dnode_black; sister->color = dnode_red; rotate_left(sister); sister = parent->left; dict_assert(sister != nil); } sister->color = parent->color; sister->left->color = dnode_black; parent->color = dnode_black; rotate_right(parent); break; } } } child->color = dnode_black; dict_root(dict)->color = dnode_black; } dict_assert(dict_verify(dict)); return delete; } #endif /* FSCK_NOTUSED */ /* * Allocate a node using the dictionary's allocator routine, give it * the data item. */ int dict_alloc_insert(dict_t *dict, const void *key, void *data) { dnode_t *node = dict->allocnode(dict->context); if (node) { dnode_init(node, data); dict_insert(dict, node, key); return 1; } return 0; } #ifdef FSCK_NOTUSED void dict_delete_free(dict_t *dict, dnode_t *node) { dict_delete(dict, node); dict->freenode(node, dict->context); } #endif /* * Return the node with the lowest (leftmost) key. If the dictionary is empty * (that is, dict_isempty(dict) returns 1) a null pointer is returned. */ dnode_t *dict_first(dict_t *dict) { dnode_t *nil = dict_nil(dict), *root = dict_root(dict), *left; if (root != nil) while ((left = root->left) != nil) root = left; return (root == nil) ? NULL : root; } /* * Return the node with the highest (rightmost) key. If the dictionary is empty * (that is, dict_isempty(dict) returns 1) a null pointer is returned. */ dnode_t *dict_last(dict_t *dict) { dnode_t *nil = dict_nil(dict), *root = dict_root(dict), *right; if (root != nil) while ((right = root->right) != nil) root = right; return (root == nil) ? NULL : root; } /* * Return the given node's successor node---the node which has the * next key in the the left to right ordering. If the node has * no successor, a null pointer is returned rather than a pointer to * the nil node. */ dnode_t *dict_next(dict_t *dict, dnode_t *curr) { dnode_t *nil = dict_nil(dict), *parent, *left; if (curr->right != nil) { curr = curr->right; while ((left = curr->left) != nil) curr = left; return curr; } parent = curr->parent; while (parent != nil && curr == parent->right) { curr = parent; parent = curr->parent; } return (parent == nil) ? NULL : parent; } /* * Return the given node's predecessor, in the key order. * The nil sentinel node is returned if there is no predecessor. */ dnode_t *dict_prev(dict_t *dict, dnode_t *curr) { dnode_t *nil = dict_nil(dict), *parent, *right; if (curr->left != nil) { curr = curr->left; while ((right = curr->right) != nil) curr = right; return curr; } parent = curr->parent; while (parent != nil && curr == parent->left) { curr = parent; parent = curr->parent; } return (parent == nil) ? NULL : parent; } void dict_allow_dupes(dict_t *dict) { dict->dupes = 1; } #undef dict_count #undef dict_isempty #undef dict_isfull #undef dnode_get #undef dnode_put #undef dnode_getkey dictcount_t dict_count(dict_t *dict) { return dict->nodecount; } int dict_isempty(dict_t *dict) { return dict->nodecount == 0; } int dict_isfull(dict_t *dict) { return dict->nodecount == dict->maxcount; } int dict_contains(dict_t *dict, dnode_t *node) { return verify_dict_has_node(dict_nil(dict), dict_root(dict), node); } static dnode_t *dnode_alloc(void *UNUSED(context)) { return malloc(sizeof *dnode_alloc(NULL)); } static void dnode_free(dnode_t *node, void *UNUSED(context)) { free(node); } dnode_t *dnode_create(void *data) { dnode_t *new = malloc(sizeof *new); if (new) { new->data = data; new->parent = NULL; new->left = NULL; new->right = NULL; } return new; } dnode_t *dnode_init(dnode_t *dnode, void *data) { dnode->data = data; dnode->parent = NULL; dnode->left = NULL; dnode->right = NULL; return dnode; } void dnode_destroy(dnode_t *dnode) { dict_assert(!dnode_is_in_a_dict(dnode)); free(dnode); } void *dnode_get(dnode_t *dnode) { return dnode->data; } const void *dnode_getkey(dnode_t *dnode) { return dnode->key; } #ifdef FSCK_NOTUSED void dnode_put(dnode_t *dnode, void *data) { dnode->data = data; } #endif #ifndef DICT_NODEBUG int dnode_is_in_a_dict(dnode_t *dnode) { return (dnode->parent && dnode->left && dnode->right); } #endif #ifdef FSCK_NOTUSED void dict_process(dict_t *dict, void *context, dnode_process_t function) { dnode_t *node = dict_first(dict), *next; while (node != NULL) { /* check for callback function deleting */ /* the next node from under us */ dict_assert(dict_contains(dict, node)); next = dict_next(dict, node); function(dict, node, context); node = next; } } static void load_begin_internal(dict_load_t *load, dict_t *dict) { load->dictptr = dict; load->nilnode.left = &load->nilnode; load->nilnode.right = &load->nilnode; } void dict_load_begin(dict_load_t *load, dict_t *dict) { dict_assert(dict_isempty(dict)); load_begin_internal(load, dict); } void dict_load_next(dict_load_t *load, dnode_t *newnode, const void *key) { dict_t *dict = load->dictptr; dnode_t *nil = &load->nilnode; dict_assert(!dnode_is_in_a_dict(newnode)); dict_assert(dict->nodecount < dict->maxcount); #ifndef DICT_NODEBUG if (dict->nodecount > 0) { if (dict->dupes) dict_assert(dict->compare(nil->left->key, key) <= 0); else dict_assert(dict->compare(nil->left->key, key) < 0); } #endif newnode->key = key; nil->right->left = newnode; nil->right = newnode; newnode->left = nil; dict->nodecount++; } void dict_load_end(dict_load_t *load) { dict_t *dict = load->dictptr; dnode_t *tree[DICT_DEPTH_MAX] = { 0 }; dnode_t *curr, *dictnil = dict_nil(dict), *loadnil = &load->nilnode, *next; dnode_t *complete = 0; dictcount_t fullcount = DICTCOUNT_T_MAX, nodecount = dict->nodecount; dictcount_t botrowcount; unsigned baselevel = 0, level = 0, i; dict_assert(dnode_red == 0 && dnode_black == 1); while (fullcount >= nodecount && fullcount) fullcount >>= 1; botrowcount = nodecount - fullcount; for (curr = loadnil->left; curr != loadnil; curr = next) { next = curr->left; if (complete == NULL && botrowcount-- == 0) { dict_assert(baselevel == 0); dict_assert(level == 0); baselevel = level = 1; complete = tree[0]; if (complete != 0) { tree[0] = 0; complete->right = dictnil; while (tree[level] != 0) { tree[level]->right = complete; complete->parent = tree[level]; complete = tree[level]; tree[level++] = 0; } } } if (complete == NULL) { curr->left = dictnil; curr->right = dictnil; curr->color = level % 2; complete = curr; dict_assert(level == baselevel); while (tree[level] != 0) { tree[level]->right = complete; complete->parent = tree[level]; complete = tree[level]; tree[level++] = 0; } } else { curr->left = complete; curr->color = (level + 1) % 2; complete->parent = curr; tree[level] = curr; complete = 0; level = baselevel; } } if (complete == NULL) complete = dictnil; for (i = 0; i < DICT_DEPTH_MAX; i++) { if (tree[i] != 0) { tree[i]->right = complete; complete->parent = tree[i]; complete = tree[i]; } } dictnil->color = dnode_black; dictnil->right = dictnil; complete->parent = dictnil; complete->color = dnode_black; dict_root(dict) = complete; dict_assert(dict_verify(dict)); } void dict_merge(dict_t *dest, dict_t *source) { dict_load_t load; dnode_t *leftnode = dict_first(dest), *rightnode = dict_first(source); dict_assert(dict_similar(dest, source)); if (source == dest) return; dest->nodecount = 0; load_begin_internal(&load, dest); for (;;) { if (leftnode != NULL && rightnode != NULL) { if (dest->compare(leftnode->key, rightnode->key) < 0) goto copyleft; else goto copyright; } else if (leftnode != NULL) { goto copyleft; } else if (rightnode != NULL) { goto copyright; } else { dict_assert(leftnode == NULL && rightnode == NULL); break; } copyleft: { dnode_t *next = dict_next(dest, leftnode); #ifndef DICT_NODEBUG leftnode->left = NULL; /* suppress assertion in dict_load_next */ #endif dict_load_next(&load, leftnode, leftnode->key); leftnode = next; continue; } copyright: { dnode_t *next = dict_next(source, rightnode); #ifndef DICT_NODEBUG rightnode->left = NULL; #endif dict_load_next(&load, rightnode, rightnode->key); rightnode = next; continue; } } dict_clear(source); dict_load_end(&load); } #endif /* FSCK_NOTUSED */ #ifdef KAZLIB_TEST_MAIN #include #include #include #include typedef char input_t[256]; static int tokenize(char *string, ...) { char **tokptr; va_list arglist; int tokcount = 0; va_start(arglist, string); tokptr = va_arg(arglist, char **); while (tokptr) { while (*string && isspace((unsigned char) *string)) string++; if (!*string) break; *tokptr = string; while (*string && !isspace((unsigned char) *string)) string++; tokptr = va_arg(arglist, char **); tokcount++; if (!*string) break; *string++ = 0; } va_end(arglist); return tokcount; } static int comparef(const void *key1, const void *key2) { return strcmp(key1, key2); } static char *dupstring(char *str) { int sz = strlen(str) + 1; char *new = malloc(sz); if (new) memcpy(new, str, sz); return new; } static dnode_t *new_node(void *c) { static dnode_t few[5]; static int count; if (count < 5) return few + count++; return NULL; } static void del_node(dnode_t *n, void *c) { } static int prompt = 0; static void construct(dict_t *d) { input_t in; int done = 0; dict_load_t dl; dnode_t *dn; char *tok1, *tok2, *val; const char *key; char *help = "p turn prompt on\n" "q finish construction\n" "a add new entry\n"; if (!dict_isempty(d)) puts("warning: dictionary not empty!"); dict_load_begin(&dl, d); while (!done) { if (prompt) putchar('>'); fflush(stdout); if (!fgets(in, sizeof(input_t), stdin)) break; switch (in[0]) { case '?': puts(help); break; case 'p': prompt = 1; break; case 'q': done = 1; break; case 'a': if (tokenize(in+1, &tok1, &tok2, (char **) 0) != 2) { puts("what?"); break; } key = dupstring(tok1); val = dupstring(tok2); dn = dnode_create(val); if (!key || !val || !dn) { puts("out of memory"); free((void *) key); free(val); if (dn) dnode_destroy(dn); } dict_load_next(&dl, dn, key); break; default: putchar('?'); putchar('\n'); break; } } dict_load_end(&dl); } int main(void) { input_t in; dict_t darray[10]; dict_t *d = &darray[0]; dnode_t *dn; int i; char *tok1, *tok2, *val; const char *key; char *help = "a add value to dictionary\n" "d delete value from dictionary\n" "l lookup value in dictionary\n" "( lookup lower bound\n" ") lookup upper bound\n" "# switch to alternate dictionary (0-9)\n" "j merge two dictionaries\n" "f free the whole dictionary\n" "k allow duplicate keys\n" "c show number of entries\n" "t dump whole dictionary in sort order\n" "m make dictionary out of sorted items\n" "p turn prompt on\n" "s switch to non-functioning allocator\n" "q quit"; for (i = 0; i < sizeof darray / sizeof *darray; i++) dict_init(&darray[i], DICTCOUNT_T_MAX, comparef); for (;;) { if (prompt) putchar('>'); fflush(stdout); if (!fgets(in, sizeof(input_t), stdin)) break; switch(in[0]) { case '?': puts(help); break; case 'a': if (tokenize(in+1, &tok1, &tok2, (char **) 0) != 2) { puts("what?"); break; } key = dupstring(tok1); val = dupstring(tok2); if (!key || !val) { puts("out of memory"); free((void *) key); free(val); } if (!dict_alloc_insert(d, key, val)) { puts("dict_alloc_insert failed"); free((void *) key); free(val); break; } break; case 'd': if (tokenize(in+1, &tok1, (char **) 0) != 1) { puts("what?"); break; } dn = dict_lookup(d, tok1); if (!dn) { puts("dict_lookup failed"); break; } val = dnode_get(dn); key = dnode_getkey(dn); dict_delete_free(d, dn); free(val); free((void *) key); break; case 'f': dict_free(d); break; case 'l': case '(': case ')': if (tokenize(in+1, &tok1, (char **) 0) != 1) { puts("what?"); break; } dn = 0; switch (in[0]) { case 'l': dn = dict_lookup(d, tok1); break; case '(': dn = dict_lower_bound(d, tok1); break; case ')': dn = dict_upper_bound(d, tok1); break; } if (!dn) { puts("lookup failed"); break; } val = dnode_get(dn); puts(val); break; case 'm': construct(d); break; case 'k': dict_allow_dupes(d); break; case 'c': printf("%lu\n", (unsigned long) dict_count(d)); break; case 't': for (dn = dict_first(d); dn; dn = dict_next(d, dn)) { printf("%s\t%s\n", (char *) dnode_getkey(dn), (char *) dnode_get(dn)); } break; case 'q': exit(0); break; case '\0': break; case 'p': prompt = 1; break; case 's': dict_set_allocator(d, new_node, del_node, NULL); break; case '#': if (tokenize(in+1, &tok1, (char **) 0) != 1) { puts("what?"); break; } else { int dictnum = atoi(tok1); if (dictnum < 0 || dictnum > 9) { puts("invalid number"); break; } d = &darray[dictnum]; } break; case 'j': if (tokenize(in+1, &tok1, &tok2, (char **) 0) != 2) { puts("what?"); break; } else { int dict1 = atoi(tok1), dict2 = atoi(tok2); if (dict1 < 0 || dict1 > 9 || dict2 < 0 || dict2 > 9) { puts("invalid number"); break; } dict_merge(&darray[dict1], &darray[dict2]); } break; default: putchar('?'); putchar('\n'); break; } } return 0; } #endif f2fs-tools-1.10.0/fsck/dict.h000066400000000000000000000107271323417143600156240ustar00rootroot00000000000000/* * Dictionary Abstract Data Type * Copyright (C) 1997 Kaz Kylheku * * Free Software License: * * All rights are reserved by the author, with the following exceptions: * Permission is granted to freely reproduce and distribute this software, * possibly in exchange for a fee, provided that this copyright notice appears * intact. Permission is also granted to adapt this software to produce * derivative works, as long as the modified versions carry this copyright * notice and additional notices stating that the work has been modified. * This source code may be translated into executable form and incorporated * into proprietary software; there is no requirement for such software to * contain a copyright notice related to this source. * * $Id: dict.h,v 1.22.2.6 2000/11/13 01:36:44 kaz Exp $ * $Name: kazlib_1_20 $ */ #ifndef DICT_H #define DICT_H #include #ifdef KAZLIB_SIDEEFFECT_DEBUG #include "sfx.h" #endif /* * Blurb for inclusion into C++ translation units */ #ifdef __cplusplus extern "C" { #endif typedef unsigned long dictcount_t; #define DICTCOUNT_T_MAX ULONG_MAX /* * The dictionary is implemented as a red-black tree */ typedef enum { dnode_red, dnode_black } dnode_color_t; typedef struct dnode_t { #if defined(DICT_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG) struct dnode_t *dict_left; struct dnode_t *dict_right; struct dnode_t *dict_parent; dnode_color_t dict_color; const void *dict_key; void *dict_data; #else int dict_dummy; #endif } dnode_t; typedef int (*dict_comp_t)(const void *, const void *); typedef dnode_t *(*dnode_alloc_t)(void *); typedef void (*dnode_free_t)(dnode_t *, void *); typedef struct dict_t { #if defined(DICT_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG) dnode_t dict_nilnode; dictcount_t dict_nodecount; dictcount_t dict_maxcount; dict_comp_t dict_compare; dnode_alloc_t dict_allocnode; dnode_free_t dict_freenode; void *dict_context; int dict_dupes; #else int dict_dummmy; #endif } dict_t; typedef void (*dnode_process_t)(dict_t *, dnode_t *, void *); typedef struct dict_load_t { #if defined(DICT_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG) dict_t *dict_dictptr; dnode_t dict_nilnode; #else int dict_dummmy; #endif } dict_load_t; extern dict_t *dict_create(dictcount_t, dict_comp_t); extern void dict_set_allocator(dict_t *, dnode_alloc_t, dnode_free_t, void *); extern void dict_destroy(dict_t *); extern void dict_free_nodes(dict_t *); extern void dict_free(dict_t *); extern dict_t *dict_init(dict_t *, dictcount_t, dict_comp_t); extern void dict_init_like(dict_t *, const dict_t *); extern int dict_verify(dict_t *); extern int dict_similar(const dict_t *, const dict_t *); extern dnode_t *dict_lookup(dict_t *, const void *); extern dnode_t *dict_lower_bound(dict_t *, const void *); extern dnode_t *dict_upper_bound(dict_t *, const void *); extern void dict_insert(dict_t *, dnode_t *, const void *); extern dnode_t *dict_delete(dict_t *, dnode_t *); extern int dict_alloc_insert(dict_t *, const void *, void *); extern void dict_delete_free(dict_t *, dnode_t *); extern dnode_t *dict_first(dict_t *); extern dnode_t *dict_last(dict_t *); extern dnode_t *dict_next(dict_t *, dnode_t *); extern dnode_t *dict_prev(dict_t *, dnode_t *); extern dictcount_t dict_count(dict_t *); extern int dict_isempty(dict_t *); extern int dict_isfull(dict_t *); extern int dict_contains(dict_t *, dnode_t *); extern void dict_allow_dupes(dict_t *); extern int dnode_is_in_a_dict(dnode_t *); extern dnode_t *dnode_create(void *); extern dnode_t *dnode_init(dnode_t *, void *); extern void dnode_destroy(dnode_t *); extern void *dnode_get(dnode_t *); extern const void *dnode_getkey(dnode_t *); extern void dnode_put(dnode_t *, void *); extern void dict_process(dict_t *, void *, dnode_process_t); extern void dict_load_begin(dict_load_t *, dict_t *); extern void dict_load_next(dict_load_t *, dnode_t *, const void *); extern void dict_load_end(dict_load_t *); extern void dict_merge(dict_t *, dict_t *); #if defined(DICT_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG) #ifdef KAZLIB_SIDEEFFECT_DEBUG #define dict_isfull(D) (SFX_CHECK(D)->dict_nodecount == (D)->dict_maxcount) #else #define dict_isfull(D) ((D)->dict_nodecount == (D)->dict_maxcount) #endif #define dict_count(D) ((D)->dict_nodecount) #define dict_isempty(D) ((D)->dict_nodecount == 0) #define dnode_get(N) ((N)->dict_data) #define dnode_getkey(N) ((N)->dict_key) #define dnode_put(N, X) ((N)->dict_data = (X)) #endif #ifdef __cplusplus } #endif #endif f2fs-tools-1.10.0/fsck/dir.c000066400000000000000000000421531323417143600154500ustar00rootroot00000000000000/** * dir.c * * Many parts of codes are copied from Linux kernel/fs/f2fs. * * Copyright (C) 2015 Huawei Ltd. * Witten by: * Hou Pengyang * Liu Shuoran * Jaegeuk Kim * * 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. */ #include "fsck.h" #include "node.h" static int room_for_filename(const u8 *bitmap, int slots, int max_slots) { int bit_start = 0; int zero_start, zero_end; next: zero_start = find_next_zero_bit_le(bitmap, max_slots, bit_start); if (zero_start >= max_slots) return max_slots; zero_end = find_next_bit_le(bitmap, max_slots, zero_start + 1); if (zero_end - zero_start >= slots) return zero_start; bit_start = zero_end; goto next; } void make_dentry_ptr(struct f2fs_dentry_ptr *d, struct f2fs_node *node_blk, void *src, int type) { if (type == 1) { struct f2fs_dentry_block *t = (struct f2fs_dentry_block *)src; d->max = NR_DENTRY_IN_BLOCK; d->nr_bitmap = SIZE_OF_DENTRY_BITMAP; d->bitmap = t->dentry_bitmap; d->dentry = t->dentry; d->filename = t->filename; } else { int entry_cnt = NR_INLINE_DENTRY(node_blk); int bitmap_size = INLINE_DENTRY_BITMAP_SIZE(node_blk); int reserved_size = INLINE_RESERVED_SIZE(node_blk); d->max = entry_cnt; d->nr_bitmap = bitmap_size; d->bitmap = (u8 *)src; d->dentry = (struct f2fs_dir_entry *) ((char *)src + bitmap_size + reserved_size); d->filename = (__u8 (*)[F2FS_SLOT_LEN])((char *)src + bitmap_size + reserved_size + SIZE_OF_DIR_ENTRY * entry_cnt); } } static struct f2fs_dir_entry *find_target_dentry(const u8 *name, unsigned int len, f2fs_hash_t namehash, int *max_slots, struct f2fs_dentry_ptr *d) { struct f2fs_dir_entry *de; unsigned long bit_pos = 0; int max_len = 0; if (max_slots) *max_slots = 0; while (bit_pos < (unsigned long)d->max) { if (!test_bit_le(bit_pos, d->bitmap)) { bit_pos++; max_len++; continue; } de = &d->dentry[bit_pos]; if (le16_to_cpu(de->name_len) == len && de->hash_code == namehash && !memcmp(d->filename[bit_pos], name, len)) { goto found; } if (max_slots && max_len > *max_slots) *max_slots = max_len; max_len = 0; bit_pos += GET_DENTRY_SLOTS(le16_to_cpu(de->name_len)); } de = NULL; found: if (max_slots && max_len > *max_slots) *max_slots = max_len; return de; } static struct f2fs_dir_entry *find_in_block(void *block, const u8 *name, int len, f2fs_hash_t namehash, int *max_slots) { struct f2fs_dentry_ptr d; make_dentry_ptr(&d, NULL, block, 1); return find_target_dentry(name, len, namehash, max_slots, &d); } static int find_in_level(struct f2fs_sb_info *sbi,struct f2fs_node *dir, unsigned int level, struct dentry *de) { unsigned int nbucket, nblock; unsigned int bidx, end_block; struct f2fs_dir_entry *dentry = NULL; struct dnode_of_data dn; void *dentry_blk; int max_slots = 214; nid_t ino = le32_to_cpu(dir->footer.ino); f2fs_hash_t namehash; unsigned int dir_level = dir->i.i_dir_level; int ret = 0; namehash = f2fs_dentry_hash(de->name, de->len); nbucket = dir_buckets(level, dir_level); nblock = bucket_blocks(level); bidx = dir_block_index(level, dir_level, le32_to_cpu(namehash) % nbucket); end_block = bidx + nblock; dentry_blk = calloc(BLOCK_SZ, 1); ASSERT(dentry_blk); memset(&dn, 0, sizeof(dn)); for (; bidx < end_block; bidx++) { /* Firstly, we should know direct node of target data blk */ if (dn.node_blk && dn.node_blk != dn.inode_blk) free(dn.node_blk); set_new_dnode(&dn, dir, NULL, ino); get_dnode_of_data(sbi, &dn, bidx, LOOKUP_NODE); if (dn.data_blkaddr == NULL_ADDR) continue; ret = dev_read_block(dentry_blk, dn.data_blkaddr); ASSERT(ret >= 0); dentry = find_in_block(dentry_blk, de->name, de->len, namehash, &max_slots); if (dentry) { ret = 1; de->ino = le32_to_cpu(dentry->ino); break; } } if (dn.node_blk && dn.node_blk != dn.inode_blk) free(dn.node_blk); free(dentry_blk); return ret; } static int f2fs_find_entry(struct f2fs_sb_info *sbi, struct f2fs_node *dir, struct dentry *de) { unsigned int max_depth; unsigned int level; max_depth = le32_to_cpu(dir->i.i_current_depth); for (level = 0; level < max_depth; level ++) { if (find_in_level(sbi, dir, level, de)) return 1; } return 0; } static void f2fs_update_dentry(nid_t ino, int file_type, struct f2fs_dentry_ptr *d, const unsigned char *name, int len, f2fs_hash_t name_hash, unsigned int bit_pos) { struct f2fs_dir_entry *de; int slots = GET_DENTRY_SLOTS(len); int i; de = &d->dentry[bit_pos]; de->name_len = cpu_to_le16(len); de->hash_code = name_hash; memcpy(d->filename[bit_pos], name, len); d->filename[bit_pos][len] = 0; de->ino = cpu_to_le32(ino); de->file_type = file_type; for (i = 0; i < slots; i++) test_and_set_bit_le(bit_pos + i, d->bitmap); } /* * f2fs_add_link - Add a new file(dir) to parent dir. */ static int f2fs_add_link(struct f2fs_sb_info *sbi, struct f2fs_node *parent, const unsigned char *name, int name_len, nid_t ino, int file_type, block_t p_blkaddr, int inc_link) { int level = 0, current_depth, bit_pos; int nbucket, nblock, bidx, block; int slots = GET_DENTRY_SLOTS(name_len); f2fs_hash_t dentry_hash = f2fs_dentry_hash(name, name_len); struct f2fs_dentry_block *dentry_blk; struct f2fs_dentry_ptr d; struct dnode_of_data dn; nid_t pino = le32_to_cpu(parent->footer.ino); unsigned int dir_level = parent->i.i_dir_level; int ret; if (parent == NULL) return -EINVAL; if (!pino) { ERR_MSG("Wrong parent ino:%d \n", pino); return -EINVAL; } dentry_blk = calloc(BLOCK_SZ, 1); ASSERT(dentry_blk); current_depth = le32_to_cpu(parent->i.i_current_depth); start: if (current_depth == MAX_DIR_HASH_DEPTH) { free(dentry_blk); ERR_MSG("\tError: MAX_DIR_HASH\n"); return -ENOSPC; } /* Need a new dentry block */ if (level == current_depth) ++current_depth; nbucket = dir_buckets(level, dir_level); nblock = bucket_blocks(level); bidx = dir_block_index(level, dir_level, le32_to_cpu(dentry_hash) % nbucket); memset(&dn, 0, sizeof(dn)); for (block = bidx; block <= (bidx + nblock - 1); block++) { /* Firstly, we should know the direct node of target data blk */ if (dn.node_blk && dn.node_blk != dn.inode_blk) free(dn.node_blk); set_new_dnode(&dn, parent, NULL, pino); get_dnode_of_data(sbi, &dn, block, ALLOC_NODE); if (dn.data_blkaddr == NULL_ADDR) { new_data_block(sbi, dentry_blk, &dn, CURSEG_HOT_DATA); } else { ret = dev_read_block(dentry_blk, dn.data_blkaddr); ASSERT(ret >= 0); } bit_pos = room_for_filename(dentry_blk->dentry_bitmap, slots, NR_DENTRY_IN_BLOCK); if (bit_pos < NR_DENTRY_IN_BLOCK) goto add_dentry; } level ++; goto start; add_dentry: make_dentry_ptr(&d, NULL, (void *)dentry_blk, 1); f2fs_update_dentry(ino, file_type, &d, name, name_len, dentry_hash, bit_pos); ret = dev_write_block(dentry_blk, dn.data_blkaddr); ASSERT(ret >= 0); /* * Parent inode needs updating, because its inode info may be changed. * such as i_current_depth and i_blocks. */ if (parent->i.i_current_depth != cpu_to_le32(current_depth)) { parent->i.i_current_depth = cpu_to_le32(current_depth); dn.idirty = 1; } /* Update parent's i_links info*/ if (inc_link && (file_type == F2FS_FT_DIR)){ u32 links = le32_to_cpu(parent->i.i_links); parent->i.i_links = cpu_to_le32(links + 1); dn.idirty = 1; } if ((__u64)((block + 1) * F2FS_BLKSIZE) > le64_to_cpu(parent->i.i_size)) { parent->i.i_size = cpu_to_le64((block + 1) * F2FS_BLKSIZE); dn.idirty = 1; } if (dn.ndirty) { ret = dev_write_block(dn.node_blk, dn.node_blkaddr); ASSERT(ret >= 0); } if (dn.idirty) { ASSERT(parent == dn.inode_blk); ret = dev_write_block(dn.inode_blk, p_blkaddr); ASSERT(ret >= 0); } if (dn.node_blk != dn.inode_blk) free(dn.node_blk); free(dentry_blk); return 0; } static void make_empty_dir(struct f2fs_sb_info *sbi, struct f2fs_node *inode) { struct f2fs_dentry_block *dent_blk; nid_t ino = le32_to_cpu(inode->footer.ino); nid_t pino = le32_to_cpu(inode->i.i_pino); struct f2fs_summary sum; struct node_info ni; block_t blkaddr = NULL_ADDR; int ret; get_node_info(sbi, ino, &ni); dent_blk = calloc(BLOCK_SZ, 1); ASSERT(dent_blk); dent_blk->dentry[0].hash_code = 0; dent_blk->dentry[0].ino = cpu_to_le32(ino); dent_blk->dentry[0].name_len = cpu_to_le16(1); dent_blk->dentry[0].file_type = F2FS_FT_DIR; memcpy(dent_blk->filename[0], ".", 1); dent_blk->dentry[1].hash_code = 0; dent_blk->dentry[1].ino = cpu_to_le32(pino); dent_blk->dentry[1].name_len = cpu_to_le16(2); dent_blk->dentry[1].file_type = F2FS_FT_DIR; memcpy(dent_blk->filename[1], "..", 2); test_and_set_bit_le(0, dent_blk->dentry_bitmap); test_and_set_bit_le(1, dent_blk->dentry_bitmap); set_summary(&sum, ino, 0, ni.version); reserve_new_block(sbi, &blkaddr, &sum, CURSEG_HOT_DATA); ret = dev_write_block(dent_blk, blkaddr); ASSERT(ret >= 0); inode->i.i_addr[get_extra_isize(inode)] = cpu_to_le32(blkaddr); free(dent_blk); } static void page_symlink(struct f2fs_sb_info *sbi, struct f2fs_node *inode, const char *symname, int symlen) { nid_t ino = le32_to_cpu(inode->footer.ino); struct f2fs_summary sum; struct node_info ni; char *data_blk; block_t blkaddr = NULL_ADDR; int ret; get_node_info(sbi, ino, &ni); /* store into inline_data */ if ((unsigned long)(symlen + 1) <= MAX_INLINE_DATA(inode)) { inode->i.i_inline |= F2FS_INLINE_DATA; inode->i.i_inline |= F2FS_DATA_EXIST; memcpy(inline_data_addr(inode), symname, symlen); return; } data_blk = calloc(BLOCK_SZ, 1); ASSERT(data_blk); memcpy(data_blk, symname, symlen); set_summary(&sum, ino, 0, ni.version); reserve_new_block(sbi, &blkaddr, &sum, CURSEG_WARM_DATA); ret = dev_write_block(data_blk, blkaddr); ASSERT(ret >= 0); inode->i.i_addr[get_extra_isize(inode)] = cpu_to_le32(blkaddr); free(data_blk); } static void init_inode_block(struct f2fs_sb_info *sbi, struct f2fs_node *node_blk, struct dentry *de) { struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi); mode_t mode = de->mode; int links = 1; unsigned int size; int blocks = 1; if (de->file_type == F2FS_FT_DIR) { mode |= S_IFDIR; size = 4096; links++; blocks++; } else if (de->file_type == F2FS_FT_REG_FILE) { mode |= S_IFREG; size = 0; } else if (de->file_type == F2FS_FT_SYMLINK) { ASSERT(de->link); mode |= S_IFLNK; size = strlen(de->link); if (size + 1 > MAX_INLINE_DATA(node_blk)) blocks++; } else { ASSERT(0); } node_blk->i.i_mode = cpu_to_le16(mode); node_blk->i.i_advise = 0; node_blk->i.i_uid = cpu_to_le32(de->uid); node_blk->i.i_gid = cpu_to_le32(de->gid); node_blk->i.i_links = cpu_to_le32(links); node_blk->i.i_size = cpu_to_le32(size); node_blk->i.i_blocks = cpu_to_le32(blocks); node_blk->i.i_atime = cpu_to_le64(de->mtime); node_blk->i.i_ctime = cpu_to_le64(de->mtime); node_blk->i.i_mtime = cpu_to_le64(de->mtime); node_blk->i.i_atime_nsec = 0; node_blk->i.i_ctime_nsec = 0; node_blk->i.i_mtime_nsec = 0; node_blk->i.i_generation = 0; node_blk->i.i_current_depth = cpu_to_le32(1); node_blk->i.i_xattr_nid = 0; node_blk->i.i_flags = 0; node_blk->i.i_inline = F2FS_INLINE_XATTR; node_blk->i.i_pino = cpu_to_le32(de->pino); node_blk->i.i_namelen = cpu_to_le32(de->len); memcpy(node_blk->i.i_name, de->name, de->len); node_blk->i.i_name[de->len] = 0; if (c.feature & cpu_to_le32(F2FS_FEATURE_EXTRA_ATTR)) { node_blk->i.i_inline |= F2FS_EXTRA_ATTR; node_blk->i.i_extra_isize = cpu_to_le16(F2FS_TOTAL_EXTRA_ATTR_SIZE); } node_blk->footer.ino = cpu_to_le32(de->ino); node_blk->footer.nid = cpu_to_le32(de->ino); node_blk->footer.flag = 0; node_blk->footer.cp_ver = ckpt->checkpoint_ver; if (S_ISDIR(mode)) make_empty_dir(sbi, node_blk); else if (S_ISLNK(mode)) page_symlink(sbi, node_blk, de->link, size); if (c.feature & cpu_to_le32(F2FS_FEATURE_INODE_CHKSUM)) node_blk->i.i_inode_checksum = cpu_to_le32(f2fs_inode_chksum(node_blk)); } int convert_inline_dentry(struct f2fs_sb_info *sbi, struct f2fs_node *node, block_t p_blkaddr) { struct f2fs_inode *inode = &(node->i); unsigned int dir_level = node->i.i_dir_level; nid_t ino = le32_to_cpu(node->footer.ino); char inline_data[MAX_INLINE_DATA(node)]; struct dnode_of_data dn; struct f2fs_dentry_ptr d; unsigned long bit_pos = 0; int ret = 0; if (!(inode->i_inline & F2FS_INLINE_DENTRY)) return 0; memcpy(inline_data, inline_data_addr(node), MAX_INLINE_DATA(node)); memset(inline_data_addr(node), 0, MAX_INLINE_DATA(node)); inode->i_inline &= ~F2FS_INLINE_DENTRY; ret = dev_write_block(node, p_blkaddr); ASSERT(ret >= 0); memset(&dn, 0, sizeof(dn)); if (!dir_level) { struct f2fs_dentry_block *dentry_blk; struct f2fs_dentry_ptr src, dst; dentry_blk = calloc(BLOCK_SZ, 1); ASSERT(dentry_blk); set_new_dnode(&dn, node, NULL, ino); get_dnode_of_data(sbi, &dn, 0, ALLOC_NODE); if (dn.data_blkaddr == NULL_ADDR) new_data_block(sbi, dentry_blk, &dn, CURSEG_HOT_DATA); make_dentry_ptr(&src, node, (void *)inline_data, 2); make_dentry_ptr(&dst, NULL, (void *)dentry_blk, 1); /* copy data from inline dentry block to new dentry block */ memcpy(dst.bitmap, src.bitmap, src.nr_bitmap); memset(dst.bitmap + src.nr_bitmap, 0, dst.nr_bitmap - src.nr_bitmap); memcpy(dst.dentry, src.dentry, SIZE_OF_DIR_ENTRY * src.max); memcpy(dst.filename, src.filename, src.max * F2FS_SLOT_LEN); ret = dev_write_block(dentry_blk, dn.data_blkaddr); ASSERT(ret >= 0); MSG(1, "%s: copy inline entry to block\n", __func__); free(dentry_blk); return ret; } make_empty_dir(sbi, node); make_dentry_ptr(&d, node, (void *)inline_data, 2); while (bit_pos < (unsigned long)d.max) { struct f2fs_dir_entry *de; const unsigned char *filename; int namelen; if (!test_bit_le(bit_pos, d.bitmap)) { bit_pos++; continue; } de = &d.dentry[bit_pos]; if (!de->name_len) { bit_pos++; continue; } filename = d.filename[bit_pos]; namelen = le32_to_cpu(de->name_len); if (is_dot_dotdot(filename, namelen)) { bit_pos += GET_DENTRY_SLOTS(namelen); continue; } ret = f2fs_add_link(sbi, node, filename, namelen, le32_to_cpu(de->ino), de->file_type, p_blkaddr, 0); if (ret) MSG(0, "Convert file \"%s\" ERR=%d\n", filename, ret); else MSG(1, "%s: add inline entry to block\n", __func__); bit_pos += GET_DENTRY_SLOTS(namelen); } return 0; } int f2fs_create(struct f2fs_sb_info *sbi, struct dentry *de) { struct f2fs_node *parent, *child; struct node_info ni; struct f2fs_summary sum; block_t blkaddr = NULL_ADDR; int ret; /* Find if there is a */ get_node_info(sbi, de->pino, &ni); if (ni.blk_addr == NULL_ADDR) { MSG(0, "No parent directory pino=%x\n", de->pino); return -1; } parent = calloc(BLOCK_SZ, 1); ASSERT(parent); ret = dev_read_block(parent, ni.blk_addr); ASSERT(ret >= 0); /* Must convert inline dentry before the following opertions */ ret = convert_inline_dentry(sbi, parent, ni.blk_addr); if (ret) { MSG(0, "Convert inline dentry for pino=%x failed.\n", de->pino); return -1; } ret = f2fs_find_entry(sbi, parent, de); if (ret) { MSG(0, "Skip the existing \"%s\" pino=%x ERR=%d\n", de->name, de->pino, ret); if (de->file_type == F2FS_FT_REG_FILE) de->ino = 0; goto free_parent_dir; } child = calloc(BLOCK_SZ, 1); ASSERT(child); f2fs_alloc_nid(sbi, &de->ino, 1); init_inode_block(sbi, child, de); ret = f2fs_add_link(sbi, parent, child->i.i_name, le32_to_cpu(child->i.i_namelen), le32_to_cpu(child->footer.ino), map_de_type(le16_to_cpu(child->i.i_mode)), ni.blk_addr, 1); if (ret) { MSG(0, "Skip the existing \"%s\" pino=%x ERR=%d\n", de->name, de->pino, ret); goto free_child_dir; } /* write child */ set_summary(&sum, de->ino, 0, ni.version); reserve_new_block(sbi, &blkaddr, &sum, CURSEG_HOT_NODE); /* update nat info */ update_nat_blkaddr(sbi, de->ino, de->ino, blkaddr); ret = dev_write_block(child, blkaddr); ASSERT(ret >= 0); update_free_segments(sbi); MSG(1, "Info: Create %s -> %s\n" " -- ino=%x, type=%x, mode=%x, uid=%x, " "gid=%x, cap=%"PRIx64", size=%lu, pino=%x\n", de->full_path, de->path, de->ino, de->file_type, de->mode, de->uid, de->gid, de->capabilities, de->size, de->pino); free_child_dir: free(child); free_parent_dir: free(parent); return 0; } int f2fs_mkdir(struct f2fs_sb_info *sbi, struct dentry *de) { return f2fs_create(sbi, de); } int f2fs_symlink(struct f2fs_sb_info *sbi, struct dentry *de) { return f2fs_create(sbi, de); } int f2fs_find_path(struct f2fs_sb_info *sbi, char *path, nid_t *ino) { struct f2fs_node *parent; struct node_info ni; struct dentry de; int err = 0; int ret; char *p; if (path[0] != '/') return -ENOENT; *ino = F2FS_ROOT_INO(sbi); parent = calloc(BLOCK_SZ, 1); ASSERT(parent); p = strtok(path, "/"); while (p) { de.name = (const u8 *)p; de.len = strlen(p); get_node_info(sbi, *ino, &ni); if (ni.blk_addr == NULL_ADDR) { err = -ENOENT; goto err; } ret = dev_read_block(parent, ni.blk_addr); ASSERT(ret >= 0); ret = f2fs_find_entry(sbi, parent, &de); if (!ret) { err = -ENOENT; goto err; } *ino = de.ino; p = strtok(NULL, "/"); } err: free(parent); return err; } f2fs-tools-1.10.0/fsck/dqblk_v2.h000066400000000000000000000014661323417143600164050ustar00rootroot00000000000000/* * Header file for disk format of new quotafile format * * Jan Kara - sponsored by SuSE CR */ #ifndef __QUOTA_DQBLK_V2_H__ #define __QUOTA_DQBLK_V2_H__ #include "quotaio_tree.h" /* Structure for format specific information */ struct v2_mem_dqinfo { struct qtree_mem_dqinfo dqi_qtree; unsigned int dqi_flags; /* Flags set in quotafile */ unsigned int dqi_used_entries; /* Number of entries in file - updated by scan_dquots */ unsigned int dqi_data_blocks; /* Number of data blocks in file - updated by scan_dquots */ }; struct v2_mem_dqblk { long long dqb_off; /* Offset of dquot in file */ }; struct quotafile_ops; /* Will be defined later in quotaio.h */ /* Operations above this format */ extern struct quotafile_ops quotafile_ops_2; #endif /* __QUOTA_DQBLK_V2_H__ */ f2fs-tools-1.10.0/fsck/dump.c000066400000000000000000000415201323417143600156340ustar00rootroot00000000000000/** * dump.c * * Copyright (c) 2013 Samsung Electronics Co., Ltd. * http://www.samsung.com/ * * 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. */ #include #include "fsck.h" #include "xattr.h" #ifdef HAVE_ATTR_XATTR_H #include #endif #ifdef HAVE_LINUX_XATTR_H #include #endif #include #define BUF_SZ 80 const char *seg_type_name[SEG_TYPE_MAX + 1] = { "SEG_TYPE_DATA", "SEG_TYPE_CUR_DATA", "SEG_TYPE_NODE", "SEG_TYPE_CUR_NODE", "SEG_TYPE_NONE", }; void nat_dump(struct f2fs_sb_info *sbi) { struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi); struct f2fs_nm_info *nm_i = NM_I(sbi); struct f2fs_nat_block *nat_block; struct f2fs_node *node_block; u32 nr_nat_blks, nid; pgoff_t block_off; pgoff_t block_addr; char buf[BUF_SZ]; int seg_off; int fd, ret, pack; unsigned int i; nat_block = (struct f2fs_nat_block *)calloc(BLOCK_SZ, 1); node_block = (struct f2fs_node *)calloc(BLOCK_SZ, 1); ASSERT(nat_block); nr_nat_blks = get_sb(segment_count_nat) << (sbi->log_blocks_per_seg - 1); fd = open("dump_nat", O_CREAT|O_WRONLY|O_TRUNC, 0666); ASSERT(fd >= 0); for (block_off = 0; block_off < nr_nat_blks; pack = 1, block_off++) { seg_off = block_off >> sbi->log_blocks_per_seg; block_addr = (pgoff_t)(nm_i->nat_blkaddr + (seg_off << sbi->log_blocks_per_seg << 1) + (block_off & ((1 << sbi->log_blocks_per_seg) - 1))); if (f2fs_test_bit(block_off, nm_i->nat_bitmap)) { block_addr += sbi->blocks_per_seg; pack = 2; } ret = dev_read_block(nat_block, block_addr); ASSERT(ret >= 0); nid = block_off * NAT_ENTRY_PER_BLOCK; for (i = 0; i < NAT_ENTRY_PER_BLOCK; i++) { struct f2fs_nat_entry raw_nat; struct node_info ni; ni.nid = nid + i; if(nid + i == 0 || nid + i == 1 || nid + i == 2 ) continue; if (lookup_nat_in_journal(sbi, nid + i, &raw_nat) >= 0) { node_info_from_raw_nat(&ni, &raw_nat); ret = dev_read_block(node_block, ni.blk_addr); ASSERT(ret >= 0); if (ni.blk_addr != 0x0) { memset(buf, 0, BUF_SZ); snprintf(buf, BUF_SZ, "nid:%5u\tino:%5u\toffset:%5u" "\tblkaddr:%10u\tpack:%d\n", ni.nid, ni.ino, le32_to_cpu(node_block->footer.flag) >> OFFSET_BIT_SHIFT, ni.blk_addr, pack); ret = write(fd, buf, strlen(buf)); ASSERT(ret >= 0); } } else { node_info_from_raw_nat(&ni, &nat_block->entries[i]); if (ni.blk_addr == 0) continue; ret = dev_read_block(node_block, ni.blk_addr); ASSERT(ret >= 0); memset(buf, 0, BUF_SZ); snprintf(buf, BUF_SZ, "nid:%5u\tino:%5u\toffset:%5u" "\tblkaddr:%10u\tpack:%d\n", ni.nid, ni.ino, le32_to_cpu(node_block->footer.flag) >> OFFSET_BIT_SHIFT, ni.blk_addr, pack); ret = write(fd, buf, strlen(buf)); ASSERT(ret >= 0); } } } free(nat_block); free(node_block); close(fd); } void sit_dump(struct f2fs_sb_info *sbi, unsigned int start_sit, unsigned int end_sit) { struct seg_entry *se; struct sit_info *sit_i = SIT_I(sbi); unsigned int segno; char buf[BUF_SZ]; u32 free_segs = 0;; u64 valid_blocks = 0; int ret; int fd, i; unsigned int offset; fd = open("dump_sit", O_CREAT|O_WRONLY|O_TRUNC, 0666); ASSERT(fd >= 0); snprintf(buf, BUF_SZ, "segment_type(0:HD, 1:WD, 2:CD, " "3:HN, 4:WN, 5:CN)\n"); ret = write(fd, buf, strlen(buf)); ASSERT(ret >= 0); for (segno = start_sit; segno < end_sit; segno++) { se = get_seg_entry(sbi, segno); offset = SIT_BLOCK_OFFSET(sit_i, segno); memset(buf, 0, BUF_SZ); snprintf(buf, BUF_SZ, "\nsegno:%8u\tvblocks:%3u\tseg_type:%d\tsit_pack:%d\n\n", segno, se->valid_blocks, se->type, f2fs_test_bit(offset, sit_i->sit_bitmap) ? 2 : 1); ret = write(fd, buf, strlen(buf)); ASSERT(ret >= 0); if (se->valid_blocks == 0x0) { free_segs++; continue; } ASSERT(se->valid_blocks <= 512); valid_blocks += se->valid_blocks; for (i = 0; i < 64; i++) { memset(buf, 0, BUF_SZ); snprintf(buf, BUF_SZ, " %02x", *(se->cur_valid_map + i)); ret = write(fd, buf, strlen(buf)); ASSERT(ret >= 0); if ((i + 1) % 16 == 0) { snprintf(buf, BUF_SZ, "\n"); ret = write(fd, buf, strlen(buf)); ASSERT(ret >= 0); } } } memset(buf, 0, BUF_SZ); snprintf(buf, BUF_SZ, "valid_blocks:[0x%" PRIx64 "]\tvalid_segs:%d\t free_segs:%d\n", valid_blocks, SM_I(sbi)->main_segments - free_segs, free_segs); ret = write(fd, buf, strlen(buf)); ASSERT(ret >= 0); close(fd); } void ssa_dump(struct f2fs_sb_info *sbi, int start_ssa, int end_ssa) { struct f2fs_summary_block *sum_blk; char buf[BUF_SZ]; int segno, i, ret; int fd; fd = open("dump_ssa", O_CREAT|O_WRONLY|O_TRUNC, 0666); ASSERT(fd >= 0); snprintf(buf, BUF_SZ, "Note: dump.f2fs -b blkaddr = 0x%x + segno * " " 0x200 + offset\n", sbi->sm_info->main_blkaddr); ret = write(fd, buf, strlen(buf)); ASSERT(ret >= 0); for (segno = start_ssa; segno < end_ssa; segno++) { sum_blk = get_sum_block(sbi, segno, &ret); memset(buf, 0, BUF_SZ); switch (ret) { case SEG_TYPE_CUR_NODE: snprintf(buf, BUF_SZ, "\n\nsegno: %x, Current Node\n", segno); break; case SEG_TYPE_CUR_DATA: snprintf(buf, BUF_SZ, "\n\nsegno: %x, Current Data\n", segno); break; case SEG_TYPE_NODE: snprintf(buf, BUF_SZ, "\n\nsegno: %x, Node\n", segno); break; case SEG_TYPE_DATA: snprintf(buf, BUF_SZ, "\n\nsegno: %x, Data\n", segno); break; } ret = write(fd, buf, strlen(buf)); ASSERT(ret >= 0); for (i = 0; i < ENTRIES_IN_SUM; i++) { memset(buf, 0, BUF_SZ); if (i % 10 == 0) { buf[0] = '\n'; ret = write(fd, buf, strlen(buf)); ASSERT(ret >= 0); } snprintf(buf, BUF_SZ, "[%3d: %6x]", i, le32_to_cpu(sum_blk->entries[i].nid)); ret = write(fd, buf, strlen(buf)); ASSERT(ret >= 0); } if (ret == SEG_TYPE_NODE || ret == SEG_TYPE_DATA || ret == SEG_TYPE_MAX) free(sum_blk); } close(fd); } static void dump_data_blk(struct f2fs_sb_info *sbi, __u64 offset, u32 blkaddr) { char buf[F2FS_BLKSIZE]; if (blkaddr == NULL_ADDR) return; /* get data */ if (blkaddr == NEW_ADDR || !IS_VALID_BLK_ADDR(sbi, blkaddr)) { memset(buf, 0, F2FS_BLKSIZE); } else { int ret; ret = dev_read_block(buf, blkaddr); ASSERT(ret >= 0); } /* write blkaddr */ dev_write_dump(buf, offset, F2FS_BLKSIZE); } static void dump_node_blk(struct f2fs_sb_info *sbi, int ntype, u32 nid, u64 *ofs) { struct node_info ni; struct f2fs_node *node_blk; u32 skip = 0; u32 i, idx; switch (ntype) { case TYPE_DIRECT_NODE: skip = idx = ADDRS_PER_BLOCK; break; case TYPE_INDIRECT_NODE: idx = NIDS_PER_BLOCK; skip = idx * ADDRS_PER_BLOCK; break; case TYPE_DOUBLE_INDIRECT_NODE: skip = 0; idx = NIDS_PER_BLOCK; break; } if (nid == 0) { *ofs += skip; return; } get_node_info(sbi, nid, &ni); node_blk = calloc(BLOCK_SZ, 1); dev_read_block(node_blk, ni.blk_addr); for (i = 0; i < idx; i++, (*ofs)++) { switch (ntype) { case TYPE_DIRECT_NODE: dump_data_blk(sbi, *ofs * F2FS_BLKSIZE, le32_to_cpu(node_blk->dn.addr[i])); break; case TYPE_INDIRECT_NODE: dump_node_blk(sbi, TYPE_DIRECT_NODE, le32_to_cpu(node_blk->in.nid[i]), ofs); break; case TYPE_DOUBLE_INDIRECT_NODE: dump_node_blk(sbi, TYPE_INDIRECT_NODE, le32_to_cpu(node_blk->in.nid[i]), ofs); break; } } free(node_blk); } #ifdef HAVE_FSETXATTR static void dump_xattr(struct f2fs_sb_info *sbi, struct f2fs_node *node_blk) { void *xattr; struct f2fs_xattr_entry *ent; char xattr_name[F2FS_NAME_LEN] = {0}; int ret; xattr = read_all_xattrs(sbi, node_blk); list_for_each_xattr(ent, xattr) { char *name = strndup(ent->e_name, ent->e_name_len); void *value = ent->e_name + ent->e_name_len; if (!name) continue; switch (ent->e_name_index) { case F2FS_XATTR_INDEX_USER: ret = snprintf(xattr_name, F2FS_NAME_LEN, "%s%s", XATTR_USER_PREFIX, name); break; case F2FS_XATTR_INDEX_SECURITY: ret = snprintf(xattr_name, F2FS_NAME_LEN, "%s%s", XATTR_SECURITY_PREFIX, name); break; case F2FS_XATTR_INDEX_TRUSTED: ret = snprintf(xattr_name, F2FS_NAME_LEN, "%s%s", XATTR_TRUSTED_PREFIX, name); break; default: MSG(0, "Unknown xattr index 0x%x\n", ent->e_name_index); free(name); continue; } if (ret >= F2FS_NAME_LEN) { MSG(0, "XATTR index 0x%x name too long\n", ent->e_name_index); free(name); continue; } DBG(1, "fd %d xattr_name %s\n", c.dump_fd, xattr_name); #if defined(__linux__) ret = fsetxattr(c.dump_fd, xattr_name, value, le16_to_cpu(ent->e_value_size), 0); #elif defined(__APPLE__) ret = fsetxattr(c.dump_fd, xattr_name, value, le16_to_cpu(ent->e_value_size), 0, XATTR_CREATE); #endif if (ret) MSG(0, "XATTR index 0x%x set xattr failed error %d\n", ent->e_name_index, errno); free(name); } free(xattr); } #else static void dump_xattr(struct f2fs_sb_info *UNUSED(sbi), struct f2fs_node *UNUSED(node_blk)) { MSG(0, "XATTR does not support\n"); } #endif static void dump_inode_blk(struct f2fs_sb_info *sbi, u32 nid, struct f2fs_node *node_blk) { u32 i = 0; u64 ofs = 0; if((node_blk->i.i_inline & F2FS_INLINE_DATA)) { DBG(3, "ino[0x%x] has inline data!\n", nid); /* recover from inline data */ dev_write_dump(((unsigned char *)node_blk) + INLINE_DATA_OFFSET, 0, MAX_INLINE_DATA(node_blk)); return; } /* check data blocks in inode */ for (i = 0; i < ADDRS_PER_INODE(&node_blk->i); i++, ofs++) dump_data_blk(sbi, ofs * F2FS_BLKSIZE, le32_to_cpu( node_blk->i.i_addr[get_extra_isize(node_blk) + i])); /* check node blocks in inode */ for (i = 0; i < 5; i++) { if (i == 0 || i == 1) dump_node_blk(sbi, TYPE_DIRECT_NODE, le32_to_cpu(node_blk->i.i_nid[i]), &ofs); else if (i == 2 || i == 3) dump_node_blk(sbi, TYPE_INDIRECT_NODE, le32_to_cpu(node_blk->i.i_nid[i]), &ofs); else if (i == 4) dump_node_blk(sbi, TYPE_DOUBLE_INDIRECT_NODE, le32_to_cpu(node_blk->i.i_nid[i]), &ofs); else ASSERT(0); } dump_xattr(sbi, node_blk); } static void dump_file(struct f2fs_sb_info *sbi, struct node_info *ni, struct f2fs_node *node_blk, int force) { struct f2fs_inode *inode = &node_blk->i; u32 imode = le32_to_cpu(inode->i_mode); u32 namelen = le32_to_cpu(inode->i_namelen); char name[F2FS_NAME_LEN + 1] = {0}; char path[1024] = {0}; char ans[255] = {0}; int is_encrypted = file_is_encrypt(inode); int ret; if (is_encrypted) { MSG(force, "File is encrypted\n"); return; } if (!S_ISREG(imode) || namelen == 0 || namelen > F2FS_NAME_LEN) { MSG(force, "Not a regular file or wrong name info\n\n"); return; } if (force) goto dump; printf("Do you want to dump this file into ./lost_found/? [Y/N] "); ret = scanf("%s", ans); ASSERT(ret >= 0); if (!strcasecmp(ans, "y")) { dump: ret = system("mkdir -p ./lost_found"); ASSERT(ret >= 0); /* make a file */ strncpy(name, (const char *)inode->i_name, namelen); name[namelen] = 0; sprintf(path, "./lost_found/%s", name); c.dump_fd = open(path, O_TRUNC|O_CREAT|O_RDWR, 0666); ASSERT(c.dump_fd >= 0); /* dump file's data */ dump_inode_blk(sbi, ni->ino, node_blk); /* adjust file size */ ret = ftruncate(c.dump_fd, le32_to_cpu(inode->i_size)); ASSERT(ret >= 0); close(c.dump_fd); } } void dump_node(struct f2fs_sb_info *sbi, nid_t nid, int force) { struct node_info ni; struct f2fs_node *node_blk; get_node_info(sbi, nid, &ni); node_blk = calloc(BLOCK_SZ, 1); dev_read_block(node_blk, ni.blk_addr); DBG(1, "Node ID [0x%x]\n", nid); DBG(1, "nat_entry.block_addr [0x%x]\n", ni.blk_addr); DBG(1, "nat_entry.version [0x%x]\n", ni.version); DBG(1, "nat_entry.ino [0x%x]\n", ni.ino); if (ni.blk_addr == 0x0) MSG(force, "Invalid nat entry\n\n"); DBG(1, "node_blk.footer.ino [0x%x]\n", le32_to_cpu(node_blk->footer.ino)); DBG(1, "node_blk.footer.nid [0x%x]\n", le32_to_cpu(node_blk->footer.nid)); if (le32_to_cpu(node_blk->footer.ino) == ni.ino && le32_to_cpu(node_blk->footer.nid) == ni.nid && ni.ino == ni.nid) { print_node_info(sbi, node_blk, force); dump_file(sbi, &ni, node_blk, force); } else { print_node_info(sbi, node_blk, force); MSG(force, "Invalid (i)node block\n\n"); } free(node_blk); } static void dump_node_from_blkaddr(struct f2fs_sb_info *sbi, u32 blk_addr) { struct f2fs_node *node_blk; int ret; node_blk = calloc(BLOCK_SZ, 1); ASSERT(node_blk); ret = dev_read_block(node_blk, blk_addr); ASSERT(ret >= 0); if (c.dbg_lv > 0) print_node_info(sbi, node_blk, 0); else print_inode_info(sbi, node_blk, 1); free(node_blk); } static void dump_data_offset(u32 blk_addr, int ofs_in_node) { struct f2fs_node *node_blk; unsigned int indirect_blks = 2 * NIDS_PER_BLOCK + 4; unsigned int bidx = 0; unsigned int node_ofs; int ret; node_blk = calloc(BLOCK_SZ, 1); ASSERT(node_blk); ret = dev_read_block(node_blk, blk_addr); ASSERT(ret >= 0); node_ofs = ofs_of_node(node_blk); if (node_ofs == 0) goto got_it; if (node_ofs > 0 && node_ofs <= 2) { bidx = node_ofs - 1; } else if (node_ofs <= indirect_blks) { int dec = (node_ofs - 4) / (NIDS_PER_BLOCK + 1); bidx = node_ofs - 2 - dec; } else { int dec = (node_ofs - indirect_blks - 3) / (NIDS_PER_BLOCK + 1); bidx = node_ofs - 5 - dec; } bidx = bidx * ADDRS_PER_BLOCK + ADDRS_PER_INODE(&node_blk->i); got_it: bidx += ofs_in_node; setlocale(LC_ALL, ""); MSG(0, " - Data offset : 0x%x (4KB), %'u (bytes)\n", bidx, bidx * 4096); free(node_blk); } static void dump_node_offset(u32 blk_addr) { struct f2fs_node *node_blk; int ret; node_blk = calloc(BLOCK_SZ, 1); ASSERT(node_blk); ret = dev_read_block(node_blk, blk_addr); ASSERT(ret >= 0); MSG(0, " - Node offset : 0x%x\n", ofs_of_node(node_blk)); free(node_blk); } int dump_info_from_blkaddr(struct f2fs_sb_info *sbi, u32 blk_addr) { nid_t nid; int type; struct f2fs_summary sum_entry; struct node_info ni, ino_ni; struct seg_entry *se; u32 offset; int ret = 0; MSG(0, "\n== Dump data from block address ==\n\n"); if (blk_addr < SM_I(sbi)->seg0_blkaddr) { MSG(0, "\nFS Reserved Area for SEG #0: "); ret = -EINVAL; } else if (blk_addr < SIT_I(sbi)->sit_base_addr) { MSG(0, "\nFS Metadata Area: "); ret = -EINVAL; } else if (blk_addr < NM_I(sbi)->nat_blkaddr) { MSG(0, "\nFS SIT Area: "); ret = -EINVAL; } else if (blk_addr < SM_I(sbi)->ssa_blkaddr) { MSG(0, "\nFS NAT Area: "); ret = -EINVAL; } else if (blk_addr < SM_I(sbi)->main_blkaddr) { MSG(0, "\nFS SSA Area: "); ret = -EINVAL; } else if (blk_addr > __end_block_addr(sbi)) { MSG(0, "\nOut of address space: "); ret = -EINVAL; } if (ret) { MSG(0, "User data is from 0x%x to 0x%x\n\n", SM_I(sbi)->main_blkaddr, __end_block_addr(sbi)); return ret; } se = get_seg_entry(sbi, GET_SEGNO(sbi, blk_addr)); offset = OFFSET_IN_SEG(sbi, blk_addr); if (f2fs_test_bit(offset, (const char *)se->cur_valid_map) == 0) { MSG(0, "\nblkaddr is not valid\n"); } type = get_sum_entry(sbi, blk_addr, &sum_entry); nid = le32_to_cpu(sum_entry.nid); get_node_info(sbi, nid, &ni); DBG(1, "Note: blkaddr = main_blkaddr + segno * 512 + offset\n"); DBG(1, "Block_addr [0x%x]\n", blk_addr); DBG(1, " - Segno [0x%x]\n", GET_SEGNO(sbi, blk_addr)); DBG(1, " - Offset [0x%x]\n", OFFSET_IN_SEG(sbi, blk_addr)); DBG(1, "SUM.nid [0x%x]\n", nid); DBG(1, "SUM.type [%s]\n", type >= 0 ? seg_type_name[type] : "Broken"); DBG(1, "SUM.version [%d]\n", sum_entry.version); DBG(1, "SUM.ofs_in_node [0x%x]\n", sum_entry.ofs_in_node); DBG(1, "NAT.blkaddr [0x%x]\n", ni.blk_addr); DBG(1, "NAT.ino [0x%x]\n", ni.ino); get_node_info(sbi, ni.ino, &ino_ni); /* inode block address */ if (ni.blk_addr == NULL_ADDR || ino_ni.blk_addr == NULL_ADDR) { MSG(0, "FS Userdata Area: Obsolete block from 0x%x\n", blk_addr); return -EINVAL; } /* print inode */ if (c.dbg_lv > 0) dump_node_from_blkaddr(sbi, ino_ni.blk_addr); if (type == SEG_TYPE_CUR_DATA || type == SEG_TYPE_DATA) { MSG(0, "FS Userdata Area: Data block from 0x%x\n", blk_addr); MSG(0, " - Direct node block : id = 0x%x from 0x%x\n", nid, ni.blk_addr); MSG(0, " - Inode block : id = 0x%x from 0x%x\n", ni.ino, ino_ni.blk_addr); dump_node_from_blkaddr(sbi, ino_ni.blk_addr); dump_data_offset(ni.blk_addr, le16_to_cpu(sum_entry.ofs_in_node)); } else { MSG(0, "FS Userdata Area: Node block from 0x%x\n", blk_addr); if (ni.ino == ni.nid) { MSG(0, " - Inode block : id = 0x%x from 0x%x\n", ni.ino, ino_ni.blk_addr); dump_node_from_blkaddr(sbi, ino_ni.blk_addr); } else { MSG(0, " - Node block : id = 0x%x from 0x%x\n", nid, ni.blk_addr); MSG(0, " - Inode block : id = 0x%x from 0x%x\n", ni.ino, ino_ni.blk_addr); dump_node_from_blkaddr(sbi, ino_ni.blk_addr); dump_node_offset(ni.blk_addr); } } return 0; } f2fs-tools-1.10.0/fsck/f2fs.h000066400000000000000000000335211323417143600155360ustar00rootroot00000000000000/** * f2fs.h * * Copyright (c) 2013 Samsung Electronics Co., Ltd. * http://www.samsung.com/ * * 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. */ #ifndef _F2FS_H_ #define _F2FS_H_ #include #include #include #include #include #include #include #include #ifdef HAVE_MNTENT_H #include #endif #include #include #include #include #define EXIT_ERR_CODE (-1) #define ver_after(a, b) (typecheck(unsigned long long, a) && \ typecheck(unsigned long long, b) && \ ((long long)((a) - (b)) > 0)) struct list_head { struct list_head *next, *prev; }; enum { NAT_BITMAP, SIT_BITMAP }; struct node_info { nid_t nid; nid_t ino; u32 blk_addr; unsigned char version; }; struct f2fs_nm_info { block_t nat_blkaddr; nid_t max_nid; nid_t init_scan_nid; nid_t next_scan_nid; unsigned int nat_cnt; unsigned int fcnt; char *nat_bitmap; int bitmap_size; char *nid_bitmap; }; struct seg_entry { unsigned short valid_blocks; /* # of valid blocks */ unsigned char *cur_valid_map; /* validity bitmap of blocks */ unsigned char type; /* segment type like CURSEG_XXX_TYPE */ unsigned char orig_type; /* segment type like CURSEG_XXX_TYPE */ unsigned long long mtime; /* modification time of the segment */ int dirty; }; struct sec_entry { unsigned int valid_blocks; /* # of valid blocks in a section */ }; struct sit_info { block_t sit_base_addr; /* start block address of SIT area */ block_t sit_blocks; /* # of blocks used by SIT area */ block_t written_valid_blocks; /* # of valid blocks in main area */ char *sit_bitmap; /* SIT bitmap pointer */ unsigned int bitmap_size; /* SIT bitmap size */ unsigned long *dirty_sentries_bitmap; /* bitmap for dirty sentries */ unsigned int dirty_sentries; /* # of dirty sentries */ unsigned int sents_per_block; /* # of SIT entries per block */ struct seg_entry *sentries; /* SIT segment-level cache */ struct sec_entry *sec_entries; /* SIT section-level cache */ unsigned long long elapsed_time; /* elapsed time after mount */ unsigned long long mounted_time; /* mount time */ unsigned long long min_mtime; /* min. modification time */ unsigned long long max_mtime; /* max. modification time */ }; struct curseg_info { struct f2fs_summary_block *sum_blk; /* cached summary block */ unsigned char alloc_type; /* current allocation type */ unsigned int segno; /* current segment number */ unsigned short next_blkoff; /* next block offset to write */ unsigned int zone; /* current zone number */ unsigned int next_segno; /* preallocated segment */ }; struct f2fs_sm_info { struct sit_info *sit_info; struct curseg_info *curseg_array; block_t seg0_blkaddr; block_t main_blkaddr; block_t ssa_blkaddr; unsigned int segment_count; unsigned int main_segments; unsigned int reserved_segments; unsigned int ovp_segments; }; struct f2fs_dentry_ptr { struct inode *inode; u8 *bitmap; struct f2fs_dir_entry *dentry; __u8 (*filename)[F2FS_SLOT_LEN]; int max; int nr_bitmap; }; struct dentry { char *path; char *full_path; const u8 *name; int len; char *link; unsigned long size; u8 file_type; u16 mode; u16 uid; u16 gid; u32 *inode; u32 mtime; char *secon; uint64_t capabilities; nid_t ino; nid_t pino; }; /* different from dnode_of_data in kernel */ struct dnode_of_data { struct f2fs_node *inode_blk; /* inode page */ struct f2fs_node *node_blk; /* cached direct node page */ nid_t nid; unsigned int ofs_in_node; block_t data_blkaddr; block_t node_blkaddr; int idirty, ndirty; }; struct f2fs_sb_info { struct f2fs_fsck *fsck; struct f2fs_super_block *raw_super; struct f2fs_nm_info *nm_info; struct f2fs_sm_info *sm_info; struct f2fs_checkpoint *ckpt; int cur_cp; struct list_head orphan_inode_list; unsigned int n_orphans; /* basic file system units */ unsigned int log_sectors_per_block; /* log2 sectors per block */ unsigned int log_blocksize; /* log2 block size */ unsigned int blocksize; /* block size */ unsigned int root_ino_num; /* root inode number*/ unsigned int node_ino_num; /* node inode number*/ unsigned int meta_ino_num; /* meta inode number*/ unsigned int log_blocks_per_seg; /* log2 blocks per segment */ unsigned int blocks_per_seg; /* blocks per segment */ unsigned int segs_per_sec; /* segments per section */ unsigned int secs_per_zone; /* sections per zone */ unsigned int total_sections; /* total section count */ unsigned int total_node_count; /* total node block count */ unsigned int total_valid_node_count; /* valid node block count */ unsigned int total_valid_inode_count; /* valid inode count */ int active_logs; /* # of active logs */ block_t user_block_count; /* # of user blocks */ block_t total_valid_block_count; /* # of valid blocks */ block_t alloc_valid_block_count; /* # of allocated blocks */ block_t last_valid_block_count; /* for recovery */ u32 s_next_generation; /* for NFS support */ unsigned int cur_victim_sec; /* current victim section num */ u32 free_segments; }; static inline struct f2fs_super_block *F2FS_RAW_SUPER(struct f2fs_sb_info *sbi) { return (struct f2fs_super_block *)(sbi->raw_super); } static inline struct f2fs_checkpoint *F2FS_CKPT(struct f2fs_sb_info *sbi) { return (struct f2fs_checkpoint *)(sbi->ckpt); } static inline struct f2fs_fsck *F2FS_FSCK(struct f2fs_sb_info *sbi) { return (struct f2fs_fsck *)(sbi->fsck); } static inline struct f2fs_nm_info *NM_I(struct f2fs_sb_info *sbi) { return (struct f2fs_nm_info *)(sbi->nm_info); } static inline struct f2fs_sm_info *SM_I(struct f2fs_sb_info *sbi) { return (struct f2fs_sm_info *)(sbi->sm_info); } static inline struct sit_info *SIT_I(struct f2fs_sb_info *sbi) { return (struct sit_info *)(SM_I(sbi)->sit_info); } static inline void *inline_data_addr(struct f2fs_node *node_blk) { int ofs = get_extra_isize(node_blk) + DEF_INLINE_RESERVED_SIZE; return (void *)&(node_blk->i.i_addr[ofs]); } static inline unsigned int ofs_of_node(struct f2fs_node *node_blk) { unsigned flag = le32_to_cpu(node_blk->footer.flag); return flag >> OFFSET_BIT_SHIFT; } static inline unsigned long __bitmap_size(struct f2fs_sb_info *sbi, int flag) { struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi); /* return NAT or SIT bitmap */ if (flag == NAT_BITMAP) return le32_to_cpu(ckpt->nat_ver_bitmap_bytesize); else if (flag == SIT_BITMAP) return le32_to_cpu(ckpt->sit_ver_bitmap_bytesize); return 0; } static inline void *__bitmap_ptr(struct f2fs_sb_info *sbi, int flag) { struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi); int offset; if (le32_to_cpu(F2FS_RAW_SUPER(sbi)->cp_payload) > 0) { if (flag == NAT_BITMAP) return &ckpt->sit_nat_version_bitmap; else return ((char *)ckpt + F2FS_BLKSIZE); } else { offset = (flag == NAT_BITMAP) ? le32_to_cpu(ckpt->sit_ver_bitmap_bytesize) : 0; return &ckpt->sit_nat_version_bitmap + offset; } } static inline bool is_set_ckpt_flags(struct f2fs_checkpoint *cp, unsigned int f) { unsigned int ckpt_flags = le32_to_cpu(cp->ckpt_flags); return ckpt_flags & f ? 1 : 0; } static inline block_t __start_cp_addr(struct f2fs_sb_info *sbi) { block_t start_addr = le32_to_cpu(F2FS_RAW_SUPER(sbi)->cp_blkaddr); if (sbi->cur_cp == 2) start_addr += sbi->blocks_per_seg; return start_addr; } static inline block_t __start_sum_addr(struct f2fs_sb_info *sbi) { return le32_to_cpu(F2FS_CKPT(sbi)->cp_pack_start_sum); } static inline block_t __end_block_addr(struct f2fs_sb_info *sbi) { block_t end = SM_I(sbi)->main_blkaddr; return end + le64_to_cpu(F2FS_RAW_SUPER(sbi)->block_count); } #define GET_ZONENO_FROM_SEGNO(sbi, segno) \ ((segno / sbi->segs_per_sec) / sbi->secs_per_zone) #define IS_DATASEG(t) \ ((t == CURSEG_HOT_DATA) || (t == CURSEG_COLD_DATA) || \ (t == CURSEG_WARM_DATA)) #define IS_NODESEG(t) \ ((t == CURSEG_HOT_NODE) || (t == CURSEG_COLD_NODE) || \ (t == CURSEG_WARM_NODE)) #define GET_SUM_BLKADDR(sbi, segno) \ ((sbi->sm_info->ssa_blkaddr) + segno) #define GET_SEGOFF_FROM_SEG0(sbi, blk_addr) \ ((blk_addr) - SM_I(sbi)->seg0_blkaddr) #define GET_SEGNO_FROM_SEG0(sbi, blk_addr) \ (GET_SEGOFF_FROM_SEG0(sbi, blk_addr) >> sbi->log_blocks_per_seg) #define GET_BLKOFF_FROM_SEG0(sbi, blk_addr) \ (GET_SEGOFF_FROM_SEG0(sbi, blk_addr) & (sbi->blocks_per_seg - 1)) #define FREE_I_START_SEGNO(sbi) \ GET_SEGNO_FROM_SEG0(sbi, SM_I(sbi)->main_blkaddr) #define GET_R2L_SEGNO(sbi, segno) (segno + FREE_I_START_SEGNO(sbi)) #define START_BLOCK(sbi, segno) (SM_I(sbi)->main_blkaddr + \ ((segno) << sbi->log_blocks_per_seg)) static inline struct curseg_info *CURSEG_I(struct f2fs_sb_info *sbi, int type) { return (struct curseg_info *)(SM_I(sbi)->curseg_array + type); } static inline block_t start_sum_block(struct f2fs_sb_info *sbi) { return __start_cp_addr(sbi) + le32_to_cpu(F2FS_CKPT(sbi)->cp_pack_start_sum); } static inline block_t sum_blk_addr(struct f2fs_sb_info *sbi, int base, int type) { return __start_cp_addr(sbi) + le32_to_cpu(F2FS_CKPT(sbi)->cp_pack_total_block_count) - (base + 1) + type; } #define nats_in_cursum(jnl) (le16_to_cpu(jnl->n_nats)) #define sits_in_cursum(jnl) (le16_to_cpu(jnl->n_sits)) #define nat_in_journal(jnl, i) (jnl->nat_j.entries[i].ne) #define nid_in_journal(jnl, i) (jnl->nat_j.entries[i].nid) #define sit_in_journal(jnl, i) (jnl->sit_j.entries[i].se) #define segno_in_journal(jnl, i) (jnl->sit_j.entries[i].segno) #define SIT_ENTRY_OFFSET(sit_i, segno) \ ((segno) % sit_i->sents_per_block) #define SIT_BLOCK_OFFSET(sit_i, segno) \ ((segno) / SIT_ENTRY_PER_BLOCK) #define TOTAL_SEGS(sbi) (SM_I(sbi)->main_segments) static inline bool IS_VALID_NID(struct f2fs_sb_info *sbi, u32 nid) { return (nid <= (NAT_ENTRY_PER_BLOCK * le32_to_cpu(F2FS_RAW_SUPER(sbi)->segment_count_nat) << (sbi->log_blocks_per_seg - 1))); } static inline bool IS_VALID_BLK_ADDR(struct f2fs_sb_info *sbi, u32 addr) { if (addr >= le64_to_cpu(F2FS_RAW_SUPER(sbi)->block_count) || addr < SM_I(sbi)->main_blkaddr) { DBG(1, "block addr [0x%x]\n", addr); return 0; } /* next block offset will be checked at the end of fsck. */ return 1; } static inline int IS_CUR_SEGNO(struct f2fs_sb_info *sbi, u32 segno, int type) { int i; for (i = 0; i < NO_CHECK_TYPE; i++) { struct curseg_info *curseg = CURSEG_I(sbi, i); if (type == i) continue; if (segno == curseg->segno) return 1; } return 0; } static inline u64 BLKOFF_FROM_MAIN(struct f2fs_sb_info *sbi, u64 blk_addr) { ASSERT(blk_addr >= SM_I(sbi)->main_blkaddr); return blk_addr - SM_I(sbi)->main_blkaddr; } static inline u32 GET_SEGNO(struct f2fs_sb_info *sbi, u64 blk_addr) { return (u32)(BLKOFF_FROM_MAIN(sbi, blk_addr) >> sbi->log_blocks_per_seg); } static inline u32 OFFSET_IN_SEG(struct f2fs_sb_info *sbi, u64 blk_addr) { return (u32)(BLKOFF_FROM_MAIN(sbi, blk_addr) % (1 << sbi->log_blocks_per_seg)); } static inline void node_info_from_raw_nat(struct node_info *ni, struct f2fs_nat_entry *raw_nat) { ni->ino = le32_to_cpu(raw_nat->ino); ni->blk_addr = le32_to_cpu(raw_nat->block_addr); ni->version = raw_nat->version; } static inline void set_summary(struct f2fs_summary *sum, nid_t nid, unsigned int ofs_in_node, unsigned char version) { sum->nid = cpu_to_le32(nid); sum->ofs_in_node = cpu_to_le16(ofs_in_node); sum->version = version; } #define S_SHIFT 12 static unsigned char f2fs_type_by_mode[S_IFMT >> S_SHIFT] = { [S_IFREG >> S_SHIFT] = F2FS_FT_REG_FILE, [S_IFDIR >> S_SHIFT] = F2FS_FT_DIR, [S_IFCHR >> S_SHIFT] = F2FS_FT_CHRDEV, [S_IFBLK >> S_SHIFT] = F2FS_FT_BLKDEV, [S_IFIFO >> S_SHIFT] = F2FS_FT_FIFO, [S_IFSOCK >> S_SHIFT] = F2FS_FT_SOCK, [S_IFLNK >> S_SHIFT] = F2FS_FT_SYMLINK, }; static inline int map_de_type(umode_t mode) { return f2fs_type_by_mode[(mode & S_IFMT) >> S_SHIFT]; } static inline void *inline_xattr_addr(struct f2fs_inode *inode) { return (void *)&(inode->i_addr[DEF_ADDRS_PER_INODE - get_inline_xattr_addrs(inode)]); } static inline int inline_xattr_size(struct f2fs_inode *inode) { return get_inline_xattr_addrs(inode) * sizeof(__le32); } extern int lookup_nat_in_journal(struct f2fs_sb_info *sbi, u32 nid, struct f2fs_nat_entry *ne); #define IS_SUM_NODE_SEG(footer) (footer.entry_type == SUM_TYPE_NODE) #define IS_SUM_DATA_SEG(footer) (footer.entry_type == SUM_TYPE_DATA) static inline unsigned int dir_buckets(unsigned int level, int dir_level) { if (level + dir_level < MAX_DIR_HASH_DEPTH / 2) return 1 << (level + dir_level); else return MAX_DIR_BUCKETS; } static inline unsigned int bucket_blocks(unsigned int level) { if (level < MAX_DIR_HASH_DEPTH / 2) return 2; else return 4; } static inline unsigned long dir_block_index(unsigned int level, int dir_level, unsigned int idx) { unsigned long i; unsigned long bidx = 0; for (i = 0; i < level; i++) bidx += dir_buckets(i, dir_level) * bucket_blocks(i); bidx += idx * bucket_blocks(level); return bidx; } static inline int is_dot_dotdot(const unsigned char *name, const int len) { if (len == 1 && name[0] == '.') return 1; if (len == 2 && name[0] == '.' && name[1] == '.') return 1; return 0; } #endif /* _F2FS_H_ */ f2fs-tools-1.10.0/fsck/fsck.c000066400000000000000000001571521323417143600156260ustar00rootroot00000000000000/** * fsck.c * * Copyright (c) 2013 Samsung Electronics Co., Ltd. * http://www.samsung.com/ * * 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. */ #include "fsck.h" #include "quotaio.h" char *tree_mark; uint32_t tree_mark_size = 256; int f2fs_set_main_bitmap(struct f2fs_sb_info *sbi, u32 blk, int type) { struct f2fs_fsck *fsck = F2FS_FSCK(sbi); struct seg_entry *se; int fix = 0; se = get_seg_entry(sbi, GET_SEGNO(sbi, blk)); if (se->type >= NO_CHECK_TYPE) fix = 1; else if (IS_DATASEG(se->type) != IS_DATASEG(type)) fix = 1; /* just check data and node types */ if (fix) { DBG(1, "Wrong segment type [0x%x] %x -> %x", GET_SEGNO(sbi, blk), se->type, type); se->type = type; } return f2fs_set_bit(BLKOFF_FROM_MAIN(sbi, blk), fsck->main_area_bitmap); } static inline int f2fs_test_main_bitmap(struct f2fs_sb_info *sbi, u32 blk) { struct f2fs_fsck *fsck = F2FS_FSCK(sbi); return f2fs_test_bit(BLKOFF_FROM_MAIN(sbi, blk), fsck->main_area_bitmap); } static inline int f2fs_test_sit_bitmap(struct f2fs_sb_info *sbi, u32 blk) { struct f2fs_fsck *fsck = F2FS_FSCK(sbi); return f2fs_test_bit(BLKOFF_FROM_MAIN(sbi, blk), fsck->sit_area_bitmap); } int f2fs_set_sit_bitmap(struct f2fs_sb_info *sbi, u32 blk) { struct f2fs_fsck *fsck = F2FS_FSCK(sbi); return f2fs_set_bit(BLKOFF_FROM_MAIN(sbi, blk), fsck->sit_area_bitmap); } static int add_into_hard_link_list(struct f2fs_sb_info *sbi, u32 nid, u32 link_cnt) { struct f2fs_fsck *fsck = F2FS_FSCK(sbi); struct hard_link_node *node = NULL, *tmp = NULL, *prev = NULL; node = calloc(sizeof(struct hard_link_node), 1); ASSERT(node != NULL); node->nid = nid; node->links = link_cnt; node->actual_links = 1; node->next = NULL; if (fsck->hard_link_list_head == NULL) { fsck->hard_link_list_head = node; goto out; } tmp = fsck->hard_link_list_head; /* Find insertion position */ while (tmp && (nid < tmp->nid)) { ASSERT(tmp->nid != nid); prev = tmp; tmp = tmp->next; } if (tmp == fsck->hard_link_list_head) { node->next = tmp; fsck->hard_link_list_head = node; } else { prev->next = node; node->next = tmp; } out: DBG(2, "ino[0x%x] has hard links [0x%x]\n", nid, link_cnt); return 0; } static int find_and_dec_hard_link_list(struct f2fs_sb_info *sbi, u32 nid) { struct f2fs_fsck *fsck = F2FS_FSCK(sbi); struct hard_link_node *node = NULL, *prev = NULL; if (fsck->hard_link_list_head == NULL) return -EINVAL; node = fsck->hard_link_list_head; while (node && (nid < node->nid)) { prev = node; node = node->next; } if (node == NULL || (nid != node->nid)) return -EINVAL; /* Decrease link count */ node->links = node->links - 1; node->actual_links++; /* if link count becomes one, remove the node */ if (node->links == 1) { if (fsck->hard_link_list_head == node) fsck->hard_link_list_head = node->next; else prev->next = node->next; free(node); } return 0; } static int is_valid_ssa_node_blk(struct f2fs_sb_info *sbi, u32 nid, u32 blk_addr) { struct f2fs_summary_block *sum_blk; struct f2fs_summary *sum_entry; struct seg_entry * se; u32 segno, offset; int need_fix = 0, ret = 0; int type; segno = GET_SEGNO(sbi, blk_addr); offset = OFFSET_IN_SEG(sbi, blk_addr); sum_blk = get_sum_block(sbi, segno, &type); if (type != SEG_TYPE_NODE && type != SEG_TYPE_CUR_NODE) { /* can't fix current summary, then drop the block */ if (!c.fix_on || type < 0) { ASSERT_MSG("Summary footer is not for node segment"); ret = -EINVAL; goto out; } need_fix = 1; se = get_seg_entry(sbi, segno); if(IS_NODESEG(se->type)) { FIX_MSG("Summary footer indicates a node segment: 0x%x", segno); sum_blk->footer.entry_type = SUM_TYPE_NODE; } else { ret = -EINVAL; goto out; } } sum_entry = &(sum_blk->entries[offset]); if (le32_to_cpu(sum_entry->nid) != nid) { if (!c.fix_on || type < 0) { DBG(0, "nid [0x%x]\n", nid); DBG(0, "target blk_addr [0x%x]\n", blk_addr); DBG(0, "summary blk_addr [0x%x]\n", GET_SUM_BLKADDR(sbi, GET_SEGNO(sbi, blk_addr))); DBG(0, "seg no / offset [0x%x / 0x%x]\n", GET_SEGNO(sbi, blk_addr), OFFSET_IN_SEG(sbi, blk_addr)); DBG(0, "summary_entry.nid [0x%x]\n", le32_to_cpu(sum_entry->nid)); DBG(0, "--> node block's nid [0x%x]\n", nid); ASSERT_MSG("Invalid node seg summary\n"); ret = -EINVAL; } else { FIX_MSG("Set node summary 0x%x -> [0x%x] [0x%x]", segno, nid, blk_addr); sum_entry->nid = cpu_to_le32(nid); need_fix = 1; } } if (need_fix && !c.ro) { u64 ssa_blk; int ret2; ssa_blk = GET_SUM_BLKADDR(sbi, segno); ret2 = dev_write_block(sum_blk, ssa_blk); ASSERT(ret2 >= 0); } out: if (type == SEG_TYPE_NODE || type == SEG_TYPE_DATA || type == SEG_TYPE_MAX) free(sum_blk); return ret; } static int is_valid_summary(struct f2fs_sb_info *sbi, struct f2fs_summary *sum, u32 blk_addr) { u16 ofs_in_node = le16_to_cpu(sum->ofs_in_node); u32 nid = le32_to_cpu(sum->nid); struct f2fs_node *node_blk = NULL; __le32 target_blk_addr; struct node_info ni; int ret = 0; node_blk = (struct f2fs_node *)calloc(BLOCK_SZ, 1); ASSERT(node_blk != NULL); if (!IS_VALID_NID(sbi, nid)) goto out; get_node_info(sbi, nid, &ni); if (!IS_VALID_BLK_ADDR(sbi, ni.blk_addr)) goto out; /* read node_block */ ret = dev_read_block(node_blk, ni.blk_addr); ASSERT(ret >= 0); if (le32_to_cpu(node_blk->footer.nid) != nid) goto out; /* check its block address */ if (node_blk->footer.nid == node_blk->footer.ino) { int ofs = get_extra_isize(node_blk); target_blk_addr = node_blk->i.i_addr[ofs + ofs_in_node]; } else { target_blk_addr = node_blk->dn.addr[ofs_in_node]; } if (blk_addr == le32_to_cpu(target_blk_addr)) ret = 1; out: free(node_blk); return ret; } static int is_valid_ssa_data_blk(struct f2fs_sb_info *sbi, u32 blk_addr, u32 parent_nid, u16 idx_in_node, u8 version) { struct f2fs_summary_block *sum_blk; struct f2fs_summary *sum_entry; struct seg_entry * se; u32 segno, offset; int need_fix = 0, ret = 0; int type; segno = GET_SEGNO(sbi, blk_addr); offset = OFFSET_IN_SEG(sbi, blk_addr); sum_blk = get_sum_block(sbi, segno, &type); if (type != SEG_TYPE_DATA && type != SEG_TYPE_CUR_DATA) { /* can't fix current summary, then drop the block */ if (!c.fix_on || type < 0) { ASSERT_MSG("Summary footer is not for data segment"); ret = -EINVAL; goto out; } need_fix = 1; se = get_seg_entry(sbi, segno); if (IS_DATASEG(se->type)) { FIX_MSG("Summary footer indicates a data segment: 0x%x", segno); sum_blk->footer.entry_type = SUM_TYPE_DATA; } else { ret = -EINVAL; goto out; } } sum_entry = &(sum_blk->entries[offset]); if (le32_to_cpu(sum_entry->nid) != parent_nid || sum_entry->version != version || le16_to_cpu(sum_entry->ofs_in_node) != idx_in_node) { if (!c.fix_on || type < 0) { DBG(0, "summary_entry.nid [0x%x]\n", le32_to_cpu(sum_entry->nid)); DBG(0, "summary_entry.version [0x%x]\n", sum_entry->version); DBG(0, "summary_entry.ofs_in_node [0x%x]\n", le16_to_cpu(sum_entry->ofs_in_node)); DBG(0, "parent nid [0x%x]\n", parent_nid); DBG(0, "version from nat [0x%x]\n", version); DBG(0, "idx in parent node [0x%x]\n", idx_in_node); DBG(0, "Target data block addr [0x%x]\n", blk_addr); ASSERT_MSG("Invalid data seg summary\n"); ret = -EINVAL; } else if (is_valid_summary(sbi, sum_entry, blk_addr)) { /* delete wrong index */ ret = -EINVAL; } else { FIX_MSG("Set data summary 0x%x -> [0x%x] [0x%x] [0x%x]", segno, parent_nid, version, idx_in_node); sum_entry->nid = cpu_to_le32(parent_nid); sum_entry->version = version; sum_entry->ofs_in_node = cpu_to_le16(idx_in_node); need_fix = 1; } } if (need_fix && !c.ro) { u64 ssa_blk; int ret2; ssa_blk = GET_SUM_BLKADDR(sbi, segno); ret2 = dev_write_block(sum_blk, ssa_blk); ASSERT(ret2 >= 0); } out: if (type == SEG_TYPE_NODE || type == SEG_TYPE_DATA || type == SEG_TYPE_MAX) free(sum_blk); return ret; } static int __check_inode_mode(u32 nid, enum FILE_TYPE ftype, u32 mode) { if (ftype >= F2FS_FT_MAX) return 0; if (S_ISLNK(mode) && ftype != F2FS_FT_SYMLINK) goto err; if (S_ISREG(mode) && ftype != F2FS_FT_REG_FILE) goto err; if (S_ISDIR(mode) && ftype != F2FS_FT_DIR) goto err; if (S_ISCHR(mode) && ftype != F2FS_FT_CHRDEV) goto err; if (S_ISBLK(mode) && ftype != F2FS_FT_BLKDEV) goto err; if (S_ISFIFO(mode) && ftype != F2FS_FT_FIFO) goto err; if (S_ISSOCK(mode) && ftype != F2FS_FT_SOCK) goto err; return 0; err: ASSERT_MSG("mismatch i_mode [0x%x] [0x%x vs. 0x%x]", nid, ftype, mode); return -1; } static int sanity_check_nid(struct f2fs_sb_info *sbi, u32 nid, struct f2fs_node *node_blk, enum FILE_TYPE ftype, enum NODE_TYPE ntype, struct node_info *ni) { struct f2fs_fsck *fsck = F2FS_FSCK(sbi); int ret; if (!IS_VALID_NID(sbi, nid)) { ASSERT_MSG("nid is not valid. [0x%x]", nid); return -EINVAL; } get_node_info(sbi, nid, ni); if (ni->ino == 0) { ASSERT_MSG("nid[0x%x] ino is 0", nid); return -EINVAL; } if (ni->blk_addr == NEW_ADDR) { ASSERT_MSG("nid is NEW_ADDR. [0x%x]", nid); return -EINVAL; } if (!IS_VALID_BLK_ADDR(sbi, ni->blk_addr)) { ASSERT_MSG("blkaddress is not valid. [0x%x]", ni->blk_addr); return -EINVAL; } ret = dev_read_block(node_blk, ni->blk_addr); ASSERT(ret >= 0); if (ntype == TYPE_INODE && node_blk->footer.nid != node_blk->footer.ino) { ASSERT_MSG("nid[0x%x] footer.nid[0x%x] footer.ino[0x%x]", nid, le32_to_cpu(node_blk->footer.nid), le32_to_cpu(node_blk->footer.ino)); return -EINVAL; } if (ni->ino != le32_to_cpu(node_blk->footer.ino)) { ASSERT_MSG("nid[0x%x] nat_entry->ino[0x%x] footer.ino[0x%x]", nid, ni->ino, le32_to_cpu(node_blk->footer.ino)); return -EINVAL; } if (ntype != TYPE_INODE && node_blk->footer.nid == node_blk->footer.ino) { ASSERT_MSG("nid[0x%x] footer.nid[0x%x] footer.ino[0x%x]", nid, le32_to_cpu(node_blk->footer.nid), le32_to_cpu(node_blk->footer.ino)); return -EINVAL; } if (le32_to_cpu(node_blk->footer.nid) != nid) { ASSERT_MSG("nid[0x%x] blk_addr[0x%x] footer.nid[0x%x]", nid, ni->blk_addr, le32_to_cpu(node_blk->footer.nid)); return -EINVAL; } if (ntype == TYPE_XATTR) { u32 flag = le32_to_cpu(node_blk->footer.flag); if ((flag >> OFFSET_BIT_SHIFT) != XATTR_NODE_OFFSET) { ASSERT_MSG("xnid[0x%x] has wrong ofs:[0x%x]", nid, flag); return -EINVAL; } } if ((ntype == TYPE_INODE && ftype == F2FS_FT_DIR) || (ntype == TYPE_XATTR && ftype == F2FS_FT_XATTR)) { /* not included '.' & '..' */ if (f2fs_test_main_bitmap(sbi, ni->blk_addr) != 0) { ASSERT_MSG("Duplicated node blk. nid[0x%x][0x%x]\n", nid, ni->blk_addr); return -EINVAL; } } /* this if only from fix_hard_links */ if (ftype == F2FS_FT_MAX) return 0; if (ntype == TYPE_INODE && __check_inode_mode(nid, ftype, le32_to_cpu(node_blk->i.i_mode))) return -EINVAL; /* workaround to fix later */ if (ftype != F2FS_FT_ORPHAN || f2fs_test_bit(nid, fsck->nat_area_bitmap) != 0) f2fs_clear_bit(nid, fsck->nat_area_bitmap); else ASSERT_MSG("orphan or xattr nid is duplicated [0x%x]\n", nid); if (is_valid_ssa_node_blk(sbi, nid, ni->blk_addr)) { ASSERT_MSG("summary node block is not valid. [0x%x]", nid); return -EINVAL; } if (f2fs_test_sit_bitmap(sbi, ni->blk_addr) == 0) ASSERT_MSG("SIT bitmap is 0x0. blk_addr[0x%x]", ni->blk_addr); if (f2fs_test_main_bitmap(sbi, ni->blk_addr) == 0) { fsck->chk.valid_blk_cnt++; fsck->chk.valid_node_cnt++; } return 0; } static int sanity_check_inode(struct f2fs_sb_info *sbi, struct f2fs_node *node) { struct f2fs_fsck *fsck = F2FS_FSCK(sbi); struct f2fs_inode *fi = &node->i; if (!(le16_to_cpu(fi->i_mode) & S_IFMT)) { ASSERT_MSG("i_mode is not valid. [0x%x]", le16_to_cpu(fi->i_mode)); goto remove_node; } return 0; remove_node: f2fs_set_bit(le32_to_cpu(node->footer.ino), fsck->nat_area_bitmap); fsck->chk.valid_blk_cnt--; fsck->chk.valid_node_cnt--; return -EINVAL; } static int fsck_chk_xattr_blk(struct f2fs_sb_info *sbi, u32 ino, u32 x_nid, u32 *blk_cnt) { struct f2fs_node *node_blk = NULL; struct node_info ni; int ret = 0; if (x_nid == 0x0) return 0; node_blk = (struct f2fs_node *)calloc(BLOCK_SZ, 1); ASSERT(node_blk != NULL); /* Sanity check */ if (sanity_check_nid(sbi, x_nid, node_blk, F2FS_FT_XATTR, TYPE_XATTR, &ni)) { ret = -EINVAL; goto out; } *blk_cnt = *blk_cnt + 1; f2fs_set_main_bitmap(sbi, ni.blk_addr, CURSEG_COLD_NODE); DBG(2, "ino[0x%x] x_nid[0x%x]\n", ino, x_nid); out: free(node_blk); return ret; } int fsck_chk_node_blk(struct f2fs_sb_info *sbi, struct f2fs_inode *inode, u32 nid, enum FILE_TYPE ftype, enum NODE_TYPE ntype, u32 *blk_cnt, struct child_info *child) { struct node_info ni; struct f2fs_node *node_blk = NULL; node_blk = (struct f2fs_node *)calloc(BLOCK_SZ, 1); ASSERT(node_blk != NULL); if (sanity_check_nid(sbi, nid, node_blk, ftype, ntype, &ni)) goto err; if (ntype == TYPE_INODE) { struct f2fs_fsck *fsck = F2FS_FSCK(sbi); if (sanity_check_inode(sbi, node_blk)) goto err; fsck_chk_inode_blk(sbi, nid, ftype, node_blk, blk_cnt, &ni, child); quota_add_inode_usage(fsck->qctx, nid, &node_blk->i); } else { switch (ntype) { case TYPE_DIRECT_NODE: f2fs_set_main_bitmap(sbi, ni.blk_addr, CURSEG_WARM_NODE); fsck_chk_dnode_blk(sbi, inode, nid, ftype, node_blk, blk_cnt, child, &ni); break; case TYPE_INDIRECT_NODE: f2fs_set_main_bitmap(sbi, ni.blk_addr, CURSEG_COLD_NODE); fsck_chk_idnode_blk(sbi, inode, ftype, node_blk, blk_cnt, child); break; case TYPE_DOUBLE_INDIRECT_NODE: f2fs_set_main_bitmap(sbi, ni.blk_addr, CURSEG_COLD_NODE); fsck_chk_didnode_blk(sbi, inode, ftype, node_blk, blk_cnt, child); break; default: ASSERT(0); } } free(node_blk); return 0; err: free(node_blk); return -EINVAL; } static inline void get_extent_info(struct extent_info *ext, struct f2fs_extent *i_ext) { ext->fofs = le32_to_cpu(i_ext->fofs); ext->blk = le32_to_cpu(i_ext->blk_addr); ext->len = le32_to_cpu(i_ext->len); } static void check_extent_info(struct child_info *child, block_t blkaddr, int last) { struct extent_info *ei = &child->ei; u32 pgofs = child->pgofs; int is_hole = 0; if (!ei->len) return; if (child->state & FSCK_UNMATCHED_EXTENT) return; if (last) { /* hole exist in the back of extent */ if (child->last_blk != ei->blk + ei->len - 1) child->state |= FSCK_UNMATCHED_EXTENT; return; } if (blkaddr == NULL_ADDR || blkaddr == NEW_ADDR) is_hole = 1; if (pgofs >= ei->fofs && pgofs < ei->fofs + ei->len) { /* unmatched blkaddr */ if (is_hole || (blkaddr != pgofs - ei->fofs + ei->blk)) goto unmatched; if (!child->last_blk) { /* hole exists in the front of extent */ if (pgofs != ei->fofs) goto unmatched; } else if (child->last_blk + 1 != blkaddr) { /* hole exists in the middle of extent */ goto unmatched; } child->last_blk = blkaddr; return; } if (is_hole) return; if (blkaddr < ei->blk || blkaddr >= ei->blk + ei->len) return; /* unmatched file offset */ unmatched: child->state |= FSCK_UNMATCHED_EXTENT; } /* start with valid nid and blkaddr */ void fsck_chk_inode_blk(struct f2fs_sb_info *sbi, u32 nid, enum FILE_TYPE ftype, struct f2fs_node *node_blk, u32 *blk_cnt, struct node_info *ni, struct child_info *child_d) { struct f2fs_fsck *fsck = F2FS_FSCK(sbi); struct child_info child; enum NODE_TYPE ntype; u32 i_links = le32_to_cpu(node_blk->i.i_links); u64 i_size = le64_to_cpu(node_blk->i.i_size); u64 i_blocks = le64_to_cpu(node_blk->i.i_blocks); int ofs = get_extra_isize(node_blk); unsigned char *en; int namelen; unsigned int idx = 0; int need_fix = 0; int ret; memset(&child, 0, sizeof(child)); child.links = 2; child.p_ino = nid; child.pp_ino = le32_to_cpu(node_blk->i.i_pino); child.dir_level = node_blk->i.i_dir_level; if (f2fs_test_main_bitmap(sbi, ni->blk_addr) == 0) fsck->chk.valid_inode_cnt++; if (ftype == F2FS_FT_DIR) { f2fs_set_main_bitmap(sbi, ni->blk_addr, CURSEG_HOT_NODE); } else { if (f2fs_test_main_bitmap(sbi, ni->blk_addr) == 0) { f2fs_set_main_bitmap(sbi, ni->blk_addr, CURSEG_WARM_NODE); if (i_links > 1 && ftype != F2FS_FT_ORPHAN && !is_qf_ino(F2FS_RAW_SUPER(sbi), nid)) { /* First time. Create new hard link node */ add_into_hard_link_list(sbi, nid, i_links); fsck->chk.multi_hard_link_files++; } } else { DBG(3, "[0x%x] has hard links [0x%x]\n", nid, i_links); if (find_and_dec_hard_link_list(sbi, nid)) { ASSERT_MSG("[0x%x] needs more i_links=0x%x", nid, i_links); if (c.fix_on) { node_blk->i.i_links = cpu_to_le32(i_links + 1); need_fix = 1; FIX_MSG("File: 0x%x " "i_links= 0x%x -> 0x%x", nid, i_links, i_links + 1); } goto skip_blkcnt_fix; } /* No need to go deep into the node */ return; } } if (fsck_chk_xattr_blk(sbi, nid, le32_to_cpu(node_blk->i.i_xattr_nid), blk_cnt) && c.fix_on) { node_blk->i.i_xattr_nid = 0; need_fix = 1; FIX_MSG("Remove xattr block: 0x%x, x_nid = 0x%x", nid, le32_to_cpu(node_blk->i.i_xattr_nid)); } if (ftype == F2FS_FT_CHRDEV || ftype == F2FS_FT_BLKDEV || ftype == F2FS_FT_FIFO || ftype == F2FS_FT_SOCK) goto check; if ((node_blk->i.i_inline & F2FS_INLINE_DATA)) { if (le32_to_cpu(node_blk->i.i_addr[ofs]) != 0) { /* should fix this bug all the time */ FIX_MSG("inline_data has wrong 0'th block = %x", le32_to_cpu(node_blk->i.i_addr[ofs])); node_blk->i.i_addr[ofs] = 0; node_blk->i.i_blocks = cpu_to_le64(*blk_cnt); need_fix = 1; } if (!(node_blk->i.i_inline & F2FS_DATA_EXIST)) { char buf[MAX_INLINE_DATA(node_blk)]; memset(buf, 0, MAX_INLINE_DATA(node_blk)); if (memcmp(buf, inline_data_addr(node_blk), MAX_INLINE_DATA(node_blk))) { FIX_MSG("inline_data has DATA_EXIST"); node_blk->i.i_inline |= F2FS_DATA_EXIST; need_fix = 1; } } DBG(3, "ino[0x%x] has inline data!\n", nid); goto check; } if ((node_blk->i.i_inline & F2FS_INLINE_DENTRY)) { DBG(3, "ino[0x%x] has inline dentry!\n", nid); if (le32_to_cpu(node_blk->i.i_addr[ofs]) != 0) { /* should fix this bug all the time */ FIX_MSG("inline_dentry has wrong 0'th block = %x", le32_to_cpu(node_blk->i.i_addr[ofs])); node_blk->i.i_addr[ofs] = 0; node_blk->i.i_blocks = cpu_to_le64(*blk_cnt); need_fix = 1; } ret = fsck_chk_inline_dentries(sbi, node_blk, &child); if (ret < 0) { /* should fix this bug all the time */ need_fix = 1; } goto check; } /* readahead node blocks */ for (idx = 0; idx < 5; idx++) { u32 nid = le32_to_cpu(node_blk->i.i_nid[idx]); if (nid != 0 && IS_VALID_NID(sbi, nid)) { struct node_info ni; get_node_info(sbi, nid, &ni); if (IS_VALID_BLK_ADDR(sbi, ni.blk_addr)) dev_reada_block(ni.blk_addr); } } /* init extent info */ get_extent_info(&child.ei, &node_blk->i.i_ext); child.last_blk = 0; /* check data blocks in inode */ for (idx = 0; idx < ADDRS_PER_INODE(&node_blk->i); idx++, child.pgofs++) { block_t blkaddr = le32_to_cpu(node_blk->i.i_addr[ofs + idx]); /* check extent info */ check_extent_info(&child, blkaddr, 0); if (blkaddr != 0) { ret = fsck_chk_data_blk(sbi, blkaddr, &child, (i_blocks == *blk_cnt), ftype, nid, idx, ni->version, file_is_encrypt(&node_blk->i)); if (!ret) { *blk_cnt = *blk_cnt + 1; } else if (c.fix_on) { node_blk->i.i_addr[ofs + idx] = 0; need_fix = 1; FIX_MSG("[0x%x] i_addr[%d] = 0", nid, ofs + idx); } } } /* check node blocks in inode */ for (idx = 0; idx < 5; idx++) { nid_t i_nid = le32_to_cpu(node_blk->i.i_nid[idx]); if (idx == 0 || idx == 1) ntype = TYPE_DIRECT_NODE; else if (idx == 2 || idx == 3) ntype = TYPE_INDIRECT_NODE; else if (idx == 4) ntype = TYPE_DOUBLE_INDIRECT_NODE; else ASSERT(0); if (i_nid == 0x0) goto skip; ret = fsck_chk_node_blk(sbi, &node_blk->i, i_nid, ftype, ntype, blk_cnt, &child); if (!ret) { *blk_cnt = *blk_cnt + 1; } else if (ret == -EINVAL) { if (c.fix_on) { node_blk->i.i_nid[idx] = 0; need_fix = 1; FIX_MSG("[0x%x] i_nid[%d] = 0", nid, idx); } skip: if (ntype == TYPE_DIRECT_NODE) child.pgofs += ADDRS_PER_BLOCK; else if (ntype == TYPE_INDIRECT_NODE) child.pgofs += ADDRS_PER_BLOCK * NIDS_PER_BLOCK; else child.pgofs += ADDRS_PER_BLOCK * NIDS_PER_BLOCK * NIDS_PER_BLOCK; } } /* check uncovered range in the back of extent */ check_extent_info(&child, 0, 1); if (child.state & FSCK_UNMATCHED_EXTENT) { ASSERT_MSG("ino: 0x%x has wrong ext: [pgofs:%u, blk:%u, len:%u]", nid, child.ei.fofs, child.ei.blk, child.ei.len); if (c.fix_on) need_fix = 1; } check: if (i_blocks != *blk_cnt) { ASSERT_MSG("ino: 0x%x has i_blocks: %08"PRIx64", " "but has %u blocks", nid, i_blocks, *blk_cnt); if (c.fix_on) { node_blk->i.i_blocks = cpu_to_le64(*blk_cnt); need_fix = 1; FIX_MSG("[0x%x] i_blocks=0x%08"PRIx64" -> 0x%x", nid, i_blocks, *blk_cnt); } } skip_blkcnt_fix: en = malloc(F2FS_NAME_LEN + 1); ASSERT(en); namelen = le32_to_cpu(node_blk->i.i_namelen); if (namelen > F2FS_NAME_LEN) { if (child_d && child_d->i_namelen <= F2FS_NAME_LEN) { ASSERT_MSG("ino: 0x%x has i_namelen: 0x%x, " "but has %d characters for name", nid, namelen, child_d->i_namelen); if (c.fix_on) { FIX_MSG("[0x%x] i_namelen=0x%x -> 0x%x", nid, namelen, child_d->i_namelen); node_blk->i.i_namelen = cpu_to_le32(child_d->i_namelen); need_fix = 1; } namelen = child_d->i_namelen; } else namelen = F2FS_NAME_LEN; } namelen = convert_encrypted_name(node_blk->i.i_name, namelen, en, file_enc_name(&node_blk->i)); en[namelen] = '\0'; if (ftype == F2FS_FT_ORPHAN) DBG(1, "Orphan Inode: 0x%x [%s] i_blocks: %u\n\n", le32_to_cpu(node_blk->footer.ino), en, (u32)i_blocks); if (is_qf_ino(F2FS_RAW_SUPER(sbi), nid)) DBG(1, "Quota Inode: 0x%x [%s] i_blocks: %u\n\n", le32_to_cpu(node_blk->footer.ino), en, (u32)i_blocks); if (ftype == F2FS_FT_DIR) { DBG(1, "Directory Inode: 0x%x [%s] depth: %d has %d files\n\n", le32_to_cpu(node_blk->footer.ino), en, le32_to_cpu(node_blk->i.i_current_depth), child.files); if (i_links != child.links) { ASSERT_MSG("ino: 0x%x i_links: %u, real links: %u", nid, i_links, child.links); if (c.fix_on) { node_blk->i.i_links = cpu_to_le32(child.links); need_fix = 1; FIX_MSG("Dir: 0x%x i_links= 0x%x -> 0x%x", nid, i_links, child.links); } } if (child.dots < 2 && !(node_blk->i.i_inline & F2FS_INLINE_DOTS)) { ASSERT_MSG("ino: 0x%x dots: %u", nid, child.dots); if (c.fix_on) { node_blk->i.i_inline |= F2FS_INLINE_DOTS; need_fix = 1; FIX_MSG("Dir: 0x%x set inline_dots", nid); } } } free(en); if (ftype == F2FS_FT_SYMLINK && i_blocks && i_size == 0) { DBG(1, "ino: 0x%x i_blocks: %lu with zero i_size", nid, (unsigned long)i_blocks); if (c.fix_on) { u64 i_size = i_blocks * F2FS_BLKSIZE; node_blk->i.i_size = cpu_to_le64(i_size); need_fix = 1; FIX_MSG("Symlink: recover 0x%x with i_size=%lu", nid, (unsigned long)i_size); } } if (ftype == F2FS_FT_ORPHAN && i_links) { MSG(0, "ino: 0x%x is orphan inode, but has i_links: %u", nid, i_links); if (c.fix_on) { node_blk->i.i_links = 0; need_fix = 1; FIX_MSG("ino: 0x%x orphan_inode, i_links= 0x%x -> 0", nid, i_links); } } /* drop extent information to avoid potential wrong access */ if (need_fix && !c.ro) node_blk->i.i_ext.len = 0; if ((c.feature & cpu_to_le32(F2FS_FEATURE_INODE_CHKSUM)) && f2fs_has_extra_isize(&node_blk->i)) { __u32 provided, calculated; provided = le32_to_cpu(node_blk->i.i_inode_checksum); calculated = f2fs_inode_chksum(node_blk); if (provided != calculated) { ASSERT_MSG("ino: 0x%x chksum:0x%x, but calculated one is: 0x%x", nid, provided, calculated); if (c.fix_on) { node_blk->i.i_inode_checksum = cpu_to_le32(calculated); need_fix = 1; FIX_MSG("ino: 0x%x recover, i_inode_checksum= 0x%x -> 0x%x", nid, provided, calculated); } } } if (need_fix && !c.ro) { ret = dev_write_block(node_blk, ni->blk_addr); ASSERT(ret >= 0); } } int fsck_chk_dnode_blk(struct f2fs_sb_info *sbi, struct f2fs_inode *inode, u32 nid, enum FILE_TYPE ftype, struct f2fs_node *node_blk, u32 *blk_cnt, struct child_info *child, struct node_info *ni) { int idx, ret; int need_fix = 0; child->p_ino = nid; child->pp_ino = le32_to_cpu(inode->i_pino); for (idx = 0; idx < ADDRS_PER_BLOCK; idx++, child->pgofs++) { block_t blkaddr = le32_to_cpu(node_blk->dn.addr[idx]); check_extent_info(child, blkaddr, 0); if (blkaddr == 0x0) continue; ret = fsck_chk_data_blk(sbi, blkaddr, child, le64_to_cpu(inode->i_blocks) == *blk_cnt, ftype, nid, idx, ni->version, file_is_encrypt(inode)); if (!ret) { *blk_cnt = *blk_cnt + 1; } else if (c.fix_on) { node_blk->dn.addr[idx] = 0; need_fix = 1; FIX_MSG("[0x%x] dn.addr[%d] = 0", nid, idx); } } if (need_fix && !c.ro) { ret = dev_write_block(node_blk, ni->blk_addr); ASSERT(ret >= 0); } return 0; } int fsck_chk_idnode_blk(struct f2fs_sb_info *sbi, struct f2fs_inode *inode, enum FILE_TYPE ftype, struct f2fs_node *node_blk, u32 *blk_cnt, struct child_info *child) { int need_fix = 0, ret; int i = 0; for (i = 0; i < NIDS_PER_BLOCK; i++) { if (le32_to_cpu(node_blk->in.nid[i]) == 0x0) goto skip; ret = fsck_chk_node_blk(sbi, inode, le32_to_cpu(node_blk->in.nid[i]), ftype, TYPE_DIRECT_NODE, blk_cnt, child); if (!ret) *blk_cnt = *blk_cnt + 1; else if (ret == -EINVAL) { if (!c.fix_on) printf("should delete in.nid[i] = 0;\n"); else { node_blk->in.nid[i] = 0; need_fix = 1; FIX_MSG("Set indirect node 0x%x -> 0", i); } skip: child->pgofs += ADDRS_PER_BLOCK; } } if (need_fix && !c.ro) { struct node_info ni; nid_t nid = le32_to_cpu(node_blk->footer.nid); get_node_info(sbi, nid, &ni); ret = dev_write_block(node_blk, ni.blk_addr); ASSERT(ret >= 0); } return 0; } int fsck_chk_didnode_blk(struct f2fs_sb_info *sbi, struct f2fs_inode *inode, enum FILE_TYPE ftype, struct f2fs_node *node_blk, u32 *blk_cnt, struct child_info *child) { int i = 0; int need_fix = 0, ret = 0; for (i = 0; i < NIDS_PER_BLOCK; i++) { if (le32_to_cpu(node_blk->in.nid[i]) == 0x0) goto skip; ret = fsck_chk_node_blk(sbi, inode, le32_to_cpu(node_blk->in.nid[i]), ftype, TYPE_INDIRECT_NODE, blk_cnt, child); if (!ret) *blk_cnt = *blk_cnt + 1; else if (ret == -EINVAL) { if (!c.fix_on) printf("should delete in.nid[i] = 0;\n"); else { node_blk->in.nid[i] = 0; need_fix = 1; FIX_MSG("Set double indirect node 0x%x -> 0", i); } skip: child->pgofs += ADDRS_PER_BLOCK * NIDS_PER_BLOCK; } } if (need_fix && !c.ro) { struct node_info ni; nid_t nid = le32_to_cpu(node_blk->footer.nid); get_node_info(sbi, nid, &ni); ret = dev_write_block(node_blk, ni.blk_addr); ASSERT(ret >= 0); } return 0; } static const char *lookup_table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+,"; /** * digest_encode() - * * Encodes the input digest using characters from the set [a-zA-Z0-9_+]. * The encoded string is roughly 4/3 times the size of the input string. */ static int digest_encode(const char *src, int len, char *dst) { int i = 0, bits = 0, ac = 0; char *cp = dst; while (i < len) { ac += (((unsigned char) src[i]) << bits); bits += 8; do { *cp++ = lookup_table[ac & 0x3f]; ac >>= 6; bits -= 6; } while (bits >= 6); i++; } if (bits) *cp++ = lookup_table[ac & 0x3f]; *cp = 0; return cp - dst; } int convert_encrypted_name(unsigned char *name, int len, unsigned char *new, int enc_name) { if (!enc_name) { if (len > F2FS_NAME_LEN) len = F2FS_NAME_LEN; memcpy(new, name, len); new[len] = 0; return len; } *new = '_'; return digest_encode((const char *)name, 24, (char *)new + 1); } static void print_dentry(__u32 depth, __u8 *name, u8 *bitmap, struct f2fs_dir_entry *dentry, int max, int idx, int last_blk, int enc_name) { int last_de = 0; int next_idx = 0; int name_len; unsigned int i; int bit_offset; unsigned char new[F2FS_NAME_LEN + 1]; if (!c.show_dentry) return; name_len = le16_to_cpu(dentry[idx].name_len); next_idx = idx + (name_len + F2FS_SLOT_LEN - 1) / F2FS_SLOT_LEN; bit_offset = find_next_bit_le(bitmap, max, next_idx); if (bit_offset >= max && last_blk) last_de = 1; if (tree_mark_size <= depth) { tree_mark_size *= 2; ASSERT(tree_mark_size != 0); tree_mark = realloc(tree_mark, tree_mark_size); ASSERT(tree_mark != NULL); } if (last_de) tree_mark[depth] = '`'; else tree_mark[depth] = '|'; if (tree_mark[depth - 1] == '`') tree_mark[depth - 1] = ' '; for (i = 1; i < depth; i++) printf("%c ", tree_mark[i]); convert_encrypted_name(name, name_len, new, enc_name); printf("%c-- %s , \n", last_de ? '`' : '|', new, le32_to_cpu(dentry[idx].ino), enc_name); } static int f2fs_check_hash_code(struct f2fs_dir_entry *dentry, const unsigned char *name, u32 len, int enc_name) { f2fs_hash_t hash_code = f2fs_dentry_hash(name, len); /* fix hash_code made by old buggy code */ if (dentry->hash_code != hash_code) { unsigned char new[F2FS_NAME_LEN + 1]; convert_encrypted_name((unsigned char *)name, len, new, enc_name); FIX_MSG("Mismatch hash_code for \"%s\" [%x:%x]", new, le32_to_cpu(dentry->hash_code), hash_code); dentry->hash_code = cpu_to_le32(hash_code); return 1; } return 0; } static int __get_current_level(int dir_level, u32 pgofs) { unsigned int bidx = 0; int i; for (i = 0; i < MAX_DIR_HASH_DEPTH; i++) { bidx += dir_buckets(i, dir_level) * bucket_blocks(i); if (bidx > pgofs) break; } return i; } static int f2fs_check_dirent_position(u8 *name, u16 name_len, u32 pgofs, u8 dir_level, u32 pino) { f2fs_hash_t namehash = f2fs_dentry_hash(name, name_len); unsigned int nbucket, nblock; unsigned int bidx, end_block; int level; level = __get_current_level(dir_level, pgofs); nbucket = dir_buckets(level, dir_level); nblock = bucket_blocks(level); bidx = dir_block_index(level, dir_level, le32_to_cpu(namehash) % nbucket); end_block = bidx + nblock; if (pgofs >= bidx && pgofs < end_block) return 0; ASSERT_MSG("Wrong position of dirent pino:%u, name:%s, level:%d, " "dir_level:%d, pgofs:%u, correct range:[%u, %u]\n", pino, name, level, dir_level, pgofs, bidx, end_block - 1); return 1; } static int __chk_dots_dentries(struct f2fs_sb_info *sbi, struct f2fs_dir_entry *dentry, struct child_info *child, u8 *name, int len, __u8 (*filename)[F2FS_SLOT_LEN], int enc_name) { int fixed = 0; if ((name[0] == '.' && len == 1)) { if (le32_to_cpu(dentry->ino) != child->p_ino) { ASSERT_MSG("Bad inode number[0x%x] for '.', parent_ino is [0x%x]\n", le32_to_cpu(dentry->ino), child->p_ino); dentry->ino = cpu_to_le32(child->p_ino); fixed = 1; } } if (name[0] == '.' && name[1] == '.' && len == 2) { if (child->p_ino == F2FS_ROOT_INO(sbi)) { if (le32_to_cpu(dentry->ino) != F2FS_ROOT_INO(sbi)) { ASSERT_MSG("Bad inode number[0x%x] for '..'\n", le32_to_cpu(dentry->ino)); dentry->ino = cpu_to_le32(F2FS_ROOT_INO(sbi)); fixed = 1; } } else if (le32_to_cpu(dentry->ino) != child->pp_ino) { ASSERT_MSG("Bad inode number[0x%x] for '..', parent parent ino is [0x%x]\n", le32_to_cpu(dentry->ino), child->pp_ino); dentry->ino = cpu_to_le32(child->pp_ino); fixed = 1; } } if (f2fs_check_hash_code(dentry, name, len, enc_name)) fixed = 1; if (name[len] != '\0') { ASSERT_MSG("'.' is not NULL terminated\n"); name[len] = '\0'; memcpy(*filename, name, len); fixed = 1; } return fixed; } static void nullify_dentry(struct f2fs_dir_entry *dentry, int offs, __u8 (*filename)[F2FS_SLOT_LEN], u8 **bitmap) { memset(dentry, 0, sizeof(struct f2fs_dir_entry)); test_and_clear_bit_le(offs, *bitmap); memset(*filename, 0, F2FS_SLOT_LEN); } static int __chk_dentries(struct f2fs_sb_info *sbi, struct child_info *child, u8 *bitmap, struct f2fs_dir_entry *dentry, __u8 (*filenames)[F2FS_SLOT_LEN], int max, int last_blk, int enc_name) { struct f2fs_fsck *fsck = F2FS_FSCK(sbi); enum FILE_TYPE ftype; int dentries = 0; u32 blk_cnt; u8 *name; unsigned char en[F2FS_NAME_LEN + 1]; u16 name_len, en_len; int ret = 0; int fixed = 0; int i, slots; /* readahead inode blocks */ for (i = 0; i < max; i++) { u32 ino; if (test_bit_le(i, bitmap) == 0) continue; ino = le32_to_cpu(dentry[i].ino); if (IS_VALID_NID(sbi, ino)) { struct node_info ni; get_node_info(sbi, ino, &ni); if (IS_VALID_BLK_ADDR(sbi, ni.blk_addr)) { dev_reada_block(ni.blk_addr); name_len = le16_to_cpu(dentry[i].name_len); if (name_len > 0) i += (name_len + F2FS_SLOT_LEN - 1) / F2FS_SLOT_LEN - 1; } } } for (i = 0; i < max;) { if (test_bit_le(i, bitmap) == 0) { i++; continue; } if (!IS_VALID_NID(sbi, le32_to_cpu(dentry[i].ino))) { ASSERT_MSG("Bad dentry 0x%x with invalid NID/ino 0x%x", i, le32_to_cpu(dentry[i].ino)); if (c.fix_on) { FIX_MSG("Clear bad dentry 0x%x with bad ino 0x%x", i, le32_to_cpu(dentry[i].ino)); test_and_clear_bit_le(i, bitmap); fixed = 1; } i++; continue; } ftype = dentry[i].file_type; if ((ftype <= F2FS_FT_UNKNOWN || ftype > F2FS_FT_LAST_FILE_TYPE)) { ASSERT_MSG("Bad dentry 0x%x with unexpected ftype 0x%x", le32_to_cpu(dentry[i].ino), ftype); if (c.fix_on) { FIX_MSG("Clear bad dentry 0x%x with bad ftype 0x%x", i, ftype); test_and_clear_bit_le(i, bitmap); fixed = 1; } i++; continue; } name_len = le16_to_cpu(dentry[i].name_len); if (name_len == 0 || name_len > F2FS_NAME_LEN) { ASSERT_MSG("Bad dentry 0x%x with invalid name_len", i); if (c.fix_on) { FIX_MSG("Clear bad dentry 0x%x", i); test_and_clear_bit_le(i, bitmap); fixed = 1; } i++; continue; } name = calloc(name_len + 1, 1); memcpy(name, filenames[i], name_len); slots = (name_len + F2FS_SLOT_LEN - 1) / F2FS_SLOT_LEN; /* Becareful. 'dentry.file_type' is not imode. */ if (ftype == F2FS_FT_DIR) { if ((name[0] == '.' && name_len == 1) || (name[0] == '.' && name[1] == '.' && name_len == 2)) { ret = __chk_dots_dentries(sbi, &dentry[i], child, name, name_len, &filenames[i], enc_name); switch (ret) { case 1: fixed = 1; case 0: child->dots++; break; } if (child->dots > 2) { ASSERT_MSG("More than one '.' or '..', should delete the extra one\n"); nullify_dentry(&dentry[i], i, &filenames[i], &bitmap); child->dots--; fixed = 1; } i++; free(name); continue; } } if (f2fs_check_hash_code(dentry + i, name, name_len, enc_name)) fixed = 1; if (max == NR_DENTRY_IN_BLOCK) { ret = f2fs_check_dirent_position(name, name_len, child->pgofs, child->dir_level, child->p_ino); if (ret) { if (c.fix_on) { FIX_MSG("Clear bad dentry 0x%x", i); test_and_clear_bit_le(i, bitmap); fixed = 1; } i++; free(name); continue; } } en_len = convert_encrypted_name(name, name_len, en, enc_name); en[en_len] = '\0'; DBG(1, "[%3u]-[0x%x] name[%s] len[0x%x] ino[0x%x] type[0x%x]\n", fsck->dentry_depth, i, en, name_len, le32_to_cpu(dentry[i].ino), dentry[i].file_type); print_dentry(fsck->dentry_depth, name, bitmap, dentry, max, i, last_blk, enc_name); blk_cnt = 1; child->i_namelen = name_len; ret = fsck_chk_node_blk(sbi, NULL, le32_to_cpu(dentry[i].ino), ftype, TYPE_INODE, &blk_cnt, child); if (ret && c.fix_on) { int j; for (j = 0; j < slots; j++) test_and_clear_bit_le(i + j, bitmap); FIX_MSG("Unlink [0x%x] - %s len[0x%x], type[0x%x]", le32_to_cpu(dentry[i].ino), en, name_len, dentry[i].file_type); fixed = 1; } else if (ret == 0) { if (ftype == F2FS_FT_DIR) child->links++; dentries++; child->files++; } i += slots; free(name); } return fixed ? -1 : dentries; } int fsck_chk_inline_dentries(struct f2fs_sb_info *sbi, struct f2fs_node *node_blk, struct child_info *child) { struct f2fs_fsck *fsck = F2FS_FSCK(sbi); struct f2fs_dentry_ptr d; void *inline_dentry; int dentries; inline_dentry = inline_data_addr(node_blk); ASSERT(inline_dentry != NULL); make_dentry_ptr(&d, node_blk, inline_dentry, 2); fsck->dentry_depth++; dentries = __chk_dentries(sbi, child, d.bitmap, d.dentry, d.filename, d.max, 1, file_is_encrypt(&node_blk->i)); if (dentries < 0) { DBG(1, "[%3d] Inline Dentry Block Fixed hash_codes\n\n", fsck->dentry_depth); } else { DBG(1, "[%3d] Inline Dentry Block Done : " "dentries:%d in %d slots (len:%d)\n\n", fsck->dentry_depth, dentries, d.max, F2FS_NAME_LEN); } fsck->dentry_depth--; return dentries; } int fsck_chk_dentry_blk(struct f2fs_sb_info *sbi, u32 blk_addr, struct child_info *child, int last_blk, int enc_name) { struct f2fs_fsck *fsck = F2FS_FSCK(sbi); struct f2fs_dentry_block *de_blk; int dentries, ret; de_blk = (struct f2fs_dentry_block *)calloc(BLOCK_SZ, 1); ASSERT(de_blk != NULL); ret = dev_read_block(de_blk, blk_addr); ASSERT(ret >= 0); fsck->dentry_depth++; dentries = __chk_dentries(sbi, child, de_blk->dentry_bitmap, de_blk->dentry, de_blk->filename, NR_DENTRY_IN_BLOCK, last_blk, enc_name); if (dentries < 0 && !c.ro) { ret = dev_write_block(de_blk, blk_addr); ASSERT(ret >= 0); DBG(1, "[%3d] Dentry Block [0x%x] Fixed hash_codes\n\n", fsck->dentry_depth, blk_addr); } else { DBG(1, "[%3d] Dentry Block [0x%x] Done : " "dentries:%d in %d slots (len:%d)\n\n", fsck->dentry_depth, blk_addr, dentries, NR_DENTRY_IN_BLOCK, F2FS_NAME_LEN); } fsck->dentry_depth--; free(de_blk); return 0; } int fsck_chk_data_blk(struct f2fs_sb_info *sbi, u32 blk_addr, struct child_info *child, int last_blk, enum FILE_TYPE ftype, u32 parent_nid, u16 idx_in_node, u8 ver, int enc_name) { struct f2fs_fsck *fsck = F2FS_FSCK(sbi); /* Is it reserved block? */ if (blk_addr == NEW_ADDR) { fsck->chk.valid_blk_cnt++; return 0; } if (!IS_VALID_BLK_ADDR(sbi, blk_addr)) { ASSERT_MSG("blkaddress is not valid. [0x%x]", blk_addr); return -EINVAL; } if (is_valid_ssa_data_blk(sbi, blk_addr, parent_nid, idx_in_node, ver)) { ASSERT_MSG("summary data block is not valid. [0x%x]", parent_nid); return -EINVAL; } if (f2fs_test_sit_bitmap(sbi, blk_addr) == 0) ASSERT_MSG("SIT bitmap is 0x0. blk_addr[0x%x]", blk_addr); if (f2fs_test_main_bitmap(sbi, blk_addr) != 0) ASSERT_MSG("Duplicated data [0x%x]. pnid[0x%x] idx[0x%x]", blk_addr, parent_nid, idx_in_node); fsck->chk.valid_blk_cnt++; if (ftype == F2FS_FT_DIR) { f2fs_set_main_bitmap(sbi, blk_addr, CURSEG_HOT_DATA); return fsck_chk_dentry_blk(sbi, blk_addr, child, last_blk, enc_name); } else { f2fs_set_main_bitmap(sbi, blk_addr, CURSEG_WARM_DATA); } return 0; } int fsck_chk_orphan_node(struct f2fs_sb_info *sbi) { u32 blk_cnt = 0; block_t start_blk, orphan_blkaddr, i, j; struct f2fs_orphan_block *orphan_blk, *new_blk; struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi); u32 entry_count; if (!is_set_ckpt_flags(F2FS_CKPT(sbi), CP_ORPHAN_PRESENT_FLAG)) return 0; start_blk = __start_cp_addr(sbi) + 1 + get_sb(cp_payload); orphan_blkaddr = __start_sum_addr(sbi) - 1 - get_sb(cp_payload); orphan_blk = calloc(BLOCK_SZ, 1); ASSERT(orphan_blk); new_blk = calloc(BLOCK_SZ, 1); ASSERT(new_blk); for (i = 0; i < orphan_blkaddr; i++) { int ret = dev_read_block(orphan_blk, start_blk + i); u32 new_entry_count = 0; ASSERT(ret >= 0); entry_count = le32_to_cpu(orphan_blk->entry_count); for (j = 0; j < entry_count; j++) { nid_t ino = le32_to_cpu(orphan_blk->ino[j]); DBG(1, "[%3d] ino [0x%x]\n", i, ino); struct node_info ni; blk_cnt = 1; if (c.preen_mode == PREEN_MODE_1 && !c.fix_on) { get_node_info(sbi, ino, &ni); if (!IS_VALID_NID(sbi, ino) || !IS_VALID_BLK_ADDR(sbi, ni.blk_addr)) return -EINVAL; continue; } ret = fsck_chk_node_blk(sbi, NULL, ino, F2FS_FT_ORPHAN, TYPE_INODE, &blk_cnt, NULL); if (!ret) new_blk->ino[new_entry_count++] = orphan_blk->ino[j]; else if (ret && c.fix_on) FIX_MSG("[0x%x] remove from orphan list", ino); else if (ret) ASSERT_MSG("[0x%x] wrong orphan inode", ino); } if (!c.ro && c.fix_on && entry_count != new_entry_count) { new_blk->entry_count = cpu_to_le32(new_entry_count); ret = dev_write_block(new_blk, start_blk + i); ASSERT(ret >= 0); } memset(orphan_blk, 0, BLOCK_SZ); memset(new_blk, 0, BLOCK_SZ); } free(orphan_blk); free(new_blk); return 0; } int fsck_chk_quota_node(struct f2fs_sb_info *sbi) { struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi); enum quota_type qtype; int ret = 0; u32 blk_cnt = 0; for (qtype = 0; qtype < F2FS_MAX_QUOTAS; qtype++) { if (sb->qf_ino[qtype] == 0) continue; nid_t ino = QUOTA_INO(sb, qtype); struct node_info ni; DBG(1, "[%3d] ino [0x%x]\n", qtype, ino); blk_cnt = 1; if (c.preen_mode == PREEN_MODE_1 && !c.fix_on) { get_node_info(sbi, ino, &ni); if (!IS_VALID_NID(sbi, ino) || !IS_VALID_BLK_ADDR(sbi, ni.blk_addr)) return -EINVAL; } ret = fsck_chk_node_blk(sbi, NULL, ino, F2FS_FT_REG_FILE, TYPE_INODE, &blk_cnt, NULL); if (ret) ASSERT_MSG("[0x%x] wrong orphan inode", ino); } return ret; } int fsck_chk_quota_files(struct f2fs_sb_info *sbi) { struct f2fs_fsck *fsck = F2FS_FSCK(sbi); struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi); enum quota_type qtype; f2fs_ino_t ino; int ret = 0; int needs_writeout; /* Return if quota feature is disabled */ if (!fsck->qctx) return 0; for (qtype = 0; qtype < F2FS_MAX_QUOTAS; qtype++) { ino = sb->qf_ino[qtype]; if (!ino) continue; DBG(1, "Checking Quota file ([%3d] ino [0x%x])\n", qtype, ino); needs_writeout = 0; ret = quota_compare_and_update(sbi, qtype, &needs_writeout, c.preserve_limits); if (ret == 0 && needs_writeout == 0) { DBG(1, "OK\n"); continue; } /* Something is wrong */ if (c.fix_on) { DBG(0, "Fixing Quota file ([%3d] ino [0x%x])\n", qtype, ino); f2fs_filesize_update(sbi, ino, 0); ret = quota_write_inode(sbi, qtype); if (!ret) { c.bug_on = 1; DBG(1, "OK\n"); } else { ASSERT_MSG("Unable to write quota file"); } } else { ASSERT_MSG("Quota file is missing or invalid" " quota file content found."); } } return ret; } int fsck_chk_meta(struct f2fs_sb_info *sbi) { struct f2fs_fsck *fsck = F2FS_FSCK(sbi); struct f2fs_checkpoint *cp = F2FS_CKPT(sbi); struct seg_entry *se; unsigned int sit_valid_segs = 0, sit_node_blks = 0; unsigned int i; /* 1. check sit usage with CP: curseg is lost? */ for (i = 0; i < TOTAL_SEGS(sbi); i++) { se = get_seg_entry(sbi, i); if (se->valid_blocks != 0) sit_valid_segs++; else if (IS_CUR_SEGNO(sbi, i, NO_CHECK_TYPE)) { /* curseg has not been written back to device */ MSG(1, "\tInfo: curseg %u is counted in valid segs\n", i); sit_valid_segs++; } if (IS_NODESEG(se->type)) sit_node_blks += se->valid_blocks; } if (fsck->chk.sit_free_segs + sit_valid_segs != TOTAL_SEGS(sbi)) { ASSERT_MSG("SIT usage does not match: sit_free_segs %u, " "sit_valid_segs %u, total_segs %u", fsck->chk.sit_free_segs, sit_valid_segs, TOTAL_SEGS(sbi)); return -EINVAL; } /* 2. check node count */ if (fsck->chk.valid_nat_entry_cnt != sit_node_blks) { ASSERT_MSG("node count does not match: valid_nat_entry_cnt %u," " sit_node_blks %u", fsck->chk.valid_nat_entry_cnt, sit_node_blks); return -EINVAL; } /* 3. check SIT with CP */ if (fsck->chk.sit_free_segs != le32_to_cpu(cp->free_segment_count)) { ASSERT_MSG("free segs does not match: sit_free_segs %u, " "free_segment_count %u", fsck->chk.sit_free_segs, le32_to_cpu(cp->free_segment_count)); return -EINVAL; } /* 4. check NAT with CP */ if (fsck->chk.valid_nat_entry_cnt != le32_to_cpu(cp->valid_node_count)) { ASSERT_MSG("valid node does not match: valid_nat_entry_cnt %u," " valid_node_count %u", fsck->chk.valid_nat_entry_cnt, le32_to_cpu(cp->valid_node_count)); return -EINVAL; } /* 4. check orphan inode simply */ if (fsck_chk_orphan_node(sbi)) return -EINVAL; /* 5. check quota inode simply */ if (fsck_chk_quota_node(sbi)) return -EINVAL; if (fsck->nat_valid_inode_cnt != le32_to_cpu(cp->valid_inode_count)) { ASSERT_MSG("valid inode does not match: nat_valid_inode_cnt %u," " valid_inode_count %u", fsck->nat_valid_inode_cnt, le32_to_cpu(cp->valid_inode_count)); return -EINVAL; } /*check nat entry with sit_area_bitmap*/ for (i = 0; i < fsck->nr_nat_entries; i++) { u32 blk = le32_to_cpu(fsck->entries[i].block_addr); nid_t ino = le32_to_cpu(fsck->entries[i].ino); if (!blk) /* * skip entry whose ino is 0, otherwise, we will * get a negative number by BLKOFF_FROM_MAIN(sbi, blk) */ continue; if (!IS_VALID_BLK_ADDR(sbi, blk)) { MSG(0, "\tError: nat entry[ino %u block_addr 0x%x]" " is in valid\n", ino, blk); return -EINVAL; } if (!f2fs_test_sit_bitmap(sbi, blk)) { MSG(0, "\tError: nat entry[ino %u block_addr 0x%x]" " not find it in sit_area_bitmap\n", ino, blk); return -EINVAL; } if (!IS_VALID_NID(sbi, ino)) { MSG(0, "\tError: nat_entry->ino %u exceeds the range" " of nat entries %u\n", ino, fsck->nr_nat_entries); return -EINVAL; } if (!f2fs_test_bit(ino, fsck->nat_area_bitmap)) { MSG(0, "\tError: nat_entry->ino %u is not set in" " nat_area_bitmap\n", ino); return -EINVAL; } } return 0; } void fsck_init(struct f2fs_sb_info *sbi) { struct f2fs_fsck *fsck = F2FS_FSCK(sbi); struct f2fs_sm_info *sm_i = SM_I(sbi); /* * We build three bitmap for main/sit/nat so that may check consistency * of filesystem. * 1. main_area_bitmap will be used to check whether all blocks of main * area is used or not. * 2. nat_area_bitmap has bitmap information of used nid in NAT. * 3. sit_area_bitmap has bitmap information of used main block. * At Last sequence, we compare main_area_bitmap with sit_area_bitmap. */ fsck->nr_main_blks = sm_i->main_segments << sbi->log_blocks_per_seg; fsck->main_area_bitmap_sz = (fsck->nr_main_blks + 7) / 8; fsck->main_area_bitmap = calloc(fsck->main_area_bitmap_sz, 1); ASSERT(fsck->main_area_bitmap != NULL); build_nat_area_bitmap(sbi); build_sit_area_bitmap(sbi); ASSERT(tree_mark_size != 0); tree_mark = calloc(tree_mark_size, 1); ASSERT(tree_mark != NULL); } static void fix_hard_links(struct f2fs_sb_info *sbi) { struct f2fs_fsck *fsck = F2FS_FSCK(sbi); struct hard_link_node *tmp, *node; struct f2fs_node *node_blk = NULL; struct node_info ni; int ret; if (fsck->hard_link_list_head == NULL) return; node_blk = (struct f2fs_node *)calloc(BLOCK_SZ, 1); ASSERT(node_blk != NULL); node = fsck->hard_link_list_head; while (node) { /* Sanity check */ if (sanity_check_nid(sbi, node->nid, node_blk, F2FS_FT_MAX, TYPE_INODE, &ni)) FIX_MSG("Failed to fix, rerun fsck.f2fs"); node_blk->i.i_links = cpu_to_le32(node->actual_links); FIX_MSG("File: 0x%x i_links= 0x%x -> 0x%x", node->nid, node->links, node->actual_links); ret = dev_write_block(node_blk, ni.blk_addr); ASSERT(ret >= 0); tmp = node; node = node->next; free(tmp); } free(node_blk); } static void fix_nat_entries(struct f2fs_sb_info *sbi) { struct f2fs_fsck *fsck = F2FS_FSCK(sbi); u32 i; for (i = 0; i < fsck->nr_nat_entries; i++) if (f2fs_test_bit(i, fsck->nat_area_bitmap) != 0) nullify_nat_entry(sbi, i); } static void flush_curseg_sit_entries(struct f2fs_sb_info *sbi) { struct sit_info *sit_i = SIT_I(sbi); int i; /* update curseg sit entries, since we may change * a segment type in move_curseg_info */ for (i = 0; i < NO_CHECK_TYPE; i++) { struct curseg_info *curseg = CURSEG_I(sbi, i); struct f2fs_sit_block *sit_blk; struct f2fs_sit_entry *sit; struct seg_entry *se; se = get_seg_entry(sbi, curseg->segno); sit_blk = get_current_sit_page(sbi, curseg->segno); sit = &sit_blk->entries[SIT_ENTRY_OFFSET(sit_i, curseg->segno)]; sit->vblocks = cpu_to_le16((se->type << SIT_VBLOCKS_SHIFT) | se->valid_blocks); rewrite_current_sit_page(sbi, curseg->segno, sit_blk); free(sit_blk); } } static void fix_checkpoint(struct f2fs_sb_info *sbi) { struct f2fs_fsck *fsck = F2FS_FSCK(sbi); struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi); struct f2fs_checkpoint *cp = F2FS_CKPT(sbi); unsigned long long cp_blk_no; u32 flags = CP_UMOUNT_FLAG; block_t orphan_blks = 0; u32 i; int ret; u_int32_t crc = 0; if (is_set_ckpt_flags(cp, CP_ORPHAN_PRESENT_FLAG)) { orphan_blks = __start_sum_addr(sbi) - 1; flags |= CP_ORPHAN_PRESENT_FLAG; } set_cp(cp_pack_total_block_count, 8 + orphan_blks + get_sb(cp_payload)); flags = update_nat_bits_flags(sb, cp, flags); flags |= CP_NOCRC_RECOVERY_FLAG; set_cp(ckpt_flags, flags); set_cp(free_segment_count, get_free_segments(sbi)); set_cp(valid_block_count, fsck->chk.valid_blk_cnt); set_cp(valid_node_count, fsck->chk.valid_node_cnt); set_cp(valid_inode_count, fsck->chk.valid_inode_cnt); crc = f2fs_cal_crc32(F2FS_SUPER_MAGIC, cp, CHECKSUM_OFFSET); *((__le32 *)((unsigned char *)cp + CHECKSUM_OFFSET)) = cpu_to_le32(crc); cp_blk_no = get_sb(cp_blkaddr); if (sbi->cur_cp == 2) cp_blk_no += 1 << get_sb(log_blocks_per_seg); ret = dev_write_block(cp, cp_blk_no++); ASSERT(ret >= 0); for (i = 0; i < get_sb(cp_payload); i++) { ret = dev_write_block(((unsigned char *)cp) + i * F2FS_BLKSIZE, cp_blk_no++); ASSERT(ret >= 0); } cp_blk_no += orphan_blks; for (i = 0; i < NO_CHECK_TYPE; i++) { struct curseg_info *curseg = CURSEG_I(sbi, i); ret = dev_write_block(curseg->sum_blk, cp_blk_no++); ASSERT(ret >= 0); } ret = dev_write_block(cp, cp_blk_no++); ASSERT(ret >= 0); /* Write nat bits */ if (flags & CP_NAT_BITS_FLAG) write_nat_bits(sbi, sb, cp, sbi->cur_cp); } int check_curseg_offset(struct f2fs_sb_info *sbi) { int i; for (i = 0; i < NO_CHECK_TYPE; i++) { struct curseg_info *curseg = CURSEG_I(sbi, i); struct seg_entry *se; int j, nblocks; if ((curseg->next_blkoff >> 3) >= SIT_VBLOCK_MAP_SIZE) return -EINVAL; se = get_seg_entry(sbi, curseg->segno); if (f2fs_test_bit(curseg->next_blkoff, (const char *)se->cur_valid_map)) { ASSERT_MSG("Next block offset is not free, type:%d", i); return -EINVAL; } if (curseg->alloc_type == SSR) return 0; nblocks = sbi->blocks_per_seg; for (j = curseg->next_blkoff + 1; j < nblocks; j++) { if (f2fs_test_bit(j, (const char *)se->cur_valid_map)) { ASSERT_MSG("LFS must have free section:%d", i); return -EINVAL; } } } return 0; } int check_sit_types(struct f2fs_sb_info *sbi) { unsigned int i; int err = 0; for (i = 0; i < TOTAL_SEGS(sbi); i++) { struct seg_entry *se; se = get_seg_entry(sbi, i); if (se->orig_type != se->type) { if (se->orig_type == CURSEG_COLD_DATA && se->type <= CURSEG_COLD_DATA) { se->type = se->orig_type; } else { FIX_MSG("Wrong segment type [0x%x] %x -> %x", i, se->orig_type, se->type); err = -EINVAL; } } } return err; } int fsck_verify(struct f2fs_sb_info *sbi) { unsigned int i = 0; int ret = 0; int force = 0; u32 nr_unref_nid = 0; struct f2fs_fsck *fsck = F2FS_FSCK(sbi); struct hard_link_node *node = NULL; printf("\n"); for (i = 0; i < fsck->nr_nat_entries; i++) { if (f2fs_test_bit(i, fsck->nat_area_bitmap) != 0) { printf("NID[0x%x] is unreachable\n", i); nr_unref_nid++; } } if (fsck->hard_link_list_head != NULL) { node = fsck->hard_link_list_head; while (node) { printf("NID[0x%x] has [0x%x] more unreachable links\n", node->nid, node->links); node = node->next; } c.bug_on = 1; } printf("[FSCK] Unreachable nat entries "); if (nr_unref_nid == 0x0) { printf(" [Ok..] [0x%x]\n", nr_unref_nid); } else { printf(" [Fail] [0x%x]\n", nr_unref_nid); ret = EXIT_ERR_CODE; c.bug_on = 1; } printf("[FSCK] SIT valid block bitmap checking "); if (memcmp(fsck->sit_area_bitmap, fsck->main_area_bitmap, fsck->sit_area_bitmap_sz) == 0x0) { printf("[Ok..]\n"); } else { printf("[Fail]\n"); ret = EXIT_ERR_CODE; c.bug_on = 1; } printf("[FSCK] Hard link checking for regular file "); if (fsck->hard_link_list_head == NULL) { printf(" [Ok..] [0x%x]\n", fsck->chk.multi_hard_link_files); } else { printf(" [Fail] [0x%x]\n", fsck->chk.multi_hard_link_files); ret = EXIT_ERR_CODE; c.bug_on = 1; } printf("[FSCK] valid_block_count matching with CP "); if (sbi->total_valid_block_count == fsck->chk.valid_blk_cnt) { printf(" [Ok..] [0x%x]\n", (u32)fsck->chk.valid_blk_cnt); } else { printf(" [Fail] [0x%x]\n", (u32)fsck->chk.valid_blk_cnt); ret = EXIT_ERR_CODE; c.bug_on = 1; } printf("[FSCK] valid_node_count matcing with CP (de lookup) "); if (sbi->total_valid_node_count == fsck->chk.valid_node_cnt) { printf(" [Ok..] [0x%x]\n", fsck->chk.valid_node_cnt); } else { printf(" [Fail] [0x%x]\n", fsck->chk.valid_node_cnt); ret = EXIT_ERR_CODE; c.bug_on = 1; } printf("[FSCK] valid_node_count matcing with CP (nat lookup) "); if (sbi->total_valid_node_count == fsck->chk.valid_nat_entry_cnt) { printf(" [Ok..] [0x%x]\n", fsck->chk.valid_nat_entry_cnt); } else { printf(" [Fail] [0x%x]\n", fsck->chk.valid_nat_entry_cnt); ret = EXIT_ERR_CODE; c.bug_on = 1; } printf("[FSCK] valid_inode_count matched with CP "); if (sbi->total_valid_inode_count == fsck->chk.valid_inode_cnt) { printf(" [Ok..] [0x%x]\n", fsck->chk.valid_inode_cnt); } else { printf(" [Fail] [0x%x]\n", fsck->chk.valid_inode_cnt); ret = EXIT_ERR_CODE; c.bug_on = 1; } printf("[FSCK] free segment_count matched with CP "); if (le32_to_cpu(F2FS_CKPT(sbi)->free_segment_count) == fsck->chk.sit_free_segs) { printf(" [Ok..] [0x%x]\n", fsck->chk.sit_free_segs); } else { printf(" [Fail] [0x%x]\n", fsck->chk.sit_free_segs); ret = EXIT_ERR_CODE; c.bug_on = 1; } printf("[FSCK] next block offset is free "); if (check_curseg_offset(sbi) == 0) { printf(" [Ok..]\n"); } else { printf(" [Fail]\n"); ret = EXIT_ERR_CODE; c.bug_on = 1; } printf("[FSCK] fixing SIT types\n"); if (check_sit_types(sbi) != 0) force = 1; printf("[FSCK] other corrupted bugs "); if (c.bug_on == 0) { printf(" [Ok..]\n"); } else { printf(" [Fail]\n"); ret = EXIT_ERR_CODE; } #ifndef WITH_ANDROID if (nr_unref_nid && !c.ro) { char ans[255] = {0}; printf("\nDo you want to restore lost files into ./lost_found/? [Y/N] "); ret = scanf("%s", ans); ASSERT(ret >= 0); if (!strcasecmp(ans, "y")) { for (i = 0; i < fsck->nr_nat_entries; i++) { if (f2fs_test_bit(i, fsck->nat_area_bitmap)) dump_node(sbi, i, 1); } } } #endif /* fix global metadata */ if (force || (c.fix_on && !c.ro)) { struct f2fs_checkpoint *cp = F2FS_CKPT(sbi); if (force || c.bug_on) { fix_hard_links(sbi); fix_nat_entries(sbi); rewrite_sit_area_bitmap(sbi); if (check_curseg_offset(sbi)) { move_curseg_info(sbi, SM_I(sbi)->main_blkaddr); write_curseg_info(sbi); flush_curseg_sit_entries(sbi); } fix_checkpoint(sbi); } else if (is_set_ckpt_flags(cp, CP_FSCK_FLAG)) { write_checkpoint(sbi); } } return ret; } void fsck_free(struct f2fs_sb_info *sbi) { struct f2fs_fsck *fsck = F2FS_FSCK(sbi); if (fsck->qctx) quota_release_context(&fsck->qctx); if (fsck->main_area_bitmap) free(fsck->main_area_bitmap); if (fsck->nat_area_bitmap) free(fsck->nat_area_bitmap); if (fsck->sit_area_bitmap) free(fsck->sit_area_bitmap); if (fsck->entries) free(fsck->entries); if (tree_mark) free(tree_mark); } f2fs-tools-1.10.0/fsck/fsck.h000066400000000000000000000167371323417143600156360ustar00rootroot00000000000000/** * fsck.h * * Copyright (c) 2013 Samsung Electronics Co., Ltd. * http://www.samsung.com/ * * 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. */ #ifndef _FSCK_H_ #define _FSCK_H_ #include "f2fs.h" struct quota_ctx; #define FSCK_UNMATCHED_EXTENT 0x00000001 enum { PREEN_MODE_0, PREEN_MODE_1, PREEN_MODE_MAX }; enum { NOERROR, EWRONG_OPT, ENEED_ARG, EUNKNOWN_OPT, EUNKNOWN_ARG, }; /* fsck.c */ struct orphan_info { u32 nr_inodes; u32 *ino_list; }; struct extent_info { u32 fofs; /* start offset in a file */ u32 blk; /* start block address of the extent */ u32 len; /* length of the extent */ }; struct child_info { u32 state; u32 links; u32 files; u32 pgofs; u8 dots; u8 dir_level; u32 p_ino; /*parent ino*/ u32 pp_ino; /*parent parent ino*/ struct extent_info ei; u32 last_blk; u32 i_namelen; /* dentry namelen */ }; struct f2fs_fsck { struct f2fs_sb_info sbi; struct orphan_info orphani; struct chk_result { u64 valid_blk_cnt; u32 valid_nat_entry_cnt; u32 valid_node_cnt; u32 valid_inode_cnt; u32 multi_hard_link_files; u64 sit_valid_blocks; u32 sit_free_segs; } chk; struct hard_link_node *hard_link_list_head; char *main_seg_usage; char *main_area_bitmap; char *nat_area_bitmap; char *sit_area_bitmap; u64 main_area_bitmap_sz; u32 nat_area_bitmap_sz; u32 sit_area_bitmap_sz; u64 nr_main_blks; u32 nr_nat_entries; u32 dentry_depth; struct f2fs_nat_entry *entries; u32 nat_valid_inode_cnt; struct quota_ctx *qctx; }; #define BLOCK_SZ 4096 struct block { unsigned char buf[BLOCK_SZ]; }; enum NODE_TYPE { TYPE_INODE = 37, TYPE_DIRECT_NODE = 43, TYPE_INDIRECT_NODE = 53, TYPE_DOUBLE_INDIRECT_NODE = 67, TYPE_XATTR = 77 }; struct hard_link_node { u32 nid; u32 links; u32 actual_links; struct hard_link_node *next; }; enum seg_type { SEG_TYPE_DATA, SEG_TYPE_CUR_DATA, SEG_TYPE_NODE, SEG_TYPE_CUR_NODE, SEG_TYPE_MAX, }; struct selabel_handle; extern int fsck_chk_orphan_node(struct f2fs_sb_info *); extern int fsck_chk_quota_node(struct f2fs_sb_info *); extern int fsck_chk_quota_files(struct f2fs_sb_info *); extern int fsck_chk_node_blk(struct f2fs_sb_info *, struct f2fs_inode *, u32, enum FILE_TYPE, enum NODE_TYPE, u32 *, struct child_info *); extern void fsck_chk_inode_blk(struct f2fs_sb_info *, u32, enum FILE_TYPE, struct f2fs_node *, u32 *, struct node_info *, struct child_info *); extern int fsck_chk_dnode_blk(struct f2fs_sb_info *, struct f2fs_inode *, u32, enum FILE_TYPE, struct f2fs_node *, u32 *, struct child_info *, struct node_info *); extern int fsck_chk_idnode_blk(struct f2fs_sb_info *, struct f2fs_inode *, enum FILE_TYPE, struct f2fs_node *, u32 *, struct child_info *); extern int fsck_chk_didnode_blk(struct f2fs_sb_info *, struct f2fs_inode *, enum FILE_TYPE, struct f2fs_node *, u32 *, struct child_info *); extern int fsck_chk_data_blk(struct f2fs_sb_info *sbi, u32, struct child_info *, int, enum FILE_TYPE, u32, u16, u8, int); extern int fsck_chk_dentry_blk(struct f2fs_sb_info *, u32, struct child_info *, int, int); int fsck_chk_inline_dentries(struct f2fs_sb_info *, struct f2fs_node *, struct child_info *); int fsck_chk_meta(struct f2fs_sb_info *sbi); int convert_encrypted_name(unsigned char *, int, unsigned char *, int); extern void update_free_segments(struct f2fs_sb_info *); void print_cp_state(u32); extern void print_node_info(struct f2fs_sb_info *, struct f2fs_node *, int); extern void print_inode_info(struct f2fs_sb_info *, struct f2fs_node *, int); extern struct seg_entry *get_seg_entry(struct f2fs_sb_info *, unsigned int); extern struct f2fs_summary_block *get_sum_block(struct f2fs_sb_info *, unsigned int, int *); extern int get_sum_entry(struct f2fs_sb_info *, u32, struct f2fs_summary *); extern void update_sum_entry(struct f2fs_sb_info *, block_t, struct f2fs_summary *); extern void get_node_info(struct f2fs_sb_info *, nid_t, struct node_info *); extern void nullify_nat_entry(struct f2fs_sb_info *, u32); extern void rewrite_sit_area_bitmap(struct f2fs_sb_info *); extern void build_nat_area_bitmap(struct f2fs_sb_info *); extern void build_sit_area_bitmap(struct f2fs_sb_info *); extern int f2fs_set_main_bitmap(struct f2fs_sb_info *, u32, int); extern int f2fs_set_sit_bitmap(struct f2fs_sb_info *, u32); extern void fsck_init(struct f2fs_sb_info *); extern int fsck_verify(struct f2fs_sb_info *); extern void fsck_free(struct f2fs_sb_info *); extern int f2fs_do_mount(struct f2fs_sb_info *); extern void f2fs_do_umount(struct f2fs_sb_info *); extern void flush_journal_entries(struct f2fs_sb_info *); extern void zero_journal_entries(struct f2fs_sb_info *); extern void flush_sit_entries(struct f2fs_sb_info *); extern void move_curseg_info(struct f2fs_sb_info *, u64); extern void write_curseg_info(struct f2fs_sb_info *); extern int find_next_free_block(struct f2fs_sb_info *, u64 *, int, int); extern void write_checkpoint(struct f2fs_sb_info *); extern void update_data_blkaddr(struct f2fs_sb_info *, nid_t, u16, block_t); extern void update_nat_blkaddr(struct f2fs_sb_info *, nid_t, nid_t, block_t); extern void print_raw_sb_info(struct f2fs_super_block *); extern u32 get_free_segments(struct f2fs_sb_info *); extern struct f2fs_sit_block *get_current_sit_page(struct f2fs_sb_info *, unsigned int); extern void rewrite_current_sit_page(struct f2fs_sb_info *, unsigned int, struct f2fs_sit_block *); extern u32 update_nat_bits_flags(struct f2fs_super_block *, struct f2fs_checkpoint *, u32); extern void write_nat_bits(struct f2fs_sb_info *, struct f2fs_super_block *, struct f2fs_checkpoint *, int); /* dump.c */ struct dump_option { nid_t nid; int start_nat; int end_nat; int start_sit; int end_sit; int start_ssa; int end_ssa; int32_t blk_addr; }; extern void nat_dump(struct f2fs_sb_info *); extern void sit_dump(struct f2fs_sb_info *, unsigned int, unsigned int); extern void ssa_dump(struct f2fs_sb_info *, int, int); extern void dump_node(struct f2fs_sb_info *, nid_t, int); extern int dump_info_from_blkaddr(struct f2fs_sb_info *, u32); /* defrag.c */ int f2fs_defragment(struct f2fs_sb_info *, u64, u64, u64, int); /* resize.c */ int f2fs_resize(struct f2fs_sb_info *); /* sload.c */ int f2fs_sload(struct f2fs_sb_info *); /* segment.c */ void reserve_new_block(struct f2fs_sb_info *, block_t *, struct f2fs_summary *, int); void new_data_block(struct f2fs_sb_info *, void *, struct dnode_of_data *, int); int f2fs_build_file(struct f2fs_sb_info *, struct dentry *); void f2fs_alloc_nid(struct f2fs_sb_info *, nid_t *, int); void set_data_blkaddr(struct dnode_of_data *); block_t new_node_block(struct f2fs_sb_info *, struct dnode_of_data *, unsigned int); /* segment.c */ u64 f2fs_read(struct f2fs_sb_info *, nid_t, u8 *, u64, pgoff_t); u64 f2fs_write(struct f2fs_sb_info *, nid_t, u8 *, u64, pgoff_t); void f2fs_filesize_update(struct f2fs_sb_info *, nid_t, u64); void get_dnode_of_data(struct f2fs_sb_info *, struct dnode_of_data *, pgoff_t, int); void make_dentry_ptr(struct f2fs_dentry_ptr *, struct f2fs_node *, void *, int); int f2fs_create(struct f2fs_sb_info *, struct dentry *); int f2fs_mkdir(struct f2fs_sb_info *, struct dentry *); int f2fs_symlink(struct f2fs_sb_info *, struct dentry *); int inode_set_selinux(struct f2fs_sb_info *, u32, const char *); int f2fs_find_path(struct f2fs_sb_info *, char *, nid_t *); /* xattr.c */ void *read_all_xattrs(struct f2fs_sb_info *, struct f2fs_node *); #endif /* _FSCK_H_ */ f2fs-tools-1.10.0/fsck/main.c000066400000000000000000000430201323417143600156100ustar00rootroot00000000000000/** * main.c * * Copyright (c) 2013 Samsung Electronics Co., Ltd. * http://www.samsung.com/ * Copyright (c) 2015 Jaegeuk Kim * : implement defrag.f2fs * Copyright (C) 2015 Huawei Ltd. * Hou Pengyang * Liu Shuoran * Jaegeuk Kim * : add sload.f2fs * * 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. */ #include "fsck.h" #include #include #include #include "quotaio.h" struct f2fs_fsck gfsck; #ifdef WITH_ANDROID #include extern struct sparse_file *f2fs_sparse_file; #endif static char *absolute_path(const char *file) { char *ret; char cwd[PATH_MAX]; if (file[0] != '/') { if (getcwd(cwd, PATH_MAX) == NULL) { fprintf(stderr, "Failed to getcwd\n"); exit(EXIT_FAILURE); } ret = malloc(strlen(cwd) + 1 + strlen(file) + 1); if (ret) sprintf(ret, "%s/%s", cwd, file); } else ret = strdup(file); return ret; } void fsck_usage() { MSG(0, "\nUsage: fsck.f2fs [options] device\n"); MSG(0, "[options]:\n"); MSG(0, " -a check/fix potential corruption, reported by f2fs\n"); MSG(0, " -d debug level [default:0]\n"); MSG(0, " -f check/fix entire partition\n"); MSG(0, " -p preen mode [default:0 the same as -a [0|1]]\n"); MSG(0, " -S sparse_mode\n"); MSG(0, " -t show directory tree\n"); MSG(0, " -q preserve quota limits\n"); MSG(0, " -y fix all the time\n"); MSG(0, " --dry-run do not really fix corruptions\n"); exit(1); } void dump_usage() { MSG(0, "\nUsage: dump.f2fs [options] device\n"); MSG(0, "[options]:\n"); MSG(0, " -d debug level [default:0]\n"); MSG(0, " -i inode no (hex)\n"); MSG(0, " -n [NAT dump segno from #1~#2 (decimal), for all 0~-1]\n"); MSG(0, " -s [SIT dump segno from #1~#2 (decimal), for all 0~-1]\n"); MSG(0, " -S sparse_mode\n"); MSG(0, " -a [SSA dump segno from #1~#2 (decimal), for all 0~-1]\n"); MSG(0, " -b blk_addr (in 4KB)\n"); exit(1); } void defrag_usage() { MSG(0, "\nUsage: defrag.f2fs [options] device\n"); MSG(0, "[options]:\n"); MSG(0, " -d debug level [default:0]\n"); MSG(0, " -s start block address [default: main_blkaddr]\n"); MSG(0, " -S sparse_mode\n"); MSG(0, " -l length [default:512 (2MB)]\n"); MSG(0, " -t target block address [default: main_blkaddr + 2MB]\n"); MSG(0, " -i set direction as shrink [default: expand]\n"); exit(1); } void resize_usage() { MSG(0, "\nUsage: resize.f2fs [options] device\n"); MSG(0, "[options]:\n"); MSG(0, " -d debug level [default:0]\n"); MSG(0, " -t target sectors [default: device size]\n"); exit(1); } void sload_usage() { MSG(0, "\nUsage: sload.f2fs [options] device\n"); MSG(0, "[options]:\n"); MSG(0, " -C fs_config\n"); MSG(0, " -f source directory [path of the source directory]\n"); MSG(0, " -p product out directory\n"); MSG(0, " -s file_contexts\n"); MSG(0, " -S sparse_mode\n"); MSG(0, " -t mount point [prefix of target fs path, default:/]\n"); MSG(0, " -T timestamp\n"); MSG(0, " -d debug level [default:0]\n"); exit(1); } static int is_digits(char *optarg) { unsigned int i; for (i = 0; i < strlen(optarg); i++) if (!isdigit(optarg[i])) break; return i == strlen(optarg); } static void error_out(char *prog) { if (!strcmp("fsck.f2fs", prog)) fsck_usage(); else if (!strcmp("dump.f2fs", prog)) dump_usage(); else if (!strcmp("defrag.f2fs", prog)) defrag_usage(); else if (!strcmp("resize.f2fs", prog)) resize_usage(); else if (!strcmp("sload.f2fs", prog)) sload_usage(); else MSG(0, "\nWrong progam.\n"); } void f2fs_parse_options(int argc, char *argv[]) { int option = 0; char *prog = basename(argv[0]); int err = NOERROR; #ifdef WITH_ANDROID int i; /* Allow prog names (e.g, sload_f2fs, fsck_f2fs, etc) */ for (i = 0; i < strlen(prog); i++) { if (prog[i] == '_') prog[i] = '.'; } #endif if (argc < 2) { MSG(0, "\tError: Device not specified\n"); error_out(prog); } if (!strcmp("fsck.f2fs", prog)) { const char *option_string = ":ad:fp:q:Sty"; int opt = 0; struct option long_opt[] = { {"dry-run", no_argument, 0, 1}, {0, 0, 0, 0} }; c.func = FSCK; while ((option = getopt_long(argc, argv, option_string, long_opt, &opt)) != EOF) { switch (option) { case 1: c.dry_run = 1; MSG(0, "Info: Dry run\n"); break; case 'a': c.auto_fix = 1; MSG(0, "Info: Fix the reported corruption.\n"); break; case 'p': /* preen mode has different levels: * 0: default level, the same as -a * 1: check meta */ if (optarg[0] == '-') { c.preen_mode = PREEN_MODE_0; optind--; break; } else if (!is_digits(optarg)) { err = EWRONG_OPT; break; } c.preen_mode = atoi(optarg); if (c.preen_mode < 0) c.preen_mode = PREEN_MODE_0; else if (c.preen_mode >= PREEN_MODE_MAX) c.preen_mode = PREEN_MODE_MAX - 1; if (c.preen_mode == PREEN_MODE_0) c.auto_fix = 1; MSG(0, "Info: Fix the reported corruption in " "preen mode %d\n", c.preen_mode); break; case 'd': if (optarg[0] == '-') { err = ENEED_ARG; break; } else if (!is_digits(optarg)) { err = EWRONG_OPT; break; } c.dbg_lv = atoi(optarg); MSG(0, "Info: Debug level = %d\n", c.dbg_lv); break; case 'f': case 'y': c.fix_on = 1; MSG(0, "Info: Force to fix corruption\n"); break; case 'q': c.preserve_limits = atoi(optarg); MSG(0, "Info: Preserve quota limits = %d\n", c.preserve_limits); break; case 'S': c.sparse_mode = 1; break; case 't': c.show_dentry = 1; break; case ':': if (optopt == 'p') { MSG(0, "Info: Use default preen mode\n"); c.preen_mode = PREEN_MODE_0; c.auto_fix = 1; } else { option = optopt; err = ENEED_ARG; break; } break; case '?': option = optopt; default: err = EUNKNOWN_OPT; break; } if (err != NOERROR) break; } } else if (!strcmp("dump.f2fs", prog)) { const char *option_string = "d:i:n:s:Sa:b:"; static struct dump_option dump_opt = { .nid = 0, /* default root ino */ .start_nat = -1, .end_nat = -1, .start_sit = -1, .end_sit = -1, .start_ssa = -1, .end_ssa = -1, .blk_addr = -1, }; c.func = DUMP; while ((option = getopt(argc, argv, option_string)) != EOF) { int ret = 0; switch (option) { case 'd': if (!is_digits(optarg)) { err = EWRONG_OPT; break; } c.dbg_lv = atoi(optarg); MSG(0, "Info: Debug level = %d\n", c.dbg_lv); break; case 'i': if (strncmp(optarg, "0x", 2)) ret = sscanf(optarg, "%d", &dump_opt.nid); else ret = sscanf(optarg, "%x", &dump_opt.nid); break; case 'n': ret = sscanf(optarg, "%d~%d", &dump_opt.start_nat, &dump_opt.end_nat); break; case 's': ret = sscanf(optarg, "%d~%d", &dump_opt.start_sit, &dump_opt.end_sit); break; case 'S': c.sparse_mode = 1; break; case 'a': ret = sscanf(optarg, "%d~%d", &dump_opt.start_ssa, &dump_opt.end_ssa); break; case 'b': if (strncmp(optarg, "0x", 2)) ret = sscanf(optarg, "%d", &dump_opt.blk_addr); else ret = sscanf(optarg, "%x", &dump_opt.blk_addr); break; default: err = EUNKNOWN_OPT; break; } ASSERT(ret >= 0); if (err != NOERROR) break; } c.private = &dump_opt; } else if (!strcmp("defrag.f2fs", prog)) { const char *option_string = "d:s:Sl:t:i"; c.func = DEFRAG; while ((option = getopt(argc, argv, option_string)) != EOF) { int ret = 0; switch (option) { case 'd': if (!is_digits(optarg)) { err = EWRONG_OPT; break; } c.dbg_lv = atoi(optarg); MSG(0, "Info: Debug level = %d\n", c.dbg_lv); break; case 's': if (strncmp(optarg, "0x", 2)) ret = sscanf(optarg, "%"PRIu64"", &c.defrag_start); else ret = sscanf(optarg, "%"PRIx64"", &c.defrag_start); break; case 'S': c.sparse_mode = 1; break; case 'l': if (strncmp(optarg, "0x", 2)) ret = sscanf(optarg, "%"PRIu64"", &c.defrag_len); else ret = sscanf(optarg, "%"PRIx64"", &c.defrag_len); break; case 't': if (strncmp(optarg, "0x", 2)) ret = sscanf(optarg, "%"PRIu64"", &c.defrag_target); else ret = sscanf(optarg, "%"PRIx64"", &c.defrag_target); break; case 'i': c.defrag_shrink = 1; break; default: err = EUNKNOWN_OPT; break; } ASSERT(ret >= 0); if (err != NOERROR) break; } } else if (!strcmp("resize.f2fs", prog)) { const char *option_string = "d:t:"; c.func = RESIZE; while ((option = getopt(argc, argv, option_string)) != EOF) { int ret = 0; switch (option) { case 'd': if (!is_digits(optarg)) { err = EWRONG_OPT; break; } c.dbg_lv = atoi(optarg); MSG(0, "Info: Debug level = %d\n", c.dbg_lv); break; case 't': if (strncmp(optarg, "0x", 2)) ret = sscanf(optarg, "%"PRIu64"", &c.target_sectors); else ret = sscanf(optarg, "%"PRIx64"", &c.target_sectors); break; default: err = EUNKNOWN_OPT; break; } ASSERT(ret >= 0); if (err != NOERROR) break; } } else if (!strcmp("sload.f2fs", prog)) { const char *option_string = "C:d:f:p:s:St:T:"; #ifdef HAVE_LIBSELINUX int max_nr_opt = (int)sizeof(c.seopt_file) / sizeof(c.seopt_file[0]); char *token; #endif char *p; c.func = SLOAD; while ((option = getopt(argc, argv, option_string)) != EOF) { switch (option) { case 'C': c.fs_config_file = absolute_path(optarg); break; case 'd': if (!is_digits(optarg)) { err = EWRONG_OPT; break; } c.dbg_lv = atoi(optarg); MSG(0, "Info: Debug level = %d\n", c.dbg_lv); break; case 'f': c.from_dir = absolute_path(optarg); break; case 'p': c.target_out_dir = absolute_path(optarg); break; case 's': #ifdef HAVE_LIBSELINUX token = strtok(optarg, ","); while (token) { if (c.nr_opt == max_nr_opt) { MSG(0, "\tError: Expected at most %d selinux opts\n", max_nr_opt); error_out(prog); } c.seopt_file[c.nr_opt].type = SELABEL_OPT_PATH; c.seopt_file[c.nr_opt].value = absolute_path(token); c.nr_opt++; token = strtok(NULL, ","); } #else MSG(0, "Info: Not support selinux opts\n"); #endif break; case 'S': c.sparse_mode = 1; break; case 't': c.mount_point = (char *)optarg; break; case 'T': c.fixed_time = strtoul(optarg, &p, 0); break; default: err = EUNKNOWN_OPT; break; } if (err != NOERROR) break; } } if (optind >= argc) { MSG(0, "\tError: Device not specified\n"); error_out(prog); } c.devices[0].path = strdup(argv[optind]); if (argc > (optind + 1)) { c.dbg_lv = 0; err = EUNKNOWN_ARG; } if (err == NOERROR) return; /* print out error */ switch (err) { case EWRONG_OPT: MSG(0, "\tError: Wrong option -%c %s\n", option, optarg); break; case ENEED_ARG: MSG(0, "\tError: Need argument for -%c\n", option); break; case EUNKNOWN_OPT: MSG(0, "\tError: Unknown option %c\n", option); break; case EUNKNOWN_ARG: MSG(0, "\tError: Unknown argument %s\n", argv[optind]); break; } error_out(prog); } static void do_fsck(struct f2fs_sb_info *sbi) { struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi); u32 flag = le32_to_cpu(ckpt->ckpt_flags); u32 blk_cnt; errcode_t ret; fsck_init(sbi); print_cp_state(flag); if (!c.fix_on && !c.bug_on) { switch (c.preen_mode) { case PREEN_MODE_1: if (fsck_chk_meta(sbi)) { MSG(0, "[FSCK] F2FS metadata [Fail]"); MSG(0, "\tError: meta does not match, " "force check all\n"); } else { MSG(0, "[FSCK] F2FS metadata [Ok..]"); fsck_free(sbi); return; } if (!c.ro) c.fix_on = 1; break; } } else if (c.preen_mode) { /* * we can hit this in 3 situations: * 1. fsck -f, fix_on has already been set to 1 when * parsing options; * 2. fsck -a && CP_FSCK_FLAG is set, fix_on has already * been set to 1 when checking CP_FSCK_FLAG; * 3. fsck -p 1 && error is detected, then bug_on is set, * we set fix_on = 1 here, so that fsck can fix errors * automatically */ c.fix_on = 1; } fsck_chk_quota_node(sbi); /* Traverse all block recursively from root inode */ blk_cnt = 1; if (c.feature & cpu_to_le32(F2FS_FEATURE_QUOTA_INO)) { ret = quota_init_context(sbi); if (ret) { ASSERT_MSG("quota_init_context failure: %d", ret); return; } } fsck_chk_orphan_node(sbi); fsck_chk_node_blk(sbi, NULL, sbi->root_ino_num, F2FS_FT_DIR, TYPE_INODE, &blk_cnt, NULL); fsck_chk_quota_files(sbi); fsck_verify(sbi); fsck_free(sbi); } static void do_dump(struct f2fs_sb_info *sbi) { struct dump_option *opt = (struct dump_option *)c.private; struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi); u32 flag = le32_to_cpu(ckpt->ckpt_flags); if (opt->end_nat == -1) opt->end_nat = NM_I(sbi)->max_nid; if (opt->end_sit == -1) opt->end_sit = SM_I(sbi)->main_segments; if (opt->end_ssa == -1) opt->end_ssa = SM_I(sbi)->main_segments; if (opt->start_nat != -1) nat_dump(sbi); if (opt->start_sit != -1) sit_dump(sbi, opt->start_sit, opt->end_sit); if (opt->start_ssa != -1) ssa_dump(sbi, opt->start_ssa, opt->end_ssa); if (opt->blk_addr != -1) dump_info_from_blkaddr(sbi, opt->blk_addr); if (opt->nid) dump_node(sbi, opt->nid, 0); print_cp_state(flag); } static int do_defrag(struct f2fs_sb_info *sbi) { struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi); if (c.defrag_start > get_sb(block_count)) goto out_range; if (c.defrag_start < SM_I(sbi)->main_blkaddr) c.defrag_start = SM_I(sbi)->main_blkaddr; if (c.defrag_len == 0) c.defrag_len = sbi->blocks_per_seg; if (c.defrag_start + c.defrag_len > get_sb(block_count)) c.defrag_len = get_sb(block_count) - c.defrag_start; if (c.defrag_target == 0) { c.defrag_target = c.defrag_start - 1; if (!c.defrag_shrink) c.defrag_target += c.defrag_len + 1; } if (c.defrag_target < SM_I(sbi)->main_blkaddr || c.defrag_target > get_sb(block_count)) goto out_range; if (c.defrag_target >= c.defrag_start && c.defrag_target < c.defrag_start + c.defrag_len) goto out_range; if (c.defrag_start > c.defrag_target) MSG(0, "Info: Move 0x%"PRIx64" <- [0x%"PRIx64"-0x%"PRIx64"]\n", c.defrag_target, c.defrag_start, c.defrag_start + c.defrag_len - 1); else MSG(0, "Info: Move [0x%"PRIx64"-0x%"PRIx64"] -> 0x%"PRIx64"\n", c.defrag_start, c.defrag_start + c.defrag_len - 1, c.defrag_target); return f2fs_defragment(sbi, c.defrag_start, c.defrag_len, c.defrag_target, c.defrag_shrink); out_range: ASSERT_MSG("Out-of-range [0x%"PRIx64" ~ 0x%"PRIx64"] to 0x%"PRIx64"", c.defrag_start, c.defrag_start + c.defrag_len - 1, c.defrag_target); return -1; } static int do_resize(struct f2fs_sb_info *sbi) { struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi); if (!c.target_sectors) c.target_sectors = c.total_sectors; if (c.target_sectors > c.total_sectors) { ASSERT_MSG("Out-of-range Target=0x%"PRIx64" / 0x%"PRIx64"", c.target_sectors, c.total_sectors); return -1; } /* may different sector size */ if ((c.target_sectors * c.sector_size >> get_sb(log_blocksize)) <= get_sb(block_count)) { ASSERT_MSG("Nothing to resize, now only support resize to expand\n"); return -1; } return f2fs_resize(sbi); } static int do_sload(struct f2fs_sb_info *sbi) { if (!c.from_dir) { MSG(0, "Info: No source directory, but it's okay.\n"); return 0; } if (!c.mount_point) c.mount_point = "/"; return f2fs_sload(sbi); } int main(int argc, char **argv) { struct f2fs_sb_info *sbi; int ret = 0; f2fs_init_configuration(); f2fs_parse_options(argc, argv); if (f2fs_devs_are_umounted() < 0) { if (errno == EBUSY) return -1; if (!c.ro || c.func == DEFRAG) { MSG(0, "\tError: Not available on mounted device!\n"); return -1; } /* allow ro-mounted partition */ MSG(0, "Info: Check FS only due to RO\n"); c.fix_on = 0; c.auto_fix = 0; } /* Get device */ if (f2fs_get_device_info() < 0) return -1; fsck_again: memset(&gfsck, 0, sizeof(gfsck)); gfsck.sbi.fsck = &gfsck; sbi = &gfsck.sbi; ret = f2fs_do_mount(sbi); if (ret != 0) { if (ret == 1) { MSG(0, "Info: No error was reported\n"); ret = 0; } goto out_err; } switch (c.func) { case FSCK: do_fsck(sbi); break; #ifdef WITH_DUMP case DUMP: do_dump(sbi); break; #endif #ifdef WITH_DEFRAG case DEFRAG: ret = do_defrag(sbi); if (ret) goto out_err; break; #endif #ifdef WITH_RESIZE case RESIZE: if (do_resize(sbi)) goto out_err; break; #endif #ifdef WITH_SLOAD case SLOAD: if (do_sload(sbi)) goto out_err; f2fs_do_umount(sbi); /* fsck to fix missing quota */ c.func = FSCK; c.fix_on = 1; goto fsck_again; #endif default: ERR_MSG("Wrong program name\n"); ASSERT(0); } f2fs_do_umount(sbi); if (c.func == FSCK && c.bug_on) { if (!c.ro && c.fix_on == 0 && c.auto_fix == 0) { char ans[255] = {0}; retry: printf("Do you want to fix this partition? [Y/N] "); ret = scanf("%s", ans); ASSERT(ret >= 0); if (!strcasecmp(ans, "y")) c.fix_on = 1; else if (!strcasecmp(ans, "n")) c.fix_on = 0; else goto retry; if (c.fix_on) goto fsck_again; } } ret = f2fs_finalize_device(); if (ret < 0) return ret; printf("\nDone.\n"); return 0; out_err: if (sbi->ckpt) free(sbi->ckpt); if (sbi->raw_super) free(sbi->raw_super); return ret; } f2fs-tools-1.10.0/fsck/mkquota.c000066400000000000000000000222541323417143600163530ustar00rootroot00000000000000/* * mkquota.c --- create quota files for a filesystem * * Aditya Kali * Hyojun Kim - Ported to f2fs-tools */ #include "config.h" #include #include #include #include #include #include #include #include #include "quotaio.h" #include "quotaio_v2.h" #include "quotaio_tree.h" #include "common.h" #include "dict.h" /* Needed for architectures where sizeof(int) != sizeof(void *) */ #define UINT_TO_VOIDPTR(val) ((void *)(intptr_t)(val)) #define VOIDPTR_TO_UINT(ptr) ((unsigned int)(intptr_t)(ptr)) #if DEBUG_QUOTA static void print_dquot(const char *desc, struct dquot *dq) { if (desc) fprintf(stderr, "%s: ", desc); fprintf(stderr, "%u %lld:%lld:%lld %lld:%lld:%lld\n", dq->dq_id, (long long) dq->dq_dqb.dqb_curspace, (long long) dq->dq_dqb.dqb_bsoftlimit, (long long) dq->dq_dqb.dqb_bhardlimit, (long long) dq->dq_dqb.dqb_curinodes, (long long) dq->dq_dqb.dqb_isoftlimit, (long long) dq->dq_dqb.dqb_ihardlimit); } #else #define print_dquot(...) #endif static void write_dquots(dict_t *dict, struct quota_handle *qh) { dnode_t *n; struct dquot *dq; for (n = dict_first(dict); n; n = dict_next(dict, n)) { dq = dnode_get(n); if (dq) { print_dquot("write", dq); dq->dq_h = qh; update_grace_times(dq); qh->qh_ops->commit_dquot(dq); } } } errcode_t quota_write_inode(struct f2fs_sb_info *sbi, enum quota_type qtype) { struct f2fs_fsck *fsck = F2FS_FSCK(sbi); struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi); quota_ctx_t qctx = fsck->qctx; struct quota_handle *h = NULL; int retval = 0; dict_t *dict; if ((!qctx) || (!sb->qf_ino[qtype])) return 0; retval = quota_get_mem(sizeof(struct quota_handle), &h); if (retval) { log_debug("Unable to allocate quota handle"); goto out; } dict = qctx->quota_dict[qtype]; if (dict) { retval = quota_file_create(sbi, h, qtype); if (retval) { log_debug("Cannot initialize io on quotafile"); } else { write_dquots(dict, h); quota_file_close(sbi, h, 1); } } out: if (h) quota_free_mem(&h); return retval; } /******************************************************************/ /* Helper functions for computing quota in memory. */ /******************************************************************/ static int dict_uint_cmp(const void *a, const void *b) { unsigned int c, d; c = VOIDPTR_TO_UINT(a); d = VOIDPTR_TO_UINT(b); if (c == d) return 0; else if (c > d) return 1; else return -1; } static inline qid_t get_qid(struct f2fs_inode *inode, enum quota_type qtype) { switch (qtype) { case USRQUOTA: return inode->i_uid; case GRPQUOTA: return inode->i_gid; case PRJQUOTA: return inode->i_projid; default: return 0; } return 0; } static void quota_dnode_free(dnode_t *node, void *UNUSED(context)) { void *ptr = node ? dnode_get(node) : 0; quota_free_mem(&ptr); free(node); } /* * Set up the quota tracking data structures. */ errcode_t quota_init_context(struct f2fs_sb_info *sbi) { struct f2fs_fsck *fsck = F2FS_FSCK(sbi); struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi); errcode_t err; dict_t *dict; quota_ctx_t ctx; enum quota_type qtype; err = quota_get_mem(sizeof(struct quota_ctx), &ctx); if (err) { log_debug("Failed to allocate quota context"); return err; } memset(ctx, 0, sizeof(struct quota_ctx)); dict_init(&ctx->linked_inode_dict, DICTCOUNT_T_MAX, dict_uint_cmp); for (qtype = 0; qtype < MAXQUOTAS; qtype++) { ctx->quota_file[qtype] = NULL; if (!sb->qf_ino[qtype]) continue; err = quota_get_mem(sizeof(dict_t), &dict); if (err) { log_debug("Failed to allocate dictionary"); quota_release_context(&ctx); return err; } ctx->quota_dict[qtype] = dict; dict_init(dict, DICTCOUNT_T_MAX, dict_uint_cmp); dict_set_allocator(dict, NULL, quota_dnode_free, NULL); } ctx->sbi = sbi; fsck->qctx = ctx; return 0; } void quota_release_context(quota_ctx_t *qctx) { dict_t *dict; enum quota_type qtype; quota_ctx_t ctx; if (!qctx) return; ctx = *qctx; for (qtype = 0; qtype < MAXQUOTAS; qtype++) { dict = ctx->quota_dict[qtype]; ctx->quota_dict[qtype] = 0; if (dict) { dict_free_nodes(dict); free(dict); } } dict_free_nodes(&ctx->linked_inode_dict); *qctx = NULL; free(ctx); } static struct dquot *get_dq(dict_t *dict, __u32 key) { struct dquot *dq; dnode_t *n; n = dict_lookup(dict, UINT_TO_VOIDPTR(key)); if (n) dq = dnode_get(n); else { if (quota_get_mem(sizeof(struct dquot), &dq)) { log_err("Unable to allocate dquot"); return NULL; } memset(dq, 0, sizeof(struct dquot)); dict_alloc_insert(dict, UINT_TO_VOIDPTR(key), dq); dq->dq_id = key; } return dq; } /* * Called to update the blocks used by a particular inode */ void quota_data_add(quota_ctx_t qctx, struct f2fs_inode *inode, qsize_t space) { struct dquot *dq; dict_t *dict; enum quota_type qtype; if (!qctx) return; for (qtype = 0; qtype < MAXQUOTAS; qtype++) { dict = qctx->quota_dict[qtype]; if (dict) { dq = get_dq(dict, get_qid(inode, qtype)); if (dq) dq->dq_dqb.dqb_curspace += space; } } } /* * Called to remove some blocks used by a particular inode */ void quota_data_sub(quota_ctx_t qctx, struct f2fs_inode *inode, qsize_t space) { struct dquot *dq; dict_t *dict; enum quota_type qtype; if (!qctx) return; for (qtype = 0; qtype < MAXQUOTAS; qtype++) { dict = qctx->quota_dict[qtype]; if (dict) { dq = get_dq(dict, get_qid(inode, qtype)); dq->dq_dqb.dqb_curspace -= space; } } } /* * Called to count the files used by an inode's user/group */ void quota_data_inodes(quota_ctx_t qctx, struct f2fs_inode *inode, int adjust) { struct dquot *dq; dict_t *dict; enum quota_type qtype; if (!qctx) return; for (qtype = 0; qtype < MAXQUOTAS; qtype++) { dict = qctx->quota_dict[qtype]; if (dict) { dq = get_dq(dict, get_qid(inode, qtype)); dq->dq_dqb.dqb_curinodes += adjust; } } } /* * Called from fsck to count quota. */ void quota_add_inode_usage(quota_ctx_t qctx, f2fs_ino_t ino, struct f2fs_inode* inode) { if (qctx) { /* Handle hard linked inodes */ if (inode->i_links > 1) { if (dict_lookup(&qctx->linked_inode_dict, UINT_TO_VOIDPTR(ino))) { return; } dict_alloc_insert(&qctx->linked_inode_dict, UINT_TO_VOIDPTR(ino), NULL); } qsize_t space = (inode->i_blocks - 1) * BLOCK_SZ; quota_data_add(qctx, inode, space); quota_data_inodes(qctx, inode, +1); } } struct scan_dquots_data { dict_t *quota_dict; int update_limits; /* update limits from disk */ int update_usage; int usage_is_inconsistent; }; static int scan_dquots_callback(struct dquot *dquot, void *cb_data) { struct scan_dquots_data *scan_data = cb_data; dict_t *quota_dict = scan_data->quota_dict; struct dquot *dq; dq = get_dq(quota_dict, dquot->dq_id); dq->dq_id = dquot->dq_id; dq->dq_flags |= DQF_SEEN; print_dquot("mem", dq); print_dquot("dsk", dquot); /* Check if there is inconsistency */ if (dq->dq_dqb.dqb_curspace != dquot->dq_dqb.dqb_curspace || dq->dq_dqb.dqb_curinodes != dquot->dq_dqb.dqb_curinodes) { scan_data->usage_is_inconsistent = 1; log_debug("[QUOTA WARNING] Usage inconsistent for ID %u:" "actual (%lld, %lld) != expected (%lld, %lld)\n", dq->dq_id, (long long) dq->dq_dqb.dqb_curspace, (long long) dq->dq_dqb.dqb_curinodes, (long long) dquot->dq_dqb.dqb_curspace, (long long) dquot->dq_dqb.dqb_curinodes); } if (scan_data->update_limits) { dq->dq_dqb.dqb_ihardlimit = dquot->dq_dqb.dqb_ihardlimit; dq->dq_dqb.dqb_isoftlimit = dquot->dq_dqb.dqb_isoftlimit; dq->dq_dqb.dqb_bhardlimit = dquot->dq_dqb.dqb_bhardlimit; dq->dq_dqb.dqb_bsoftlimit = dquot->dq_dqb.dqb_bsoftlimit; } if (scan_data->update_usage) { dq->dq_dqb.dqb_curspace = dquot->dq_dqb.dqb_curspace; dq->dq_dqb.dqb_curinodes = dquot->dq_dqb.dqb_curinodes; } return 0; } /* * Compares the measured quota in qctx->quota_dict with that in the quota inode * on disk and updates the limits in qctx->quota_dict. 'usage_inconsistent' is * set to 1 if the supplied and on-disk quota usage values are not identical. */ errcode_t quota_compare_and_update(struct f2fs_sb_info *sbi, enum quota_type qtype, int *usage_inconsistent, int preserve_limits) { struct f2fs_fsck *fsck = F2FS_FSCK(sbi); quota_ctx_t qctx = fsck->qctx; struct quota_handle qh; struct scan_dquots_data scan_data; struct dquot *dq; dnode_t *n; dict_t *dict = qctx->quota_dict[qtype]; errcode_t err = 0; if (!dict) goto out; err = quota_file_open(sbi, &qh, qtype, 0); if (err) { log_debug("Open quota file failed"); goto out; } scan_data.quota_dict = qctx->quota_dict[qtype]; scan_data.update_limits = preserve_limits; scan_data.update_usage = 0; scan_data.usage_is_inconsistent = 0; err = qh.qh_ops->scan_dquots(&qh, scan_dquots_callback, &scan_data); if (err) { log_debug("Error scanning dquots"); goto out; } for (n = dict_first(dict); n; n = dict_next(dict, n)) { dq = dnode_get(n); if (!dq) continue; if ((dq->dq_flags & DQF_SEEN) == 0) { log_debug("[QUOTA WARNING] " "Missing quota entry ID %d\n", dq->dq_id); scan_data.usage_is_inconsistent = 1; } } *usage_inconsistent = scan_data.usage_is_inconsistent; out: return err; } f2fs-tools-1.10.0/fsck/mount.c000066400000000000000000001771051323417143600160420ustar00rootroot00000000000000/** * mount.c * * Copyright (c) 2013 Samsung Electronics Co., Ltd. * http://www.samsung.com/ * * 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. */ #include "fsck.h" #include "xattr.h" #include #ifdef HAVE_LINUX_POSIX_ACL_H #include #endif #ifdef HAVE_SYS_ACL_H #include #endif #ifndef ACL_UNDEFINED_TAG #define ACL_UNDEFINED_TAG (0x00) #define ACL_USER_OBJ (0x01) #define ACL_USER (0x02) #define ACL_GROUP_OBJ (0x04) #define ACL_GROUP (0x08) #define ACL_MASK (0x10) #define ACL_OTHER (0x20) #endif u32 get_free_segments(struct f2fs_sb_info *sbi) { u32 i, free_segs = 0; for (i = 0; i < TOTAL_SEGS(sbi); i++) { struct seg_entry *se = get_seg_entry(sbi, i); if (se->valid_blocks == 0x0 && !IS_CUR_SEGNO(sbi, i, NO_CHECK_TYPE)) free_segs++; } return free_segs; } void update_free_segments(struct f2fs_sb_info *sbi) { char *progress = "-*|*-"; static int i = 0; if (c.dbg_lv) return; MSG(0, "\r [ %c ] Free segments: 0x%x", progress[i % 5], get_free_segments(sbi)); fflush(stdout); i++; } #if defined(HAVE_LINUX_POSIX_ACL_H) || defined(HAVE_SYS_ACL_H) void print_acl(char *value, int size) { struct f2fs_acl_header *hdr = (struct f2fs_acl_header *)value; struct f2fs_acl_entry *entry = (struct f2fs_acl_entry *)(hdr + 1); const char *end = value + size; int i, count; if (hdr->a_version != cpu_to_le32(F2FS_ACL_VERSION)) { MSG(0, "Invalid ACL version [0x%x : 0x%x]\n", le32_to_cpu(hdr->a_version), F2FS_ACL_VERSION); return; } count = f2fs_acl_count(size); if (count <= 0) { MSG(0, "Invalid ACL value size %d\n", size); return; } for (i = 0; i < count; i++) { if ((char *)entry > end) { MSG(0, "Invalid ACL entries count %d\n", count); return; } switch (le16_to_cpu(entry->e_tag)) { case ACL_USER_OBJ: case ACL_GROUP_OBJ: case ACL_MASK: case ACL_OTHER: MSG(0, "tag:0x%x perm:0x%x\n", le16_to_cpu(entry->e_tag), le16_to_cpu(entry->e_perm)); entry = (struct f2fs_acl_entry *)((char *)entry + sizeof(struct f2fs_acl_entry_short)); break; case ACL_USER: MSG(0, "tag:0x%x perm:0x%x uid:%u\n", le16_to_cpu(entry->e_tag), le16_to_cpu(entry->e_perm), le32_to_cpu(entry->e_id)); entry = (struct f2fs_acl_entry *)((char *)entry + sizeof(struct f2fs_acl_entry)); break; case ACL_GROUP: MSG(0, "tag:0x%x perm:0x%x gid:%u\n", le16_to_cpu(entry->e_tag), le16_to_cpu(entry->e_perm), le32_to_cpu(entry->e_id)); entry = (struct f2fs_acl_entry *)((char *)entry + sizeof(struct f2fs_acl_entry)); break; default: MSG(0, "Unknown ACL tag 0x%x\n", le16_to_cpu(entry->e_tag)); return; } } } #else #define print_acl(value, size) do { \ int i; \ for (i = 0; i < size; i++) \ MSG(0, "%02X", value[i]); \ MSG(0, "\n"); \ } while (0) #endif void print_xattr_entry(struct f2fs_xattr_entry *ent) { char *value = (char *)(ent->e_name + le16_to_cpu(ent->e_name_len)); struct fscrypt_context *ctx; int i; MSG(0, "\nxattr: e_name_index:%d e_name:", ent->e_name_index); for (i = 0; i < le16_to_cpu(ent->e_name_len); i++) MSG(0, "%c", ent->e_name[i]); MSG(0, " e_name_len:%d e_value_size:%d e_value:\n", ent->e_name_len, le16_to_cpu(ent->e_value_size)); switch (ent->e_name_index) { case F2FS_XATTR_INDEX_POSIX_ACL_ACCESS: case F2FS_XATTR_INDEX_POSIX_ACL_DEFAULT: print_acl(value, le16_to_cpu(ent->e_value_size)); break; case F2FS_XATTR_INDEX_USER: case F2FS_XATTR_INDEX_SECURITY: case F2FS_XATTR_INDEX_TRUSTED: case F2FS_XATTR_INDEX_LUSTRE: for (i = 0; i < le16_to_cpu(ent->e_value_size); i++) MSG(0, "%02X", value[i]); MSG(0, "\n"); break; case F2FS_XATTR_INDEX_ENCRYPTION: ctx = (struct fscrypt_context *)value; MSG(0, "format: %d\n", ctx->format); MSG(0, "contents_encryption_mode: 0x%x\n", ctx->contents_encryption_mode); MSG(0, "filenames_encryption_mode: 0x%x\n", ctx->filenames_encryption_mode); MSG(0, "flags: 0x%x\n", ctx->flags); MSG(0, "master_key_descriptor: "); for (i = 0; i < FS_KEY_DESCRIPTOR_SIZE; i++) MSG(0, "%02X", ctx->master_key_descriptor[i]); MSG(0, "\nnonce: "); for (i = 0; i < FS_KEY_DERIVATION_NONCE_SIZE; i++) MSG(0, "%02X", ctx->nonce[i]); MSG(0, "\n"); break; default: break; } } void print_inode_info(struct f2fs_sb_info *sbi, struct f2fs_node *node, int name) { struct f2fs_inode *inode = &node->i; void *xattr_addr; struct f2fs_xattr_entry *ent; unsigned char en[F2FS_NAME_LEN + 1]; unsigned int i = 0; int namelen = le32_to_cpu(inode->i_namelen); int enc_name = file_enc_name(inode); int ofs = __get_extra_isize(inode); namelen = convert_encrypted_name(inode->i_name, namelen, en, enc_name); en[namelen] = '\0'; if (name && namelen) { inode->i_name[namelen] = '\0'; MSG(0, " - File name : %s%s\n", en, enc_name ? " " : ""); setlocale(LC_ALL, ""); MSG(0, " - File size : %'llu (bytes)\n", le64_to_cpu(inode->i_size)); return; } DISP_u32(inode, i_mode); DISP_u32(inode, i_advise); DISP_u32(inode, i_uid); DISP_u32(inode, i_gid); DISP_u32(inode, i_links); DISP_u64(inode, i_size); DISP_u64(inode, i_blocks); DISP_u64(inode, i_atime); DISP_u32(inode, i_atime_nsec); DISP_u64(inode, i_ctime); DISP_u32(inode, i_ctime_nsec); DISP_u64(inode, i_mtime); DISP_u32(inode, i_mtime_nsec); DISP_u32(inode, i_generation); DISP_u32(inode, i_current_depth); DISP_u32(inode, i_xattr_nid); DISP_u32(inode, i_flags); DISP_u32(inode, i_inline); DISP_u32(inode, i_pino); DISP_u32(inode, i_dir_level); if (namelen) { DISP_u32(inode, i_namelen); printf("%-30s\t\t[%s]\n", "i_name", en); } printf("i_ext: fofs:%x blkaddr:%x len:%x\n", le32_to_cpu(inode->i_ext.fofs), le32_to_cpu(inode->i_ext.blk_addr), le32_to_cpu(inode->i_ext.len)); if (c.feature & cpu_to_le32(F2FS_FEATURE_EXTRA_ATTR)) { DISP_u16(inode, i_extra_isize); if (c.feature & cpu_to_le32(F2FS_FEATURE_FLEXIBLE_INLINE_XATTR)) DISP_u16(inode, i_inline_xattr_size); if (c.feature & cpu_to_le32(F2FS_FEATURE_PRJQUOTA)) DISP_u32(inode, i_projid); if (c.feature & cpu_to_le32(F2FS_FEATURE_INODE_CHKSUM)) DISP_u32(inode, i_inode_checksum); if (c.feature & cpu_to_le32(F2FS_FEATURE_INODE_CRTIME)) { DISP_u64(inode, i_crtime); DISP_u32(inode, i_crtime_nsec); } } DISP_u32(inode, i_addr[ofs]); /* Pointers to data blocks */ DISP_u32(inode, i_addr[ofs + 1]); /* Pointers to data blocks */ DISP_u32(inode, i_addr[ofs + 2]); /* Pointers to data blocks */ DISP_u32(inode, i_addr[ofs + 3]); /* Pointers to data blocks */ for (i = ofs + 3; i < ADDRS_PER_INODE(inode); i++) { if (inode->i_addr[i] == 0x0) break; printf("i_addr[0x%x] points data block\t\t[0x%4x]\n", i, le32_to_cpu(inode->i_addr[i])); } DISP_u32(inode, i_nid[0]); /* direct */ DISP_u32(inode, i_nid[1]); /* direct */ DISP_u32(inode, i_nid[2]); /* indirect */ DISP_u32(inode, i_nid[3]); /* indirect */ DISP_u32(inode, i_nid[4]); /* double indirect */ xattr_addr = read_all_xattrs(sbi, node); list_for_each_xattr(ent, xattr_addr) { print_xattr_entry(ent); } free(xattr_addr); printf("\n"); } void print_node_info(struct f2fs_sb_info *sbi, struct f2fs_node *node_block, int verbose) { nid_t ino = le32_to_cpu(node_block->footer.ino); nid_t nid = le32_to_cpu(node_block->footer.nid); /* Is this inode? */ if (ino == nid) { DBG(verbose, "Node ID [0x%x:%u] is inode\n", nid, nid); print_inode_info(sbi, node_block, verbose); } else { int i; u32 *dump_blk = (u32 *)node_block; DBG(verbose, "Node ID [0x%x:%u] is direct node or indirect node.\n", nid, nid); for (i = 0; i <= 10; i++) MSG(verbose, "[%d]\t\t\t[0x%8x : %d]\n", i, dump_blk[i], dump_blk[i]); } } static void DISP_label(u_int16_t *name) { char buffer[MAX_VOLUME_NAME]; utf16_to_utf8(buffer, name, MAX_VOLUME_NAME, MAX_VOLUME_NAME); printf("%-30s" "\t\t[%s]\n", "volum_name", buffer); } void print_raw_sb_info(struct f2fs_super_block *sb) { if (!c.dbg_lv) return; printf("\n"); printf("+--------------------------------------------------------+\n"); printf("| Super block |\n"); printf("+--------------------------------------------------------+\n"); DISP_u32(sb, magic); DISP_u32(sb, major_ver); DISP_label(sb->volume_name); DISP_u32(sb, minor_ver); DISP_u32(sb, log_sectorsize); DISP_u32(sb, log_sectors_per_block); DISP_u32(sb, log_blocksize); DISP_u32(sb, log_blocks_per_seg); DISP_u32(sb, segs_per_sec); DISP_u32(sb, secs_per_zone); DISP_u32(sb, checksum_offset); DISP_u64(sb, block_count); DISP_u32(sb, section_count); DISP_u32(sb, segment_count); DISP_u32(sb, segment_count_ckpt); DISP_u32(sb, segment_count_sit); DISP_u32(sb, segment_count_nat); DISP_u32(sb, segment_count_ssa); DISP_u32(sb, segment_count_main); DISP_u32(sb, segment0_blkaddr); DISP_u32(sb, cp_blkaddr); DISP_u32(sb, sit_blkaddr); DISP_u32(sb, nat_blkaddr); DISP_u32(sb, ssa_blkaddr); DISP_u32(sb, main_blkaddr); DISP_u32(sb, root_ino); DISP_u32(sb, node_ino); DISP_u32(sb, meta_ino); DISP_u32(sb, cp_payload); DISP("%s", sb, version); printf("\n"); } void print_ckpt_info(struct f2fs_sb_info *sbi) { struct f2fs_checkpoint *cp = F2FS_CKPT(sbi); if (!c.dbg_lv) return; printf("\n"); printf("+--------------------------------------------------------+\n"); printf("| Checkpoint |\n"); printf("+--------------------------------------------------------+\n"); DISP_u64(cp, checkpoint_ver); DISP_u64(cp, user_block_count); DISP_u64(cp, valid_block_count); DISP_u32(cp, rsvd_segment_count); DISP_u32(cp, overprov_segment_count); DISP_u32(cp, free_segment_count); DISP_u32(cp, alloc_type[CURSEG_HOT_NODE]); DISP_u32(cp, alloc_type[CURSEG_WARM_NODE]); DISP_u32(cp, alloc_type[CURSEG_COLD_NODE]); DISP_u32(cp, cur_node_segno[0]); DISP_u32(cp, cur_node_segno[1]); DISP_u32(cp, cur_node_segno[2]); DISP_u32(cp, cur_node_blkoff[0]); DISP_u32(cp, cur_node_blkoff[1]); DISP_u32(cp, cur_node_blkoff[2]); DISP_u32(cp, alloc_type[CURSEG_HOT_DATA]); DISP_u32(cp, alloc_type[CURSEG_WARM_DATA]); DISP_u32(cp, alloc_type[CURSEG_COLD_DATA]); DISP_u32(cp, cur_data_segno[0]); DISP_u32(cp, cur_data_segno[1]); DISP_u32(cp, cur_data_segno[2]); DISP_u32(cp, cur_data_blkoff[0]); DISP_u32(cp, cur_data_blkoff[1]); DISP_u32(cp, cur_data_blkoff[2]); DISP_u32(cp, ckpt_flags); DISP_u32(cp, cp_pack_total_block_count); DISP_u32(cp, cp_pack_start_sum); DISP_u32(cp, valid_node_count); DISP_u32(cp, valid_inode_count); DISP_u32(cp, next_free_nid); DISP_u32(cp, sit_ver_bitmap_bytesize); DISP_u32(cp, nat_ver_bitmap_bytesize); DISP_u32(cp, checksum_offset); DISP_u64(cp, elapsed_time); DISP_u32(cp, sit_nat_version_bitmap[0]); printf("\n\n"); } void print_cp_state(u32 flag) { MSG(0, "Info: checkpoint state = %x : ", flag); if (flag & CP_NOCRC_RECOVERY_FLAG) MSG(0, "%s", " allow_nocrc"); if (flag & CP_TRIMMED_FLAG) MSG(0, "%s", " trimmed"); if (flag & CP_NAT_BITS_FLAG) MSG(0, "%s", " nat_bits"); if (flag & CP_CRC_RECOVERY_FLAG) MSG(0, "%s", " crc"); if (flag & CP_FASTBOOT_FLAG) MSG(0, "%s", " fastboot"); if (flag & CP_FSCK_FLAG) MSG(0, "%s", " fsck"); if (flag & CP_ERROR_FLAG) MSG(0, "%s", " error"); if (flag & CP_COMPACT_SUM_FLAG) MSG(0, "%s", " compacted_summary"); if (flag & CP_ORPHAN_PRESENT_FLAG) MSG(0, "%s", " orphan_inodes"); if (flag & CP_UMOUNT_FLAG) MSG(0, "%s", " unmount"); else MSG(0, "%s", " sudden-power-off"); MSG(0, "\n"); } void print_sb_state(struct f2fs_super_block *sb) { __le32 f = sb->feature; int i; MSG(0, "Info: superblock features = %x : ", f); if (f & cpu_to_le32(F2FS_FEATURE_ENCRYPT)) { MSG(0, "%s", " encrypt"); } if (f & cpu_to_le32(F2FS_FEATURE_BLKZONED)) { MSG(0, "%s", " blkzoned"); } if (f & cpu_to_le32(F2FS_FEATURE_EXTRA_ATTR)) { MSG(0, "%s", " extra_attr"); } if (f & cpu_to_le32(F2FS_FEATURE_PRJQUOTA)) { MSG(0, "%s", " project_quota"); } if (f & cpu_to_le32(F2FS_FEATURE_INODE_CHKSUM)) { MSG(0, "%s", " inode_checksum"); } if (f & cpu_to_le32(F2FS_FEATURE_FLEXIBLE_INLINE_XATTR)) { MSG(0, "%s", " flexible_inline_xattr"); } if (f & cpu_to_le32(F2FS_FEATURE_QUOTA_INO)) { MSG(0, "%s", " quota_ino"); } if (f & cpu_to_le32(F2FS_FEATURE_INODE_CRTIME)) { MSG(0, "%s", " inode_crtime"); } MSG(0, "\n"); MSG(0, "Info: superblock encrypt level = %d, salt = ", sb->encryption_level); for (i = 0; i < 16; i++) MSG(0, "%02x", sb->encrypt_pw_salt[i]); MSG(0, "\n"); } static inline int sanity_check_area_boundary(struct f2fs_super_block *sb, u64 offset) { u32 segment0_blkaddr = get_sb(segment0_blkaddr); u32 cp_blkaddr = get_sb(cp_blkaddr); u32 sit_blkaddr = get_sb(sit_blkaddr); u32 nat_blkaddr = get_sb(nat_blkaddr); u32 ssa_blkaddr = get_sb(ssa_blkaddr); u32 main_blkaddr = get_sb(main_blkaddr); u32 segment_count_ckpt = get_sb(segment_count_ckpt); u32 segment_count_sit = get_sb(segment_count_sit); u32 segment_count_nat = get_sb(segment_count_nat); u32 segment_count_ssa = get_sb(segment_count_ssa); u32 segment_count_main = get_sb(segment_count_main); u32 segment_count = get_sb(segment_count); u32 log_blocks_per_seg = get_sb(log_blocks_per_seg); u64 main_end_blkaddr = main_blkaddr + (segment_count_main << log_blocks_per_seg); u64 seg_end_blkaddr = segment0_blkaddr + (segment_count << log_blocks_per_seg); if (segment0_blkaddr != cp_blkaddr) { MSG(0, "\tMismatch segment0(%u) cp_blkaddr(%u)\n", segment0_blkaddr, cp_blkaddr); return -1; } if (cp_blkaddr + (segment_count_ckpt << log_blocks_per_seg) != sit_blkaddr) { MSG(0, "\tWrong CP boundary, start(%u) end(%u) blocks(%u)\n", cp_blkaddr, sit_blkaddr, segment_count_ckpt << log_blocks_per_seg); return -1; } if (sit_blkaddr + (segment_count_sit << log_blocks_per_seg) != nat_blkaddr) { MSG(0, "\tWrong SIT boundary, start(%u) end(%u) blocks(%u)\n", sit_blkaddr, nat_blkaddr, segment_count_sit << log_blocks_per_seg); return -1; } if (nat_blkaddr + (segment_count_nat << log_blocks_per_seg) != ssa_blkaddr) { MSG(0, "\tWrong NAT boundary, start(%u) end(%u) blocks(%u)\n", nat_blkaddr, ssa_blkaddr, segment_count_nat << log_blocks_per_seg); return -1; } if (ssa_blkaddr + (segment_count_ssa << log_blocks_per_seg) != main_blkaddr) { MSG(0, "\tWrong SSA boundary, start(%u) end(%u) blocks(%u)\n", ssa_blkaddr, main_blkaddr, segment_count_ssa << log_blocks_per_seg); return -1; } if (main_end_blkaddr > seg_end_blkaddr) { MSG(0, "\tWrong MAIN_AREA, start(%u) end(%u) block(%u)\n", main_blkaddr, segment0_blkaddr + (segment_count << log_blocks_per_seg), segment_count_main << log_blocks_per_seg); return -1; } else if (main_end_blkaddr < seg_end_blkaddr) { int err; set_sb(segment_count, (main_end_blkaddr - segment0_blkaddr) >> log_blocks_per_seg); err = dev_write(sb, offset, sizeof(struct f2fs_super_block)); MSG(0, "Info: Fix alignment: %s, start(%u) end(%u) block(%u)\n", err ? "failed": "done", main_blkaddr, segment0_blkaddr + (segment_count << log_blocks_per_seg), segment_count_main << log_blocks_per_seg); } return 0; } int sanity_check_raw_super(struct f2fs_super_block *sb, u64 offset) { unsigned int blocksize; if (F2FS_SUPER_MAGIC != get_sb(magic)) return -1; if (F2FS_BLKSIZE != PAGE_CACHE_SIZE) return -1; blocksize = 1 << get_sb(log_blocksize); if (F2FS_BLKSIZE != blocksize) return -1; /* check log blocks per segment */ if (get_sb(log_blocks_per_seg) != 9) return -1; /* Currently, support 512/1024/2048/4096 bytes sector size */ if (get_sb(log_sectorsize) > F2FS_MAX_LOG_SECTOR_SIZE || get_sb(log_sectorsize) < F2FS_MIN_LOG_SECTOR_SIZE) return -1; if (get_sb(log_sectors_per_block) + get_sb(log_sectorsize) != F2FS_MAX_LOG_SECTOR_SIZE) return -1; /* check reserved ino info */ if (get_sb(node_ino) != 1 || get_sb(meta_ino) != 2 || get_sb(root_ino) != 3) return -1; /* Check zoned block device feature */ if (c.devices[0].zoned_model == F2FS_ZONED_HM && !(sb->feature & cpu_to_le32(F2FS_FEATURE_BLKZONED))) { MSG(0, "\tMissing zoned block device feature\n"); return -1; } if (get_sb(segment_count) > F2FS_MAX_SEGMENT) return -1; if (sanity_check_area_boundary(sb, offset)) return -1; return 0; } int validate_super_block(struct f2fs_sb_info *sbi, int block) { u64 offset; char buf[F2FS_BLKSIZE]; sbi->raw_super = malloc(sizeof(struct f2fs_super_block)); if (block == 0) offset = F2FS_SUPER_OFFSET; else offset = F2FS_BLKSIZE + F2FS_SUPER_OFFSET; if (dev_read_block(buf, block)) return -1; memcpy(sbi->raw_super, buf + F2FS_SUPER_OFFSET, sizeof(struct f2fs_super_block)); if (!sanity_check_raw_super(sbi->raw_super, offset)) { /* get kernel version */ if (c.kd >= 0) { dev_read_version(c.version, 0, VERSION_LEN); get_kernel_version(c.version); } else { memset(c.version, 0, VERSION_LEN); } /* build sb version */ memcpy(c.sb_version, sbi->raw_super->version, VERSION_LEN); get_kernel_version(c.sb_version); memcpy(c.init_version, sbi->raw_super->init_version, VERSION_LEN); get_kernel_version(c.init_version); MSG(0, "Info: MKFS version\n \"%s\"\n", c.init_version); MSG(0, "Info: FSCK version\n from \"%s\"\n to \"%s\"\n", c.sb_version, c.version); if (memcmp(c.sb_version, c.version, VERSION_LEN)) { int ret; memcpy(sbi->raw_super->version, c.version, VERSION_LEN); ret = dev_write(sbi->raw_super, offset, sizeof(struct f2fs_super_block)); ASSERT(ret >= 0); c.auto_fix = 0; c.fix_on = 1; } print_sb_state(sbi->raw_super); return 0; } free(sbi->raw_super); sbi->raw_super = NULL; MSG(0, "\tCan't find a valid F2FS superblock at 0x%x\n", block); return -EINVAL; } int init_sb_info(struct f2fs_sb_info *sbi) { struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi); u64 total_sectors; int i; sbi->log_sectors_per_block = get_sb(log_sectors_per_block); sbi->log_blocksize = get_sb(log_blocksize); sbi->blocksize = 1 << sbi->log_blocksize; sbi->log_blocks_per_seg = get_sb(log_blocks_per_seg); sbi->blocks_per_seg = 1 << sbi->log_blocks_per_seg; sbi->segs_per_sec = get_sb(segs_per_sec); sbi->secs_per_zone = get_sb(secs_per_zone); sbi->total_sections = get_sb(section_count); sbi->total_node_count = (get_sb(segment_count_nat) / 2) * sbi->blocks_per_seg * NAT_ENTRY_PER_BLOCK; sbi->root_ino_num = get_sb(root_ino); sbi->node_ino_num = get_sb(node_ino); sbi->meta_ino_num = get_sb(meta_ino); sbi->cur_victim_sec = NULL_SEGNO; for (i = 0; i < MAX_DEVICES; i++) { if (!sb->devs[i].path[0]) break; if (i) { c.devices[i].path = strdup((char *)sb->devs[i].path); if (get_device_info(i)) ASSERT(0); } else { ASSERT(!strcmp((char *)sb->devs[i].path, (char *)c.devices[i].path)); } c.devices[i].total_segments = le32_to_cpu(sb->devs[i].total_segments); if (i) c.devices[i].start_blkaddr = c.devices[i - 1].end_blkaddr + 1; c.devices[i].end_blkaddr = c.devices[i].start_blkaddr + c.devices[i].total_segments * c.blks_per_seg - 1; if (i == 0) c.devices[i].end_blkaddr += get_sb(segment0_blkaddr); c.ndevs = i + 1; MSG(0, "Info: Device[%d] : %s blkaddr = %"PRIx64"--%"PRIx64"\n", i, c.devices[i].path, c.devices[i].start_blkaddr, c.devices[i].end_blkaddr); } total_sectors = get_sb(block_count) << sbi->log_sectors_per_block; MSG(0, "Info: total FS sectors = %"PRIu64" (%"PRIu64" MB)\n", total_sectors, total_sectors >> (20 - get_sb(log_sectorsize))); return 0; } void *validate_checkpoint(struct f2fs_sb_info *sbi, block_t cp_addr, unsigned long long *version) { void *cp_page_1, *cp_page_2; struct f2fs_checkpoint *cp; unsigned long blk_size = sbi->blocksize; unsigned long long cur_version = 0, pre_version = 0; unsigned int crc = 0; size_t crc_offset; /* Read the 1st cp block in this CP pack */ cp_page_1 = malloc(PAGE_SIZE); if (dev_read_block(cp_page_1, cp_addr) < 0) goto invalid_cp1; cp = (struct f2fs_checkpoint *)cp_page_1; crc_offset = get_cp(checksum_offset); if (crc_offset > (blk_size - sizeof(__le32))) goto invalid_cp1; crc = le32_to_cpu(*(__le32 *)((unsigned char *)cp + crc_offset)); if (f2fs_crc_valid(crc, cp, crc_offset)) goto invalid_cp1; pre_version = get_cp(checkpoint_ver); /* Read the 2nd cp block in this CP pack */ cp_page_2 = malloc(PAGE_SIZE); cp_addr += get_cp(cp_pack_total_block_count) - 1; if (dev_read_block(cp_page_2, cp_addr) < 0) goto invalid_cp2; cp = (struct f2fs_checkpoint *)cp_page_2; crc_offset = get_cp(checksum_offset); if (crc_offset > (blk_size - sizeof(__le32))) goto invalid_cp2; crc = le32_to_cpu(*(__le32 *)((unsigned char *)cp + crc_offset)); if (f2fs_crc_valid(crc, cp, crc_offset)) goto invalid_cp2; cur_version = get_cp(checkpoint_ver); if (cur_version == pre_version) { *version = cur_version; free(cp_page_2); return cp_page_1; } invalid_cp2: free(cp_page_2); invalid_cp1: free(cp_page_1); return NULL; } int get_valid_checkpoint(struct f2fs_sb_info *sbi) { struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi); void *cp1, *cp2, *cur_page; unsigned long blk_size = sbi->blocksize; unsigned long long cp1_version = 0, cp2_version = 0, version; unsigned long long cp_start_blk_no; unsigned int cp_payload, cp_blks; int ret; cp_payload = get_sb(cp_payload); if (cp_payload > F2FS_BLK_ALIGN(MAX_SIT_BITMAP_SIZE)) return -EINVAL; cp_blks = 1 + cp_payload; sbi->ckpt = malloc(cp_blks * blk_size); if (!sbi->ckpt) return -ENOMEM; /* * Finding out valid cp block involves read both * sets( cp pack1 and cp pack 2) */ cp_start_blk_no = get_sb(cp_blkaddr); cp1 = validate_checkpoint(sbi, cp_start_blk_no, &cp1_version); /* The second checkpoint pack should start at the next segment */ cp_start_blk_no += 1 << get_sb(log_blocks_per_seg); cp2 = validate_checkpoint(sbi, cp_start_blk_no, &cp2_version); if (cp1 && cp2) { if (ver_after(cp2_version, cp1_version)) { cur_page = cp2; sbi->cur_cp = 2; version = cp2_version; } else { cur_page = cp1; sbi->cur_cp = 1; version = cp1_version; } } else if (cp1) { cur_page = cp1; sbi->cur_cp = 1; version = cp1_version; } else if (cp2) { cur_page = cp2; sbi->cur_cp = 2; version = cp2_version; } else goto fail_no_cp; MSG(0, "Info: CKPT version = %llx\n", version); memcpy(sbi->ckpt, cur_page, blk_size); if (cp_blks > 1) { unsigned int i; unsigned long long cp_blk_no; cp_blk_no = get_sb(cp_blkaddr); if (cur_page == cp2) cp_blk_no += 1 << get_sb(log_blocks_per_seg); /* copy sit bitmap */ for (i = 1; i < cp_blks; i++) { unsigned char *ckpt = (unsigned char *)sbi->ckpt; ret = dev_read_block(cur_page, cp_blk_no + i); ASSERT(ret >= 0); memcpy(ckpt + i * blk_size, cur_page, blk_size); } } if (cp1) free(cp1); if (cp2) free(cp2); return 0; fail_no_cp: free(sbi->ckpt); sbi->ckpt = NULL; return -EINVAL; } int sanity_check_ckpt(struct f2fs_sb_info *sbi) { unsigned int total, fsmeta; struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi); struct f2fs_checkpoint *cp = F2FS_CKPT(sbi); total = get_sb(segment_count); fsmeta = get_sb(segment_count_ckpt); fsmeta += get_sb(segment_count_sit); fsmeta += get_sb(segment_count_nat); fsmeta += get_cp(rsvd_segment_count); fsmeta += get_sb(segment_count_ssa); if (fsmeta >= total) return 1; return 0; } static pgoff_t current_nat_addr(struct f2fs_sb_info *sbi, nid_t start) { struct f2fs_nm_info *nm_i = NM_I(sbi); pgoff_t block_off; pgoff_t block_addr; int seg_off; block_off = NAT_BLOCK_OFFSET(start); seg_off = block_off >> sbi->log_blocks_per_seg; block_addr = (pgoff_t)(nm_i->nat_blkaddr + (seg_off << sbi->log_blocks_per_seg << 1) + (block_off & ((1 << sbi->log_blocks_per_seg) -1))); if (f2fs_test_bit(block_off, nm_i->nat_bitmap)) block_addr += sbi->blocks_per_seg; return block_addr; } static int f2fs_init_nid_bitmap(struct f2fs_sb_info *sbi) { struct f2fs_nm_info *nm_i = NM_I(sbi); int nid_bitmap_size = (nm_i->max_nid + BITS_PER_BYTE - 1) / BITS_PER_BYTE; struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_HOT_DATA); struct f2fs_summary_block *sum = curseg->sum_blk; struct f2fs_journal *journal = &sum->journal; struct f2fs_nat_block nat_block; block_t start_blk; nid_t nid; int i; if (!(c.func == SLOAD || c.func == FSCK)) return 0; nm_i->nid_bitmap = (char *)calloc(nid_bitmap_size, 1); if (!nm_i->nid_bitmap) return -ENOMEM; /* arbitrarily set 0 bit */ f2fs_set_bit(0, nm_i->nid_bitmap); memset((void *)&nat_block, 0, sizeof(struct f2fs_nat_block)); for (nid = 0; nid < nm_i->max_nid; nid++) { if (!(nid % NAT_ENTRY_PER_BLOCK)) { int ret; start_blk = current_nat_addr(sbi, nid); ret = dev_read_block((void *)&nat_block, start_blk); ASSERT(ret >= 0); } if (nat_block.entries[nid % NAT_ENTRY_PER_BLOCK].block_addr) f2fs_set_bit(nid, nm_i->nid_bitmap); } for (i = 0; i < nats_in_cursum(journal); i++) { block_t addr; addr = le32_to_cpu(nat_in_journal(journal, i).block_addr); nid = le32_to_cpu(nid_in_journal(journal, i)); if (addr != NULL_ADDR) f2fs_set_bit(nid, nm_i->nid_bitmap); } return 0; } u32 update_nat_bits_flags(struct f2fs_super_block *sb, struct f2fs_checkpoint *cp, u32 flags) { u_int32_t nat_bits_bytes, nat_bits_blocks; nat_bits_bytes = get_sb(segment_count_nat) << 5; nat_bits_blocks = F2FS_BYTES_TO_BLK((nat_bits_bytes << 1) + 8 + F2FS_BLKSIZE - 1); if (get_cp(cp_pack_total_block_count) <= (1 << get_sb(log_blocks_per_seg)) - nat_bits_blocks) flags |= CP_NAT_BITS_FLAG; else flags &= (~CP_NAT_BITS_FLAG); return flags; } /* should call flush_journal_entries() bfore this */ void write_nat_bits(struct f2fs_sb_info *sbi, struct f2fs_super_block *sb, struct f2fs_checkpoint *cp, int set) { struct f2fs_nm_info *nm_i = NM_I(sbi); u_int32_t nat_blocks = get_sb(segment_count_nat) << (get_sb(log_blocks_per_seg) - 1); u_int32_t nat_bits_bytes = nat_blocks >> 3; u_int32_t nat_bits_blocks = F2FS_BYTES_TO_BLK((nat_bits_bytes << 1) + 8 + F2FS_BLKSIZE - 1); unsigned char *nat_bits, *full_nat_bits, *empty_nat_bits; struct f2fs_nat_block *nat_block; u_int32_t i, j; block_t blkaddr; int ret; nat_bits = calloc(F2FS_BLKSIZE, nat_bits_blocks); ASSERT(nat_bits); nat_block = malloc(F2FS_BLKSIZE); ASSERT(nat_block); full_nat_bits = nat_bits + 8; empty_nat_bits = full_nat_bits + nat_bits_bytes; memset(full_nat_bits, 0, nat_bits_bytes); memset(empty_nat_bits, 0, nat_bits_bytes); for (i = 0; i < nat_blocks; i++) { int seg_off = i >> get_sb(log_blocks_per_seg); int valid = 0; blkaddr = (pgoff_t)(get_sb(nat_blkaddr) + (seg_off << get_sb(log_blocks_per_seg) << 1) + (i & ((1 << get_sb(log_blocks_per_seg)) - 1))); if (f2fs_test_bit(i, nm_i->nat_bitmap)) blkaddr += (1 << get_sb(log_blocks_per_seg)); ret = dev_read_block(nat_block, blkaddr); ASSERT(ret >= 0); for (j = 0; j < NAT_ENTRY_PER_BLOCK; j++) { if ((i == 0 && j == 0) || nat_block->entries[j].block_addr != NULL_ADDR) valid++; } if (valid == 0) test_and_set_bit_le(i, empty_nat_bits); else if (valid == NAT_ENTRY_PER_BLOCK) test_and_set_bit_le(i, full_nat_bits); } *(__le64 *)nat_bits = get_cp_crc(cp); free(nat_block); blkaddr = get_sb(segment0_blkaddr) + (set << get_sb(log_blocks_per_seg)) - nat_bits_blocks; DBG(1, "\tWriting NAT bits pages, at offset 0x%08x\n", blkaddr); for (i = 0; i < nat_bits_blocks; i++) { if (dev_write_block(nat_bits + i * F2FS_BLKSIZE, blkaddr + i)) ASSERT_MSG("\tError: write NAT bits to disk!!!\n"); } MSG(0, "Info: Write valid nat_bits in checkpoint\n"); free(nat_bits); } int init_node_manager(struct f2fs_sb_info *sbi) { struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi); struct f2fs_checkpoint *cp = F2FS_CKPT(sbi); struct f2fs_nm_info *nm_i = NM_I(sbi); unsigned char *version_bitmap; unsigned int nat_segs, nat_blocks; nm_i->nat_blkaddr = get_sb(nat_blkaddr); /* segment_count_nat includes pair segment so divide to 2. */ nat_segs = get_sb(segment_count_nat) >> 1; nat_blocks = nat_segs << get_sb(log_blocks_per_seg); nm_i->max_nid = NAT_ENTRY_PER_BLOCK * nat_blocks; nm_i->fcnt = 0; nm_i->nat_cnt = 0; nm_i->init_scan_nid = get_cp(next_free_nid); nm_i->next_scan_nid = get_cp(next_free_nid); nm_i->bitmap_size = __bitmap_size(sbi, NAT_BITMAP); nm_i->nat_bitmap = malloc(nm_i->bitmap_size); if (!nm_i->nat_bitmap) return -ENOMEM; version_bitmap = __bitmap_ptr(sbi, NAT_BITMAP); if (!version_bitmap) return -EFAULT; /* copy version bitmap */ memcpy(nm_i->nat_bitmap, version_bitmap, nm_i->bitmap_size); return f2fs_init_nid_bitmap(sbi); } int build_node_manager(struct f2fs_sb_info *sbi) { int err; sbi->nm_info = malloc(sizeof(struct f2fs_nm_info)); if (!sbi->nm_info) return -ENOMEM; err = init_node_manager(sbi); if (err) return err; return 0; } int build_sit_info(struct f2fs_sb_info *sbi) { struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi); struct f2fs_checkpoint *cp = F2FS_CKPT(sbi); struct sit_info *sit_i; unsigned int sit_segs, start; char *src_bitmap, *dst_bitmap; unsigned int bitmap_size; sit_i = malloc(sizeof(struct sit_info)); if (!sit_i) return -ENOMEM; SM_I(sbi)->sit_info = sit_i; sit_i->sentries = calloc(TOTAL_SEGS(sbi) * sizeof(struct seg_entry), 1); if (!sit_i->sentries) return -ENOMEM; for (start = 0; start < TOTAL_SEGS(sbi); start++) { sit_i->sentries[start].cur_valid_map = calloc(SIT_VBLOCK_MAP_SIZE, 1); if (!sit_i->sentries[start].cur_valid_map) return -ENOMEM; } sit_segs = get_sb(segment_count_sit) >> 1; bitmap_size = __bitmap_size(sbi, SIT_BITMAP); src_bitmap = __bitmap_ptr(sbi, SIT_BITMAP); dst_bitmap = malloc(bitmap_size); memcpy(dst_bitmap, src_bitmap, bitmap_size); sit_i->sit_base_addr = get_sb(sit_blkaddr); sit_i->sit_blocks = sit_segs << sbi->log_blocks_per_seg; sit_i->written_valid_blocks = get_cp(valid_block_count); sit_i->sit_bitmap = dst_bitmap; sit_i->bitmap_size = bitmap_size; sit_i->dirty_sentries = 0; sit_i->sents_per_block = SIT_ENTRY_PER_BLOCK; sit_i->elapsed_time = get_cp(elapsed_time); return 0; } void reset_curseg(struct f2fs_sb_info *sbi, int type) { struct curseg_info *curseg = CURSEG_I(sbi, type); struct summary_footer *sum_footer; struct seg_entry *se; sum_footer = &(curseg->sum_blk->footer); memset(sum_footer, 0, sizeof(struct summary_footer)); if (IS_DATASEG(type)) SET_SUM_TYPE(sum_footer, SUM_TYPE_DATA); if (IS_NODESEG(type)) SET_SUM_TYPE(sum_footer, SUM_TYPE_NODE); se = get_seg_entry(sbi, curseg->segno); se->type = type; } static void read_compacted_summaries(struct f2fs_sb_info *sbi) { struct curseg_info *curseg; unsigned int i, j, offset; block_t start; char *kaddr; int ret; start = start_sum_block(sbi); kaddr = (char *)malloc(PAGE_SIZE); ret = dev_read_block(kaddr, start++); ASSERT(ret >= 0); curseg = CURSEG_I(sbi, CURSEG_HOT_DATA); memcpy(&curseg->sum_blk->journal.n_nats, kaddr, SUM_JOURNAL_SIZE); curseg = CURSEG_I(sbi, CURSEG_COLD_DATA); memcpy(&curseg->sum_blk->journal.n_sits, kaddr + SUM_JOURNAL_SIZE, SUM_JOURNAL_SIZE); offset = 2 * SUM_JOURNAL_SIZE; for (i = CURSEG_HOT_DATA; i <= CURSEG_COLD_DATA; i++) { unsigned short blk_off; struct curseg_info *curseg = CURSEG_I(sbi, i); reset_curseg(sbi, i); if (curseg->alloc_type == SSR) blk_off = sbi->blocks_per_seg; else blk_off = curseg->next_blkoff; ASSERT(blk_off <= ENTRIES_IN_SUM); for (j = 0; j < blk_off; j++) { struct f2fs_summary *s; s = (struct f2fs_summary *)(kaddr + offset); curseg->sum_blk->entries[j] = *s; offset += SUMMARY_SIZE; if (offset + SUMMARY_SIZE <= PAGE_CACHE_SIZE - SUM_FOOTER_SIZE) continue; memset(kaddr, 0, PAGE_SIZE); ret = dev_read_block(kaddr, start++); ASSERT(ret >= 0); offset = 0; } } free(kaddr); } static void restore_node_summary(struct f2fs_sb_info *sbi, unsigned int segno, struct f2fs_summary_block *sum_blk) { struct f2fs_node *node_blk; struct f2fs_summary *sum_entry; block_t addr; unsigned int i; int ret; node_blk = malloc(F2FS_BLKSIZE); ASSERT(node_blk); /* scan the node segment */ addr = START_BLOCK(sbi, segno); sum_entry = &sum_blk->entries[0]; for (i = 0; i < sbi->blocks_per_seg; i++, sum_entry++) { ret = dev_read_block(node_blk, addr); ASSERT(ret >= 0); sum_entry->nid = node_blk->footer.nid; addr++; } free(node_blk); } static void read_normal_summaries(struct f2fs_sb_info *sbi, int type) { struct f2fs_checkpoint *cp = F2FS_CKPT(sbi); struct f2fs_summary_block *sum_blk; struct curseg_info *curseg; unsigned int segno = 0; block_t blk_addr = 0; int ret; if (IS_DATASEG(type)) { segno = get_cp(cur_data_segno[type]); if (is_set_ckpt_flags(cp, CP_UMOUNT_FLAG)) blk_addr = sum_blk_addr(sbi, NR_CURSEG_TYPE, type); else blk_addr = sum_blk_addr(sbi, NR_CURSEG_DATA_TYPE, type); } else { segno = get_cp(cur_node_segno[type - CURSEG_HOT_NODE]); if (is_set_ckpt_flags(cp, CP_UMOUNT_FLAG)) blk_addr = sum_blk_addr(sbi, NR_CURSEG_NODE_TYPE, type - CURSEG_HOT_NODE); else blk_addr = GET_SUM_BLKADDR(sbi, segno); } sum_blk = (struct f2fs_summary_block *)malloc(PAGE_SIZE); ret = dev_read_block(sum_blk, blk_addr); ASSERT(ret >= 0); if (IS_NODESEG(type) && !is_set_ckpt_flags(cp, CP_UMOUNT_FLAG)) restore_node_summary(sbi, segno, sum_blk); curseg = CURSEG_I(sbi, type); memcpy(curseg->sum_blk, sum_blk, PAGE_CACHE_SIZE); reset_curseg(sbi, type); free(sum_blk); } void update_sum_entry(struct f2fs_sb_info *sbi, block_t blk_addr, struct f2fs_summary *sum) { struct f2fs_summary_block *sum_blk; u32 segno, offset; int type, ret; struct seg_entry *se; segno = GET_SEGNO(sbi, blk_addr); offset = OFFSET_IN_SEG(sbi, blk_addr); se = get_seg_entry(sbi, segno); sum_blk = get_sum_block(sbi, segno, &type); memcpy(&sum_blk->entries[offset], sum, sizeof(*sum)); sum_blk->footer.entry_type = IS_NODESEG(se->type) ? SUM_TYPE_NODE : SUM_TYPE_DATA; /* write SSA all the time */ ret = dev_write_block(sum_blk, GET_SUM_BLKADDR(sbi, segno)); ASSERT(ret >= 0); if (type == SEG_TYPE_NODE || type == SEG_TYPE_DATA || type == SEG_TYPE_MAX) free(sum_blk); } static void restore_curseg_summaries(struct f2fs_sb_info *sbi) { int type = CURSEG_HOT_DATA; if (is_set_ckpt_flags(F2FS_CKPT(sbi), CP_COMPACT_SUM_FLAG)) { read_compacted_summaries(sbi); type = CURSEG_HOT_NODE; } for (; type <= CURSEG_COLD_NODE; type++) read_normal_summaries(sbi, type); } static void build_curseg(struct f2fs_sb_info *sbi) { struct f2fs_checkpoint *cp = F2FS_CKPT(sbi); struct curseg_info *array; unsigned short blk_off; unsigned int segno; int i; array = malloc(sizeof(*array) * NR_CURSEG_TYPE); ASSERT(array); SM_I(sbi)->curseg_array = array; for (i = 0; i < NR_CURSEG_TYPE; i++) { array[i].sum_blk = malloc(PAGE_CACHE_SIZE); ASSERT(array[i].sum_blk); if (i <= CURSEG_COLD_DATA) { blk_off = get_cp(cur_data_blkoff[i]); segno = get_cp(cur_data_segno[i]); } if (i > CURSEG_COLD_DATA) { blk_off = get_cp(cur_node_blkoff[i - CURSEG_HOT_NODE]); segno = get_cp(cur_node_segno[i - CURSEG_HOT_NODE]); } ASSERT(segno < TOTAL_SEGS(sbi)); ASSERT(blk_off < DEFAULT_BLOCKS_PER_SEGMENT); array[i].segno = segno; array[i].zone = GET_ZONENO_FROM_SEGNO(sbi, segno); array[i].next_segno = NULL_SEGNO; array[i].next_blkoff = blk_off; array[i].alloc_type = cp->alloc_type[i]; } restore_curseg_summaries(sbi); } static inline void check_seg_range(struct f2fs_sb_info *sbi, unsigned int segno) { unsigned int end_segno = SM_I(sbi)->segment_count - 1; ASSERT(segno <= end_segno); } struct f2fs_sit_block *get_current_sit_page(struct f2fs_sb_info *sbi, unsigned int segno) { struct sit_info *sit_i = SIT_I(sbi); unsigned int offset = SIT_BLOCK_OFFSET(sit_i, segno); block_t blk_addr = sit_i->sit_base_addr + offset; struct f2fs_sit_block *sit_blk; int ret; sit_blk = calloc(BLOCK_SZ, 1); ASSERT(sit_blk); check_seg_range(sbi, segno); /* calculate sit block address */ if (f2fs_test_bit(offset, sit_i->sit_bitmap)) blk_addr += sit_i->sit_blocks; ret = dev_read_block(sit_blk, blk_addr); ASSERT(ret >= 0); return sit_blk; } void rewrite_current_sit_page(struct f2fs_sb_info *sbi, unsigned int segno, struct f2fs_sit_block *sit_blk) { struct sit_info *sit_i = SIT_I(sbi); unsigned int offset = SIT_BLOCK_OFFSET(sit_i, segno); block_t blk_addr = sit_i->sit_base_addr + offset; int ret; /* calculate sit block address */ if (f2fs_test_bit(offset, sit_i->sit_bitmap)) blk_addr += sit_i->sit_blocks; ret = dev_write_block(sit_blk, blk_addr); ASSERT(ret >= 0); } void check_block_count(struct f2fs_sb_info *sbi, unsigned int segno, struct f2fs_sit_entry *raw_sit) { struct f2fs_sm_info *sm_info = SM_I(sbi); unsigned int end_segno = sm_info->segment_count - 1; int valid_blocks = 0; unsigned int i; /* check segment usage */ if (GET_SIT_VBLOCKS(raw_sit) > sbi->blocks_per_seg) ASSERT_MSG("Invalid SIT vblocks: segno=0x%x, %u", segno, GET_SIT_VBLOCKS(raw_sit)); /* check boundary of a given segment number */ if (segno > end_segno) ASSERT_MSG("Invalid SEGNO: 0x%x", segno); /* check bitmap with valid block count */ for (i = 0; i < SIT_VBLOCK_MAP_SIZE; i++) valid_blocks += get_bits_in_byte(raw_sit->valid_map[i]); if (GET_SIT_VBLOCKS(raw_sit) != valid_blocks) ASSERT_MSG("Wrong SIT valid blocks: segno=0x%x, %u vs. %u", segno, GET_SIT_VBLOCKS(raw_sit), valid_blocks); if (GET_SIT_TYPE(raw_sit) >= NO_CHECK_TYPE) ASSERT_MSG("Wrong SIT type: segno=0x%x, %u", segno, GET_SIT_TYPE(raw_sit)); } void seg_info_from_raw_sit(struct seg_entry *se, struct f2fs_sit_entry *raw_sit) { se->valid_blocks = GET_SIT_VBLOCKS(raw_sit); memcpy(se->cur_valid_map, raw_sit->valid_map, SIT_VBLOCK_MAP_SIZE); se->type = GET_SIT_TYPE(raw_sit); se->orig_type = GET_SIT_TYPE(raw_sit); se->mtime = le64_to_cpu(raw_sit->mtime); } struct seg_entry *get_seg_entry(struct f2fs_sb_info *sbi, unsigned int segno) { struct sit_info *sit_i = SIT_I(sbi); return &sit_i->sentries[segno]; } struct f2fs_summary_block *get_sum_block(struct f2fs_sb_info *sbi, unsigned int segno, int *ret_type) { struct f2fs_checkpoint *cp = F2FS_CKPT(sbi); struct f2fs_summary_block *sum_blk; struct curseg_info *curseg; int type, ret; u64 ssa_blk; *ret_type= SEG_TYPE_MAX; ssa_blk = GET_SUM_BLKADDR(sbi, segno); for (type = 0; type < NR_CURSEG_NODE_TYPE; type++) { if (segno == get_cp(cur_node_segno[type])) { curseg = CURSEG_I(sbi, CURSEG_HOT_NODE + type); if (!IS_SUM_NODE_SEG(curseg->sum_blk->footer)) { ASSERT_MSG("segno [0x%x] indicates a data " "segment, but should be node", segno); *ret_type = -SEG_TYPE_CUR_NODE; } else { *ret_type = SEG_TYPE_CUR_NODE; } return curseg->sum_blk; } } for (type = 0; type < NR_CURSEG_DATA_TYPE; type++) { if (segno == get_cp(cur_data_segno[type])) { curseg = CURSEG_I(sbi, type); if (IS_SUM_NODE_SEG(curseg->sum_blk->footer)) { ASSERT_MSG("segno [0x%x] indicates a node " "segment, but should be data", segno); *ret_type = -SEG_TYPE_CUR_DATA; } else { *ret_type = SEG_TYPE_CUR_DATA; } return curseg->sum_blk; } } sum_blk = calloc(BLOCK_SZ, 1); ASSERT(sum_blk); ret = dev_read_block(sum_blk, ssa_blk); ASSERT(ret >= 0); if (IS_SUM_NODE_SEG(sum_blk->footer)) *ret_type = SEG_TYPE_NODE; else if (IS_SUM_DATA_SEG(sum_blk->footer)) *ret_type = SEG_TYPE_DATA; return sum_blk; } int get_sum_entry(struct f2fs_sb_info *sbi, u32 blk_addr, struct f2fs_summary *sum_entry) { struct f2fs_summary_block *sum_blk; u32 segno, offset; int type; segno = GET_SEGNO(sbi, blk_addr); offset = OFFSET_IN_SEG(sbi, blk_addr); sum_blk = get_sum_block(sbi, segno, &type); memcpy(sum_entry, &(sum_blk->entries[offset]), sizeof(struct f2fs_summary)); if (type == SEG_TYPE_NODE || type == SEG_TYPE_DATA || type == SEG_TYPE_MAX) free(sum_blk); return type; } static void get_nat_entry(struct f2fs_sb_info *sbi, nid_t nid, struct f2fs_nat_entry *raw_nat) { struct f2fs_nat_block *nat_block; pgoff_t block_addr; int entry_off; int ret; if (lookup_nat_in_journal(sbi, nid, raw_nat) >= 0) return; nat_block = (struct f2fs_nat_block *)calloc(BLOCK_SZ, 1); ASSERT(nat_block); entry_off = nid % NAT_ENTRY_PER_BLOCK; block_addr = current_nat_addr(sbi, nid); ret = dev_read_block(nat_block, block_addr); ASSERT(ret >= 0); memcpy(raw_nat, &nat_block->entries[entry_off], sizeof(struct f2fs_nat_entry)); free(nat_block); } void update_data_blkaddr(struct f2fs_sb_info *sbi, nid_t nid, u16 ofs_in_node, block_t newaddr) { struct f2fs_node *node_blk = NULL; struct node_info ni; block_t oldaddr, startaddr, endaddr; int ret; node_blk = (struct f2fs_node *)calloc(BLOCK_SZ, 1); ASSERT(node_blk); get_node_info(sbi, nid, &ni); /* read node_block */ ret = dev_read_block(node_blk, ni.blk_addr); ASSERT(ret >= 0); /* check its block address */ if (node_blk->footer.nid == node_blk->footer.ino) { int ofs = get_extra_isize(node_blk); oldaddr = le32_to_cpu(node_blk->i.i_addr[ofs + ofs_in_node]); node_blk->i.i_addr[ofs + ofs_in_node] = cpu_to_le32(newaddr); } else { oldaddr = le32_to_cpu(node_blk->dn.addr[ofs_in_node]); node_blk->dn.addr[ofs_in_node] = cpu_to_le32(newaddr); } ret = dev_write_block(node_blk, ni.blk_addr); ASSERT(ret >= 0); /* check extent cache entry */ if (node_blk->footer.nid != node_blk->footer.ino) { get_node_info(sbi, le32_to_cpu(node_blk->footer.ino), &ni); /* read inode block */ ret = dev_read_block(node_blk, ni.blk_addr); ASSERT(ret >= 0); } startaddr = le32_to_cpu(node_blk->i.i_ext.blk_addr); endaddr = startaddr + le32_to_cpu(node_blk->i.i_ext.len); if (oldaddr >= startaddr && oldaddr < endaddr) { node_blk->i.i_ext.len = 0; /* update inode block */ ret = dev_write_block(node_blk, ni.blk_addr); ASSERT(ret >= 0); } free(node_blk); } void update_nat_blkaddr(struct f2fs_sb_info *sbi, nid_t ino, nid_t nid, block_t newaddr) { struct f2fs_nat_block *nat_block; pgoff_t block_addr; int entry_off; int ret; nat_block = (struct f2fs_nat_block *)calloc(BLOCK_SZ, 1); ASSERT(nat_block); entry_off = nid % NAT_ENTRY_PER_BLOCK; block_addr = current_nat_addr(sbi, nid); ret = dev_read_block(nat_block, block_addr); ASSERT(ret >= 0); if (ino) nat_block->entries[entry_off].ino = cpu_to_le32(ino); nat_block->entries[entry_off].block_addr = cpu_to_le32(newaddr); ret = dev_write_block(nat_block, block_addr); ASSERT(ret >= 0); free(nat_block); } void get_node_info(struct f2fs_sb_info *sbi, nid_t nid, struct node_info *ni) { struct f2fs_nat_entry raw_nat; ni->nid = nid; if (c.func == FSCK) { node_info_from_raw_nat(ni, &(F2FS_FSCK(sbi)->entries[nid])); return; } get_nat_entry(sbi, nid, &raw_nat); node_info_from_raw_nat(ni, &raw_nat); } void build_sit_entries(struct f2fs_sb_info *sbi) { struct sit_info *sit_i = SIT_I(sbi); struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_COLD_DATA); struct f2fs_journal *journal = &curseg->sum_blk->journal; struct seg_entry *se; struct f2fs_sit_entry sit; unsigned int i, segno; for (segno = 0; segno < TOTAL_SEGS(sbi); segno++) { se = &sit_i->sentries[segno]; struct f2fs_sit_block *sit_blk; sit_blk = get_current_sit_page(sbi, segno); sit = sit_blk->entries[SIT_ENTRY_OFFSET(sit_i, segno)]; free(sit_blk); check_block_count(sbi, segno, &sit); seg_info_from_raw_sit(se, &sit); } for (i = 0; i < sits_in_cursum(journal); i++) { segno = le32_to_cpu(segno_in_journal(journal, i)); se = &sit_i->sentries[segno]; sit = sit_in_journal(journal, i); check_block_count(sbi, segno, &sit); seg_info_from_raw_sit(se, &sit); } } int build_segment_manager(struct f2fs_sb_info *sbi) { struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi); struct f2fs_checkpoint *cp = F2FS_CKPT(sbi); struct f2fs_sm_info *sm_info; sm_info = malloc(sizeof(struct f2fs_sm_info)); if (!sm_info) return -ENOMEM; /* init sm info */ sbi->sm_info = sm_info; sm_info->seg0_blkaddr = get_sb(segment0_blkaddr); sm_info->main_blkaddr = get_sb(main_blkaddr); sm_info->segment_count = get_sb(segment_count); sm_info->reserved_segments = get_cp(rsvd_segment_count); sm_info->ovp_segments = get_cp(overprov_segment_count); sm_info->main_segments = get_sb(segment_count_main); sm_info->ssa_blkaddr = get_sb(ssa_blkaddr); build_sit_info(sbi); build_curseg(sbi); build_sit_entries(sbi); return 0; } void build_sit_area_bitmap(struct f2fs_sb_info *sbi) { struct f2fs_fsck *fsck = F2FS_FSCK(sbi); struct f2fs_sm_info *sm_i = SM_I(sbi); unsigned int segno = 0; char *ptr = NULL; u32 sum_vblocks = 0; u32 free_segs = 0; struct seg_entry *se; fsck->sit_area_bitmap_sz = sm_i->main_segments * SIT_VBLOCK_MAP_SIZE; fsck->sit_area_bitmap = calloc(1, fsck->sit_area_bitmap_sz); ASSERT(fsck->sit_area_bitmap); ptr = fsck->sit_area_bitmap; ASSERT(fsck->sit_area_bitmap_sz == fsck->main_area_bitmap_sz); for (segno = 0; segno < TOTAL_SEGS(sbi); segno++) { se = get_seg_entry(sbi, segno); memcpy(ptr, se->cur_valid_map, SIT_VBLOCK_MAP_SIZE); ptr += SIT_VBLOCK_MAP_SIZE; if (se->valid_blocks == 0x0) { if (le32_to_cpu(sbi->ckpt->cur_node_segno[0]) == segno || le32_to_cpu(sbi->ckpt->cur_data_segno[0]) == segno || le32_to_cpu(sbi->ckpt->cur_node_segno[1]) == segno || le32_to_cpu(sbi->ckpt->cur_data_segno[1]) == segno || le32_to_cpu(sbi->ckpt->cur_node_segno[2]) == segno || le32_to_cpu(sbi->ckpt->cur_data_segno[2]) == segno) { continue; } else { free_segs++; } } else { sum_vblocks += se->valid_blocks; } } fsck->chk.sit_valid_blocks = sum_vblocks; fsck->chk.sit_free_segs = free_segs; DBG(1, "Blocks [0x%x : %d] Free Segs [0x%x : %d]\n\n", sum_vblocks, sum_vblocks, free_segs, free_segs); } void rewrite_sit_area_bitmap(struct f2fs_sb_info *sbi) { struct f2fs_fsck *fsck = F2FS_FSCK(sbi); struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_COLD_DATA); struct sit_info *sit_i = SIT_I(sbi); unsigned int segno = 0; struct f2fs_summary_block *sum = curseg->sum_blk; char *ptr = NULL; /* remove sit journal */ sum->journal.n_sits = 0; ptr = fsck->main_area_bitmap; for (segno = 0; segno < TOTAL_SEGS(sbi); segno++) { struct f2fs_sit_block *sit_blk; struct f2fs_sit_entry *sit; struct seg_entry *se; u16 valid_blocks = 0; u16 type; int i; sit_blk = get_current_sit_page(sbi, segno); sit = &sit_blk->entries[SIT_ENTRY_OFFSET(sit_i, segno)]; memcpy(sit->valid_map, ptr, SIT_VBLOCK_MAP_SIZE); /* update valid block count */ for (i = 0; i < SIT_VBLOCK_MAP_SIZE; i++) valid_blocks += get_bits_in_byte(sit->valid_map[i]); se = get_seg_entry(sbi, segno); memcpy(se->cur_valid_map, ptr, SIT_VBLOCK_MAP_SIZE); se->valid_blocks = valid_blocks; type = se->type; if (type >= NO_CHECK_TYPE) { ASSERT_MSG("Invalide type and valid blocks=%x,%x", segno, valid_blocks); type = 0; } sit->vblocks = cpu_to_le16((type << SIT_VBLOCKS_SHIFT) | valid_blocks); rewrite_current_sit_page(sbi, segno, sit_blk); free(sit_blk); ptr += SIT_VBLOCK_MAP_SIZE; } } static int flush_sit_journal_entries(struct f2fs_sb_info *sbi) { struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_COLD_DATA); struct f2fs_journal *journal = &curseg->sum_blk->journal; struct sit_info *sit_i = SIT_I(sbi); unsigned int segno; int i; for (i = 0; i < sits_in_cursum(journal); i++) { struct f2fs_sit_block *sit_blk; struct f2fs_sit_entry *sit; struct seg_entry *se; segno = segno_in_journal(journal, i); se = get_seg_entry(sbi, segno); sit_blk = get_current_sit_page(sbi, segno); sit = &sit_blk->entries[SIT_ENTRY_OFFSET(sit_i, segno)]; memcpy(sit->valid_map, se->cur_valid_map, SIT_VBLOCK_MAP_SIZE); sit->vblocks = cpu_to_le16((se->type << SIT_VBLOCKS_SHIFT) | se->valid_blocks); sit->mtime = cpu_to_le64(se->mtime); rewrite_current_sit_page(sbi, segno, sit_blk); free(sit_blk); } journal->n_sits = 0; return i; } static int flush_nat_journal_entries(struct f2fs_sb_info *sbi) { struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_HOT_DATA); struct f2fs_journal *journal = &curseg->sum_blk->journal; struct f2fs_nat_block *nat_block; pgoff_t block_addr; int entry_off; nid_t nid; int ret; int i = 0; nat_block = (struct f2fs_nat_block *)calloc(BLOCK_SZ, 1); ASSERT(nat_block); next: if (i >= nats_in_cursum(journal)) { free(nat_block); journal->n_nats = 0; return i; } nid = le32_to_cpu(nid_in_journal(journal, i)); entry_off = nid % NAT_ENTRY_PER_BLOCK; block_addr = current_nat_addr(sbi, nid); ret = dev_read_block(nat_block, block_addr); ASSERT(ret >= 0); memcpy(&nat_block->entries[entry_off], &nat_in_journal(journal, i), sizeof(struct f2fs_nat_entry)); ret = dev_write_block(nat_block, block_addr); ASSERT(ret >= 0); i++; goto next; } void flush_journal_entries(struct f2fs_sb_info *sbi) { int n_nats = flush_nat_journal_entries(sbi); int n_sits = flush_sit_journal_entries(sbi); if (n_nats || n_sits) write_checkpoint(sbi); } void flush_sit_entries(struct f2fs_sb_info *sbi) { struct f2fs_checkpoint *cp = F2FS_CKPT(sbi); struct sit_info *sit_i = SIT_I(sbi); unsigned int segno = 0; u32 free_segs = 0; /* update free segments */ for (segno = 0; segno < TOTAL_SEGS(sbi); segno++) { struct f2fs_sit_block *sit_blk; struct f2fs_sit_entry *sit; struct seg_entry *se; se = get_seg_entry(sbi, segno); if (!se->dirty) continue; sit_blk = get_current_sit_page(sbi, segno); sit = &sit_blk->entries[SIT_ENTRY_OFFSET(sit_i, segno)]; memcpy(sit->valid_map, se->cur_valid_map, SIT_VBLOCK_MAP_SIZE); sit->vblocks = cpu_to_le16((se->type << SIT_VBLOCKS_SHIFT) | se->valid_blocks); rewrite_current_sit_page(sbi, segno, sit_blk); free(sit_blk); if (se->valid_blocks == 0x0 && !IS_CUR_SEGNO(sbi, segno, NO_CHECK_TYPE)) free_segs++; } set_cp(free_segment_count, free_segs); } int find_next_free_block(struct f2fs_sb_info *sbi, u64 *to, int left, int type) { struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi); struct seg_entry *se; u32 segno; u32 offset; int not_enough = 0; u64 end_blkaddr = (get_sb(segment_count_main) << get_sb(log_blocks_per_seg)) + get_sb(main_blkaddr); if (get_free_segments(sbi) <= SM_I(sbi)->reserved_segments + 1) not_enough = 1; while (*to >= SM_I(sbi)->main_blkaddr && *to < end_blkaddr) { segno = GET_SEGNO(sbi, *to); offset = OFFSET_IN_SEG(sbi, *to); se = get_seg_entry(sbi, segno); if (se->valid_blocks == sbi->blocks_per_seg || IS_CUR_SEGNO(sbi, segno, type)) { *to = left ? START_BLOCK(sbi, segno) - 1: START_BLOCK(sbi, segno + 1); continue; } if (se->valid_blocks == 0 && not_enough) { *to = left ? START_BLOCK(sbi, segno) - 1: START_BLOCK(sbi, segno + 1); continue; } if (se->valid_blocks == 0 && !(segno % sbi->segs_per_sec)) { struct seg_entry *se2; unsigned int i; for (i = 1; i < sbi->segs_per_sec; i++) { se2 = get_seg_entry(sbi, segno + i); if (se2->valid_blocks) break; } if (i == sbi->segs_per_sec) return 0; } if (se->type == type && !f2fs_test_bit(offset, (const char *)se->cur_valid_map)) return 0; *to = left ? *to - 1: *to + 1; } return -1; } void move_curseg_info(struct f2fs_sb_info *sbi, u64 from) { int i, ret; /* update summary blocks having nullified journal entries */ for (i = 0; i < NO_CHECK_TYPE; i++) { struct curseg_info *curseg = CURSEG_I(sbi, i); struct f2fs_summary_block buf; u32 old_segno; u64 ssa_blk, to; /* update original SSA too */ ssa_blk = GET_SUM_BLKADDR(sbi, curseg->segno); ret = dev_write_block(curseg->sum_blk, ssa_blk); ASSERT(ret >= 0); to = from; ret = find_next_free_block(sbi, &to, 0, i); ASSERT(ret == 0); old_segno = curseg->segno; curseg->segno = GET_SEGNO(sbi, to); curseg->next_blkoff = OFFSET_IN_SEG(sbi, to); curseg->alloc_type = SSR; /* update new segno */ ssa_blk = GET_SUM_BLKADDR(sbi, curseg->segno); ret = dev_read_block(&buf, ssa_blk); ASSERT(ret >= 0); memcpy(curseg->sum_blk, &buf, SUM_ENTRIES_SIZE); /* update se->types */ reset_curseg(sbi, i); DBG(1, "Move curseg[%d] %x -> %x after %"PRIx64"\n", i, old_segno, curseg->segno, from); } } void zero_journal_entries(struct f2fs_sb_info *sbi) { int i; for (i = 0; i < NO_CHECK_TYPE; i++) CURSEG_I(sbi, i)->sum_blk->journal.n_nats = 0; } void write_curseg_info(struct f2fs_sb_info *sbi) { struct f2fs_checkpoint *cp = F2FS_CKPT(sbi); int i; for (i = 0; i < NO_CHECK_TYPE; i++) { cp->alloc_type[i] = CURSEG_I(sbi, i)->alloc_type; if (i < CURSEG_HOT_NODE) { set_cp(cur_data_segno[i], CURSEG_I(sbi, i)->segno); set_cp(cur_data_blkoff[i], CURSEG_I(sbi, i)->next_blkoff); } else { int n = i - CURSEG_HOT_NODE; set_cp(cur_node_segno[n], CURSEG_I(sbi, i)->segno); set_cp(cur_node_blkoff[n], CURSEG_I(sbi, i)->next_blkoff); } } } int lookup_nat_in_journal(struct f2fs_sb_info *sbi, u32 nid, struct f2fs_nat_entry *raw_nat) { struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_HOT_DATA); struct f2fs_journal *journal = &curseg->sum_blk->journal; int i = 0; for (i = 0; i < nats_in_cursum(journal); i++) { if (le32_to_cpu(nid_in_journal(journal, i)) == nid) { memcpy(raw_nat, &nat_in_journal(journal, i), sizeof(struct f2fs_nat_entry)); DBG(3, "==> Found nid [0x%x] in nat cache\n", nid); return i; } } return -1; } void nullify_nat_entry(struct f2fs_sb_info *sbi, u32 nid) { struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_HOT_DATA); struct f2fs_journal *journal = &curseg->sum_blk->journal; struct f2fs_nat_block *nat_block; pgoff_t block_addr; int entry_off; int ret; int i = 0; /* check in journal */ for (i = 0; i < nats_in_cursum(journal); i++) { if (le32_to_cpu(nid_in_journal(journal, i)) == nid) { memset(&nat_in_journal(journal, i), 0, sizeof(struct f2fs_nat_entry)); FIX_MSG("Remove nid [0x%x] in nat journal", nid); return; } } nat_block = (struct f2fs_nat_block *)calloc(BLOCK_SZ, 1); ASSERT(nat_block); entry_off = nid % NAT_ENTRY_PER_BLOCK; block_addr = current_nat_addr(sbi, nid); ret = dev_read_block(nat_block, block_addr); ASSERT(ret >= 0); if (nid == F2FS_NODE_INO(sbi) || nid == F2FS_META_INO(sbi)) { FIX_MSG("nid [0x%x] block_addr= 0x%x -> 0x1", nid, le32_to_cpu(nat_block->entries[entry_off].block_addr)); nat_block->entries[entry_off].block_addr = cpu_to_le32(0x1); } else { memset(&nat_block->entries[entry_off], 0, sizeof(struct f2fs_nat_entry)); FIX_MSG("Remove nid [0x%x] in NAT", nid); } ret = dev_write_block(nat_block, block_addr); ASSERT(ret >= 0); free(nat_block); } void write_checkpoint(struct f2fs_sb_info *sbi) { struct f2fs_checkpoint *cp = F2FS_CKPT(sbi); struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi); block_t orphan_blks = 0; unsigned long long cp_blk_no; u32 flags = CP_UMOUNT_FLAG; int i, ret; u_int32_t crc = 0; if (is_set_ckpt_flags(cp, CP_ORPHAN_PRESENT_FLAG)) { orphan_blks = __start_sum_addr(sbi) - 1; flags |= CP_ORPHAN_PRESENT_FLAG; } set_cp(free_segment_count, get_free_segments(sbi)); set_cp(valid_block_count, sbi->total_valid_block_count); set_cp(cp_pack_total_block_count, 8 + orphan_blks + get_sb(cp_payload)); flags = update_nat_bits_flags(sb, cp, flags); set_cp(ckpt_flags, flags); crc = f2fs_cal_crc32(F2FS_SUPER_MAGIC, cp, CHECKSUM_OFFSET); *((__le32 *)((unsigned char *)cp + CHECKSUM_OFFSET)) = cpu_to_le32(crc); cp_blk_no = get_sb(cp_blkaddr); if (sbi->cur_cp == 2) cp_blk_no += 1 << get_sb(log_blocks_per_seg); /* write the first cp */ ret = dev_write_block(cp, cp_blk_no++); ASSERT(ret >= 0); /* skip payload */ cp_blk_no += get_sb(cp_payload); /* skip orphan blocks */ cp_blk_no += orphan_blks; /* update summary blocks having nullified journal entries */ for (i = 0; i < NO_CHECK_TYPE; i++) { struct curseg_info *curseg = CURSEG_I(sbi, i); u64 ssa_blk; ret = dev_write_block(curseg->sum_blk, cp_blk_no++); ASSERT(ret >= 0); /* update original SSA too */ ssa_blk = GET_SUM_BLKADDR(sbi, curseg->segno); ret = dev_write_block(curseg->sum_blk, ssa_blk); ASSERT(ret >= 0); } /* Write nat bits */ if (flags & CP_NAT_BITS_FLAG) write_nat_bits(sbi, sb, cp, sbi->cur_cp); /* in case of sudden power off */ ret = f2fs_fsync_device(); ASSERT(ret >= 0); /* write the last cp */ ret = dev_write_block(cp, cp_blk_no++); ASSERT(ret >= 0); } void build_nat_area_bitmap(struct f2fs_sb_info *sbi) { struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_HOT_DATA); struct f2fs_journal *journal = &curseg->sum_blk->journal; struct f2fs_fsck *fsck = F2FS_FSCK(sbi); struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi); struct f2fs_nm_info *nm_i = NM_I(sbi); struct f2fs_nat_block *nat_block; struct node_info ni; u32 nid, nr_nat_blks; pgoff_t block_off; pgoff_t block_addr; int seg_off; int ret; unsigned int i; nat_block = (struct f2fs_nat_block *)calloc(BLOCK_SZ, 1); ASSERT(nat_block); /* Alloc & build nat entry bitmap */ nr_nat_blks = (get_sb(segment_count_nat) / 2) << sbi->log_blocks_per_seg; fsck->nr_nat_entries = nr_nat_blks * NAT_ENTRY_PER_BLOCK; fsck->nat_area_bitmap_sz = (fsck->nr_nat_entries + 7) / 8; fsck->nat_area_bitmap = calloc(fsck->nat_area_bitmap_sz, 1); ASSERT(fsck->nat_area_bitmap); fsck->entries = calloc(sizeof(struct f2fs_nat_entry), fsck->nr_nat_entries); ASSERT(fsck->entries); for (block_off = 0; block_off < nr_nat_blks; block_off++) { seg_off = block_off >> sbi->log_blocks_per_seg; block_addr = (pgoff_t)(nm_i->nat_blkaddr + (seg_off << sbi->log_blocks_per_seg << 1) + (block_off & ((1 << sbi->log_blocks_per_seg) - 1))); if (f2fs_test_bit(block_off, nm_i->nat_bitmap)) block_addr += sbi->blocks_per_seg; ret = dev_read_block(nat_block, block_addr); ASSERT(ret >= 0); nid = block_off * NAT_ENTRY_PER_BLOCK; for (i = 0; i < NAT_ENTRY_PER_BLOCK; i++) { ni.nid = nid + i; if ((nid + i) == F2FS_NODE_INO(sbi) || (nid + i) == F2FS_META_INO(sbi)) { /* * block_addr of node/meta inode should be 0x1. * Set this bit, and fsck_verify will fix it. */ if (le32_to_cpu(nat_block->entries[i].block_addr) != 0x1) { ASSERT_MSG("\tError: ino[0x%x] block_addr[0x%x] is invalid\n", nid + i, le32_to_cpu(nat_block->entries[i].block_addr)); f2fs_set_bit(nid + i, fsck->nat_area_bitmap); } continue; } node_info_from_raw_nat(&ni, &nat_block->entries[i]); if (ni.blk_addr == 0x0) continue; if (ni.ino == 0x0) { ASSERT_MSG("\tError: ino[0x%8x] or blk_addr[0x%16x]" " is invalid\n", ni.ino, ni.blk_addr); } if (ni.ino == (nid + i)) { fsck->nat_valid_inode_cnt++; DBG(3, "ino[0x%8x] maybe is inode\n", ni.ino); } if (nid + i == 0) { /* * nat entry [0] must be null. If * it is corrupted, set its bit in * nat_area_bitmap, fsck_verify will * nullify it */ ASSERT_MSG("Invalid nat entry[0]: " "blk_addr[0x%x]\n", ni.blk_addr); fsck->chk.valid_nat_entry_cnt--; } DBG(3, "nid[0x%8x] addr[0x%16x] ino[0x%8x]\n", nid + i, ni.blk_addr, ni.ino); f2fs_set_bit(nid + i, fsck->nat_area_bitmap); fsck->chk.valid_nat_entry_cnt++; fsck->entries[nid + i] = nat_block->entries[i]; } } /* Traverse nat journal, update the corresponding entries */ for (i = 0; i < nats_in_cursum(journal); i++) { struct f2fs_nat_entry raw_nat; nid = le32_to_cpu(nid_in_journal(journal, i)); ni.nid = nid; DBG(3, "==> Found nid [0x%x] in nat cache, update it\n", nid); /* Clear the original bit and count */ if (fsck->entries[nid].block_addr != 0x0) { fsck->chk.valid_nat_entry_cnt--; f2fs_clear_bit(nid, fsck->nat_area_bitmap); if (fsck->entries[nid].ino == nid) fsck->nat_valid_inode_cnt--; } /* Use nat entries in journal */ memcpy(&raw_nat, &nat_in_journal(journal, i), sizeof(struct f2fs_nat_entry)); node_info_from_raw_nat(&ni, &raw_nat); if (ni.blk_addr != 0x0) { if (ni.ino == 0x0) ASSERT_MSG("\tError: ino[0x%8x] or blk_addr[0x%16x]" " is invalid\n", ni.ino, ni.blk_addr); if (ni.ino == nid) { fsck->nat_valid_inode_cnt++; DBG(3, "ino[0x%8x] maybe is inode\n", ni.ino); } f2fs_set_bit(nid, fsck->nat_area_bitmap); fsck->chk.valid_nat_entry_cnt++; DBG(3, "nid[0x%x] in nat cache\n", nid); } fsck->entries[nid] = raw_nat; } free(nat_block); DBG(1, "valid nat entries (block_addr != 0x0) [0x%8x : %u]\n", fsck->chk.valid_nat_entry_cnt, fsck->chk.valid_nat_entry_cnt); } static int check_sector_size(struct f2fs_super_block *sb) { int index; u_int32_t log_sectorsize, log_sectors_per_block; u_int8_t *zero_buff; log_sectorsize = log_base_2(c.sector_size); log_sectors_per_block = log_base_2(c.sectors_per_blk); if (log_sectorsize == get_sb(log_sectorsize) && log_sectors_per_block == get_sb(log_sectors_per_block)) return 0; zero_buff = calloc(F2FS_BLKSIZE, 1); ASSERT(zero_buff); set_sb(log_sectorsize, log_sectorsize); set_sb(log_sectors_per_block, log_sectors_per_block); memcpy(zero_buff + F2FS_SUPER_OFFSET, sb, sizeof(*sb)); DBG(1, "\tWriting super block, at offset 0x%08x\n", 0); for (index = 0; index < 2; index++) { if (dev_write(zero_buff, index * F2FS_BLKSIZE, F2FS_BLKSIZE)) { MSG(1, "\tError: Failed while writing supe_blk " "on disk!!! index : %d\n", index); free(zero_buff); return -1; } } free(zero_buff); return 0; } int f2fs_do_mount(struct f2fs_sb_info *sbi) { struct f2fs_checkpoint *cp = NULL; struct f2fs_super_block *sb = NULL; int ret; sbi->active_logs = NR_CURSEG_TYPE; ret = validate_super_block(sbi, 0); if (ret) { ret = validate_super_block(sbi, 1); if (ret) return -1; } sb = F2FS_RAW_SUPER(sbi); ret = check_sector_size(sb); if (ret) return -1; print_raw_sb_info(sb); init_sb_info(sbi); ret = get_valid_checkpoint(sbi); if (ret) { ERR_MSG("Can't find valid checkpoint\n"); return -1; } if (sanity_check_ckpt(sbi)) { ERR_MSG("Checkpoint is polluted\n"); return -1; } cp = F2FS_CKPT(sbi); print_ckpt_info(sbi); if (c.auto_fix || c.preen_mode) { u32 flag = get_cp(ckpt_flags); if (flag & CP_FSCK_FLAG || (exist_qf_ino(sb) && (!(flag & CP_UMOUNT_FLAG) || flag & CP_ERROR_FLAG))) { c.fix_on = 1; } else if (!c.preen_mode) { print_cp_state(flag); return 1; } } c.bug_on = 0; c.feature = sb->feature; /* precompute checksum seed for metadata */ if (c.feature & cpu_to_le32(F2FS_FEATURE_INODE_CHKSUM)) c.chksum_seed = f2fs_cal_crc32(~0, sb->uuid, sizeof(sb->uuid)); sbi->total_valid_node_count = get_cp(valid_node_count); sbi->total_valid_inode_count = get_cp(valid_inode_count); sbi->user_block_count = get_cp(user_block_count); sbi->total_valid_block_count = get_cp(valid_block_count); sbi->last_valid_block_count = sbi->total_valid_block_count; sbi->alloc_valid_block_count = 0; if (build_segment_manager(sbi)) { ERR_MSG("build_segment_manager failed\n"); return -1; } if (build_node_manager(sbi)) { ERR_MSG("build_node_manager failed\n"); return -1; } /* Check nat_bits */ if (c.func != DUMP && is_set_ckpt_flags(cp, CP_NAT_BITS_FLAG)) { u_int32_t nat_bits_bytes, nat_bits_blocks; __le64 *kaddr; u_int32_t blk; blk = get_sb(cp_blkaddr) + (1 << get_sb(log_blocks_per_seg)); if (sbi->cur_cp == 2) blk += 1 << get_sb(log_blocks_per_seg); nat_bits_bytes = get_sb(segment_count_nat) << 5; nat_bits_blocks = F2FS_BYTES_TO_BLK((nat_bits_bytes << 1) + 8 + F2FS_BLKSIZE - 1); blk -= nat_bits_blocks; kaddr = malloc(PAGE_SIZE); ret = dev_read_block(kaddr, blk); ASSERT(ret >= 0); if (*kaddr != get_cp_crc(cp)) write_nat_bits(sbi, sb, cp, sbi->cur_cp); else MSG(0, "Info: Found valid nat_bits in checkpoint\n"); free(kaddr); } return 0; } void f2fs_do_umount(struct f2fs_sb_info *sbi) { struct sit_info *sit_i = SIT_I(sbi); struct f2fs_sm_info *sm_i = SM_I(sbi); struct f2fs_nm_info *nm_i = NM_I(sbi); unsigned int i; /* free nm_info */ if (c.func == SLOAD || c.func == FSCK) free(nm_i->nid_bitmap); free(nm_i->nat_bitmap); free(sbi->nm_info); /* free sit_info */ for (i = 0; i < TOTAL_SEGS(sbi); i++) free(sit_i->sentries[i].cur_valid_map); free(sit_i->sit_bitmap); free(sm_i->sit_info); /* free sm_info */ for (i = 0; i < NR_CURSEG_TYPE; i++) free(sm_i->curseg_array[i].sum_blk); free(sm_i->curseg_array); free(sbi->sm_info); free(sbi->ckpt); free(sbi->raw_super); } f2fs-tools-1.10.0/fsck/node.c000066400000000000000000000141271323417143600156170ustar00rootroot00000000000000/** * node.c * * Many parts of codes are copied from Linux kernel/fs/f2fs. * * Copyright (C) 2015 Huawei Ltd. * Witten by: * Hou Pengyang * Liu Shuoran * Jaegeuk Kim * * 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. */ #include "fsck.h" #include "node.h" void f2fs_alloc_nid(struct f2fs_sb_info *sbi, nid_t *nid, int inode) { struct f2fs_nm_info *nm_i = NM_I(sbi); struct f2fs_checkpoint *cp = F2FS_CKPT(sbi); nid_t i, inode_cnt, node_cnt; for (i = 0; i < nm_i->max_nid; i++) if(f2fs_test_bit(i, nm_i->nid_bitmap) == 0) break; ASSERT(i < nm_i->max_nid); f2fs_set_bit(i, nm_i->nid_bitmap); *nid = i; inode_cnt = get_cp(valid_inode_count); node_cnt = get_cp(valid_node_count); if (inode) set_cp(valid_inode_count, inode_cnt + 1); set_cp(valid_node_count, node_cnt + 1); } void set_data_blkaddr(struct dnode_of_data *dn) { __le32 *addr_array; struct f2fs_node *node_blk = dn->node_blk; unsigned int ofs_in_node = dn->ofs_in_node; addr_array = blkaddr_in_node(node_blk); addr_array[ofs_in_node] = cpu_to_le32(dn->data_blkaddr); if (dn->node_blk != dn->inode_blk) dn->ndirty = 1; else dn->idirty = 1; } /* * In this function, we get a new node blk, and write back * node_blk would be sloadd in RAM, linked by dn->node_blk */ block_t new_node_block(struct f2fs_sb_info *sbi, struct dnode_of_data *dn, unsigned int ofs) { struct f2fs_node *f2fs_inode; struct f2fs_node *node_blk; struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi); struct f2fs_summary sum; struct node_info ni; block_t blkaddr = NULL_ADDR; int type; f2fs_inode = dn->inode_blk; node_blk = calloc(BLOCK_SZ, 1); ASSERT(node_blk); node_blk->footer.nid = cpu_to_le32(dn->nid); node_blk->footer.ino = f2fs_inode->footer.ino; node_blk->footer.flag = cpu_to_le32(ofs << OFFSET_BIT_SHIFT); node_blk->footer.cp_ver = ckpt->checkpoint_ver; type = CURSEG_COLD_NODE; if (IS_DNODE(node_blk)) { if (S_ISDIR(le16_to_cpu(f2fs_inode->i.i_mode))) type = CURSEG_HOT_NODE; else type = CURSEG_WARM_NODE; } get_node_info(sbi, dn->nid, &ni); set_summary(&sum, dn->nid, 0, ni.version); reserve_new_block(sbi, &blkaddr, &sum, type); /* update nat info */ update_nat_blkaddr(sbi, le32_to_cpu(f2fs_inode->footer.ino), dn->nid, blkaddr); dn->node_blk = node_blk; inc_inode_blocks(dn); return blkaddr; } /* * get_node_path - Get the index path of pgoff_t block * @offset: offset in the current index node block. * @noffset: NO. of the index block within a file. * return: depth of the index path. * * By default, it sets inline_xattr and inline_data */ static int get_node_path(struct f2fs_node *node, long block, int offset[4], unsigned int noffset[4]) { const long direct_index = ADDRS_PER_INODE(&node->i); const long direct_blks = ADDRS_PER_BLOCK; const long dptrs_per_blk = NIDS_PER_BLOCK; const long indirect_blks = ADDRS_PER_BLOCK * NIDS_PER_BLOCK; const long dindirect_blks = indirect_blks * NIDS_PER_BLOCK; int n = 0; int level = 0; noffset[0] = 0; if (block < direct_index) { offset[n] = block; goto got; } block -= direct_index; if (block < direct_blks) { offset[n++] = NODE_DIR1_BLOCK; noffset[n]= 1; offset[n] = block; level = 1; goto got; } block -= direct_blks; if (block < direct_blks) { offset[n++] = NODE_DIR2_BLOCK; noffset[n] = 2; offset[n] = block; level = 1; goto got; } block -= direct_blks; if (block < indirect_blks) { offset[n++] = NODE_IND1_BLOCK; noffset[n] = 3; offset[n++] = block / direct_blks; noffset[n] = 4 + offset[n - 1]; offset[n] = block % direct_blks; level = 2; goto got; } block -= indirect_blks; if (block < indirect_blks) { offset[n++] = NODE_IND2_BLOCK; noffset[n] = 4 + dptrs_per_blk; offset[n++] = block / direct_blks; noffset[n] = 5 + dptrs_per_blk + offset[n - 1]; offset[n] = block % direct_blks; level = 2; goto got; } block -= indirect_blks; if (block < dindirect_blks) { offset[n++] = NODE_DIND_BLOCK; noffset[n] = 5 + (dptrs_per_blk * 2); offset[n++] = block / indirect_blks; noffset[n] = 6 + (dptrs_per_blk * 2) + offset[n - 1] * (dptrs_per_blk + 1); offset[n++] = (block / direct_blks) % dptrs_per_blk; noffset[n] = 7 + (dptrs_per_blk * 2) + offset[n - 2] * (dptrs_per_blk + 1) + offset[n - 1]; offset[n] = block % direct_blks; level = 3; goto got; } else { ASSERT(0); } got: return level; } void get_dnode_of_data(struct f2fs_sb_info *sbi, struct dnode_of_data *dn, pgoff_t index, int mode) { int offset[4]; unsigned int noffset[4]; struct f2fs_node *parent = NULL; nid_t nids[4]; block_t nblk[4]; struct node_info ni; int level, i; int ret; level = get_node_path(dn->inode_blk, index, offset, noffset); nids[0] = dn->nid; parent = dn->inode_blk; if (level != 0) nids[1] = get_nid(parent, offset[0], 1); else dn->node_blk = dn->inode_blk; get_node_info(sbi, nids[0], &ni); nblk[0] = ni.blk_addr; for (i = 1; i <= level; i++) { if (!nids[i] && mode == ALLOC_NODE) { f2fs_alloc_nid(sbi, &nids[i], 0); dn->nid = nids[i]; /* Function new_node_blk get a new f2fs_node blk and update*/ /* We should make sure that dn->node_blk == NULL*/ nblk[i] = new_node_block(sbi, dn, noffset[i]); ASSERT(nblk[i]); set_nid(parent, offset[i - 1], nids[i], i == 1); } else { /* If Sparse file no read API, */ struct node_info ni; get_node_info(sbi, nids[i], &ni); dn->node_blk = calloc(BLOCK_SZ, 1); ASSERT(dn->node_blk); ret = dev_read_block(dn->node_blk, ni.blk_addr); ASSERT(ret >= 0); nblk[i] = ni.blk_addr; } if (mode == ALLOC_NODE){ /* Parent node may have changed */ ret = dev_write_block(parent, nblk[i - 1]); ASSERT(ret >= 0); } if (i != 1) free(parent); if (i < level) { parent = dn->node_blk; nids[i + 1] = get_nid(parent, offset[i], 0); } } dn->nid = nids[level]; dn->ofs_in_node = offset[level]; dn->data_blkaddr = datablock_addr(dn->node_blk, dn->ofs_in_node); dn->node_blkaddr = nblk[level]; } f2fs-tools-1.10.0/fsck/node.h000066400000000000000000000046551323417143600156310ustar00rootroot00000000000000/** * node.h * * Many parts of codes are copied from Linux kernel/fs/f2fs. * * Copyright (C) 2015 Huawei Ltd. * Witten by: * Hou Pengyang * Liu Shuoran * Jaegeuk Kim * * 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. */ #ifndef _NODE_H_ #define _NODE_H_ #include "fsck.h" #define ADDRS_PER_PAGE(page) \ (IS_INODE(page) ? ADDRS_PER_INODE(&page->i) : ADDRS_PER_BLOCK) static inline int IS_INODE(struct f2fs_node *node) { return ((node)->footer.nid == (node)->footer.ino); } static inline __le32 *blkaddr_in_inode(struct f2fs_node *node) { return node->i.i_addr + get_extra_isize(node); } static inline __le32 *blkaddr_in_node(struct f2fs_node *node) { return IS_INODE(node) ? blkaddr_in_inode(node) : node->dn.addr; } static inline block_t datablock_addr(struct f2fs_node *node_page, unsigned int offset) { __le32 *addr_array; ASSERT(node_page); addr_array = blkaddr_in_node(node_page); return le32_to_cpu(addr_array[offset]); } static inline void set_nid(struct f2fs_node * rn, int off, nid_t nid, int i) { if (i) rn->i.i_nid[off - NODE_DIR1_BLOCK] = cpu_to_le32(nid); else rn->in.nid[off] = cpu_to_le32(nid); } static inline nid_t get_nid(struct f2fs_node * rn, int off, int i) { if (i) return le32_to_cpu(rn->i.i_nid[off - NODE_DIR1_BLOCK]); else return le32_to_cpu(rn->in.nid[off]); } enum { ALLOC_NODE, /* allocate a new node page if needed */ LOOKUP_NODE, /* lookup up a node without readahead */ LOOKUP_NODE_RA, }; static inline void set_new_dnode(struct dnode_of_data *dn, struct f2fs_node *iblk, struct f2fs_node *nblk, nid_t nid) { memset(dn, 0, sizeof(*dn)); dn->inode_blk = iblk; dn->node_blk = nblk; dn->nid = nid; dn->idirty = 0; dn->ndirty = 0; } static inline void inc_inode_blocks(struct dnode_of_data *dn) { u64 blocks = le64_to_cpu(dn->inode_blk->i.i_blocks); dn->inode_blk->i.i_blocks = cpu_to_le64(blocks + 1); dn->idirty = 1; } static inline int IS_DNODE(struct f2fs_node *node_page) { unsigned int ofs = ofs_of_node(node_page); if (ofs == 3 || ofs == 4 + NIDS_PER_BLOCK || ofs == 5 + 2 * NIDS_PER_BLOCK) return 0; if (ofs >= 6 + 2 * NIDS_PER_BLOCK) { ofs -= 6 + 2 * NIDS_PER_BLOCK; if (!((long int)ofs % (NIDS_PER_BLOCK + 1))) return 0; } return 1; } #endif f2fs-tools-1.10.0/fsck/quotaio.c000066400000000000000000000115701323417143600163520ustar00rootroot00000000000000/** quotaio.c * * Generic IO operations on quotafiles * Jan Kara - sponsored by SuSE CR * Aditya Kali - Ported to e2fsprogs * Hyojun Kim - Ported to f2fs-tools */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include "common.h" #include "quotaio.h" static const char * const extensions[MAXQUOTAS] = { [USRQUOTA] = "user", [GRPQUOTA] = "group", [PRJQUOTA] = "project", }; /* Header in all newer quotafiles */ struct disk_dqheader { __le32 dqh_magic; __le32 dqh_version; } __attribute__ ((packed)); /** * Convert type of quota to written representation */ const char *quota_type2name(enum quota_type qtype) { if (qtype >= MAXQUOTAS) return "unknown"; return extensions[qtype]; } /* * Set grace time if needed */ void update_grace_times(struct dquot *q) { time_t now; time(&now); if (q->dq_dqb.dqb_bsoftlimit && toqb(q->dq_dqb.dqb_curspace) > q->dq_dqb.dqb_bsoftlimit) { if (!q->dq_dqb.dqb_btime) q->dq_dqb.dqb_btime = now + q->dq_h->qh_info.dqi_bgrace; } else { q->dq_dqb.dqb_btime = 0; } if (q->dq_dqb.dqb_isoftlimit && q->dq_dqb.dqb_curinodes > q->dq_dqb.dqb_isoftlimit) { if (!q->dq_dqb.dqb_itime) q->dq_dqb.dqb_itime = now + q->dq_h->qh_info.dqi_igrace; } else { q->dq_dqb.dqb_itime = 0; } } /* Functions to read/write quota file. */ static unsigned int quota_write_nomount(struct quota_file *qf, long offset, void *buf, unsigned int size) { unsigned int written; written = f2fs_write(qf->sbi, qf->ino, buf, size, offset); if (qf->filesize < offset + written) qf->filesize = offset + written; return written; } static unsigned int quota_read_nomount(struct quota_file *qf, long offset, void *buf, unsigned int size) { return f2fs_read(qf->sbi, qf->ino, buf, size, offset); } /* * Detect quota format and initialize quota IO */ errcode_t quota_file_open(struct f2fs_sb_info *sbi, struct quota_handle *h, enum quota_type qtype, int flags) { struct f2fs_fsck *fsck = F2FS_FSCK(sbi); struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi); quota_ctx_t qctx = fsck->qctx; f2fs_ino_t qf_ino; errcode_t err = 0; int allocated_handle = 0; if (qtype >= MAXQUOTAS) return EINVAL; qf_ino = sb->qf_ino[qtype]; if (!h) { if (qctx->quota_file[qtype]) { h = qctx->quota_file[qtype]; (void) quota_file_close(sbi, h, 0); } err = quota_get_mem(sizeof(struct quota_handle), &h); if (err) { log_err("Unable to allocate quota handle"); return err; } allocated_handle = 1; } h->qh_qf.sbi = sbi; h->qh_qf.ino = qf_ino; h->write = quota_write_nomount; h->read = quota_read_nomount; h->qh_file_flags = flags; h->qh_io_flags = 0; h->qh_type = qtype; h->qh_fmt = QFMT_VFS_V1; memset(&h->qh_info, 0, sizeof(h->qh_info)); h->qh_ops = "afile_ops_2; if (h->qh_ops->check_file && (h->qh_ops->check_file(h, qtype) == 0)) { log_err("qh_ops->check_file failed"); err = EIO; goto errout; } if (h->qh_ops->init_io && (h->qh_ops->init_io(h) < 0)) { log_err("qh_ops->init_io failed"); err = EIO; goto errout; } if (allocated_handle) qctx->quota_file[qtype] = h; errout: return err; } /* * Create new quotafile of specified format on given filesystem */ errcode_t quota_file_create(struct f2fs_sb_info *sbi, struct quota_handle *h, enum quota_type qtype) { struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi); f2fs_ino_t qf_inum = sb->qf_ino[qtype]; errcode_t err = 0; h->qh_qf.sbi = sbi; h->qh_qf.ino = qf_inum; h->write = quota_write_nomount; h->read = quota_read_nomount; log_debug("Creating quota ino=%u, type=%d", qf_inum, qtype); h->qh_io_flags = 0; h->qh_type = qtype; h->qh_fmt = QFMT_VFS_V1; memset(&h->qh_info, 0, sizeof(h->qh_info)); h->qh_ops = "afile_ops_2; if (h->qh_ops->new_io && (h->qh_ops->new_io(h) < 0)) { log_err("qh_ops->new_io failed"); err = EIO; } return err; } /* * Close quotafile and release handle */ errcode_t quota_file_close(struct f2fs_sb_info *sbi, struct quota_handle *h, int update_filesize) { struct f2fs_fsck *fsck = F2FS_FSCK(sbi); quota_ctx_t qctx = fsck->qctx; if (h->qh_io_flags & IOFL_INFODIRTY) { if (h->qh_ops->write_info && h->qh_ops->write_info(h) < 0) return EIO; h->qh_io_flags &= ~IOFL_INFODIRTY; } if (h->qh_ops->end_io && h->qh_ops->end_io(h) < 0) return EIO; if (update_filesize) { f2fs_filesize_update(sbi, h->qh_qf.ino, h->qh_qf.filesize); } if (qctx->quota_file[h->qh_type] == h) quota_free_mem(&qctx->quota_file[h->qh_type]); return 0; } /* * Create empty quota structure */ struct dquot *get_empty_dquot(void) { struct dquot *dquot; if (quota_get_memzero(sizeof(struct dquot), &dquot)) { log_err("Failed to allocate dquot"); return NULL; } dquot->dq_id = -1; return dquot; } f2fs-tools-1.10.0/fsck/quotaio.h000066400000000000000000000161231323417143600163560ustar00rootroot00000000000000/** quotaio.h * * Interface to the quota library. * * The quota library provides interface for creating and updating the quota * files and the ext4 superblock fields. It supports the new VFS_V1 quota * format. The quota library also provides support for keeping track of quotas * in memory. * * Aditya Kali * Header of IO operations for quota utilities * * Jan Kara * * Hyojun Kim - Ported to f2fs-tools */ #ifndef GUARD_QUOTAIO_H #define GUARD_QUOTAIO_H #include #include #include #include #include "dict.h" #include "f2fs_fs.h" #include "f2fs.h" #include "node.h" #include "fsck.h" #include "dqblk_v2.h" typedef int64_t qsize_t; /* Type in which we store size limitations */ typedef int32_t f2fs_ino_t; typedef int errcode_t; enum quota_type { USRQUOTA = 0, GRPQUOTA = 1, PRJQUOTA = 2, MAXQUOTAS = 3, }; #if MAXQUOTAS > 32 #error "cannot have more than 32 quota types to fit in qtype_bits" #endif #define QUOTA_USR_BIT (1 << USRQUOTA) #define QUOTA_GRP_BIT (1 << GRPQUOTA) #define QUOTA_PRJ_BIT (1 << PRJQUOTA) #define QUOTA_ALL_BIT (QUOTA_USR_BIT | QUOTA_GRP_BIT | QUOTA_PRJ_BIT) typedef struct quota_ctx *quota_ctx_t; struct quota_ctx { struct f2fs_sb_info *sbi; struct dict_t *quota_dict[MAXQUOTAS]; struct quota_handle *quota_file[MAXQUOTAS]; struct dict_t linked_inode_dict; }; /* * Definitions of magics and versions of current quota files */ #define INITQMAGICS {\ 0xd9c01f11, /* USRQUOTA */\ 0xd9c01927, /* GRPQUOTA */\ 0xd9c03f14 /* PRJQUOTA */\ } /* Size of blocks in which are counted size limits in generic utility parts */ #define QUOTABLOCK_BITS 10 #define QUOTABLOCK_SIZE (1 << QUOTABLOCK_BITS) #define toqb(x) (((x) + QUOTABLOCK_SIZE - 1) >> QUOTABLOCK_BITS) /* Quota format type IDs */ #define QFMT_VFS_OLD 1 #define QFMT_VFS_V0 2 #define QFMT_VFS_V1 4 /* * The following constants define the default amount of time given a user * before the soft limits are treated as hard limits (usually resulting * in an allocation failure). The timer is started when the user crosses * their soft limit, it is reset when they go below their soft limit. */ #define MAX_IQ_TIME 604800 /* (7*24*60*60) 1 week */ #define MAX_DQ_TIME 604800 /* (7*24*60*60) 1 week */ #define IOFL_INFODIRTY 0x01 /* Did info change? */ struct quotafile_ops; /* Generic information about quotafile */ struct util_dqinfo { time_t dqi_bgrace; /* Block grace time for given quotafile */ time_t dqi_igrace; /* Inode grace time for given quotafile */ union { struct v2_mem_dqinfo v2_mdqi; } u; /* Format specific info about quotafile */ }; struct quota_file { struct f2fs_sb_info *sbi; f2fs_ino_t ino; int64_t filesize; }; /* Structure for one opened quota file */ struct quota_handle { enum quota_type qh_type; /* Type of quotafile */ int qh_fmt; /* Quotafile format */ int qh_file_flags; int qh_io_flags; /* IO flags for file */ struct quota_file qh_qf; unsigned int (*read)(struct quota_file *qf, long offset, void *buf, unsigned int size); unsigned int (*write)(struct quota_file *qf, long offset, void *buf, unsigned int size); struct quotafile_ops *qh_ops; /* Operations on quotafile */ struct util_dqinfo qh_info; /* Generic quotafile info */ }; /* Utility quota block */ struct util_dqblk { qsize_t dqb_ihardlimit; qsize_t dqb_isoftlimit; qsize_t dqb_curinodes; qsize_t dqb_bhardlimit; qsize_t dqb_bsoftlimit; qsize_t dqb_curspace; time_t dqb_btime; time_t dqb_itime; union { struct v2_mem_dqblk v2_mdqb; } u; /* Format specific dquot information */ }; /* Structure for one loaded quota */ struct dquot { struct dquot *dq_next; /* Pointer to next dquot in the list */ qid_t dq_id; /* ID dquot belongs to */ int dq_flags; /* Some flags for utils */ struct quota_handle *dq_h; /* Handle of quotafile for this dquot */ struct util_dqblk dq_dqb; /* Parsed data of dquot */ }; #define DQF_SEEN 0x0001 /* Structure of quotafile operations */ struct quotafile_ops { /* Check whether quotafile is in our format */ int (*check_file) (struct quota_handle *h, int type); /* Open quotafile */ int (*init_io) (struct quota_handle *h); /* Create new quotafile */ int (*new_io) (struct quota_handle *h); /* Write all changes and close quotafile */ int (*end_io) (struct quota_handle *h); /* Write info about quotafile */ int (*write_info) (struct quota_handle *h); /* Read dquot into memory */ struct dquot *(*read_dquot) (struct quota_handle *h, qid_t id); /* Write given dquot to disk */ int (*commit_dquot) (struct dquot *dquot); /* Scan quotafile and call callback on every structure */ int (*scan_dquots) (struct quota_handle *h, int (*process_dquot) (struct dquot *dquot, void *data), void *data); /* Function to print format specific file information */ int (*report) (struct quota_handle *h, int verbose); }; #ifdef __CHECKER__ # ifndef __bitwise # define __bitwise __attribute__((bitwise)) # endif #define __force __attribute__((force)) #else # ifndef __bitwise # define __bitwise # endif #define __force #endif #define be32_to_cpu(n) ntohl(n) /* Open existing quotafile of given type (and verify its format) on given * filesystem. */ errcode_t quota_file_open(struct f2fs_sb_info *sbi, struct quota_handle *h, enum quota_type qtype, int flags); /* Create new quotafile of specified format on given filesystem */ errcode_t quota_file_create(struct f2fs_sb_info *sbi, struct quota_handle *h, enum quota_type qtype); /* Close quotafile */ errcode_t quota_file_close(struct f2fs_sb_info *sbi, struct quota_handle *h, int update_filesize); /* Get empty quota structure */ struct dquot *get_empty_dquot(void); const char *quota_type2name(enum quota_type qtype); void update_grace_times(struct dquot *q); /* In mkquota.c */ errcode_t quota_init_context(struct f2fs_sb_info *sbi); void quota_data_inodes(quota_ctx_t qctx, struct f2fs_inode *inode, int adjust); void quota_data_add(quota_ctx_t qctx, struct f2fs_inode *inode, qsize_t space); void quota_data_sub(quota_ctx_t qctx, struct f2fs_inode *inode, qsize_t space); errcode_t quota_write_inode(struct f2fs_sb_info *sbi, enum quota_type qtype); void quota_add_inode_usage(quota_ctx_t qctx, f2fs_ino_t ino, struct f2fs_inode* inode); void quota_release_context(quota_ctx_t *qctx); errcode_t quota_compare_and_update(struct f2fs_sb_info *sbi, enum quota_type qtype, int *usage_inconsistent, int preserve_limits); static inline errcode_t quota_get_mem(unsigned long size, void *ptr) { void *pp; pp = malloc(size); if (!pp) return -1; memcpy(ptr, &pp, sizeof (pp)); return 0; } static inline errcode_t quota_get_memzero(unsigned long size, void *ptr) { void *pp; pp = malloc(size); if (!pp) return -1; memset(pp, 0, size); memcpy(ptr, &pp, sizeof(pp)); return 0; } static inline errcode_t quota_free_mem(void *ptr) { void *p; memcpy(&p, ptr, sizeof(p)); free(p); p = 0; memcpy(ptr, &p, sizeof(p)); return 0; } #endif /* GUARD_QUOTAIO_H */ f2fs-tools-1.10.0/fsck/quotaio_tree.c000066400000000000000000000407211323417143600173710ustar00rootroot00000000000000/* * Implementation of new quotafile format * * Jan Kara - sponsored by SuSE CR * Hyojun Kim - Ported to f2fs-tools */ #include "config.h" #include #include #include #include #include #include #include "common.h" #include "quotaio_tree.h" #include "quotaio.h" typedef char *dqbuf_t; #define freedqbuf(buf) quota_free_mem(&buf) static inline dqbuf_t getdqbuf(void) { dqbuf_t buf; if (quota_get_memzero(QT_BLKSIZE, &buf)) { log_err("Failed to allocate dqbuf"); return NULL; } return buf; } /* Is given dquot empty? */ int qtree_entry_unused(struct qtree_mem_dqinfo *info, char *disk) { unsigned int i; for (i = 0; i < info->dqi_entry_size; i++) if (disk[i]) return 0; return 1; } int qtree_dqstr_in_blk(struct qtree_mem_dqinfo *info) { return (QT_BLKSIZE - sizeof(struct qt_disk_dqdbheader)) / info->dqi_entry_size; } static int get_index(qid_t id, int depth) { return (id >> ((QT_TREEDEPTH - depth - 1) * 8)) & 0xff; } static inline void mark_quotafile_info_dirty(struct quota_handle *h) { h->qh_io_flags |= IOFL_INFODIRTY; } /* Read given block */ static void read_blk(struct quota_handle *h, unsigned int blk, dqbuf_t buf) { int err; err = h->read(&h->qh_qf, blk << QT_BLKSIZE_BITS, buf, QT_BLKSIZE); if (err < 0) log_err("Cannot read block %u: %s", blk, strerror(errno)); else if (err != QT_BLKSIZE) memset(buf + err, 0, QT_BLKSIZE - err); } /* Write block */ static int write_blk(struct quota_handle *h, unsigned int blk, dqbuf_t buf) { int err; err = h->write(&h->qh_qf, blk << QT_BLKSIZE_BITS, buf, QT_BLKSIZE); if (err < 0 && errno != ENOSPC) log_err("Cannot write block (%u): %s", blk, strerror(errno)); if (err != QT_BLKSIZE) return -ENOSPC; return 0; } /* Get free block in file (either from free list or create new one) */ static int get_free_dqblk(struct quota_handle *h) { dqbuf_t buf = getdqbuf(); struct qt_disk_dqdbheader *dh = (struct qt_disk_dqdbheader *)buf; struct qtree_mem_dqinfo *info = &h->qh_info.u.v2_mdqi.dqi_qtree; int blk; if (!buf) return -ENOMEM; if (info->dqi_free_blk) { blk = info->dqi_free_blk; read_blk(h, blk, buf); info->dqi_free_blk = le32_to_cpu(dh->dqdh_next_free); } else { memset(buf, 0, QT_BLKSIZE); /* Assure block allocation... */ if (write_blk(h, info->dqi_blocks, buf) < 0) { freedqbuf(buf); log_err("Cannot allocate new quota block " "(out of disk space)."); return -ENOSPC; } blk = info->dqi_blocks++; } mark_quotafile_info_dirty(h); freedqbuf(buf); return blk; } /* Put given block to free list */ static void put_free_dqblk(struct quota_handle *h, dqbuf_t buf, unsigned int blk) { struct qt_disk_dqdbheader *dh = (struct qt_disk_dqdbheader *)buf; struct qtree_mem_dqinfo *info = &h->qh_info.u.v2_mdqi.dqi_qtree; dh->dqdh_next_free = cpu_to_le32(info->dqi_free_blk); dh->dqdh_prev_free = cpu_to_le32(0); dh->dqdh_entries = cpu_to_le16(0); info->dqi_free_blk = blk; mark_quotafile_info_dirty(h); write_blk(h, blk, buf); } /* Remove given block from the list of blocks with free entries */ static void remove_free_dqentry(struct quota_handle *h, dqbuf_t buf, unsigned int blk) { dqbuf_t tmpbuf = getdqbuf(); struct qt_disk_dqdbheader *dh = (struct qt_disk_dqdbheader *)buf; unsigned int nextblk = le32_to_cpu(dh->dqdh_next_free), prevblk = le32_to_cpu(dh->dqdh_prev_free); if (!tmpbuf) return; if (nextblk) { read_blk(h, nextblk, tmpbuf); ((struct qt_disk_dqdbheader *)tmpbuf)->dqdh_prev_free = dh->dqdh_prev_free; write_blk(h, nextblk, tmpbuf); } if (prevblk) { read_blk(h, prevblk, tmpbuf); ((struct qt_disk_dqdbheader *)tmpbuf)->dqdh_next_free = dh->dqdh_next_free; write_blk(h, prevblk, tmpbuf); } else { h->qh_info.u.v2_mdqi.dqi_qtree.dqi_free_entry = nextblk; mark_quotafile_info_dirty(h); } freedqbuf(tmpbuf); dh->dqdh_next_free = dh->dqdh_prev_free = cpu_to_le32(0); write_blk(h, blk, buf); /* No matter whether write succeeds * block is out of list */ } /* Insert given block to the beginning of list with free entries */ static void insert_free_dqentry(struct quota_handle *h, dqbuf_t buf, unsigned int blk) { dqbuf_t tmpbuf = getdqbuf(); struct qt_disk_dqdbheader *dh = (struct qt_disk_dqdbheader *)buf; struct qtree_mem_dqinfo *info = &h->qh_info.u.v2_mdqi.dqi_qtree; if (!tmpbuf) return; dh->dqdh_next_free = cpu_to_le32(info->dqi_free_entry); dh->dqdh_prev_free = cpu_to_le32(0); write_blk(h, blk, buf); if (info->dqi_free_entry) { read_blk(h, info->dqi_free_entry, tmpbuf); ((struct qt_disk_dqdbheader *)tmpbuf)->dqdh_prev_free = cpu_to_le32(blk); write_blk(h, info->dqi_free_entry, tmpbuf); } freedqbuf(tmpbuf); info->dqi_free_entry = blk; mark_quotafile_info_dirty(h); } /* Find space for dquot */ static unsigned int find_free_dqentry(struct quota_handle *h, struct dquot *dquot, int *err) { int blk, i; struct qt_disk_dqdbheader *dh; struct qtree_mem_dqinfo *info = &h->qh_info.u.v2_mdqi.dqi_qtree; char *ddquot; dqbuf_t buf; *err = 0; buf = getdqbuf(); if (!buf) { *err = -ENOMEM; return 0; } dh = (struct qt_disk_dqdbheader *)buf; if (info->dqi_free_entry) { blk = info->dqi_free_entry; read_blk(h, blk, buf); } else { blk = get_free_dqblk(h); if (blk < 0) { freedqbuf(buf); *err = blk; return 0; } memset(buf, 0, QT_BLKSIZE); info->dqi_free_entry = blk; mark_quotafile_info_dirty(h); } /* Block will be full? */ if (le16_to_cpu(dh->dqdh_entries) + 1 >= qtree_dqstr_in_blk(info)) remove_free_dqentry(h, buf, blk); dh->dqdh_entries = cpu_to_le16(le16_to_cpu(dh->dqdh_entries) + 1); /* Find free structure in block */ ddquot = buf + sizeof(struct qt_disk_dqdbheader); for (i = 0; i < qtree_dqstr_in_blk(info) && !qtree_entry_unused(info, ddquot); i++) ddquot += info->dqi_entry_size; if (i == qtree_dqstr_in_blk(info)) log_err("find_free_dqentry(): Data block full unexpectedly."); write_blk(h, blk, buf); dquot->dq_dqb.u.v2_mdqb.dqb_off = (blk << QT_BLKSIZE_BITS) + sizeof(struct qt_disk_dqdbheader) + i * info->dqi_entry_size; freedqbuf(buf); return blk; } /* Insert reference to structure into the trie */ static int do_insert_tree(struct quota_handle *h, struct dquot *dquot, unsigned int * treeblk, int depth) { dqbuf_t buf; int newson = 0, newact = 0; __le32 *ref; unsigned int newblk; int ret = 0; log_debug("inserting in tree: treeblk=%u, depth=%d", *treeblk, depth); buf = getdqbuf(); if (!buf) return -ENOMEM; if (!*treeblk) { ret = get_free_dqblk(h); if (ret < 0) goto out_buf; *treeblk = ret; memset(buf, 0, QT_BLKSIZE); newact = 1; } else { read_blk(h, *treeblk, buf); } ref = (__le32 *) buf; newblk = le32_to_cpu(ref[get_index(dquot->dq_id, depth)]); if (!newblk) newson = 1; if (depth == QT_TREEDEPTH - 1) { if (newblk) log_err("Inserting already present quota entry " "(block %u).", ref[get_index(dquot->dq_id, depth)]); newblk = find_free_dqentry(h, dquot, &ret); } else { ret = do_insert_tree(h, dquot, &newblk, depth + 1); } if (newson && ret >= 0) { ref[get_index(dquot->dq_id, depth)] = cpu_to_le32(newblk); write_blk(h, *treeblk, buf); } else if (newact && ret < 0) { put_free_dqblk(h, buf, *treeblk); } out_buf: freedqbuf(buf); return ret; } /* Wrapper for inserting quota structure into tree */ static void dq_insert_tree(struct quota_handle *h, struct dquot *dquot) { unsigned int tmp = QT_TREEOFF; if (do_insert_tree(h, dquot, &tmp, 0) < 0) log_err("Cannot write quota (id %u): %s", (unsigned int) dquot->dq_id, strerror(errno)); } /* Write dquot to file */ void qtree_write_dquot(struct dquot *dquot) { errcode_t retval; unsigned int ret; char *ddquot; struct quota_handle *h = dquot->dq_h; struct qtree_mem_dqinfo *info = &dquot->dq_h->qh_info.u.v2_mdqi.dqi_qtree; log_debug("writing ddquot 1: off=%llu, info->dqi_entry_size=%u", dquot->dq_dqb.u.v2_mdqb.dqb_off, info->dqi_entry_size); retval = quota_get_mem(info->dqi_entry_size, &ddquot); if (retval) { errno = ENOMEM; log_err("Quota write failed (id %u): %s", (unsigned int)dquot->dq_id, strerror(errno)); return; } memset(ddquot, 0, info->dqi_entry_size); if (!dquot->dq_dqb.u.v2_mdqb.dqb_off) { dq_insert_tree(dquot->dq_h, dquot); } info->dqi_ops->mem2disk_dqblk(ddquot, dquot); log_debug("writing ddquot 2: off=%llu, info->dqi_entry_size=%u", dquot->dq_dqb.u.v2_mdqb.dqb_off, info->dqi_entry_size); ret = h->write(&h->qh_qf, dquot->dq_dqb.u.v2_mdqb.dqb_off, ddquot, info->dqi_entry_size); if (ret != info->dqi_entry_size) { if (ret > 0) errno = ENOSPC; log_err("Quota write failed (id %u): %s", (unsigned int)dquot->dq_id, strerror(errno)); } quota_free_mem(&ddquot); } /* Free dquot entry in data block */ static void free_dqentry(struct quota_handle *h, struct dquot *dquot, unsigned int blk) { struct qt_disk_dqdbheader *dh; struct qtree_mem_dqinfo *info = &h->qh_info.u.v2_mdqi.dqi_qtree; dqbuf_t buf = getdqbuf(); if (!buf) return; if (dquot->dq_dqb.u.v2_mdqb.dqb_off >> QT_BLKSIZE_BITS != blk) log_err("Quota structure has offset to other block (%u) " "than it should (%u).", blk, (unsigned int) (dquot->dq_dqb.u.v2_mdqb.dqb_off >> QT_BLKSIZE_BITS)); read_blk(h, blk, buf); dh = (struct qt_disk_dqdbheader *)buf; dh->dqdh_entries = cpu_to_le16(le16_to_cpu(dh->dqdh_entries) - 1); if (!le16_to_cpu(dh->dqdh_entries)) { /* Block got free? */ remove_free_dqentry(h, buf, blk); put_free_dqblk(h, buf, blk); } else { memset(buf + (dquot->dq_dqb.u.v2_mdqb.dqb_off & ((1 << QT_BLKSIZE_BITS) - 1)), 0, info->dqi_entry_size); /* First free entry? */ if (le16_to_cpu(dh->dqdh_entries) == qtree_dqstr_in_blk(info) - 1) /* This will also write data block */ insert_free_dqentry(h, buf, blk); else write_blk(h, blk, buf); } dquot->dq_dqb.u.v2_mdqb.dqb_off = 0; freedqbuf(buf); } /* Remove reference to dquot from tree */ static void remove_tree(struct quota_handle *h, struct dquot *dquot, unsigned int * blk, int depth) { dqbuf_t buf = getdqbuf(); unsigned int newblk; __le32 *ref = (__le32 *) buf; if (!buf) return; read_blk(h, *blk, buf); newblk = le32_to_cpu(ref[get_index(dquot->dq_id, depth)]); if (depth == QT_TREEDEPTH - 1) { free_dqentry(h, dquot, newblk); newblk = 0; } else { remove_tree(h, dquot, &newblk, depth + 1); } if (!newblk) { int i; ref[get_index(dquot->dq_id, depth)] = cpu_to_le32(0); /* Block got empty? */ for (i = 0; i < QT_BLKSIZE && !buf[i]; i++); /* Don't put the root block into the free block list */ if (i == QT_BLKSIZE && *blk != QT_TREEOFF) { put_free_dqblk(h, buf, *blk); *blk = 0; } else { write_blk(h, *blk, buf); } } freedqbuf(buf); } /* Delete dquot from tree */ void qtree_delete_dquot(struct dquot *dquot) { unsigned int tmp = QT_TREEOFF; if (!dquot->dq_dqb.u.v2_mdqb.dqb_off) /* Even not allocated? */ return; remove_tree(dquot->dq_h, dquot, &tmp, 0); } /* Find entry in block */ static long find_block_dqentry(struct quota_handle *h, struct dquot *dquot, unsigned int blk) { struct qtree_mem_dqinfo *info = &h->qh_info.u.v2_mdqi.dqi_qtree; dqbuf_t buf = getdqbuf(); int i; char *ddquot = buf + sizeof(struct qt_disk_dqdbheader); if (!buf) return -ENOMEM; read_blk(h, blk, buf); for (i = 0; i < qtree_dqstr_in_blk(info) && !info->dqi_ops->is_id(ddquot, dquot); i++) ddquot += info->dqi_entry_size; if (i == qtree_dqstr_in_blk(info)) log_err("Quota for id %u referenced but not present.", dquot->dq_id); freedqbuf(buf); return (blk << QT_BLKSIZE_BITS) + sizeof(struct qt_disk_dqdbheader) + i * info->dqi_entry_size; } /* Find entry for given id in the tree */ static long find_tree_dqentry(struct quota_handle *h, struct dquot *dquot, unsigned int blk, int depth) { dqbuf_t buf = getdqbuf(); long ret = 0; __le32 *ref = (__le32 *) buf; if (!buf) return -ENOMEM; read_blk(h, blk, buf); ret = 0; blk = le32_to_cpu(ref[get_index(dquot->dq_id, depth)]); if (!blk) /* No reference? */ goto out_buf; if (depth < QT_TREEDEPTH - 1) ret = find_tree_dqentry(h, dquot, blk, depth + 1); else ret = find_block_dqentry(h, dquot, blk); out_buf: freedqbuf(buf); return ret; } /* Find entry for given id in the tree - wrapper function */ static inline long find_dqentry(struct quota_handle *h, struct dquot *dquot) { return find_tree_dqentry(h, dquot, QT_TREEOFF, 0); } /* * Read dquot from disk. */ struct dquot *qtree_read_dquot(struct quota_handle *h, qid_t id) { struct qtree_mem_dqinfo *info = &h->qh_info.u.v2_mdqi.dqi_qtree; long offset; unsigned int ret; char *ddquot; struct dquot *dquot = get_empty_dquot(); if (!dquot) return NULL; if (quota_get_mem(info->dqi_entry_size, &ddquot)) { quota_free_mem(&dquot); return NULL; } dquot->dq_id = id; dquot->dq_h = h; dquot->dq_dqb.u.v2_mdqb.dqb_off = 0; memset(&dquot->dq_dqb, 0, sizeof(struct util_dqblk)); offset = find_dqentry(h, dquot); if (offset > 0) { dquot->dq_dqb.u.v2_mdqb.dqb_off = offset; ret = h->read(&h->qh_qf, offset, ddquot, info->dqi_entry_size); if (ret != info->dqi_entry_size) { if (ret > 0) errno = EIO; log_err("Cannot read quota structure for id %u: %s", dquot->dq_id, strerror(errno)); } info->dqi_ops->disk2mem_dqblk(dquot, ddquot); } quota_free_mem(&ddquot); return dquot; } /* * Scan all dquots in file and call callback on each */ #define set_bit(bmp, ind) ((bmp)[(ind) >> 3] |= (1 << ((ind) & 7))) #define get_bit(bmp, ind) ((bmp)[(ind) >> 3] & (1 << ((ind) & 7))) static int report_block(struct dquot *dquot, unsigned int blk, char *bitmap, int (*process_dquot) (struct dquot *, void *), void *data) { struct qtree_mem_dqinfo *info = &dquot->dq_h->qh_info.u.v2_mdqi.dqi_qtree; dqbuf_t buf = getdqbuf(); struct qt_disk_dqdbheader *dh; char *ddata; int entries, i; if (!buf) return 0; set_bit(bitmap, blk); read_blk(dquot->dq_h, blk, buf); dh = (struct qt_disk_dqdbheader *)buf; ddata = buf + sizeof(struct qt_disk_dqdbheader); entries = le16_to_cpu(dh->dqdh_entries); for (i = 0; i < qtree_dqstr_in_blk(info); i++, ddata += info->dqi_entry_size) if (!qtree_entry_unused(info, ddata)) { dquot->dq_dqb.u.v2_mdqb.dqb_off = (blk << QT_BLKSIZE_BITS) + sizeof(struct qt_disk_dqdbheader) + i * info->dqi_entry_size; info->dqi_ops->disk2mem_dqblk(dquot, ddata); if (process_dquot(dquot, data) < 0) break; } freedqbuf(buf); return entries; } static int check_reference(struct quota_handle *h, unsigned int blk) { if (blk >= h->qh_info.u.v2_mdqi.dqi_qtree.dqi_blocks) { log_err("Illegal reference (%u >= %u) in %s quota file. " "Quota file is probably corrupted.\n" "Please run fsck (8) to fix it.", blk, h->qh_info.u.v2_mdqi.dqi_qtree.dqi_blocks, quota_type2name(h->qh_type)); return -1; } return 0; } /* Return 0 for successful run */ static int report_tree(struct dquot *dquot, unsigned int blk, int depth, char *bitmap, int *entries, int (*process_dquot) (struct dquot *, void *), void *data) { int i; dqbuf_t buf = getdqbuf(); __le32 *ref = (__le32 *) buf; if (!buf) return -1; read_blk(dquot->dq_h, blk, buf); for (i = 0; i < QT_BLKSIZE >> 2; i++) { blk = le32_to_cpu(ref[i]); if (blk == 0) continue; if (check_reference(dquot->dq_h, blk)) break; if (depth == QT_TREEDEPTH - 1) { if (!get_bit(bitmap, blk)) *entries += report_block(dquot, blk, bitmap, process_dquot, data); } else { if (report_tree(dquot, blk, depth + 1, bitmap, entries, process_dquot, data)) break; } } freedqbuf(buf); return (i < QT_BLKSIZE >> 2) ? -1 : 0; } static unsigned int find_set_bits(char *bmp, int blocks) { unsigned int used = 0; int i; for (i = 0; i < blocks; i++) if (get_bit(bmp, i)) used++; return used; } int qtree_scan_dquots(struct quota_handle *h, int (*process_dquot) (struct dquot *, void *), void *data) { struct v2_mem_dqinfo *v2info = &h->qh_info.u.v2_mdqi; struct qtree_mem_dqinfo *info = &v2info->dqi_qtree; struct dquot *dquot = get_empty_dquot(); char *bitmap = NULL; int ret = -1; int entries = 0; if (!dquot) return -1; dquot->dq_h = h; if (quota_get_memzero((info->dqi_blocks + 7) >> 3, &bitmap)) goto out; if (report_tree(dquot, QT_TREEOFF, 0, bitmap, &entries, process_dquot, data)) goto out; v2info->dqi_used_entries = entries; v2info->dqi_data_blocks = find_set_bits(bitmap, info->dqi_blocks); ret = 0; out: if (bitmap) quota_free_mem(&bitmap); if (dquot) quota_free_mem(&dquot); return ret; } f2fs-tools-1.10.0/fsck/quotaio_tree.h000066400000000000000000000043461323417143600174010ustar00rootroot00000000000000/* * Definitions of structures for vfsv0 quota format */ #ifndef _LINUX_QUOTA_TREE_H #define _LINUX_QUOTA_TREE_H #include #ifdef HAVE_LINUX_TYPES_H #include #endif #include #include typedef __u32 qid_t; /* Type in which we store ids in memory */ #define QT_TREEOFF 1 /* Offset of tree in file in blocks */ #define QT_TREEDEPTH 4 /* Depth of quota tree */ #define QT_BLKSIZE_BITS 10 #define QT_BLKSIZE (1 << QT_BLKSIZE_BITS) /* Size of block with quota * structures */ /* * Structure of header of block with quota structures. It is padded to 16 bytes * so there will be space for exactly 21 quota-entries in a 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; } __attribute__ ((packed)); struct dquot; struct quota_handle; /* Operations */ struct qtree_fmt_operations { /* Convert given entry from in memory format to disk one */ void (*mem2disk_dqblk)(void *disk, struct dquot *dquot); /* Convert given entry from disk format to in memory one */ void (*disk2mem_dqblk)(struct dquot *dquot, void *disk); /* Is this structure for given id? */ int (*is_id)(void *disk, struct dquot *dquot); }; /* Inmemory copy of version specific information */ struct qtree_mem_dqinfo { unsigned int dqi_blocks; /* # of blocks in quota file */ unsigned int dqi_free_blk; /* First block in list of free blocks */ unsigned int dqi_free_entry; /* First block with free entry */ unsigned int dqi_entry_size; /* Size of quota entry in quota file */ struct qtree_fmt_operations *dqi_ops; /* Operations for entry * manipulation */ }; void qtree_write_dquot(struct dquot *dquot); struct dquot *qtree_read_dquot(struct quota_handle *h, qid_t id); void qtree_delete_dquot(struct dquot *dquot); int qtree_entry_unused(struct qtree_mem_dqinfo *info, char *disk); int qtree_scan_dquots(struct quota_handle *h, int (*process_dquot) (struct dquot *, void *), void *data); int qtree_dqstr_in_blk(struct qtree_mem_dqinfo *info); #endif /* _LINUX_QUOTAIO_TREE_H */ f2fs-tools-1.10.0/fsck/quotaio_v2.c000066400000000000000000000171331323417143600167620ustar00rootroot00000000000000/* * Implementation of new quotafile format * * Jan Kara - sponsored by SuSE CR * Hyojun Kim - Ported to f2fs-tools */ #include "config.h" #include #include #include #include #include #include #include "common.h" #include "quotaio_v2.h" #include "dqblk_v2.h" #include "quotaio_tree.h" static int v2_check_file(struct quota_handle *h, int type); static int v2_init_io(struct quota_handle *h); static int v2_new_io(struct quota_handle *h); static int v2_write_info(struct quota_handle *h); static struct dquot *v2_read_dquot(struct quota_handle *h, qid_t id); static int v2_commit_dquot(struct dquot *dquot); static int v2_scan_dquots(struct quota_handle *h, int (*process_dquot) (struct dquot *dquot, void *data), void *data); static int v2_report(struct quota_handle *h, int verbose); struct quotafile_ops quotafile_ops_2 = { .check_file = v2_check_file, .init_io = v2_init_io, .new_io = v2_new_io, .write_info = v2_write_info, .read_dquot = v2_read_dquot, .commit_dquot = v2_commit_dquot, .scan_dquots = v2_scan_dquots, .report = v2_report, }; /* * Copy dquot from disk to memory */ static void v2r1_disk2memdqblk(struct dquot *dquot, void *dp) { struct util_dqblk *m = &dquot->dq_dqb; struct v2r1_disk_dqblk *d = dp, empty; dquot->dq_id = le32_to_cpu(d->dqb_id); m->dqb_ihardlimit = le64_to_cpu(d->dqb_ihardlimit); m->dqb_isoftlimit = le64_to_cpu(d->dqb_isoftlimit); m->dqb_bhardlimit = le64_to_cpu(d->dqb_bhardlimit); m->dqb_bsoftlimit = le64_to_cpu(d->dqb_bsoftlimit); m->dqb_curinodes = le64_to_cpu(d->dqb_curinodes); m->dqb_curspace = le64_to_cpu(d->dqb_curspace); m->dqb_itime = le64_to_cpu(d->dqb_itime); m->dqb_btime = le64_to_cpu(d->dqb_btime); memset(&empty, 0, sizeof(struct v2r1_disk_dqblk)); empty.dqb_itime = cpu_to_le64(1); if (!memcmp(&empty, dp, sizeof(struct v2r1_disk_dqblk))) m->dqb_itime = 0; } /* * Copy dquot from memory to disk */ static void v2r1_mem2diskdqblk(void *dp, struct dquot *dquot) { struct util_dqblk *m = &dquot->dq_dqb; struct v2r1_disk_dqblk *d = dp; d->dqb_ihardlimit = cpu_to_le64(m->dqb_ihardlimit); d->dqb_isoftlimit = cpu_to_le64(m->dqb_isoftlimit); d->dqb_bhardlimit = cpu_to_le64(m->dqb_bhardlimit); d->dqb_bsoftlimit = cpu_to_le64(m->dqb_bsoftlimit); d->dqb_curinodes = cpu_to_le64(m->dqb_curinodes); d->dqb_curspace = cpu_to_le64(m->dqb_curspace); d->dqb_itime = cpu_to_le64(m->dqb_itime); d->dqb_btime = cpu_to_le64(m->dqb_btime); d->dqb_id = cpu_to_le32(dquot->dq_id); if (qtree_entry_unused(&dquot->dq_h->qh_info.u.v2_mdqi.dqi_qtree, dp)) d->dqb_itime = cpu_to_le64(1); } static int v2r1_is_id(void *dp, struct dquot *dquot) { struct v2r1_disk_dqblk *d = dp; struct qtree_mem_dqinfo *info = &dquot->dq_h->qh_info.u.v2_mdqi.dqi_qtree; if (qtree_entry_unused(info, dp)) return 0; return le32_to_cpu(d->dqb_id) == dquot->dq_id; } static struct qtree_fmt_operations v2r1_fmt_ops = { .mem2disk_dqblk = v2r1_mem2diskdqblk, .disk2mem_dqblk = v2r1_disk2memdqblk, .is_id = v2r1_is_id, }; /* * Copy dqinfo from disk to memory */ static inline void v2_disk2memdqinfo(struct util_dqinfo *m, struct v2_disk_dqinfo *d) { m->dqi_bgrace = le32_to_cpu(d->dqi_bgrace); m->dqi_igrace = le32_to_cpu(d->dqi_igrace); m->u.v2_mdqi.dqi_flags = le32_to_cpu(d->dqi_flags) & V2_DQF_MASK; m->u.v2_mdqi.dqi_qtree.dqi_blocks = le32_to_cpu(d->dqi_blocks); m->u.v2_mdqi.dqi_qtree.dqi_free_blk = le32_to_cpu(d->dqi_free_blk); m->u.v2_mdqi.dqi_qtree.dqi_free_entry = le32_to_cpu(d->dqi_free_entry); } /* * Copy dqinfo from memory to disk */ static inline void v2_mem2diskdqinfo(struct v2_disk_dqinfo *d, struct util_dqinfo *m) { d->dqi_bgrace = cpu_to_le32(m->dqi_bgrace); d->dqi_igrace = cpu_to_le32(m->dqi_igrace); d->dqi_flags = cpu_to_le32(m->u.v2_mdqi.dqi_flags & V2_DQF_MASK); d->dqi_blocks = cpu_to_le32(m->u.v2_mdqi.dqi_qtree.dqi_blocks); d->dqi_free_blk = cpu_to_le32(m->u.v2_mdqi.dqi_qtree.dqi_free_blk); d->dqi_free_entry = cpu_to_le32(m->u.v2_mdqi.dqi_qtree.dqi_free_entry); } static int v2_read_header(struct quota_handle *h, struct v2_disk_dqheader *dqh) { if (h->read(&h->qh_qf, 0, dqh, sizeof(struct v2_disk_dqheader)) != sizeof(struct v2_disk_dqheader)) return 0; return 1; } /* * Check whether given quota file is in our format */ static int v2_check_file(struct quota_handle *h, int type) { struct v2_disk_dqheader dqh; int file_magics[] = INITQMAGICS; int be_magic; if (!v2_read_header(h, &dqh)) return 0; be_magic = be32_to_cpu((__force __be32)dqh.dqh_magic); if (be_magic == file_magics[type]) { log_err("Your quota file is stored in wrong endianity"); return 0; } if (V2_VERSION != le32_to_cpu(dqh.dqh_version)) return 0; return 1; } /* * Open quotafile */ static int v2_init_io(struct quota_handle *h) { struct v2_disk_dqinfo ddqinfo; h->qh_info.u.v2_mdqi.dqi_qtree.dqi_entry_size = sizeof(struct v2r1_disk_dqblk); h->qh_info.u.v2_mdqi.dqi_qtree.dqi_ops = &v2r1_fmt_ops; /* Read information about quotafile */ if (h->read(&h->qh_qf, V2_DQINFOOFF, &ddqinfo, sizeof(ddqinfo)) != sizeof(ddqinfo)) return -1; v2_disk2memdqinfo(&h->qh_info, &ddqinfo); return 0; } /* * Initialize new quotafile */ static int v2_new_io(struct quota_handle *h) { int file_magics[] = INITQMAGICS; struct v2_disk_dqheader ddqheader; struct v2_disk_dqinfo ddqinfo; if (h->qh_fmt != QFMT_VFS_V1) return -1; /* Write basic quota header */ ddqheader.dqh_magic = cpu_to_le32(file_magics[h->qh_type]); ddqheader.dqh_version = cpu_to_le32(V2_VERSION); if (h->write(&h->qh_qf, 0, &ddqheader, sizeof(ddqheader)) != sizeof(ddqheader)) return -1; /* Write information about quotafile */ h->qh_info.dqi_bgrace = MAX_DQ_TIME; h->qh_info.dqi_igrace = MAX_IQ_TIME; h->qh_info.u.v2_mdqi.dqi_flags = 0; h->qh_info.u.v2_mdqi.dqi_qtree.dqi_blocks = QT_TREEOFF + 1; h->qh_info.u.v2_mdqi.dqi_qtree.dqi_free_blk = 0; h->qh_info.u.v2_mdqi.dqi_qtree.dqi_free_entry = 0; h->qh_info.u.v2_mdqi.dqi_qtree.dqi_entry_size = sizeof(struct v2r1_disk_dqblk); h->qh_info.u.v2_mdqi.dqi_qtree.dqi_ops = &v2r1_fmt_ops; v2_mem2diskdqinfo(&ddqinfo, &h->qh_info); if (h->write(&h->qh_qf, V2_DQINFOOFF, &ddqinfo, sizeof(ddqinfo)) != sizeof(ddqinfo)) return -1; return 0; } /* * Write information (grace times to file) */ static int v2_write_info(struct quota_handle *h) { struct v2_disk_dqinfo ddqinfo; v2_mem2diskdqinfo(&ddqinfo, &h->qh_info); if (h->write(&h->qh_qf, V2_DQINFOOFF, &ddqinfo, sizeof(ddqinfo)) != sizeof(ddqinfo)) return -1; return 0; } /* * Read dquot from disk */ static struct dquot *v2_read_dquot(struct quota_handle *h, qid_t id) { return qtree_read_dquot(h, id); } /* * Commit changes of dquot to disk - it might also mean deleting it when quota * became fake one and user has no blocks. * User can process use 'errno' to detect errstr. */ static int v2_commit_dquot(struct dquot *dquot) { struct util_dqblk *b = &dquot->dq_dqb; if (!b->dqb_curspace && !b->dqb_curinodes && !b->dqb_bsoftlimit && !b->dqb_isoftlimit && !b->dqb_bhardlimit && !b->dqb_ihardlimit) { qtree_delete_dquot(dquot); } else { qtree_write_dquot(dquot); } return 0; } static int v2_scan_dquots(struct quota_handle *h, int (*process_dquot) (struct dquot *, void *), void *data) { return qtree_scan_dquots(h, process_dquot, data); } /* Report information about quotafile. * TODO: Not used right now, but we should be able to use this when we add * support to debugfs to read quota files. */ static int v2_report(struct quota_handle *UNUSED(h), int UNUSED(verbose)) { log_err("Not Implemented."); return -1; } f2fs-tools-1.10.0/fsck/quotaio_v2.h000066400000000000000000000033431323417143600167650ustar00rootroot00000000000000/* * * Header file for disk format of new quotafile format * */ #ifndef GUARD_QUOTAIO_V2_H #define GUARD_QUOTAIO_V2_H #include #include "quotaio.h" /* Offset of info header in file */ #define V2_DQINFOOFF sizeof(struct v2_disk_dqheader) /* Supported version of quota-tree format */ #define V2_VERSION 1 struct v2_disk_dqheader { __le32 dqh_magic; /* Magic number identifying file */ __le32 dqh_version; /* File version */ } __attribute__ ((packed)); /* Flags for version specific files */ #define V2_DQF_MASK 0x0000 /* Mask for all valid ondisk flags */ /* Header with type and version specific information */ struct v2_disk_dqinfo { __le32 dqi_bgrace; /* Time before block soft limit becomes * hard limit */ __le32 dqi_igrace; /* Time before inode soft limit becomes * hard limit */ __le32 dqi_flags; /* Flags for quotafile (DQF_*) */ __le32 dqi_blocks; /* Number of blocks in file */ __le32 dqi_free_blk; /* Number of first free block in the list */ __le32 dqi_free_entry; /* Number of block with at least one * free entry */ } __attribute__ ((packed)); struct v2r1_disk_dqblk { __le32 dqb_id; /* id this quota applies to */ __le32 dqb_pad; __le64 dqb_ihardlimit; /* absolute limit on allocated inodes */ __le64 dqb_isoftlimit; /* preferred inode limit */ __le64 dqb_curinodes; /* current # allocated inodes */ __le64 dqb_bhardlimit; /* absolute limit on disk space * (in QUOTABLOCK_SIZE) */ __le64 dqb_bsoftlimit; /* preferred limit on disk space * (in QUOTABLOCK_SIZE) */ __le64 dqb_curspace; /* current space occupied (in bytes) */ __le64 dqb_btime; /* time limit for excessive disk use */ __le64 dqb_itime; /* time limit for excessive inode use */ } __attribute__ ((packed)); #endif f2fs-tools-1.10.0/fsck/resize.c000066400000000000000000000431631323417143600161750ustar00rootroot00000000000000/** * resize.c * * Copyright (c) 2015 Jaegeuk Kim * * 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. */ #include "fsck.h" static int get_new_sb(struct f2fs_super_block *sb) { u_int32_t zone_size_bytes, zone_align_start_offset; u_int32_t blocks_for_sit, blocks_for_nat, blocks_for_ssa; u_int32_t sit_segments, diff, total_meta_segments; u_int32_t total_valid_blks_available; u_int32_t sit_bitmap_size, max_sit_bitmap_size; u_int32_t max_nat_bitmap_size, max_nat_segments; u_int32_t segment_size_bytes = 1 << (get_sb(log_blocksize) + get_sb(log_blocks_per_seg)); u_int32_t blks_per_seg = 1 << get_sb(log_blocks_per_seg); u_int32_t segs_per_zone = get_sb(segs_per_sec) * get_sb(secs_per_zone); set_sb(block_count, c.target_sectors >> get_sb(log_sectors_per_block)); zone_size_bytes = segment_size_bytes * segs_per_zone; zone_align_start_offset = (c.start_sector * c.sector_size + 2 * F2FS_BLKSIZE + zone_size_bytes - 1) / zone_size_bytes * zone_size_bytes - c.start_sector * c.sector_size; set_sb(segment_count, (c.target_sectors * c.sector_size - zone_align_start_offset) / segment_size_bytes / c.segs_per_sec * c.segs_per_sec); blocks_for_sit = SIZE_ALIGN(get_sb(segment_count), SIT_ENTRY_PER_BLOCK); sit_segments = SEG_ALIGN(blocks_for_sit); set_sb(segment_count_sit, sit_segments * 2); set_sb(nat_blkaddr, get_sb(sit_blkaddr) + get_sb(segment_count_sit) * blks_per_seg); total_valid_blks_available = (get_sb(segment_count) - (get_sb(segment_count_ckpt) + get_sb(segment_count_sit))) * blks_per_seg; blocks_for_nat = SIZE_ALIGN(total_valid_blks_available, NAT_ENTRY_PER_BLOCK); set_sb(segment_count_nat, SEG_ALIGN(blocks_for_nat)); sit_bitmap_size = ((get_sb(segment_count_sit) / 2) << get_sb(log_blocks_per_seg)) / 8; if (sit_bitmap_size > MAX_SIT_BITMAP_SIZE) max_sit_bitmap_size = MAX_SIT_BITMAP_SIZE; else max_sit_bitmap_size = sit_bitmap_size; /* * It should be reserved minimum 1 segment for nat. * When sit is too large, we should expand cp area. It requires more pages for cp. */ if (max_sit_bitmap_size > MAX_SIT_BITMAP_SIZE_IN_CKPT) { max_nat_bitmap_size = CHECKSUM_OFFSET - sizeof(struct f2fs_checkpoint) + 1; set_sb(cp_payload, F2FS_BLK_ALIGN(max_sit_bitmap_size)); } else { max_nat_bitmap_size = CHECKSUM_OFFSET - sizeof(struct f2fs_checkpoint) + 1 - max_sit_bitmap_size; set_sb(cp_payload, 0); } max_nat_segments = (max_nat_bitmap_size * 8) >> get_sb(log_blocks_per_seg); if (get_sb(segment_count_nat) > max_nat_segments) set_sb(segment_count_nat, max_nat_segments); set_sb(segment_count_nat, get_sb(segment_count_nat) * 2); set_sb(ssa_blkaddr, get_sb(nat_blkaddr) + get_sb(segment_count_nat) * blks_per_seg); total_valid_blks_available = (get_sb(segment_count) - (get_sb(segment_count_ckpt) + get_sb(segment_count_sit) + get_sb(segment_count_nat))) * blks_per_seg; blocks_for_ssa = total_valid_blks_available / blks_per_seg + 1; set_sb(segment_count_ssa, SEG_ALIGN(blocks_for_ssa)); total_meta_segments = get_sb(segment_count_ckpt) + get_sb(segment_count_sit) + get_sb(segment_count_nat) + get_sb(segment_count_ssa); diff = total_meta_segments % segs_per_zone; if (diff) set_sb(segment_count_ssa, get_sb(segment_count_ssa) + (segs_per_zone - diff)); set_sb(main_blkaddr, get_sb(ssa_blkaddr) + get_sb(segment_count_ssa) * blks_per_seg); set_sb(segment_count_main, get_sb(segment_count) - (get_sb(segment_count_ckpt) + get_sb(segment_count_sit) + get_sb(segment_count_nat) + get_sb(segment_count_ssa))); set_sb(section_count, get_sb(segment_count_main) / get_sb(segs_per_sec)); set_sb(segment_count_main, get_sb(section_count) * get_sb(segs_per_sec)); /* Let's determine the best reserved and overprovisioned space */ c.new_overprovision = get_best_overprovision(sb); c.new_reserved_segments = (2 * (100 / c.new_overprovision + 1) + 6) * get_sb(segs_per_sec); if ((get_sb(segment_count_main) - 2) < c.new_reserved_segments || get_sb(segment_count_main) * blks_per_seg > get_sb(block_count)) { MSG(0, "\tError: Device size is not sufficient for F2FS volume, " "more segment needed =%u", c.new_reserved_segments - (get_sb(segment_count_main) - 2)); return -1; } return 0; } static void migrate_main(struct f2fs_sb_info *sbi, unsigned int offset) { void *raw = calloc(BLOCK_SZ, 1); struct seg_entry *se; block_t from, to; int i, j, ret; struct f2fs_summary sum; ASSERT(raw != NULL); for (i = TOTAL_SEGS(sbi) - 1; i >= 0; i--) { se = get_seg_entry(sbi, i); if (!se->valid_blocks) continue; for (j = sbi->blocks_per_seg - 1; j >= 0; j--) { if (!f2fs_test_bit(j, (const char *)se->cur_valid_map)) continue; from = START_BLOCK(sbi, i) + j; ret = dev_read_block(raw, from); ASSERT(ret >= 0); to = from + offset; ret = dev_write_block(raw, to); ASSERT(ret >= 0); get_sum_entry(sbi, from, &sum); if (IS_DATASEG(se->type)) update_data_blkaddr(sbi, le32_to_cpu(sum.nid), le16_to_cpu(sum.ofs_in_node), to); else update_nat_blkaddr(sbi, 0, le32_to_cpu(sum.nid), to); } } free(raw); DBG(0, "Info: Done to migrate Main area: main_blkaddr = 0x%x -> 0x%x\n", START_BLOCK(sbi, 0), START_BLOCK(sbi, 0) + offset); } static void move_ssa(struct f2fs_sb_info *sbi, unsigned int segno, block_t new_sum_blk_addr) { struct f2fs_summary_block *sum_blk; int type; sum_blk = get_sum_block(sbi, segno, &type); if (type < SEG_TYPE_MAX) { int ret; ret = dev_write_block(sum_blk, new_sum_blk_addr); ASSERT(ret >= 0); DBG(1, "Write summary block: (%d) segno=%x/%x --> (%d) %x\n", type, segno, GET_SUM_BLKADDR(sbi, segno), IS_SUM_NODE_SEG(sum_blk->footer), new_sum_blk_addr); } if (type == SEG_TYPE_NODE || type == SEG_TYPE_DATA || type == SEG_TYPE_MAX) { free(sum_blk); } DBG(1, "Info: Done to migrate SSA blocks\n"); } static void migrate_ssa(struct f2fs_sb_info *sbi, struct f2fs_super_block *new_sb, unsigned int offset) { struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi); block_t old_sum_blkaddr = get_sb(ssa_blkaddr); block_t new_sum_blkaddr = get_newsb(ssa_blkaddr); block_t end_sum_blkaddr = get_newsb(main_blkaddr); block_t expand_sum_blkaddr = new_sum_blkaddr + TOTAL_SEGS(sbi) - offset; block_t blkaddr; int ret; void *zero_block = calloc(BLOCK_SZ, 1); ASSERT(zero_block); if (offset && new_sum_blkaddr < old_sum_blkaddr + offset) { blkaddr = new_sum_blkaddr; while (blkaddr < end_sum_blkaddr) { if (blkaddr < expand_sum_blkaddr) { move_ssa(sbi, offset++, blkaddr++); } else { ret = dev_write_block(zero_block, blkaddr++); ASSERT(ret >=0); } } } else { blkaddr = end_sum_blkaddr - 1; offset = TOTAL_SEGS(sbi) - 1; while (blkaddr >= new_sum_blkaddr) { if (blkaddr >= expand_sum_blkaddr) { ret = dev_write_block(zero_block, blkaddr--); ASSERT(ret >=0); } else { move_ssa(sbi, offset--, blkaddr--); } } } DBG(0, "Info: Done to migrate SSA blocks: sum_blkaddr = 0x%x -> 0x%x\n", old_sum_blkaddr, new_sum_blkaddr); free(zero_block); } static int shrink_nats(struct f2fs_sb_info *sbi, struct f2fs_super_block *new_sb) { struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi); struct f2fs_nm_info *nm_i = NM_I(sbi); block_t old_nat_blkaddr = get_sb(nat_blkaddr); unsigned int nat_blocks; void *nat_block, *zero_block; int nid, ret, new_max_nid; pgoff_t block_off; pgoff_t block_addr; int seg_off; nat_block = malloc(BLOCK_SZ); ASSERT(nat_block); zero_block = calloc(BLOCK_SZ, 1); ASSERT(zero_block); nat_blocks = get_newsb(segment_count_nat) >> 1; nat_blocks = nat_blocks << get_sb(log_blocks_per_seg); new_max_nid = NAT_ENTRY_PER_BLOCK * nat_blocks; for (nid = nm_i->max_nid - 1; nid > new_max_nid; nid -= NAT_ENTRY_PER_BLOCK) { block_off = nid / NAT_ENTRY_PER_BLOCK; seg_off = block_off >> sbi->log_blocks_per_seg; block_addr = (pgoff_t)(old_nat_blkaddr + (seg_off << sbi->log_blocks_per_seg << 1) + (block_off & ((1 << sbi->log_blocks_per_seg) - 1))); if (f2fs_test_bit(block_off, nm_i->nat_bitmap)) block_addr += sbi->blocks_per_seg; ret = dev_read_block(nat_block, block_addr); ASSERT(ret >= 0); if (memcmp(zero_block, nat_block, BLOCK_SZ)) { ret = -1; goto not_avail; } } ret = 0; nm_i->max_nid = new_max_nid; not_avail: free(nat_block); free(zero_block); return ret; } static void migrate_nat(struct f2fs_sb_info *sbi, struct f2fs_super_block *new_sb) { struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi); struct f2fs_nm_info *nm_i = NM_I(sbi); block_t old_nat_blkaddr = get_sb(nat_blkaddr); block_t new_nat_blkaddr = get_newsb(nat_blkaddr); unsigned int nat_blocks; void *nat_block; int nid, ret, new_max_nid; pgoff_t block_off; pgoff_t block_addr; int seg_off; nat_block = malloc(BLOCK_SZ); ASSERT(nat_block); for (nid = nm_i->max_nid - 1; nid >= 0; nid -= NAT_ENTRY_PER_BLOCK) { block_off = nid / NAT_ENTRY_PER_BLOCK; seg_off = block_off >> sbi->log_blocks_per_seg; block_addr = (pgoff_t)(old_nat_blkaddr + (seg_off << sbi->log_blocks_per_seg << 1) + (block_off & ((1 << sbi->log_blocks_per_seg) - 1))); if (f2fs_test_bit(block_off, nm_i->nat_bitmap)) block_addr += sbi->blocks_per_seg; ret = dev_read_block(nat_block, block_addr); ASSERT(ret >= 0); block_addr = (pgoff_t)(new_nat_blkaddr + (seg_off << sbi->log_blocks_per_seg << 1) + (block_off & ((1 << sbi->log_blocks_per_seg) - 1))); /* new bitmap should be zeros */ ret = dev_write_block(nat_block, block_addr); ASSERT(ret >= 0); } /* zero out newly assigned nids */ memset(nat_block, 0, BLOCK_SZ); nat_blocks = get_newsb(segment_count_nat) >> 1; nat_blocks = nat_blocks << get_sb(log_blocks_per_seg); new_max_nid = NAT_ENTRY_PER_BLOCK * nat_blocks; DBG(1, "Write NAT block: %x->%x, max_nid=%x->%x\n", old_nat_blkaddr, new_nat_blkaddr, get_sb(segment_count_nat), get_newsb(segment_count_nat)); for (nid = nm_i->max_nid; nid < new_max_nid; nid += NAT_ENTRY_PER_BLOCK) { block_off = nid / NAT_ENTRY_PER_BLOCK; seg_off = block_off >> sbi->log_blocks_per_seg; block_addr = (pgoff_t)(new_nat_blkaddr + (seg_off << sbi->log_blocks_per_seg << 1) + (block_off & ((1 << sbi->log_blocks_per_seg) - 1))); ret = dev_write_block(nat_block, block_addr); ASSERT(ret >= 0); DBG(3, "Write NAT: %lx\n", block_addr); } DBG(0, "Info: Done to migrate NAT blocks: nat_blkaddr = 0x%x -> 0x%x\n", old_nat_blkaddr, new_nat_blkaddr); } static void migrate_sit(struct f2fs_sb_info *sbi, struct f2fs_super_block *new_sb, unsigned int offset) { struct sit_info *sit_i = SIT_I(sbi); unsigned int ofs = 0, pre_ofs = 0; unsigned int segno, index; struct f2fs_sit_block *sit_blk = calloc(BLOCK_SZ, 1); block_t sit_blks = get_newsb(segment_count_sit) << (sbi->log_blocks_per_seg - 1); struct seg_entry *se; block_t blk_addr = 0; int ret; ASSERT(sit_blk); /* initialize with zeros */ for (index = 0; index < sit_blks; index++) { ret = dev_write_block(sit_blk, get_newsb(sit_blkaddr) + index); ASSERT(ret >= 0); DBG(3, "Write zero sit: %x\n", get_newsb(sit_blkaddr) + index); } for (segno = 0; segno < TOTAL_SEGS(sbi); segno++) { struct f2fs_sit_entry *sit; se = get_seg_entry(sbi, segno); if (segno < offset) { ASSERT(se->valid_blocks == 0); continue; } ofs = SIT_BLOCK_OFFSET(sit_i, segno - offset); if (ofs != pre_ofs) { blk_addr = get_newsb(sit_blkaddr) + pre_ofs; ret = dev_write_block(sit_blk, blk_addr); ASSERT(ret >= 0); DBG(1, "Write valid sit: %x\n", blk_addr); pre_ofs = ofs; memset(sit_blk, 0, BLOCK_SZ); } sit = &sit_blk->entries[SIT_ENTRY_OFFSET(sit_i, segno - offset)]; memcpy(sit->valid_map, se->cur_valid_map, SIT_VBLOCK_MAP_SIZE); sit->vblocks = cpu_to_le16((se->type << SIT_VBLOCKS_SHIFT) | se->valid_blocks); } blk_addr = get_newsb(sit_blkaddr) + ofs; ret = dev_write_block(sit_blk, blk_addr); DBG(1, "Write valid sit: %x\n", blk_addr); ASSERT(ret >= 0); free(sit_blk); DBG(0, "Info: Done to restore new SIT blocks: 0x%x\n", get_newsb(sit_blkaddr)); } static void rebuild_checkpoint(struct f2fs_sb_info *sbi, struct f2fs_super_block *new_sb, unsigned int offset) { struct f2fs_checkpoint *cp = F2FS_CKPT(sbi); unsigned long long cp_ver = get_cp(checkpoint_ver); struct f2fs_checkpoint *new_cp; struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi); unsigned int free_segment_count, new_segment_count; block_t new_cp_blks = 1 + get_newsb(cp_payload); block_t orphan_blks = 0; block_t new_cp_blk_no, old_cp_blk_no; u_int32_t crc = 0; u32 flags; void *buf; int i, ret; new_cp = calloc(new_cp_blks * BLOCK_SZ, 1); ASSERT(new_cp); buf = malloc(BLOCK_SZ); ASSERT(buf); /* ovp / free segments */ set_cp(rsvd_segment_count, c.new_reserved_segments); set_cp(overprov_segment_count, (get_newsb(segment_count_main) - get_cp(rsvd_segment_count)) * c.new_overprovision / 100); set_cp(overprov_segment_count, get_cp(overprov_segment_count) + get_cp(rsvd_segment_count)); free_segment_count = get_free_segments(sbi); new_segment_count = get_newsb(segment_count_main) - get_sb(segment_count_main); set_cp(free_segment_count, free_segment_count + new_segment_count); set_cp(user_block_count, ((get_newsb(segment_count_main) - get_cp(overprov_segment_count)) * c.blks_per_seg)); if (is_set_ckpt_flags(cp, CP_ORPHAN_PRESENT_FLAG)) orphan_blks = __start_sum_addr(sbi) - 1; set_cp(cp_pack_start_sum, 1 + get_newsb(cp_payload)); set_cp(cp_pack_total_block_count, 8 + orphan_blks + get_newsb(cp_payload)); /* cur->segno - offset */ for (i = 0; i < NO_CHECK_TYPE; i++) { if (i < CURSEG_HOT_NODE) { set_cp(cur_data_segno[i], CURSEG_I(sbi, i)->segno - offset); } else { int n = i - CURSEG_HOT_NODE; set_cp(cur_node_segno[n], CURSEG_I(sbi, i)->segno - offset); } } /* sit / nat ver bitmap bytesize */ set_cp(sit_ver_bitmap_bytesize, ((get_newsb(segment_count_sit) / 2) << get_newsb(log_blocks_per_seg)) / 8); set_cp(nat_ver_bitmap_bytesize, ((get_newsb(segment_count_nat) / 2) << get_newsb(log_blocks_per_seg)) / 8); /* update nat_bits flag */ flags = update_nat_bits_flags(new_sb, cp, get_cp(ckpt_flags)); set_cp(ckpt_flags, flags); memcpy(new_cp, cp, (unsigned char *)cp->sit_nat_version_bitmap - (unsigned char *)cp); new_cp->checkpoint_ver = cpu_to_le64(cp_ver + 1); crc = f2fs_cal_crc32(F2FS_SUPER_MAGIC, new_cp, CHECKSUM_OFFSET); *((__le32 *)((unsigned char *)new_cp + CHECKSUM_OFFSET)) = cpu_to_le32(crc); /* Write a new checkpoint in the other set */ new_cp_blk_no = old_cp_blk_no = get_sb(cp_blkaddr); if (sbi->cur_cp == 2) old_cp_blk_no += 1 << get_sb(log_blocks_per_seg); else new_cp_blk_no += 1 << get_sb(log_blocks_per_seg); /* write first cp */ ret = dev_write_block(new_cp, new_cp_blk_no++); ASSERT(ret >= 0); memset(buf, 0, BLOCK_SZ); for (i = 0; i < get_newsb(cp_payload); i++) { ret = dev_write_block(buf, new_cp_blk_no++); ASSERT(ret >= 0); } for (i = 0; i < orphan_blks; i++) { block_t orphan_blk_no = old_cp_blk_no + 1 + get_sb(cp_payload); ret = dev_read_block(buf, orphan_blk_no++); ASSERT(ret >= 0); ret = dev_write_block(buf, new_cp_blk_no++); ASSERT(ret >= 0); } /* update summary blocks having nullified journal entries */ for (i = 0; i < NO_CHECK_TYPE; i++) { struct curseg_info *curseg = CURSEG_I(sbi, i); ret = dev_write_block(curseg->sum_blk, new_cp_blk_no++); ASSERT(ret >= 0); } /* write the last cp */ ret = dev_write_block(new_cp, new_cp_blk_no++); ASSERT(ret >= 0); /* Write nat bits */ if (flags & CP_NAT_BITS_FLAG) write_nat_bits(sbi, new_sb, new_cp, sbi->cur_cp == 1 ? 2 : 1); /* disable old checkpoint */ memset(buf, 0, BLOCK_SZ); ret = dev_write_block(buf, old_cp_blk_no); ASSERT(ret >= 0); free(buf); free(new_cp); DBG(0, "Info: Done to rebuild checkpoint blocks\n"); } static void rebuild_superblock(struct f2fs_super_block *new_sb) { int index, ret; u_int8_t *buf; buf = calloc(BLOCK_SZ, 1); memcpy(buf + F2FS_SUPER_OFFSET, new_sb, sizeof(*new_sb)); for (index = 0; index < 2; index++) { ret = dev_write_block(buf, index); ASSERT(ret >= 0); } free(buf); DBG(0, "Info: Done to rebuild superblock\n"); } int f2fs_resize(struct f2fs_sb_info *sbi) { struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi); struct f2fs_super_block new_sb_raw; struct f2fs_super_block *new_sb = &new_sb_raw; block_t end_blkaddr, old_main_blkaddr, new_main_blkaddr; unsigned int offset; unsigned int offset_seg = 0; int err = -1; /* flush NAT/SIT journal entries */ flush_journal_entries(sbi); memcpy(new_sb, F2FS_RAW_SUPER(sbi), sizeof(*new_sb)); if (get_new_sb(new_sb)) return -1; /* check nat availability */ if (get_sb(segment_count_nat) > get_newsb(segment_count_nat)) { err = shrink_nats(sbi, new_sb); if (err) { MSG(0, "\tError: Failed to shrink NATs\n"); return err; } } print_raw_sb_info(sb); print_raw_sb_info(new_sb); old_main_blkaddr = get_sb(main_blkaddr); new_main_blkaddr = get_newsb(main_blkaddr); offset = new_main_blkaddr - old_main_blkaddr; end_blkaddr = (get_sb(segment_count_main) << get_sb(log_blocks_per_seg)) + get_sb(main_blkaddr); err = -EAGAIN; if (new_main_blkaddr < end_blkaddr) { err = f2fs_defragment(sbi, old_main_blkaddr, offset, new_main_blkaddr, 0); if (!err) offset_seg = offset >> get_sb(log_blocks_per_seg); MSG(0, "Try to do defragement: %s\n", err ? "Skip": "Done"); } /* move whole data region */ if (err) migrate_main(sbi, offset); migrate_ssa(sbi, new_sb, offset_seg); migrate_nat(sbi, new_sb); migrate_sit(sbi, new_sb, offset_seg); rebuild_checkpoint(sbi, new_sb, offset_seg); rebuild_superblock(new_sb); return 0; } f2fs-tools-1.10.0/fsck/segment.c000066400000000000000000000206461323417143600163370ustar00rootroot00000000000000/** * segment.c * * Many parts of codes are copied from Linux kernel/fs/f2fs. * * Copyright (C) 2015 Huawei Ltd. * Witten by: * Hou Pengyang * Liu Shuoran * Jaegeuk Kim * * 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. */ #include "fsck.h" #include "node.h" static void write_inode(u64 blkaddr, struct f2fs_node *inode) { if (c.feature & cpu_to_le32(F2FS_FEATURE_INODE_CHKSUM)) inode->i.i_inode_checksum = cpu_to_le32(f2fs_inode_chksum(inode)); ASSERT(dev_write_block(inode, blkaddr) >= 0); } void reserve_new_block(struct f2fs_sb_info *sbi, block_t *to, struct f2fs_summary *sum, int type) { struct f2fs_fsck *fsck = F2FS_FSCK(sbi); struct seg_entry *se; u64 blkaddr, offset; u64 old_blkaddr = *to; blkaddr = SM_I(sbi)->main_blkaddr; if (find_next_free_block(sbi, &blkaddr, 0, type)) { ERR_MSG("Not enough space to allocate blocks"); ASSERT(0); } se = get_seg_entry(sbi, GET_SEGNO(sbi, blkaddr)); offset = OFFSET_IN_SEG(sbi, blkaddr); se->type = type; se->valid_blocks++; f2fs_set_bit(offset, (char *)se->cur_valid_map); if (c.func == FSCK) { f2fs_set_main_bitmap(sbi, blkaddr, type); f2fs_set_sit_bitmap(sbi, blkaddr); } if (old_blkaddr == NULL_ADDR) { sbi->total_valid_block_count++; if (c.func == FSCK) fsck->chk.valid_blk_cnt++; } se->dirty = 1; /* read/write SSA */ *to = (block_t)blkaddr; update_sum_entry(sbi, *to, sum); } void new_data_block(struct f2fs_sb_info *sbi, void *block, struct dnode_of_data *dn, int type) { struct f2fs_summary sum; struct node_info ni; unsigned int blkaddr = datablock_addr(dn->node_blk, dn->ofs_in_node); ASSERT(dn->node_blk); memset(block, 0, BLOCK_SZ); get_node_info(sbi, dn->nid, &ni); set_summary(&sum, dn->nid, dn->ofs_in_node, ni.version); reserve_new_block(sbi, &dn->data_blkaddr, &sum, type); if (blkaddr == NULL_ADDR) inc_inode_blocks(dn); else if (blkaddr == NEW_ADDR) dn->idirty = 1; set_data_blkaddr(dn); } u64 f2fs_read(struct f2fs_sb_info *sbi, nid_t ino, u8 *buffer, u64 count, pgoff_t offset) { struct dnode_of_data dn; struct node_info ni; struct f2fs_node *inode; char *blk_buffer; u64 filesize; u64 off_in_blk; u64 len_in_blk; u64 read_count; u64 remained_blkentries; block_t blkaddr; void *index_node = NULL; memset(&dn, 0, sizeof(dn)); /* Memory allocation for block buffer and inode. */ blk_buffer = calloc(BLOCK_SZ, 2); ASSERT(blk_buffer); inode = (struct f2fs_node*)(blk_buffer + BLOCK_SZ); /* Read inode */ get_node_info(sbi, ino, &ni); ASSERT(dev_read_block(inode, ni.blk_addr) >= 0); ASSERT(!S_ISDIR(le16_to_cpu(inode->i.i_mode))); ASSERT(!S_ISLNK(le16_to_cpu(inode->i.i_mode))); /* Adjust count with file length. */ filesize = le64_to_cpu(inode->i.i_size); if (offset > filesize) count = 0; else if (count + offset > filesize) count = filesize - offset; /* Main loop for file blocks */ read_count = remained_blkentries = 0; while (count > 0) { if (remained_blkentries == 0) { set_new_dnode(&dn, inode, NULL, ino); get_dnode_of_data(sbi, &dn, F2FS_BYTES_TO_BLK(offset), LOOKUP_NODE); if (index_node) free(index_node); index_node = (dn.node_blk == dn.inode_blk) ? NULL : dn.node_blk; remained_blkentries = ADDRS_PER_PAGE(dn.node_blk); } ASSERT(remained_blkentries > 0); blkaddr = datablock_addr(dn.node_blk, dn.ofs_in_node); if (blkaddr == NULL_ADDR || blkaddr == NEW_ADDR) break; off_in_blk = offset % BLOCK_SZ; len_in_blk = BLOCK_SZ - off_in_blk; if (len_in_blk > count) len_in_blk = count; /* Read data from single block. */ if (len_in_blk < BLOCK_SZ) { ASSERT(dev_read_block(blk_buffer, blkaddr) >= 0); memcpy(buffer, blk_buffer + off_in_blk, len_in_blk); } else { /* Direct read */ ASSERT(dev_read_block(buffer, blkaddr) >= 0); } offset += len_in_blk; count -= len_in_blk; buffer += len_in_blk; read_count += len_in_blk; dn.ofs_in_node++; remained_blkentries--; } if (index_node) free(index_node); free(blk_buffer); return read_count; } u64 f2fs_write(struct f2fs_sb_info *sbi, nid_t ino, u8 *buffer, u64 count, pgoff_t offset) { struct dnode_of_data dn; struct node_info ni; struct f2fs_node *inode; char *blk_buffer; u64 off_in_blk; u64 len_in_blk; u64 written_count; u64 remained_blkentries; block_t blkaddr; void* index_node = NULL; int idirty = 0; /* Memory allocation for block buffer and inode. */ blk_buffer = calloc(BLOCK_SZ, 2); ASSERT(blk_buffer); inode = (struct f2fs_node*)(blk_buffer + BLOCK_SZ); /* Read inode */ get_node_info(sbi, ino, &ni); ASSERT(dev_read_block(inode, ni.blk_addr) >= 0); ASSERT(!S_ISDIR(le16_to_cpu(inode->i.i_mode))); ASSERT(!S_ISLNK(le16_to_cpu(inode->i.i_mode))); /* Main loop for file blocks */ written_count = remained_blkentries = 0; while (count > 0) { if (remained_blkentries == 0) { set_new_dnode(&dn, inode, NULL, ino); get_dnode_of_data(sbi, &dn, F2FS_BYTES_TO_BLK(offset), ALLOC_NODE); idirty |= dn.idirty; if (index_node) free(index_node); index_node = (dn.node_blk == dn.inode_blk) ? NULL : dn.node_blk; remained_blkentries = ADDRS_PER_PAGE(dn.node_blk); } ASSERT(remained_blkentries > 0); blkaddr = datablock_addr(dn.node_blk, dn.ofs_in_node); if (blkaddr == NULL_ADDR || blkaddr == NEW_ADDR) { new_data_block(sbi, blk_buffer, &dn, CURSEG_WARM_DATA); blkaddr = dn.data_blkaddr; } off_in_blk = offset % BLOCK_SZ; len_in_blk = BLOCK_SZ - off_in_blk; if (len_in_blk > count) len_in_blk = count; /* Write data to single block. */ if (len_in_blk < BLOCK_SZ) { ASSERT(dev_read_block(blk_buffer, blkaddr) >= 0); memcpy(blk_buffer + off_in_blk, buffer, len_in_blk); ASSERT(dev_write_block(blk_buffer, blkaddr) >= 0); } else { /* Direct write */ ASSERT(dev_write_block(buffer, blkaddr) >= 0); } offset += len_in_blk; count -= len_in_blk; buffer += len_in_blk; written_count += len_in_blk; dn.ofs_in_node++; if ((--remained_blkentries == 0 || count == 0) && (dn.ndirty)) ASSERT(dev_write_block(dn.node_blk, dn.node_blkaddr) >= 0); } if (offset > le64_to_cpu(inode->i.i_size)) { inode->i.i_size = cpu_to_le64(offset); idirty = 1; } if (idirty) { ASSERT(inode == dn.inode_blk); write_inode(ni.blk_addr, inode); } if (index_node) free(index_node); free(blk_buffer); return written_count; } /* This function updates only inode->i.i_size */ void f2fs_filesize_update(struct f2fs_sb_info *sbi, nid_t ino, u64 filesize) { struct node_info ni; struct f2fs_node *inode; inode = calloc(BLOCK_SZ, 1); ASSERT(inode); get_node_info(sbi, ino, &ni); ASSERT(dev_read_block(inode, ni.blk_addr) >= 0); ASSERT(!S_ISDIR(le16_to_cpu(inode->i.i_mode))); ASSERT(!S_ISLNK(le16_to_cpu(inode->i.i_mode))); inode->i.i_size = cpu_to_le64(filesize); write_inode(ni.blk_addr, inode); free(inode); } int f2fs_build_file(struct f2fs_sb_info *sbi, struct dentry *de) { int fd, n; pgoff_t off = 0; u8 buffer[BLOCK_SZ]; if (de->ino == 0) return -1; fd = open(de->full_path, O_RDONLY); if (fd < 0) { MSG(0, "Skip: Fail to open %s\n", de->full_path); return -1; } /* inline_data support */ if (de->size <= DEF_MAX_INLINE_DATA) { struct node_info ni; struct f2fs_node *node_blk; int ret; get_node_info(sbi, de->ino, &ni); node_blk = calloc(BLOCK_SZ, 1); ASSERT(node_blk); ret = dev_read_block(node_blk, ni.blk_addr); ASSERT(ret >= 0); node_blk->i.i_inline |= F2FS_INLINE_DATA; node_blk->i.i_inline |= F2FS_DATA_EXIST; if (c.feature & cpu_to_le32(F2FS_FEATURE_EXTRA_ATTR)) { node_blk->i.i_inline |= F2FS_EXTRA_ATTR; node_blk->i.i_extra_isize = cpu_to_le16(F2FS_TOTAL_EXTRA_ATTR_SIZE); } n = read(fd, buffer, BLOCK_SZ); ASSERT((unsigned long)n == de->size); memcpy(inline_data_addr(node_blk), buffer, de->size); node_blk->i.i_size = cpu_to_le64(de->size); write_inode(ni.blk_addr, node_blk); free(node_blk); } else { while ((n = read(fd, buffer, BLOCK_SZ)) > 0) { f2fs_write(sbi, de->ino, buffer, n, off); off += n; } } close(fd); if (n < 0) return -1; update_free_segments(sbi); MSG(1, "Info: Create %s -> %s\n" " -- ino=%x, type=%x, mode=%x, uid=%x, " "gid=%x, cap=%"PRIx64", size=%lu, pino=%x\n", de->full_path, de->path, de->ino, de->file_type, de->mode, de->uid, de->gid, de->capabilities, de->size, de->pino); return 0; } f2fs-tools-1.10.0/fsck/sload.c000066400000000000000000000173431323417143600157770ustar00rootroot00000000000000/** * sload.c * * Copyright (C) 2015 Huawei Ltd. * Witten by: * Hou Pengyang * Liu Shuoran * Jaegeuk Kim * * 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. */ #define _GNU_SOURCE #include "fsck.h" #include #include #ifdef HAVE_MNTENT_H #include #endif #ifdef HAVE_LIBSELINUX static struct selabel_handle *sehnd = NULL; #endif typedef void (*fs_config_f)(const char *path, int dir, const char *target_out_path, unsigned *uid, unsigned *gid, unsigned *mode, uint64_t *capabilities); static fs_config_f fs_config_func = NULL; #ifdef WITH_ANDROID #include #include #include #endif static int filter_dot(const struct dirent *d) { return (strcmp(d->d_name, "..") && strcmp(d->d_name, ".")); } static void f2fs_make_directory(struct f2fs_sb_info *sbi, int entries, struct dentry *de) { int i = 0; for (i = 0; i < entries; i++) { if (de[i].file_type == F2FS_FT_DIR) f2fs_mkdir(sbi, de + i); else if (de[i].file_type == F2FS_FT_REG_FILE) f2fs_create(sbi, de + i); else if (de[i].file_type == F2FS_FT_SYMLINK) f2fs_symlink(sbi, de + i); } } #ifdef HAVE_LIBSELINUX static int set_selinux_xattr(struct f2fs_sb_info *sbi, const char *path, nid_t ino, int mode) { char *secontext = NULL; char *mnt_path = NULL; if (!sehnd) return 0; if (asprintf(&mnt_path, "%s%s", c.mount_point, path) <= 0) { ERR_MSG("cannot allocate security path for %s%s\n", c.mount_point, path); return -ENOMEM; } /* set root inode selinux context */ if (selabel_lookup(sehnd, &secontext, mnt_path, mode) < 0) { ERR_MSG("cannot lookup security context for %s\n", mnt_path); free(mnt_path); return -EINVAL; } if (secontext) { MSG(2, "%s (%d) -> SELinux context = %s\n", mnt_path, ino, secontext); inode_set_selinux(sbi, ino, secontext); } freecon(secontext); free(mnt_path); return 0; } #else #define set_selinux_xattr(...) 0 #endif static int set_perms_and_caps(struct dentry *de) { uint64_t capabilities = 0; unsigned int uid = 0, gid = 0, imode = 0; char *mnt_path = NULL; if (asprintf(&mnt_path, "%s%s", c.mount_point, de->path) <= 0) { ERR_MSG("cannot allocate mount path for %s%s\n", c.mount_point, de->path); return -ENOMEM; } /* Permissions */ if (fs_config_func != NULL) { fs_config_func(mnt_path, S_ISDIR(de->mode), c.target_out_dir, &uid, &gid, &imode, &capabilities); de->uid = uid & 0xffff; de->gid = gid & 0xffff; de->mode = (de->mode & S_IFMT) | (imode & 0xffff); de->capabilities = capabilities; } MSG(2, "%s -> mode = 0x%x, uid = 0x%x, gid = 0x%x, " "capabilities = 0x%"PRIx64"\n", mnt_path, de->mode, de->uid, de->gid, de->capabilities); free(mnt_path); return 0; } static void set_inode_metadata(struct dentry *de) { struct stat stat; int ret; ret = lstat(de->full_path, &stat); if (ret < 0) { ERR_MSG("lstat failure\n"); ASSERT(0); } if (S_ISREG(stat.st_mode)) { de->file_type = F2FS_FT_REG_FILE; } else if (S_ISDIR(stat.st_mode)) { de->file_type = F2FS_FT_DIR; } else if (S_ISCHR(stat.st_mode)) { de->file_type = F2FS_FT_CHRDEV; } else if (S_ISBLK(stat.st_mode)) { de->file_type = F2FS_FT_BLKDEV; } else if (S_ISFIFO(stat.st_mode)) { de->file_type = F2FS_FT_FIFO; } else if (S_ISSOCK(stat.st_mode)) { de->file_type = F2FS_FT_SOCK; } else if (S_ISLNK(stat.st_mode)) { de->file_type = F2FS_FT_SYMLINK; de->link = calloc(F2FS_BLKSIZE, 1); ASSERT(de->link); ret = readlink(de->full_path, de->link, F2FS_BLKSIZE - 1); ASSERT(ret >= 0); } else { ERR_MSG("unknown file type on %s", de->path); ASSERT(0); } de->size = stat.st_size; de->mode = stat.st_mode & (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO); if (c.fixed_time == -1 && c.from_dir) de->mtime = stat.st_mtime; else de->mtime = c.fixed_time; set_perms_and_caps(de); } static int build_directory(struct f2fs_sb_info *sbi, const char *full_path, const char *dir_path, const char *target_out_dir, nid_t dir_ino) { int entries = 0; struct dentry *dentries; struct dirent **namelist = NULL; int i, ret = 0; entries = scandir(full_path, &namelist, filter_dot, (void *)alphasort); if (entries < 0) { ERR_MSG("No entries in %s\n", full_path); return -ENOENT; } dentries = calloc(entries, sizeof(struct dentry)); if (dentries == NULL) return -ENOMEM; for (i = 0; i < entries; i++) { dentries[i].name = (unsigned char *)strdup(namelist[i]->d_name); if (dentries[i].name == NULL) { ERR_MSG("Skip: ENOMEM\n"); continue; } dentries[i].len = strlen((char *)dentries[i].name); ret = asprintf(&dentries[i].path, "%s%s", dir_path, namelist[i]->d_name); ASSERT(ret > 0); ret = asprintf(&dentries[i].full_path, "%s/%s", full_path, namelist[i]->d_name); ASSERT(ret > 0); free(namelist[i]); set_inode_metadata(dentries + i); dentries[i].pino = dir_ino; } free(namelist); f2fs_make_directory(sbi, entries, dentries); for (i = 0; i < entries; i++) { if (dentries[i].file_type == F2FS_FT_REG_FILE) { f2fs_build_file(sbi, dentries + i); } else if (dentries[i].file_type == F2FS_FT_DIR) { char *subdir_full_path = NULL; char *subdir_dir_path = NULL; ret = asprintf(&subdir_full_path, "%s", dentries[i].full_path); ASSERT(ret > 0); ret = asprintf(&subdir_dir_path, "%s/", dentries[i].path); ASSERT(ret > 0); build_directory(sbi, subdir_full_path, subdir_dir_path, target_out_dir, dentries[i].ino); free(subdir_full_path); free(subdir_dir_path); } else if (dentries[i].file_type == F2FS_FT_SYMLINK) { /* * It is already done in f2fs_make_directory * f2fs_make_symlink(sbi, dir_ino, &dentries[i]); */ } else { MSG(1, "Error unknown file type\n"); } ret = set_selinux_xattr(sbi, dentries[i].path, dentries[i].ino, dentries[i].mode); if (ret) return ret; free(dentries[i].path); free(dentries[i].full_path); free((void *)dentries[i].name); } free(dentries); return 0; } static int configure_files(void) { #ifdef HAVE_LIBSELINUX if (!c.nr_opt) goto skip; #if !defined(__ANDROID__) sehnd = selabel_open(SELABEL_CTX_FILE, c.seopt_file, c.nr_opt); if (!sehnd) { ERR_MSG("Failed to open file contexts \"%s\"", c.seopt_file[0].value); return -EINVAL; } #else sehnd = selinux_android_file_context_handle(); if (!sehnd) { ERR_MSG("Failed to get android file_contexts\n", c.mount_point); return -EINVAL; } #endif skip: #endif #ifdef WITH_ANDROID /* Load the FS config */ if (c.fs_config_file) { int ret = load_canned_fs_config(c.fs_config_file); if (ret < 0) { ERR_MSG("Failed to load fs_config \"%s\"", c.fs_config_file); return ret; } fs_config_func = canned_fs_config; } else { fs_config_func = fs_config; } #endif return 0; } int f2fs_sload(struct f2fs_sb_info *sbi) { int ret = 0; ret = configure_files(); if (ret) { ERR_MSG("Failed to configure files\n"); return ret; } /* flush NAT/SIT journal entries */ flush_journal_entries(sbi); ret = build_directory(sbi, c.from_dir, "/", c.target_out_dir, F2FS_ROOT_INO(sbi)); if (ret) { ERR_MSG("Failed to build due to %d\n", ret); return ret; } ret = set_selinux_xattr(sbi, c.mount_point, F2FS_ROOT_INO(sbi), S_IFDIR); if (ret) { ERR_MSG("Failed to set selinux for root: %d\n", ret); return ret; } /* update curseg info; can update sit->types */ move_curseg_info(sbi, SM_I(sbi)->main_blkaddr); zero_journal_entries(sbi); write_curseg_info(sbi); /* flush dirty sit entries */ flush_sit_entries(sbi); write_checkpoint(sbi); return 0; } f2fs-tools-1.10.0/fsck/xattr.c000066400000000000000000000130521323417143600160300ustar00rootroot00000000000000/** * xattr.c * * Many parts of codes are copied from Linux kernel/fs/f2fs. * * Copyright (C) 2015 Huawei Ltd. * Witten by: * Hou Pengyang * Liu Shuoran * Jaegeuk Kim * * 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. */ #include "fsck.h" #include "node.h" #include "xattr.h" void *read_all_xattrs(struct f2fs_sb_info *sbi, struct f2fs_node *inode) { struct f2fs_xattr_header *header; void *txattr_addr; u64 inline_size = inline_xattr_size(&inode->i); txattr_addr = calloc(inline_size + BLOCK_SZ, 1); ASSERT(txattr_addr); if (inline_size) memcpy(txattr_addr, inline_xattr_addr(&inode->i), inline_size); /* Read from xattr node block. */ if (inode->i.i_xattr_nid) { struct node_info ni; int ret; get_node_info(sbi, le32_to_cpu(inode->i.i_xattr_nid), &ni); ret = dev_read_block(txattr_addr + inline_size, ni.blk_addr); ASSERT(ret >= 0); } header = XATTR_HDR(txattr_addr); /* Never been allocated xattrs */ if (le32_to_cpu(header->h_magic) != F2FS_XATTR_MAGIC) { header->h_magic = cpu_to_le32(F2FS_XATTR_MAGIC); header->h_refcount = cpu_to_le32(1); } return txattr_addr; } static struct f2fs_xattr_entry *__find_xattr(void *base_addr, int index, size_t len, const char *name) { struct f2fs_xattr_entry *entry; list_for_each_xattr(entry, base_addr) { if (entry->e_name_index != index) continue; if (entry->e_name_len != len) continue; if (!memcmp(entry->e_name, name, len)) break; } return entry; } static void write_all_xattrs(struct f2fs_sb_info *sbi, struct f2fs_node *inode, __u32 hsize, void *txattr_addr) { void *xattr_addr; struct dnode_of_data dn; struct node_info ni; struct f2fs_node *xattr_node; nid_t new_nid = 0; block_t blkaddr; nid_t xnid = le32_to_cpu(inode->i.i_xattr_nid); u64 inline_size = inline_xattr_size(&inode->i); int ret; memcpy(inline_xattr_addr(&inode->i), txattr_addr, inline_size); if (hsize <= inline_size) return; if (!xnid) { f2fs_alloc_nid(sbi, &new_nid, 0); set_new_dnode(&dn, inode, NULL, new_nid); /* NAT entry would be updated by new_node_page. */ blkaddr = new_node_block(sbi, &dn, XATTR_NODE_OFFSET); ASSERT(dn.node_blk); xattr_node = dn.node_blk; inode->i.i_xattr_nid = cpu_to_le32(new_nid); } else { set_new_dnode(&dn, inode, NULL, xnid); get_node_info(sbi, xnid, &ni); blkaddr = ni.blk_addr; xattr_node = calloc(BLOCK_SZ, 1); ASSERT(xattr_node); ret = dev_read_block(xattr_node, ni.blk_addr); ASSERT(ret >= 0); } /* write to xattr node block */ xattr_addr = (void *)xattr_node; memcpy(xattr_addr, txattr_addr + inline_size, PAGE_SIZE - sizeof(struct node_footer)); ret = dev_write_block(xattr_node, blkaddr); ASSERT(ret >= 0); } int f2fs_setxattr(struct f2fs_sb_info *sbi, nid_t ino, int index, const char *name, const void *value, size_t size, int flags) { struct f2fs_node *inode; void *base_addr; struct f2fs_xattr_entry *here, *last; struct node_info ni; int error = 0; int len; int found, newsize; __u32 new_hsize; int ret; if (name == NULL) return -EINVAL; if (value == NULL) return -EINVAL; len = strlen(name); if (len > F2FS_NAME_LEN || size > MAX_VALUE_LEN) return -ERANGE; if (ino < 3) return -EINVAL; /* Now We just support selinux */ ASSERT(index == F2FS_XATTR_INDEX_SECURITY); get_node_info(sbi, ino, &ni); inode = calloc(BLOCK_SZ, 1); ASSERT(inode); ret = dev_read_block(inode, ni.blk_addr); ASSERT(ret >= 0); base_addr = read_all_xattrs(sbi, inode); ASSERT(base_addr); here = __find_xattr(base_addr, index, len, name); found = IS_XATTR_LAST_ENTRY(here) ? 0 : 1; if ((flags & XATTR_REPLACE) && !found) { error = -ENODATA; goto exit; } else if ((flags & XATTR_CREATE) && found) { error = -EEXIST; goto exit; } last = here; while (!IS_XATTR_LAST_ENTRY(last)) last = XATTR_NEXT_ENTRY(last); newsize = XATTR_ALIGN(sizeof(struct f2fs_xattr_entry) + len + size); /* 1. Check space */ if (value) { int free; /* * If value is NULL, it is remove operation. * In case of update operation, we calculate free. */ free = MIN_OFFSET - ((char *)last - (char *)base_addr); if (found) free = free + ENTRY_SIZE(here); if (free < newsize) { error = -ENOSPC; goto exit; } } /* 2. Remove old entry */ if (found) { /* * If entry if sound, remove old entry. * If not found, remove operation is not needed */ struct f2fs_xattr_entry *next = XATTR_NEXT_ENTRY(here); int oldsize = ENTRY_SIZE(here); memmove(here, next, (char *)last - (char *)next); last = (struct f2fs_xattr_entry *)((char *)last - oldsize); memset(last, 0, oldsize); } new_hsize = (char *)last - (char *)base_addr; /* 3. Write new entry */ if (value) { char *pval; /* * Before we come here, old entry is removed. * We just write new entry. */ memset(last, 0, newsize); last->e_name_index = index; last->e_name_len = len; memcpy(last->e_name, name, len); pval = last->e_name + len; memcpy(pval, value, size); last->e_value_size = cpu_to_le16(size); new_hsize += newsize; } write_all_xattrs(sbi, inode, new_hsize, base_addr); /* inode need update */ ret = dev_write_block(inode, ni.blk_addr); ASSERT(ret >= 0); exit: free(base_addr); return error; } int inode_set_selinux(struct f2fs_sb_info *sbi, u32 ino, const char *secon) { if (!secon) return 0; return f2fs_setxattr(sbi, ino, F2FS_XATTR_INDEX_SECURITY, XATTR_SELINUX_SUFFIX, secon, strlen(secon), 1); } f2fs-tools-1.10.0/fsck/xattr.h000066400000000000000000000065571323417143600160510ustar00rootroot00000000000000/** * xattr.h * * Many parts of codes are copied from Linux kernel/fs/f2fs. * * Copyright (C) 2015 Huawei Ltd. * Witten by: * Hou Pengyang * Liu Shuoran * Jaegeuk Kim * * 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. */ #ifndef _XATTR_H_ #define _XATTR_H_ #include "f2fs.h" #ifdef HAVE_SYS_XATTR_H #include #endif struct f2fs_xattr_header { __le32 h_magic; /* magic number for identification */ __le32 h_refcount; /* reference count */ __u32 h_sloadd[4]; /* zero right now */ }; struct f2fs_xattr_entry { __u8 e_name_index; __u8 e_name_len; __le16 e_value_size; /* size of attribute value */ char e_name[0]; /* attribute name */ }; #define FS_KEY_DESCRIPTOR_SIZE 8 #define FS_KEY_DERIVATION_NONCE_SIZE 16 struct fscrypt_context { u8 format; u8 contents_encryption_mode; u8 filenames_encryption_mode; u8 flags; u8 master_key_descriptor[FS_KEY_DESCRIPTOR_SIZE]; u8 nonce[FS_KEY_DERIVATION_NONCE_SIZE]; } __attribute__((packed)); #define F2FS_ACL_VERSION 0x0001 struct f2fs_acl_entry { __le16 e_tag; __le16 e_perm; __le32 e_id; }; struct f2fs_acl_entry_short { __le16 e_tag; __le16 e_perm; }; struct f2fs_acl_header { __le32 a_version; }; static inline int f2fs_acl_count(int size) { ssize_t s; size -= sizeof(struct f2fs_acl_header); s = size - 4 * sizeof(struct f2fs_acl_entry_short); if (s < 0) { if (size % sizeof(struct f2fs_acl_entry_short)) return -1; return size / sizeof(struct f2fs_acl_entry_short); } else { if (s % sizeof(struct f2fs_acl_entry)) return -1; return s / sizeof(struct f2fs_acl_entry) + 4; } } #ifndef XATTR_USER_PREFIX #define XATTR_USER_PREFIX "user." #endif #ifndef XATTR_SECURITY_PREFIX #define XATTR_SECURITY_PREFIX "security." #endif #ifndef XATTR_TRUSTED_PREFIX #define XATTR_TRUSTED_PREFIX "trusted." #endif #ifndef XATTR_CREATE #define XATTR_CREATE 0x1 #endif #ifndef XATTR_REPLACE #define XATTR_REPLACE 0x2 #endif #define XATTR_ROUND (3) #define XATTR_SELINUX_SUFFIX "selinux" #define F2FS_XATTR_INDEX_USER 1 #define F2FS_XATTR_INDEX_POSIX_ACL_ACCESS 2 #define F2FS_XATTR_INDEX_POSIX_ACL_DEFAULT 3 #define F2FS_XATTR_INDEX_TRUSTED 4 #define F2FS_XATTR_INDEX_LUSTRE 5 #define F2FS_XATTR_INDEX_SECURITY 6 #define F2FS_XATTR_INDEX_ENCRYPTION 9 #define IS_XATTR_LAST_ENTRY(entry) (*(__u32 *)(entry) == 0) #define XATTR_HDR(ptr) ((struct f2fs_xattr_header *)(ptr)) #define XATTR_ENTRY(ptr) ((struct f2fs_xattr_entry *)(ptr)) #define F2FS_XATTR_MAGIC 0xF2F52011 #define XATTR_NEXT_ENTRY(entry) ((struct f2fs_xattr_entry *) ((char *)(entry) +\ ENTRY_SIZE(entry))) #define XATTR_FIRST_ENTRY(ptr) (XATTR_ENTRY(XATTR_HDR(ptr) + 1)) #define XATTR_ALIGN(size) ((size + XATTR_ROUND) & ~XATTR_ROUND) #define ENTRY_SIZE(entry) (XATTR_ALIGN(sizeof(struct f2fs_xattr_entry) + \ entry->e_name_len + le16_to_cpu(entry->e_value_size))) #define list_for_each_xattr(entry, addr) \ for (entry = XATTR_FIRST_ENTRY(addr); \ !IS_XATTR_LAST_ENTRY(entry); \ entry = XATTR_NEXT_ENTRY(entry)) #define MIN_OFFSET XATTR_ALIGN(PAGE_SIZE - \ sizeof(struct node_footer) - sizeof(__u32)) #define MAX_VALUE_LEN (MIN_OFFSET - \ sizeof(struct f2fs_xattr_header) - \ sizeof(struct f2fs_xattr_entry)) #endif f2fs-tools-1.10.0/include/000077500000000000000000000000001323417143600152165ustar00rootroot00000000000000f2fs-tools-1.10.0/include/android_config.h000066400000000000000000000025561323417143600203440ustar00rootroot00000000000000#if defined(__linux__) #define HAVE_BYTESWAP_H 1 #define HAVE_FCNTL_H 1 #define HAVE_FALLOC_H 1 #define HAVE_LINUX_HDREG_H 1 #define HAVE_LINUX_LIMITS_H 1 #define HAVE_POSIX_ACL_H 1 #define HAVE_LINUX_TYPES_H 1 #define HAVE_LINUX_XATTR_H 1 #define HAVE_MNTENT_H 1 #define HAVE_STDLIB_H 1 #define HAVE_STRING_H 1 #define HAVE_SYS_IOCTL_H 1 #define HAVE_SYS_SYSCALL_H 1 #define HAVE_SYS_MOUNT_H 1 #define HAVE_SYS_SYSMACROS_H 1 #define HAVE_SYS_XATTR_H 1 #define HAVE_UNISTD_H 1 #define HAVE_ADD_KEY 1 #define HAVE_FALLOCATE 1 #define HAVE_FSETXATTR 1 #define HAVE_FSTAT 1 #define HAVE_FSTAT64 1 #define HAVE_GETMNTENT 1 #define HAVE_KEYCTL 1 #define HAVE_LLSEEK 1 #define HAVE_LSEEK64 1 #define HAVE_MEMSET 1 #define HAVE_SETMNTENT 1 #ifdef WITH_SLOAD #define HAVE_LIBSELINUX 1 #endif #endif #if defined(__APPLE__) #define HAVE_FCNTL_H 1 #define HAVE_FALLOC_H 1 #define HAVE_POSIX_ACL_H 1 #define HAVE_STDLIB_H 1 #define HAVE_STRING_H 1 #define HAVE_SYS_IOCTL_H 1 #define HAVE_SYS_SYSCALL_H 1 #define HAVE_SYS_MOUNT_H 1 #define HAVE_SYS_XATTR_H 1 #define HAVE_UNISTD_H 1 #define HAVE_ADD_KEY 1 #define HAVE_FALLOCATE 1 #define HAVE_FSETXATTR 1 #define HAVE_FSTAT 1 #define HAVE_FSTAT64 1 #define HAVE_GETMNTENT 1 #define HAVE_KEYCTL 1 #define HAVE_LLSEEK 1 #define HAVE_MEMSET 1 #ifdef WITH_SLOAD #define HAVE_LIBSELINUX 1 #endif #endif #if defined(_WIN32) #define HAVE_LSEEK64 #endif f2fs-tools-1.10.0/include/f2fs_fs.h000066400000000000000000001135131323417143600167230ustar00rootroot00000000000000/** * f2fs_fs.h * * Copyright (c) 2012 Samsung Electronics Co., Ltd. * http://www.samsung.com/ * * Dual licensed under the GPL or LGPL version 2 licenses. * * The byteswap codes are copied from: * samba_3_master/lib/ccan/endian/endian.h under LGPL 2.1 */ #ifndef __F2FS_FS_H__ #define __F2FS_FS_H__ #ifdef HAVE_CONFIG_H #include #endif #ifdef __ANDROID__ #define WITH_ANDROID #endif #ifdef WITH_ANDROID #include #else #define WITH_DUMP #define WITH_DEFRAG #define WITH_RESIZE #define WITH_SLOAD #endif #include #ifdef HAVE_LINUX_TYPES_H #include #endif #include #ifdef HAVE_LINUX_BLKZONED_H #include #endif #ifdef HAVE_LIBSELINUX #include #include #endif #ifdef UNUSED #elif defined(__GNUC__) # define UNUSED(x) UNUSED_ ## x __attribute__((unused)) #elif defined(__LCLINT__) # define UNUSED(x) x #else # define UNUSED(x) x #endif #ifdef ANDROID_WINDOWS_HOST #undef HAVE_LINUX_TYPES_H typedef uint64_t u_int64_t; typedef uint32_t u_int32_t; typedef uint16_t u_int16_t; typedef uint8_t u_int8_t; #endif typedef u_int64_t u64; typedef u_int32_t u32; typedef u_int16_t u16; typedef u_int8_t u8; typedef u32 block_t; typedef u32 nid_t; #ifndef bool typedef u8 bool; #endif typedef unsigned long pgoff_t; typedef unsigned short umode_t; #ifndef HAVE_LINUX_TYPES_H typedef u8 __u8; typedef u16 __u16; typedef u32 __u32; typedef u64 __u64; typedef u16 __le16; typedef u32 __le32; typedef u64 __le64; typedef u16 __be16; typedef u32 __be32; typedef u64 __be64; #endif #if HAVE_BYTESWAP_H #include #else /** * bswap_16 - reverse bytes in a uint16_t value. * @val: value whose bytes to swap. * * Example: * // Output contains "1024 is 4 as two bytes reversed" * printf("1024 is %u as two bytes reversed\n", bswap_16(1024)); */ static inline uint16_t bswap_16(uint16_t val) { return ((val & (uint16_t)0x00ffU) << 8) | ((val & (uint16_t)0xff00U) >> 8); } /** * bswap_32 - reverse bytes in a uint32_t value. * @val: value whose bytes to swap. * * Example: * // Output contains "1024 is 262144 as four bytes reversed" * printf("1024 is %u as four bytes reversed\n", bswap_32(1024)); */ static inline uint32_t bswap_32(uint32_t val) { return ((val & (uint32_t)0x000000ffUL) << 24) | ((val & (uint32_t)0x0000ff00UL) << 8) | ((val & (uint32_t)0x00ff0000UL) >> 8) | ((val & (uint32_t)0xff000000UL) >> 24); } #endif /* !HAVE_BYTESWAP_H */ #if defined HAVE_DECL_BSWAP_64 && !HAVE_DECL_BSWAP_64 /** * bswap_64 - reverse bytes in a uint64_t value. * @val: value whose bytes to swap. * * Example: * // Output contains "1024 is 1125899906842624 as eight bytes reversed" * printf("1024 is %llu as eight bytes reversed\n", * (unsigned long long)bswap_64(1024)); */ static inline uint64_t bswap_64(uint64_t val) { return ((val & (uint64_t)0x00000000000000ffULL) << 56) | ((val & (uint64_t)0x000000000000ff00ULL) << 40) | ((val & (uint64_t)0x0000000000ff0000ULL) << 24) | ((val & (uint64_t)0x00000000ff000000ULL) << 8) | ((val & (uint64_t)0x000000ff00000000ULL) >> 8) | ((val & (uint64_t)0x0000ff0000000000ULL) >> 24) | ((val & (uint64_t)0x00ff000000000000ULL) >> 40) | ((val & (uint64_t)0xff00000000000000ULL) >> 56); } #endif #if __BYTE_ORDER == __LITTLE_ENDIAN #define le16_to_cpu(x) ((__u16)(x)) #define le32_to_cpu(x) ((__u32)(x)) #define le64_to_cpu(x) ((__u64)(x)) #define cpu_to_le16(x) ((__u16)(x)) #define cpu_to_le32(x) ((__u32)(x)) #define cpu_to_le64(x) ((__u64)(x)) #elif __BYTE_ORDER == __BIG_ENDIAN #define le16_to_cpu(x) bswap_16(x) #define le32_to_cpu(x) bswap_32(x) #define le64_to_cpu(x) bswap_64(x) #define cpu_to_le16(x) bswap_16(x) #define cpu_to_le32(x) bswap_32(x) #define cpu_to_le64(x) bswap_64(x) #endif #define typecheck(type,x) \ ({ type __dummy; \ typeof(x) __dummy2; \ (void)(&__dummy == &__dummy2); \ 1; \ }) #define NULL_SEGNO ((unsigned int)~0) /* * Debugging interfaces */ #define FIX_MSG(fmt, ...) \ do { \ printf("[FIX] (%s:%4d) ", __func__, __LINE__); \ printf(" --> "fmt"\n", ##__VA_ARGS__); \ } while (0) #define ASSERT_MSG(fmt, ...) \ do { \ printf("[ASSERT] (%s:%4d) ", __func__, __LINE__); \ printf(" --> "fmt"\n", ##__VA_ARGS__); \ c.bug_on = 1; \ } while (0) #define ASSERT(exp) \ do { \ if (!(exp)) { \ printf("[ASSERT] (%s:%4d) " #exp"\n", \ __func__, __LINE__); \ exit(-1); \ } \ } while (0) #define ERR_MSG(fmt, ...) \ do { \ printf("[%s:%d] " fmt, __func__, __LINE__, ##__VA_ARGS__); \ } while (0) #define MSG(n, fmt, ...) \ do { \ if (c.dbg_lv >= n) { \ printf(fmt, ##__VA_ARGS__); \ } \ } while (0) #define DBG(n, fmt, ...) \ do { \ if (c.dbg_lv >= n) { \ printf("[%s:%4d] " fmt, \ __func__, __LINE__, ##__VA_ARGS__); \ } \ } while (0) /* Display on console */ #define DISP(fmt, ptr, member) \ do { \ printf("%-30s" fmt, #member, ((ptr)->member)); \ } while (0) #define DISP_u16(ptr, member) \ do { \ assert(sizeof((ptr)->member) == 2); \ printf("%-30s" "\t\t[0x%8x : %u]\n", \ #member, le16_to_cpu(((ptr)->member)), \ le16_to_cpu(((ptr)->member))); \ } while (0) #define DISP_u32(ptr, member) \ do { \ assert(sizeof((ptr)->member) <= 4); \ printf("%-30s" "\t\t[0x%8x : %u]\n", \ #member, le32_to_cpu(((ptr)->member)), \ le32_to_cpu(((ptr)->member))); \ } while (0) #define DISP_u64(ptr, member) \ do { \ assert(sizeof((ptr)->member) == 8); \ printf("%-30s" "\t\t[0x%8llx : %llu]\n", \ #member, le64_to_cpu(((ptr)->member)), \ le64_to_cpu(((ptr)->member))); \ } while (0) #define DISP_utf(ptr, member) \ do { \ printf("%-30s" "\t\t[%s]\n", #member, ((ptr)->member)); \ } while (0) /* Display to buffer */ #define BUF_DISP_u32(buf, data, len, ptr, member) \ do { \ assert(sizeof((ptr)->member) <= 4); \ snprintf(buf, len, #member); \ snprintf(data, len, "0x%x : %u", ((ptr)->member), \ ((ptr)->member)); \ } while (0) #define BUF_DISP_u64(buf, data, len, ptr, member) \ do { \ assert(sizeof((ptr)->member) == 8); \ snprintf(buf, len, #member); \ snprintf(data, len, "0x%llx : %llu", ((ptr)->member), \ ((ptr)->member)); \ } while (0) #define BUF_DISP_utf(buf, data, len, ptr, member) \ snprintf(buf, len, #member) /* these are defined in kernel */ #ifndef PAGE_SIZE #define PAGE_SIZE 4096 #endif #define PAGE_CACHE_SIZE 4096 #define BITS_PER_BYTE 8 #define F2FS_SUPER_MAGIC 0xF2F52010 /* F2FS Magic Number */ #define CHECKSUM_OFFSET 4092 #define MAX_PATH_LEN 64 #define MAX_DEVICES 8 #define F2FS_BYTES_TO_BLK(bytes) ((bytes) >> F2FS_BLKSIZE_BITS) #define F2FS_BLKSIZE_BITS 12 /* for mkfs */ #define F2FS_NUMBER_OF_CHECKPOINT_PACK 2 #define DEFAULT_SECTOR_SIZE 512 #define DEFAULT_SECTORS_PER_BLOCK 8 #define DEFAULT_BLOCKS_PER_SEGMENT 512 #define DEFAULT_SEGMENTS_PER_SECTION 1 #define VERSION_LEN 256 enum f2fs_config_func { MKFS, FSCK, DUMP, DEFRAG, RESIZE, SLOAD, }; struct device_info { char *path; int32_t fd; u_int32_t sector_size; u_int64_t total_sectors; /* got by get_device_info */ u_int64_t start_blkaddr; u_int64_t end_blkaddr; u_int32_t total_segments; /* to handle zone block devices */ int zoned_model; u_int32_t nr_zones; u_int32_t nr_rnd_zones; size_t zone_blocks; }; struct f2fs_configuration { u_int32_t reserved_segments; u_int32_t new_reserved_segments; int sparse_mode; int zoned_mode; int zoned_model; size_t zone_blocks; double overprovision; double new_overprovision; u_int32_t cur_seg[6]; u_int32_t segs_per_sec; u_int32_t secs_per_zone; u_int32_t segs_per_zone; u_int32_t start_sector; u_int32_t total_segments; u_int32_t sector_size; u_int64_t device_size; u_int64_t total_sectors; u_int64_t wanted_total_sectors; u_int64_t target_sectors; u_int32_t sectors_per_blk; u_int32_t blks_per_seg; __u8 init_version[VERSION_LEN + 1]; __u8 sb_version[VERSION_LEN + 1]; __u8 version[VERSION_LEN + 1]; char *vol_label; int heap; int32_t kd; int32_t dump_fd; struct device_info devices[MAX_DEVICES]; int ndevs; char *extension_list; const char *rootdev_name; int dbg_lv; int show_dentry; int trim; int trimmed; int func; void *private; int dry_run; int fix_on; int bug_on; int auto_fix; int preen_mode; int ro; int preserve_limits; /* preserve quota limits */ __le32 feature; /* defined features */ /* defragmentation parameters */ int defrag_shrink; u_int64_t defrag_start; u_int64_t defrag_len; u_int64_t defrag_target; /* sload parameters */ char *from_dir; char *mount_point; char *target_out_dir; char *fs_config_file; time_t fixed_time; #ifdef HAVE_LIBSELINUX struct selinux_opt seopt_file[8]; int nr_opt; #endif /* precomputed fs UUID checksum for seeding other checksums */ u_int32_t chksum_seed; }; #ifdef CONFIG_64BIT #define BITS_PER_LONG 64 #else #define BITS_PER_LONG 32 #endif #define BIT_MASK(nr) (1 << (nr % BITS_PER_LONG)) #define BIT_WORD(nr) (nr / BITS_PER_LONG) #define set_sb_le64(member, val) (sb->member = cpu_to_le64(val)) #define set_sb_le32(member, val) (sb->member = cpu_to_le32(val)) #define set_sb_le16(member, val) (sb->member = cpu_to_le16(val)) #define get_sb_le64(member) le64_to_cpu(sb->member) #define get_sb_le32(member) le32_to_cpu(sb->member) #define get_sb_le16(member) le16_to_cpu(sb->member) #define get_newsb_le64(member) le64_to_cpu(new_sb->member) #define get_newsb_le32(member) le32_to_cpu(new_sb->member) #define get_newsb_le16(member) le16_to_cpu(new_sb->member) #define set_sb(member, val) \ do { \ typeof(sb->member) t; \ switch (sizeof(t)) { \ case 8: set_sb_le64(member, val); break; \ case 4: set_sb_le32(member, val); break; \ case 2: set_sb_le16(member, val); break; \ } \ } while(0) #define get_sb(member) \ ({ \ typeof(sb->member) t; \ switch (sizeof(t)) { \ case 8: t = get_sb_le64(member); break; \ case 4: t = get_sb_le32(member); break; \ case 2: t = get_sb_le16(member); break; \ } \ t; \ }) #define get_newsb(member) \ ({ \ typeof(new_sb->member) t; \ switch (sizeof(t)) { \ case 8: t = get_newsb_le64(member); break; \ case 4: t = get_newsb_le32(member); break; \ case 2: t = get_newsb_le16(member); break; \ } \ t; \ }) #define set_cp_le64(member, val) (cp->member = cpu_to_le64(val)) #define set_cp_le32(member, val) (cp->member = cpu_to_le32(val)) #define set_cp_le16(member, val) (cp->member = cpu_to_le16(val)) #define get_cp_le64(member) le64_to_cpu(cp->member) #define get_cp_le32(member) le32_to_cpu(cp->member) #define get_cp_le16(member) le16_to_cpu(cp->member) #define set_cp(member, val) \ do { \ typeof(cp->member) t; \ switch (sizeof(t)) { \ case 8: set_cp_le64(member, val); break; \ case 4: set_cp_le32(member, val); break; \ case 2: set_cp_le16(member, val); break; \ } \ } while(0) #define get_cp(member) \ ({ \ typeof(cp->member) t; \ switch (sizeof(t)) { \ case 8: t = get_cp_le64(member); break; \ case 4: t = get_cp_le32(member); break; \ case 2: t = get_cp_le16(member); break; \ } \ t; \ }) /* * Copied from include/linux/kernel.h */ #define __round_mask(x, y) ((__typeof__(x))((y)-1)) #define round_down(x, y) ((x) & ~__round_mask(x, y)) #define min(x, y) ({ \ typeof(x) _min1 = (x); \ typeof(y) _min2 = (y); \ (void) (&_min1 == &_min2); \ _min1 < _min2 ? _min1 : _min2; }) #define max(x, y) ({ \ typeof(x) _max1 = (x); \ typeof(y) _max2 = (y); \ (void) (&_max1 == &_max2); \ _max1 > _max2 ? _max1 : _max2; }) /* * Copied from fs/f2fs/f2fs.h */ #define NR_CURSEG_DATA_TYPE (3) #define NR_CURSEG_NODE_TYPE (3) #define NR_CURSEG_TYPE (NR_CURSEG_DATA_TYPE + NR_CURSEG_NODE_TYPE) enum { CURSEG_HOT_DATA = 0, /* directory entry blocks */ CURSEG_WARM_DATA, /* data blocks */ CURSEG_COLD_DATA, /* multimedia or GCed data blocks */ CURSEG_HOT_NODE, /* direct node blocks of directory files */ CURSEG_WARM_NODE, /* direct node blocks of normal files */ CURSEG_COLD_NODE, /* indirect node blocks */ NO_CHECK_TYPE }; #define F2FS_MIN_SEGMENTS 9 /* SB + 2 (CP + SIT + NAT) + SSA + MAIN */ /* * Copied from fs/f2fs/segment.h */ #define GET_SUM_TYPE(footer) ((footer)->entry_type) #define SET_SUM_TYPE(footer, type) ((footer)->entry_type = type) /* * Copied from include/linux/f2fs_sb.h */ #define F2FS_SUPER_OFFSET 1024 /* byte-size offset */ #define F2FS_MIN_LOG_SECTOR_SIZE 9 /* 9 bits for 512 bytes */ #define F2FS_MAX_LOG_SECTOR_SIZE 12 /* 12 bits for 4096 bytes */ #define F2FS_BLKSIZE 4096 /* support only 4KB block */ #define F2FS_MAX_EXTENSION 64 /* # of extension entries */ #define F2FS_BLK_ALIGN(x) (((x) + F2FS_BLKSIZE - 1) / F2FS_BLKSIZE) #define NULL_ADDR 0x0U #define NEW_ADDR -1U #define F2FS_ROOT_INO(sbi) (sbi->root_ino_num) #define F2FS_NODE_INO(sbi) (sbi->node_ino_num) #define F2FS_META_INO(sbi) (sbi->meta_ino_num) #define F2FS_MAX_QUOTAS 3 #define QUOTA_DATA(i) (2) #define QUOTA_INO(sb,t) (le32_to_cpu((sb)->qf_ino[t])) #define FS_IMMUTABLE_FL 0x00000010 /* Immutable file */ /* This flag is used by node and meta inodes, and by recovery */ #define GFP_F2FS_ZERO (GFP_NOFS | __GFP_ZERO) /* * For further optimization on multi-head logs, on-disk layout supports maximum * 16 logs by default. The number, 16, is expected to cover all the cases * enoughly. The implementaion currently uses no more than 6 logs. * Half the logs are used for nodes, and the other half are used for data. */ #define MAX_ACTIVE_LOGS 16 #define MAX_ACTIVE_NODE_LOGS 8 #define MAX_ACTIVE_DATA_LOGS 8 #define F2FS_FEATURE_ENCRYPT 0x0001 #define F2FS_FEATURE_BLKZONED 0x0002 #define F2FS_FEATURE_ATOMIC_WRITE 0x0004 #define F2FS_FEATURE_EXTRA_ATTR 0x0008 #define F2FS_FEATURE_PRJQUOTA 0x0010 #define F2FS_FEATURE_INODE_CHKSUM 0x0020 #define F2FS_FEATURE_FLEXIBLE_INLINE_XATTR 0x0040 #define F2FS_FEATURE_QUOTA_INO 0x0080 #define F2FS_FEATURE_INODE_CRTIME 0x0100 #define MAX_VOLUME_NAME 512 /* * For superblock */ #ifdef ANDROID_WINDOWS_HOST #pragma pack(1) #endif struct f2fs_device { __u8 path[MAX_PATH_LEN]; __le32 total_segments; } __attribute__((packed)); #ifdef ANDROID_WINDOWS_HOST #pragma pack(1) #endif struct f2fs_super_block { __le32 magic; /* Magic Number */ __le16 major_ver; /* Major Version */ __le16 minor_ver; /* Minor Version */ __le32 log_sectorsize; /* log2 sector size in bytes */ __le32 log_sectors_per_block; /* log2 # of sectors per block */ __le32 log_blocksize; /* log2 block size in bytes */ __le32 log_blocks_per_seg; /* log2 # of blocks per segment */ __le32 segs_per_sec; /* # of segments per section */ __le32 secs_per_zone; /* # of sections per zone */ __le32 checksum_offset; /* checksum offset inside super block */ __le64 block_count; /* total # of user blocks */ __le32 section_count; /* total # of sections */ __le32 segment_count; /* total # of segments */ __le32 segment_count_ckpt; /* # of segments for checkpoint */ __le32 segment_count_sit; /* # of segments for SIT */ __le32 segment_count_nat; /* # of segments for NAT */ __le32 segment_count_ssa; /* # of segments for SSA */ __le32 segment_count_main; /* # of segments for main area */ __le32 segment0_blkaddr; /* start block address of segment 0 */ __le32 cp_blkaddr; /* start block address of checkpoint */ __le32 sit_blkaddr; /* start block address of SIT */ __le32 nat_blkaddr; /* start block address of NAT */ __le32 ssa_blkaddr; /* start block address of SSA */ __le32 main_blkaddr; /* start block address of main area */ __le32 root_ino; /* root inode number */ __le32 node_ino; /* node inode number */ __le32 meta_ino; /* meta inode number */ __u8 uuid[16]; /* 128-bit uuid for volume */ __le16 volume_name[MAX_VOLUME_NAME]; /* volume name */ __le32 extension_count; /* # of extensions below */ __u8 extension_list[F2FS_MAX_EXTENSION][8]; /* extension array */ __le32 cp_payload; __u8 version[VERSION_LEN]; /* the kernel version */ __u8 init_version[VERSION_LEN]; /* the initial kernel version */ __le32 feature; /* defined features */ __u8 encryption_level; /* versioning level for encryption */ __u8 encrypt_pw_salt[16]; /* Salt used for string2key algorithm */ struct f2fs_device devs[MAX_DEVICES]; /* device list */ __le32 qf_ino[F2FS_MAX_QUOTAS]; /* quota inode numbers */ __u8 reserved[315]; /* valid reserved region */ } __attribute__((packed)); /* * For checkpoint */ #define CP_NOCRC_RECOVERY_FLAG 0x00000200 #define CP_TRIMMED_FLAG 0x00000100 #define CP_NAT_BITS_FLAG 0x00000080 #define CP_CRC_RECOVERY_FLAG 0x00000040 #define CP_FASTBOOT_FLAG 0x00000020 #define CP_FSCK_FLAG 0x00000010 #define CP_ERROR_FLAG 0x00000008 #define CP_COMPACT_SUM_FLAG 0x00000004 #define CP_ORPHAN_PRESENT_FLAG 0x00000002 #define CP_UMOUNT_FLAG 0x00000001 #ifdef ANDROID_WINDOWS_HOST #pragma pack(1) #endif struct f2fs_checkpoint { __le64 checkpoint_ver; /* checkpoint block version number */ __le64 user_block_count; /* # of user blocks */ __le64 valid_block_count; /* # of valid blocks in main area */ __le32 rsvd_segment_count; /* # of reserved segments for gc */ __le32 overprov_segment_count; /* # of overprovision segments */ __le32 free_segment_count; /* # of free segments in main area */ /* information of current node segments */ __le32 cur_node_segno[MAX_ACTIVE_NODE_LOGS]; __le16 cur_node_blkoff[MAX_ACTIVE_NODE_LOGS]; /* information of current data segments */ __le32 cur_data_segno[MAX_ACTIVE_DATA_LOGS]; __le16 cur_data_blkoff[MAX_ACTIVE_DATA_LOGS]; __le32 ckpt_flags; /* Flags : umount and journal_present */ __le32 cp_pack_total_block_count; /* total # of one cp pack */ __le32 cp_pack_start_sum; /* start block number of data summary */ __le32 valid_node_count; /* Total number of valid nodes */ __le32 valid_inode_count; /* Total number of valid inodes */ __le32 next_free_nid; /* Next free node number */ __le32 sit_ver_bitmap_bytesize; /* Default value 64 */ __le32 nat_ver_bitmap_bytesize; /* Default value 256 */ __le32 checksum_offset; /* checksum offset inside cp block */ __le64 elapsed_time; /* mounted time */ /* allocation type of current segment */ unsigned char alloc_type[MAX_ACTIVE_LOGS]; /* SIT and NAT version bitmap */ unsigned char sit_nat_version_bitmap[1]; } __attribute__((packed)); #define MAX_SIT_BITMAP_SIZE_IN_CKPT \ (CHECKSUM_OFFSET - sizeof(struct f2fs_checkpoint) + 1 - 64) /* * For orphan inode management */ #define F2FS_ORPHANS_PER_BLOCK 1020 #ifdef ANDROID_WINDOWS_HOST #pragma pack(1) #endif struct f2fs_orphan_block { __le32 ino[F2FS_ORPHANS_PER_BLOCK]; /* inode numbers */ __le32 reserved; /* reserved */ __le16 blk_addr; /* block index in current CP */ __le16 blk_count; /* Number of orphan inode blocks in CP */ __le32 entry_count; /* Total number of orphan nodes in current CP */ __le32 check_sum; /* CRC32 for orphan inode block */ } __attribute__((packed)); /* * For NODE structure */ #ifdef ANDROID_WINDOWS_HOST #pragma pack(1) #endif struct f2fs_extent { __le32 fofs; /* start file offset of the extent */ __le32 blk_addr; /* start block address of the extent */ __le32 len; /* lengh of the extent */ } __attribute__((packed)); #define F2FS_NAME_LEN 255 /* 200 bytes for inline xattrs by default */ #define DEFAULT_INLINE_XATTR_ADDRS 50 #define DEF_ADDRS_PER_INODE 923 /* Address Pointers in an Inode */ #define CUR_ADDRS_PER_INODE(inode) (DEF_ADDRS_PER_INODE - \ __get_extra_isize(inode)) #define ADDRS_PER_INODE(i) addrs_per_inode(i) #define ADDRS_PER_BLOCK 1018 /* Address Pointers in a Direct Block */ #define NIDS_PER_BLOCK 1018 /* Node IDs in an Indirect Block */ #define NODE_DIR1_BLOCK (DEF_ADDRS_PER_INODE + 1) #define NODE_DIR2_BLOCK (DEF_ADDRS_PER_INODE + 2) #define NODE_IND1_BLOCK (DEF_ADDRS_PER_INODE + 3) #define NODE_IND2_BLOCK (DEF_ADDRS_PER_INODE + 4) #define NODE_DIND_BLOCK (DEF_ADDRS_PER_INODE + 5) #define F2FS_INLINE_XATTR 0x01 /* file inline xattr flag */ #define F2FS_INLINE_DATA 0x02 /* file inline data flag */ #define F2FS_INLINE_DENTRY 0x04 /* file inline dentry flag */ #define F2FS_DATA_EXIST 0x08 /* file inline data exist flag */ #define F2FS_INLINE_DOTS 0x10 /* file having implicit dot dentries */ #define F2FS_EXTRA_ATTR 0x20 /* file having extra attribute */ #if !defined(offsetof) #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) #endif #define F2FS_TOTAL_EXTRA_ATTR_SIZE \ (offsetof(struct f2fs_inode, i_extra_end) - \ offsetof(struct f2fs_inode, i_extra_isize)) \ #define F2FS_DEF_PROJID 0 /* default project ID */ #define MAX_INLINE_DATA(node) (sizeof(__le32) * \ (DEF_ADDRS_PER_INODE - \ get_inline_xattr_addrs(&node->i) - \ get_extra_isize(node) - \ DEF_INLINE_RESERVED_SIZE)) #define DEF_MAX_INLINE_DATA (sizeof(__le32) * \ (DEF_ADDRS_PER_INODE - \ DEFAULT_INLINE_XATTR_ADDRS - \ F2FS_TOTAL_EXTRA_ATTR_SIZE - \ DEF_INLINE_RESERVED_SIZE)) #define INLINE_DATA_OFFSET (PAGE_CACHE_SIZE - sizeof(struct node_footer) \ - sizeof(__le32)*(DEF_ADDRS_PER_INODE + 5 - \ DEF_INLINE_RESERVED_SIZE)) #define DEF_DIR_LEVEL 0 /* * i_advise uses FADVISE_XXX_BIT. We can add additional hints later. */ #define FADVISE_COLD_BIT 0x01 #define FADVISE_LOST_PINO_BIT 0x02 #define FADVISE_ENCRYPT_BIT 0x04 #define FADVISE_ENC_NAME_BIT 0x08 #define file_is_encrypt(fi) ((fi)->i_advise & FADVISE_ENCRYPT_BIT) #define file_enc_name(fi) ((fi)->i_advise & FADVISE_ENC_NAME_BIT) #ifdef ANDROID_WINDOWS_HOST #pragma pack(1) #endif struct f2fs_inode { __le16 i_mode; /* file mode */ __u8 i_advise; /* file hints */ __u8 i_inline; /* file inline flags */ __le32 i_uid; /* user ID */ __le32 i_gid; /* group ID */ __le32 i_links; /* links count */ __le64 i_size; /* file size in bytes */ __le64 i_blocks; /* file size in blocks */ __le64 i_atime; /* access time */ __le64 i_ctime; /* change time */ __le64 i_mtime; /* modification time */ __le32 i_atime_nsec; /* access time in nano scale */ __le32 i_ctime_nsec; /* change time in nano scale */ __le32 i_mtime_nsec; /* modification time in nano scale */ __le32 i_generation; /* file version (for NFS) */ __le32 i_current_depth; /* only for directory depth */ __le32 i_xattr_nid; /* nid to save xattr */ __le32 i_flags; /* file attributes */ __le32 i_pino; /* parent inode number */ __le32 i_namelen; /* file name length */ __u8 i_name[F2FS_NAME_LEN]; /* file name for SPOR */ __u8 i_dir_level; /* dentry_level for large dir */ struct f2fs_extent i_ext; /* caching a largest extent */ union { struct { __le16 i_extra_isize; /* extra inode attribute size */ __le16 i_inline_xattr_size; /* inline xattr size, unit: 4 bytes */ __le32 i_projid; /* project id */ __le32 i_inode_checksum;/* inode meta checksum */ __le64 i_crtime; /* creation time */ __le32 i_crtime_nsec; /* creation time in nano scale */ __le32 i_extra_end[0]; /* for attribute size calculation */ } __attribute__((packed)); __le32 i_addr[DEF_ADDRS_PER_INODE]; /* Pointers to data blocks */ }; __le32 i_nid[5]; /* direct(2), indirect(2), double_indirect(1) node id */ } __attribute__((packed)); #ifdef ANDROID_WINDOWS_HOST #pragma pack(1) #endif struct direct_node { __le32 addr[ADDRS_PER_BLOCK]; /* array of data block address */ } __attribute__((packed)); #ifdef ANDROID_WINDOWS_HOST #pragma pack(1) #endif struct indirect_node { __le32 nid[NIDS_PER_BLOCK]; /* array of data block address */ } __attribute__((packed)); enum { COLD_BIT_SHIFT = 0, FSYNC_BIT_SHIFT, DENT_BIT_SHIFT, OFFSET_BIT_SHIFT }; #define XATTR_NODE_OFFSET ((((unsigned int)-1) << OFFSET_BIT_SHIFT) \ >> OFFSET_BIT_SHIFT) #ifdef ANDROID_WINDOWS_HOST #pragma pack(1) #endif struct node_footer { __le32 nid; /* node id */ __le32 ino; /* inode nunmber */ __le32 flag; /* include cold/fsync/dentry marks and offset */ __le64 cp_ver; /* checkpoint version */ __le32 next_blkaddr; /* next node page block address */ } __attribute__((packed)); #ifdef ANDROID_WINDOWS_HOST #pragma pack(1) #endif struct f2fs_node { /* can be one of three types: inode, direct, and indirect types */ union { struct f2fs_inode i; struct direct_node dn; struct indirect_node in; }; struct node_footer footer; } __attribute__((packed)); /* * For NAT entries */ #define NAT_ENTRY_PER_BLOCK (PAGE_CACHE_SIZE / sizeof(struct f2fs_nat_entry)) #define NAT_BLOCK_OFFSET(start_nid) (start_nid / NAT_ENTRY_PER_BLOCK) #ifdef ANDROID_WINDOWS_HOST #pragma pack(1) #endif struct f2fs_nat_entry { __u8 version; /* latest version of cached nat entry */ __le32 ino; /* inode number */ __le32 block_addr; /* block address */ } __attribute__((packed)); #ifdef ANDROID_WINDOWS_HOST #pragma pack(1) #endif struct f2fs_nat_block { struct f2fs_nat_entry entries[NAT_ENTRY_PER_BLOCK]; } __attribute__((packed)); /* * For SIT entries * * Each segment is 2MB in size by default so that a bitmap for validity of * there-in blocks should occupy 64 bytes, 512 bits. * Not allow to change this. */ #define SIT_VBLOCK_MAP_SIZE 64 #define SIT_ENTRY_PER_BLOCK (PAGE_CACHE_SIZE / sizeof(struct f2fs_sit_entry)) /* * F2FS uses 4 bytes to represent block address. As a result, supported size of * disk is 16 TB and it equals to 16 * 1024 * 1024 / 2 segments. */ #define F2FS_MAX_SEGMENT ((16 * 1024 * 1024) / 2) #define MAX_SIT_BITMAP_SIZE (SEG_ALIGN(SIZE_ALIGN(F2FS_MAX_SEGMENT, \ SIT_ENTRY_PER_BLOCK)) * \ c.blks_per_seg / 8) /* * Note that f2fs_sit_entry->vblocks has the following bit-field information. * [15:10] : allocation type such as CURSEG_XXXX_TYPE * [9:0] : valid block count */ #define SIT_VBLOCKS_SHIFT 10 #define SIT_VBLOCKS_MASK ((1 << SIT_VBLOCKS_SHIFT) - 1) #define GET_SIT_VBLOCKS(raw_sit) \ (le16_to_cpu((raw_sit)->vblocks) & SIT_VBLOCKS_MASK) #define GET_SIT_TYPE(raw_sit) \ ((le16_to_cpu((raw_sit)->vblocks) & ~SIT_VBLOCKS_MASK) \ >> SIT_VBLOCKS_SHIFT) #ifdef ANDROID_WINDOWS_HOST #pragma pack(1) #endif struct f2fs_sit_entry { __le16 vblocks; /* reference above */ __u8 valid_map[SIT_VBLOCK_MAP_SIZE]; /* bitmap for valid blocks */ __le64 mtime; /* segment age for cleaning */ } __attribute__((packed)); #ifdef ANDROID_WINDOWS_HOST #pragma pack(1) #endif struct f2fs_sit_block { struct f2fs_sit_entry entries[SIT_ENTRY_PER_BLOCK]; } __attribute__((packed)); /* * For segment summary * * One summary block contains exactly 512 summary entries, which represents * exactly 2MB segment by default. Not allow to change the basic units. * * NOTE: For initializing fields, you must use set_summary * * - If data page, nid represents dnode's nid * - If node page, nid represents the node page's nid. * * The ofs_in_node is used by only data page. It represents offset * from node's page's beginning to get a data block address. * ex) data_blkaddr = (block_t)(nodepage_start_address + ofs_in_node) */ #define ENTRIES_IN_SUM 512 #define SUMMARY_SIZE (7) /* sizeof(struct summary) */ #define SUM_FOOTER_SIZE (5) /* sizeof(struct summary_footer) */ #define SUM_ENTRIES_SIZE (SUMMARY_SIZE * ENTRIES_IN_SUM) /* a summary entry for a 4KB-sized block in a segment */ #ifdef ANDROID_WINDOWS_HOST #pragma pack(1) #endif struct f2fs_summary { __le32 nid; /* parent node id */ union { __u8 reserved[3]; #ifdef ANDROID_WINDOWS_HOST #pragma pack(1) #endif struct { __u8 version; /* node version number */ __le16 ofs_in_node; /* block index in parent node */ } __attribute__((packed)); }; } __attribute__((packed)); /* summary block type, node or data, is stored to the summary_footer */ #define SUM_TYPE_NODE (1) #define SUM_TYPE_DATA (0) #ifdef ANDROID_WINDOWS_HOST #pragma pack(1) #endif struct summary_footer { unsigned char entry_type; /* SUM_TYPE_XXX */ __le32 check_sum; /* summary checksum */ } __attribute__((packed)); #define SUM_JOURNAL_SIZE (F2FS_BLKSIZE - SUM_FOOTER_SIZE -\ SUM_ENTRIES_SIZE) #define NAT_JOURNAL_ENTRIES ((SUM_JOURNAL_SIZE - 2) /\ sizeof(struct nat_journal_entry)) #define NAT_JOURNAL_RESERVED ((SUM_JOURNAL_SIZE - 2) %\ sizeof(struct nat_journal_entry)) #define SIT_JOURNAL_ENTRIES ((SUM_JOURNAL_SIZE - 2) /\ sizeof(struct sit_journal_entry)) #define SIT_JOURNAL_RESERVED ((SUM_JOURNAL_SIZE - 2) %\ sizeof(struct sit_journal_entry)) /* * Reserved area should make size of f2fs_extra_info equals to * that of nat_journal and sit_journal. */ #define EXTRA_INFO_RESERVED (SUM_JOURNAL_SIZE - 2 - 8) /* * frequently updated NAT/SIT entries can be stored in the spare area in * summary blocks */ enum { NAT_JOURNAL = 0, SIT_JOURNAL }; #ifdef ANDROID_WINDOWS_HOST #pragma pack(1) #endif struct nat_journal_entry { __le32 nid; struct f2fs_nat_entry ne; } __attribute__((packed)); #ifdef ANDROID_WINDOWS_HOST #pragma pack(1) #endif struct nat_journal { struct nat_journal_entry entries[NAT_JOURNAL_ENTRIES]; __u8 reserved[NAT_JOURNAL_RESERVED]; } __attribute__((packed)); #ifdef ANDROID_WINDOWS_HOST #pragma pack(1) #endif struct sit_journal_entry { __le32 segno; struct f2fs_sit_entry se; } __attribute__((packed)); #ifdef ANDROID_WINDOWS_HOST #pragma pack(1) #endif struct sit_journal { struct sit_journal_entry entries[SIT_JOURNAL_ENTRIES]; __u8 reserved[SIT_JOURNAL_RESERVED]; } __attribute__((packed)); #ifdef ANDROID_WINDOWS_HOST #pragma pack(1) #endif struct f2fs_extra_info { __le64 kbytes_written; __u8 reserved[EXTRA_INFO_RESERVED]; } __attribute__((packed)); #ifdef ANDROID_WINDOWS_HOST #pragma pack(1) #endif struct f2fs_journal { union { __le16 n_nats; __le16 n_sits; }; /* spare area is used by NAT or SIT journals or extra info */ union { struct nat_journal nat_j; struct sit_journal sit_j; struct f2fs_extra_info info; }; } __attribute__((packed)); /* 4KB-sized summary block structure */ #ifdef ANDROID_WINDOWS_HOST #pragma pack(1) #endif struct f2fs_summary_block { struct f2fs_summary entries[ENTRIES_IN_SUM]; struct f2fs_journal journal; struct summary_footer footer; } __attribute__((packed)); /* * For directory operations */ #define F2FS_DOT_HASH 0 #define F2FS_DDOT_HASH F2FS_DOT_HASH #define F2FS_MAX_HASH (~((0x3ULL) << 62)) #define F2FS_HASH_COL_BIT ((0x1ULL) << 63) typedef __le32 f2fs_hash_t; /* One directory entry slot covers 8bytes-long file name */ #define F2FS_SLOT_LEN 8 #define F2FS_SLOT_LEN_BITS 3 #define GET_DENTRY_SLOTS(x) ((x + F2FS_SLOT_LEN - 1) >> F2FS_SLOT_LEN_BITS) /* the number of dentry in a block */ #define NR_DENTRY_IN_BLOCK 214 /* MAX level for dir lookup */ #define MAX_DIR_HASH_DEPTH 63 /* MAX buckets in one level of dir */ #define MAX_DIR_BUCKETS (1 << ((MAX_DIR_HASH_DEPTH / 2) - 1)) #define SIZE_OF_DIR_ENTRY 11 /* by byte */ #define SIZE_OF_DENTRY_BITMAP ((NR_DENTRY_IN_BLOCK + BITS_PER_BYTE - 1) / \ BITS_PER_BYTE) #define SIZE_OF_RESERVED (PAGE_SIZE - ((SIZE_OF_DIR_ENTRY + \ F2FS_SLOT_LEN) * \ NR_DENTRY_IN_BLOCK + SIZE_OF_DENTRY_BITMAP)) /* One directory entry slot representing F2FS_SLOT_LEN-sized file name */ #ifdef ANDROID_WINDOWS_HOST #pragma pack(1) #endif struct f2fs_dir_entry { __le32 hash_code; /* hash code of file name */ __le32 ino; /* inode number */ __le16 name_len; /* lengh of file name */ __u8 file_type; /* file type */ } __attribute__((packed)); /* 4KB-sized directory entry block */ #ifdef ANDROID_WINDOWS_HOST #pragma pack(1) #endif struct f2fs_dentry_block { /* validity bitmap for directory entries in each block */ __u8 dentry_bitmap[SIZE_OF_DENTRY_BITMAP]; __u8 reserved[SIZE_OF_RESERVED]; struct f2fs_dir_entry dentry[NR_DENTRY_IN_BLOCK]; __u8 filename[NR_DENTRY_IN_BLOCK][F2FS_SLOT_LEN]; } __attribute__((packed)); /* for inline stuff */ #define DEF_INLINE_RESERVED_SIZE 1 /* for inline dir */ #define NR_INLINE_DENTRY(node) (MAX_INLINE_DATA(node) * BITS_PER_BYTE / \ ((SIZE_OF_DIR_ENTRY + F2FS_SLOT_LEN) * \ BITS_PER_BYTE + 1)) #define INLINE_DENTRY_BITMAP_SIZE(node) ((NR_INLINE_DENTRY(node) + \ BITS_PER_BYTE - 1) / BITS_PER_BYTE) #define INLINE_RESERVED_SIZE(node) (MAX_INLINE_DATA(node) - \ ((SIZE_OF_DIR_ENTRY + F2FS_SLOT_LEN) * \ NR_INLINE_DENTRY(node) + \ INLINE_DENTRY_BITMAP_SIZE(node))) /* file types used in inode_info->flags */ enum FILE_TYPE { F2FS_FT_UNKNOWN, F2FS_FT_REG_FILE, F2FS_FT_DIR, F2FS_FT_CHRDEV, F2FS_FT_BLKDEV, F2FS_FT_FIFO, F2FS_FT_SOCK, F2FS_FT_SYMLINK, F2FS_FT_MAX, /* added for fsck */ F2FS_FT_ORPHAN, F2FS_FT_XATTR, F2FS_FT_LAST_FILE_TYPE = F2FS_FT_XATTR, }; /* from f2fs/segment.h */ enum { LFS = 0, SSR }; extern int utf8_to_utf16(u_int16_t *, const char *, size_t, size_t); extern int utf16_to_utf8(char *, const u_int16_t *, size_t, size_t); extern int log_base_2(u_int32_t); extern unsigned int addrs_per_inode(struct f2fs_inode *); extern __u32 f2fs_inode_chksum(struct f2fs_node *); extern int get_bits_in_byte(unsigned char n); extern int test_and_set_bit_le(u32, u8 *); extern int test_and_clear_bit_le(u32, u8 *); extern int test_bit_le(u32, const u8 *); extern int f2fs_test_bit(unsigned int, const char *); extern int f2fs_set_bit(unsigned int, char *); extern int f2fs_clear_bit(unsigned int, char *); extern u64 find_next_bit_le(const u8 *, u64, u64); extern u64 find_next_zero_bit_le(const u8 *, u64, u64); extern u_int32_t f2fs_cal_crc32(u_int32_t, void *, int); extern int f2fs_crc_valid(u_int32_t blk_crc, void *buf, int len); extern void f2fs_init_configuration(void); extern int f2fs_devs_are_umounted(void); extern int f2fs_dev_is_umounted(char *); extern int f2fs_get_device_info(void); extern int get_device_info(int); extern int f2fs_init_sparse_file(void); extern int f2fs_finalize_device(void); extern int f2fs_fsync_device(void); extern int dev_read(void *, __u64, size_t); extern int dev_write(void *, __u64, size_t); extern int dev_write_block(void *, __u64); extern int dev_write_dump(void *, __u64, size_t); /* All bytes in the buffer must be 0 use dev_fill(). */ extern int dev_fill(void *, __u64, size_t); extern int dev_fill_block(void *, __u64); extern int dev_read_block(void *, __u64); extern int dev_reada_block(__u64); extern int dev_read_version(void *, __u64, size_t); extern void get_kernel_version(__u8 *); f2fs_hash_t f2fs_dentry_hash(const unsigned char *, int); static inline bool f2fs_has_extra_isize(struct f2fs_inode *inode) { return (inode->i_inline & F2FS_EXTRA_ATTR); } static inline int __get_extra_isize(struct f2fs_inode *inode) { if (f2fs_has_extra_isize(inode)) return le16_to_cpu(inode->i_extra_isize) / sizeof(__le32); return 0; } extern struct f2fs_configuration c; static inline int get_inline_xattr_addrs(struct f2fs_inode *inode) { if (c.feature & cpu_to_le32(F2FS_FEATURE_FLEXIBLE_INLINE_XATTR)) return le16_to_cpu(inode->i_inline_xattr_size); else if (inode->i_inline & F2FS_INLINE_XATTR || inode->i_inline & F2FS_INLINE_DENTRY) return DEFAULT_INLINE_XATTR_ADDRS; else return 0; } #define get_extra_isize(node) __get_extra_isize(&node->i) #define F2FS_ZONED_NONE 0 #define F2FS_ZONED_HA 1 #define F2FS_ZONED_HM 2 #ifdef HAVE_LINUX_BLKZONED_H #define blk_zone_type(z) (z)->type #define blk_zone_conv(z) ((z)->type == BLK_ZONE_TYPE_CONVENTIONAL) #define blk_zone_seq_req(z) ((z)->type == BLK_ZONE_TYPE_SEQWRITE_REQ) #define blk_zone_seq_pref(z) ((z)->type == BLK_ZONE_TYPE_SEQWRITE_PREF) #define blk_zone_seq(z) (blk_zone_seq_req(z) || blk_zone_seq_pref(z)) static inline const char * blk_zone_type_str(struct blk_zone *blkz) { switch (blk_zone_type(blkz)) { case BLK_ZONE_TYPE_CONVENTIONAL: return( "Conventional" ); case BLK_ZONE_TYPE_SEQWRITE_REQ: return( "Sequential-write-required" ); case BLK_ZONE_TYPE_SEQWRITE_PREF: return( "Sequential-write-preferred" ); } return( "Unknown-type" ); } #define blk_zone_cond(z) (z)->cond static inline const char * blk_zone_cond_str(struct blk_zone *blkz) { switch (blk_zone_cond(blkz)) { case BLK_ZONE_COND_NOT_WP: return "Not-write-pointer"; case BLK_ZONE_COND_EMPTY: return "Empty"; case BLK_ZONE_COND_IMP_OPEN: return "Implicit-open"; case BLK_ZONE_COND_EXP_OPEN: return "Explicit-open"; case BLK_ZONE_COND_CLOSED: return "Closed"; case BLK_ZONE_COND_READONLY: return "Read-only"; case BLK_ZONE_COND_FULL: return "Full"; case BLK_ZONE_COND_OFFLINE: return "Offline"; } return "Unknown-cond"; } #define blk_zone_empty(z) (blk_zone_cond(z) == BLK_ZONE_COND_EMPTY) #define blk_zone_sector(z) (z)->start #define blk_zone_length(z) (z)->len #define blk_zone_wp_sector(z) (z)->wp #define blk_zone_need_reset(z) (int)(z)->reset #define blk_zone_non_seq(z) (int)(z)->non_seq #endif extern void f2fs_get_zoned_model(int); extern int f2fs_get_zone_blocks(int); extern int f2fs_check_zones(int); extern int f2fs_reset_zones(int); extern struct f2fs_configuration c; #define SIZE_ALIGN(val, size) ((val) + (size) - 1) / (size) #define SEG_ALIGN(blks) SIZE_ALIGN(blks, c.blks_per_seg) #define ZONE_ALIGN(blks) SIZE_ALIGN(blks, c.blks_per_seg * \ c.segs_per_zone) static inline double get_best_overprovision(struct f2fs_super_block *sb) { double reserved, ovp, candidate, end, diff, space; double max_ovp = 0, max_space = 0; if (get_sb(segment_count_main) < 256) { candidate = 10; end = 95; diff = 5; } else { candidate = 0.01; end = 10; diff = 0.01; } for (; candidate <= end; candidate += diff) { reserved = (2 * (100 / candidate + 1) + 6) * get_sb(segs_per_sec); ovp = (get_sb(segment_count_main) - reserved) * candidate / 100; space = get_sb(segment_count_main) - reserved - ovp; if (max_space < space) { max_space = space; max_ovp = candidate; } } return max_ovp; } static inline __le64 get_cp_crc(struct f2fs_checkpoint *cp) { u_int64_t cp_ver = get_cp(checkpoint_ver); size_t crc_offset = get_cp(checksum_offset); u_int32_t crc = le32_to_cpu(*(__le32 *)((unsigned char *)cp + crc_offset)); cp_ver |= ((u_int64_t)crc << 32); return cpu_to_le64(cp_ver); } static inline int exist_qf_ino(struct f2fs_super_block *sb) { int i; for (i = 0; i < F2FS_MAX_QUOTAS; i++) if (sb->qf_ino[i]) return 1; return 0; } static inline int is_qf_ino(struct f2fs_super_block *sb, nid_t ino) { int i; for (i = 0; i < F2FS_MAX_QUOTAS; i++) if (sb->qf_ino[i] == ino) return 1; return 0; } #endif /*__F2FS_FS_H */ f2fs-tools-1.10.0/include/quota.h000066400000000000000000000051061323417143600165220ustar00rootroot00000000000000/* * * Header file for disk format of new quotafile format * * Copied essential definitions and structures for mkfs.f2fs from quotaio.h * * Aditya Kali * Jan Kara * Hyojun Kim - Ported to f2fs-tools * */ #ifndef F2FS_QUOTA_H #define F2FS_QUOTA_H enum quota_type { USRQUOTA = 0, GRPQUOTA = 1, PRJQUOTA = 2, MAXQUOTAS = 3, }; #if MAXQUOTAS > 32 #error "cannot have more than 32 quota types to fit in qtype_bits" #endif #define QUOTA_USR_BIT (1 << USRQUOTA) #define QUOTA_GRP_BIT (1 << GRPQUOTA) #define QUOTA_PRJ_BIT (1 << PRJQUOTA) #define QUOTA_ALL_BIT (QUOTA_USR_BIT | QUOTA_GRP_BIT | QUOTA_PRJ_BIT) /* * Definitions of magics and versions of current quota files */ #define INITQMAGICS {\ 0xd9c01f11, /* USRQUOTA */\ 0xd9c01927, /* GRPQUOTA */\ 0xd9c03f14 /* PRJQUOTA */\ } #define V2_DQINFOOFF sizeof(struct v2_disk_dqheader) /* Offset of info header in file */ #define MAX_IQ_TIME 604800 /* (7*24*60*60) 1 week */ #define MAX_DQ_TIME 604800 /* (7*24*60*60) 1 week */ #define QT_TREEOFF 1 /* Offset of tree in file in blocks */ #ifdef ANDROID_WINDOWS_HOST #pragma pack(1) #endif struct v2_disk_dqheader { u_int32_t dqh_magic; /* Magic number identifying file */ u_int32_t dqh_version; /* File version */ } __attribute__ ((packed)); /* Header with type and version specific information */ #ifdef ANDROID_WINDOWS_HOST #pragma pack(1) #endif struct v2_disk_dqinfo { u_int32_t dqi_bgrace; /* Time before block soft limit becomes hard limit */ u_int32_t dqi_igrace; /* Time before inode soft limit becomes hard limit */ u_int32_t dqi_flags; /* Flags for quotafile (DQF_*) */ u_int32_t dqi_blocks; /* Number of blocks in file */ u_int32_t dqi_free_blk; /* Number of first free block in the list */ u_int32_t dqi_free_entry; /* Number of block with at least one free entry */ } __attribute__ ((packed)); #ifdef ANDROID_WINDOWS_HOST #pragma pack(1) #endif struct v2r1_disk_dqblk { __le32 dqb_id; /* id this quota applies to */ __le32 dqb_pad; __le64 dqb_ihardlimit; /* absolute limit on allocated inodes */ __le64 dqb_isoftlimit; /* preferred inode limit */ __le64 dqb_curinodes; /* current # allocated inodes */ __le64 dqb_bhardlimit; /* absolute limit on disk space * (in QUOTABLOCK_SIZE) */ __le64 dqb_bsoftlimit; /* preferred limit on disk space * (in QUOTABLOCK_SIZE) */ __le64 dqb_curspace; /* current space occupied (in bytes) */ __le64 dqb_btime; /* time limit for excessive disk use */ __le64 dqb_itime; /* time limit for excessive inode use */ } __attribute__ ((packed)); #endif f2fs-tools-1.10.0/lib/000077500000000000000000000000001323417143600143415ustar00rootroot00000000000000f2fs-tools-1.10.0/lib/Makefile.am000066400000000000000000000004131323417143600163730ustar00rootroot00000000000000## Makefile.am lib_LTLIBRARIES = libf2fs.la libf2fs_la_SOURCES = libf2fs.c libf2fs_io.c libf2fs_zoned.c libf2fs_la_CFLAGS = -Wall libf2fs_la_CPPFLAGS = -I$(top_srcdir)/include libf2fs_la_LDFLAGS = -version-info $(LIBF2FS_CURRENT):$(LIBF2FS_REVISION):$(LIBF2FS_AGE) f2fs-tools-1.10.0/lib/libf2fs.c000066400000000000000000000561471323417143600160510ustar00rootroot00000000000000/** * libf2fs.c * * Copyright (c) 2013 Samsung Electronics Co., Ltd. * http://www.samsung.com/ * * Dual licensed under the GPL or LGPL version 2 licenses. */ #define _LARGEFILE64_SOURCE #include #include #include #include #include #include #include #ifdef HAVE_MNTENT_H #include #endif #include #include #ifndef ANDROID_WINDOWS_HOST #include #include #endif #ifdef HAVE_SYS_SYSMACROS_H #include #endif #ifndef WITH_ANDROID #ifdef HAVE_SCSI_SG_H #include #endif #endif #ifdef HAVE_LINUX_HDREG_H #include #endif #ifdef HAVE_LINUX_LIMITS_H #include #endif #ifndef WITH_ANDROID /* SCSI command for standard inquiry*/ #define MODELINQUIRY 0x12,0x00,0x00,0x00,0x4A,0x00 #endif #ifndef ANDROID_WINDOWS_HOST /* O_BINARY is windows-specific flag */ #define O_BINARY 0 #else /* On Windows, wchar_t is 8 bit sized and it causes compilation errors. */ #define wchar_t int #endif /* * UTF conversion codes are Copied from exfat tools. */ static const char *utf8_to_wchar(const char *input, wchar_t *wc, size_t insize) { if ((input[0] & 0x80) == 0 && insize >= 1) { *wc = (wchar_t) input[0]; return input + 1; } if ((input[0] & 0xe0) == 0xc0 && insize >= 2) { *wc = (((wchar_t) input[0] & 0x1f) << 6) | ((wchar_t) input[1] & 0x3f); return input + 2; } if ((input[0] & 0xf0) == 0xe0 && insize >= 3) { *wc = (((wchar_t) input[0] & 0x0f) << 12) | (((wchar_t) input[1] & 0x3f) << 6) | ((wchar_t) input[2] & 0x3f); return input + 3; } if ((input[0] & 0xf8) == 0xf0 && insize >= 4) { *wc = (((wchar_t) input[0] & 0x07) << 18) | (((wchar_t) input[1] & 0x3f) << 12) | (((wchar_t) input[2] & 0x3f) << 6) | ((wchar_t) input[3] & 0x3f); return input + 4; } if ((input[0] & 0xfc) == 0xf8 && insize >= 5) { *wc = (((wchar_t) input[0] & 0x03) << 24) | (((wchar_t) input[1] & 0x3f) << 18) | (((wchar_t) input[2] & 0x3f) << 12) | (((wchar_t) input[3] & 0x3f) << 6) | ((wchar_t) input[4] & 0x3f); return input + 5; } if ((input[0] & 0xfe) == 0xfc && insize >= 6) { *wc = (((wchar_t) input[0] & 0x01) << 30) | (((wchar_t) input[1] & 0x3f) << 24) | (((wchar_t) input[2] & 0x3f) << 18) | (((wchar_t) input[3] & 0x3f) << 12) | (((wchar_t) input[4] & 0x3f) << 6) | ((wchar_t) input[5] & 0x3f); return input + 6; } return NULL; } static u_int16_t *wchar_to_utf16(u_int16_t *output, wchar_t wc, size_t outsize) { if (wc <= 0xffff) { if (outsize == 0) return NULL; output[0] = cpu_to_le16(wc); return output + 1; } if (outsize < 2) return NULL; wc -= 0x10000; output[0] = cpu_to_le16(0xd800 | ((wc >> 10) & 0x3ff)); output[1] = cpu_to_le16(0xdc00 | (wc & 0x3ff)); return output + 2; } int utf8_to_utf16(u_int16_t *output, const char *input, size_t outsize, size_t insize) { const char *inp = input; u_int16_t *outp = output; wchar_t wc; while ((size_t)(inp - input) < insize && *inp) { inp = utf8_to_wchar(inp, &wc, insize - (inp - input)); if (inp == NULL) { DBG(0, "illegal UTF-8 sequence\n"); return -EILSEQ; } outp = wchar_to_utf16(outp, wc, outsize - (outp - output)); if (outp == NULL) { DBG(0, "name is too long\n"); return -ENAMETOOLONG; } } *outp = cpu_to_le16(0); return 0; } static const u_int16_t *utf16_to_wchar(const u_int16_t *input, wchar_t *wc, size_t insize) { if ((le16_to_cpu(input[0]) & 0xfc00) == 0xd800) { if (insize < 2 || (le16_to_cpu(input[1]) & 0xfc00) != 0xdc00) return NULL; *wc = ((wchar_t) (le16_to_cpu(input[0]) & 0x3ff) << 10); *wc |= (le16_to_cpu(input[1]) & 0x3ff); *wc += 0x10000; return input + 2; } else { *wc = le16_to_cpu(*input); return input + 1; } } static char *wchar_to_utf8(char *output, wchar_t wc, size_t outsize) { if (wc <= 0x7f) { if (outsize < 1) return NULL; *output++ = (char) wc; } else if (wc <= 0x7ff) { if (outsize < 2) return NULL; *output++ = 0xc0 | (wc >> 6); *output++ = 0x80 | (wc & 0x3f); } else if (wc <= 0xffff) { if (outsize < 3) return NULL; *output++ = 0xe0 | (wc >> 12); *output++ = 0x80 | ((wc >> 6) & 0x3f); *output++ = 0x80 | (wc & 0x3f); } else if (wc <= 0x1fffff) { if (outsize < 4) return NULL; *output++ = 0xf0 | (wc >> 18); *output++ = 0x80 | ((wc >> 12) & 0x3f); *output++ = 0x80 | ((wc >> 6) & 0x3f); *output++ = 0x80 | (wc & 0x3f); } else if (wc <= 0x3ffffff) { if (outsize < 5) return NULL; *output++ = 0xf8 | (wc >> 24); *output++ = 0x80 | ((wc >> 18) & 0x3f); *output++ = 0x80 | ((wc >> 12) & 0x3f); *output++ = 0x80 | ((wc >> 6) & 0x3f); *output++ = 0x80 | (wc & 0x3f); } else if (wc <= 0x7fffffff) { if (outsize < 6) return NULL; *output++ = 0xfc | (wc >> 30); *output++ = 0x80 | ((wc >> 24) & 0x3f); *output++ = 0x80 | ((wc >> 18) & 0x3f); *output++ = 0x80 | ((wc >> 12) & 0x3f); *output++ = 0x80 | ((wc >> 6) & 0x3f); *output++ = 0x80 | (wc & 0x3f); } else return NULL; return output; } int utf16_to_utf8(char *output, const u_int16_t *input, size_t outsize, size_t insize) { const u_int16_t *inp = input; char *outp = output; wchar_t wc; while ((size_t)(inp - input) < insize && le16_to_cpu(*inp)) { inp = utf16_to_wchar(inp, &wc, insize - (inp - input)); if (inp == NULL) { DBG(0, "illegal UTF-16 sequence\n"); return -EILSEQ; } outp = wchar_to_utf8(outp, wc, outsize - (outp - output)); if (outp == NULL) { DBG(0, "name is too long\n"); return -ENAMETOOLONG; } } *outp = '\0'; return 0; } int log_base_2(u_int32_t num) { int ret = 0; if (num <= 0 || (num & (num - 1)) != 0) return -1; while (num >>= 1) ret++; return ret; } /* * f2fs bit operations */ static const int bits_in_byte[256] = { 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, }; int get_bits_in_byte(unsigned char n) { return bits_in_byte[n]; } int test_and_set_bit_le(u32 nr, u8 *addr) { int mask, retval; addr += nr >> 3; mask = 1 << ((nr & 0x07)); retval = mask & *addr; *addr |= mask; return retval; } int test_and_clear_bit_le(u32 nr, u8 *addr) { int mask, retval; addr += nr >> 3; mask = 1 << ((nr & 0x07)); retval = mask & *addr; *addr &= ~mask; return retval; } int test_bit_le(u32 nr, const u8 *addr) { return ((1 << (nr & 7)) & (addr[nr >> 3])); } int f2fs_test_bit(unsigned int nr, const char *p) { int mask; char *addr = (char *)p; addr += (nr >> 3); mask = 1 << (7 - (nr & 0x07)); return (mask & *addr) != 0; } int f2fs_set_bit(unsigned int nr, char *addr) { int mask; int ret; addr += (nr >> 3); mask = 1 << (7 - (nr & 0x07)); ret = mask & *addr; *addr |= mask; return ret; } int f2fs_clear_bit(unsigned int nr, char *addr) { int mask; int ret; addr += (nr >> 3); mask = 1 << (7 - (nr & 0x07)); ret = mask & *addr; *addr &= ~mask; return ret; } static inline u64 __ffs(u8 word) { int num = 0; if ((word & 0xf) == 0) { num += 4; word >>= 4; } if ((word & 0x3) == 0) { num += 2; word >>= 2; } if ((word & 0x1) == 0) num += 1; return num; } /* Copied from linux/lib/find_bit.c */ #define BITMAP_FIRST_BYTE_MASK(start) (0xff << ((start) & (BITS_PER_BYTE - 1))) static u64 _find_next_bit_le(const u8 *addr, u64 nbits, u64 start, char invert) { u8 tmp; if (!nbits || start >= nbits) return nbits; tmp = addr[start / BITS_PER_BYTE] ^ invert; /* Handle 1st word. */ tmp &= BITMAP_FIRST_BYTE_MASK(start); start = round_down(start, BITS_PER_BYTE); while (!tmp) { start += BITS_PER_BYTE; if (start >= nbits) return nbits; tmp = addr[start / BITS_PER_BYTE] ^ invert; } return min(start + __ffs(tmp), nbits); } u64 find_next_bit_le(const u8 *addr, u64 size, u64 offset) { return _find_next_bit_le(addr, size, offset, 0); } u64 find_next_zero_bit_le(const u8 *addr, u64 size, u64 offset) { return _find_next_bit_le(addr, size, offset, 0xff); } /* * Hashing code adapted from ext3 */ #define DELTA 0x9E3779B9 static void TEA_transform(unsigned int buf[4], unsigned int const in[]) { __u32 sum = 0; __u32 b0 = buf[0], b1 = buf[1]; __u32 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 unsigned char *msg, int len, unsigned int *buf, int num) { unsigned pad, val; int i; pad = (__u32)len | ((__u32)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; } /** * Return hash value of directory entry * @param name dentry name * @param len name lenth * @return return on success hash value, errno on failure */ f2fs_hash_t f2fs_dentry_hash(const unsigned char *name, int len) { __u32 hash; f2fs_hash_t f2fs_hash; const unsigned char *p; __u32 in[8], buf[4]; /* special hash codes for special dentries */ if ((len <= 2) && (name[0] == '.') && (name[1] == '.' || name[1] == '\0')) return 0; /* Initialize the default seed for the hash checksum functions */ buf[0] = 0x67452301; buf[1] = 0xefcdab89; buf[2] = 0x98badcfe; buf[3] = 0x10325476; p = name; while (1) { str2hashbuf(p, len, in, 4); TEA_transform(buf, in); p += 16; if (len <= 16) break; len -= 16; } hash = buf[0]; f2fs_hash = cpu_to_le32(hash & ~F2FS_HASH_COL_BIT); return f2fs_hash; } unsigned int addrs_per_inode(struct f2fs_inode *i) { return CUR_ADDRS_PER_INODE(i) - get_inline_xattr_addrs(i); } /* * CRC32 */ #define CRCPOLY_LE 0xedb88320 u_int32_t f2fs_cal_crc32(u_int32_t crc, void *buf, int len) { int i; unsigned char *p = (unsigned char *)buf; while (len--) { crc ^= *p++; for (i = 0; i < 8; i++) crc = (crc >> 1) ^ ((crc & 1) ? CRCPOLY_LE : 0); } return crc; } int f2fs_crc_valid(u_int32_t blk_crc, void *buf, int len) { u_int32_t cal_crc = 0; cal_crc = f2fs_cal_crc32(F2FS_SUPER_MAGIC, buf, len); if (cal_crc != blk_crc) { DBG(0,"CRC validation failed: cal_crc = %u, " "blk_crc = %u buff_size = 0x%x\n", cal_crc, blk_crc, len); return -1; } return 0; } __u32 f2fs_inode_chksum(struct f2fs_node *node) { struct f2fs_inode *ri = &node->i; __le32 ino = node->footer.ino; __le32 gen = ri->i_generation; __u32 chksum, chksum_seed; __u32 dummy_cs = 0; unsigned int offset = offsetof(struct f2fs_inode, i_inode_checksum); unsigned int cs_size = sizeof(dummy_cs); chksum = f2fs_cal_crc32(c.chksum_seed, (__u8 *)&ino, sizeof(ino)); chksum_seed = f2fs_cal_crc32(chksum, (__u8 *)&gen, sizeof(gen)); chksum = f2fs_cal_crc32(chksum_seed, (__u8 *)ri, offset); chksum = f2fs_cal_crc32(chksum, (__u8 *)&dummy_cs, cs_size); offset += cs_size; chksum = f2fs_cal_crc32(chksum, (__u8 *)ri + offset, F2FS_BLKSIZE - offset); return chksum; } /* * try to identify the root device */ const char *get_rootdev() { #ifdef ANDROID_WINDOWS_HOST return NULL; #else struct stat sb; int fd, ret; char buf[32]; char *uevent, *ptr; static char rootdev[PATH_MAX + 1]; if (stat("/", &sb) == -1) return NULL; snprintf(buf, 32, "/sys/dev/block/%u:%u/uevent", major(sb.st_dev), minor(sb.st_dev)); fd = open(buf, O_RDONLY); if (fd < 0) return NULL; ret = lseek(fd, (off_t)0, SEEK_END); (void)lseek(fd, (off_t)0, SEEK_SET); if (ret == -1) { close(fd); return NULL; } uevent = malloc(ret + 1); uevent[ret] = '\0'; ret = read(fd, uevent, ret); close(fd); ptr = strstr(uevent, "DEVNAME"); if (!ptr) return NULL; ret = sscanf(ptr, "DEVNAME=%s\n", buf); snprintf(rootdev, PATH_MAX + 1, "/dev/%s", buf); return rootdev; #endif } /* * device information */ void f2fs_init_configuration(void) { int i; c.ndevs = 1; c.total_sectors = 0; c.sector_size = 0; c.sectors_per_blk = DEFAULT_SECTORS_PER_BLOCK; c.blks_per_seg = DEFAULT_BLOCKS_PER_SEGMENT; c.rootdev_name = get_rootdev(); c.wanted_total_sectors = -1; c.zoned_mode = 0; c.zoned_model = 0; c.zone_blocks = 0; #ifdef WITH_ANDROID c.preserve_limits = 0; #else c.preserve_limits = 1; #endif for (i = 0; i < MAX_DEVICES; i++) { memset(&c.devices[i], 0, sizeof(struct device_info)); c.devices[i].fd = -1; c.devices[i].sector_size = DEFAULT_SECTOR_SIZE; c.devices[i].end_blkaddr = -1; c.devices[i].zoned_model = F2FS_ZONED_NONE; } /* calculated by overprovision ratio */ c.reserved_segments = 0; c.overprovision = 0; c.segs_per_sec = 1; c.secs_per_zone = 1; c.segs_per_zone = 1; c.heap = 0; c.vol_label = ""; c.trim = 1; c.trimmed = 0; c.ro = 0; c.kd = -1; c.dry_run = 0; c.fixed_time = -1; } #ifdef HAVE_SETMNTENT static int is_mounted(const char *mpt, const char *device) { FILE *file = NULL; struct mntent *mnt = NULL; file = setmntent(mpt, "r"); if (file == NULL) return 0; while ((mnt = getmntent(file)) != NULL) { if (!strcmp(device, mnt->mnt_fsname)) { #ifdef MNTOPT_RO if (hasmntopt(mnt, MNTOPT_RO)) c.ro = 1; #endif break; } } endmntent(file); return mnt ? 1 : 0; } #endif int f2fs_dev_is_umounted(char *path) { #ifdef ANDROID_WINDOWS_HOST return 0; #else struct stat st_buf; int is_rootdev = 0; int ret = 0; if (c.rootdev_name && !strcmp(path, c.rootdev_name)) is_rootdev = 1; /* * try with /proc/mounts fist to detect RDONLY. * f2fs_stop_checkpoint makes RO in /proc/mounts while RW in /etc/mtab. */ #ifdef __linux__ ret = is_mounted("/proc/mounts", path); if (ret) { MSG(0, "Info: Mounted device!\n"); return -1; } #endif #if defined(MOUNTED) || defined(_PATH_MOUNTED) #ifndef MOUNTED #define MOUNTED _PATH_MOUNTED #endif ret = is_mounted(MOUNTED, path); if (ret) { MSG(0, "Info: Mounted device!\n"); return -1; } #endif /* * If we are supposed to operate on the root device, then * also check the mounts for '/dev/root', which sometimes * functions as an alias for the root device. */ if (is_rootdev) { #ifdef __linux__ ret = is_mounted("/proc/mounts", "/dev/root"); if (ret) { MSG(0, "Info: Mounted device!\n"); return -1; } #endif } /* * If f2fs is umounted with -l, the process can still use * the file system. In this case, we should not format. */ if (stat(path, &st_buf) == 0 && S_ISBLK(st_buf.st_mode)) { int fd = open(path, O_RDONLY | O_EXCL); if (fd >= 0) { close(fd); } else if (errno == EBUSY) { MSG(0, "\tError: In use by the system!\n"); return -1; } } return ret; #endif } int f2fs_devs_are_umounted(void) { int i; for (i = 0; i < c.ndevs; i++) if (f2fs_dev_is_umounted((char *)c.devices[i].path)) return -1; return 0; } void get_kernel_version(__u8 *version) { int i; for (i = 0; i < VERSION_LEN; i++) { if (version[i] == '\n') break; } memset(version + i, 0, VERSION_LEN + 1 - i); } #if defined(__linux__) && defined(_IO) && !defined(BLKGETSIZE) #define BLKGETSIZE _IO(0x12,96) #endif #if defined(__linux__) && defined(_IOR) && !defined(BLKGETSIZE64) #define BLKGETSIZE64 _IOR(0x12,114, size_t) #endif #if defined(__linux__) && defined(_IO) && !defined(BLKSSZGET) #define BLKSSZGET _IO(0x12,104) #endif #if defined(__APPLE__) #include #define BLKGETSIZE DKIOCGETBLOCKCOUNT #define BLKSSZGET DKIOCGETBLOCKCOUNT #endif /* APPLE_DARWIN */ #ifndef ANDROID_WINDOWS_HOST int get_device_info(int i) { int32_t fd = 0; uint32_t sector_size; #ifndef BLKGETSIZE64 uint32_t total_sectors; #endif struct stat stat_buf; #ifdef HDIO_GETGIO struct hd_geometry geom; #endif #if !defined(WITH_ANDROID) && defined(__linux__) sg_io_hdr_t io_hdr; unsigned char reply_buffer[96] = {0}; unsigned char model_inq[6] = {MODELINQUIRY}; #endif struct device_info *dev = c.devices + i; if (c.sparse_mode) { fd = open((char *)dev->path, O_RDWR | O_CREAT | O_BINARY, 0644); } else { fd = open((char *)dev->path, O_RDWR); } if (fd < 0) { MSG(0, "\tError: Failed to open the device!\n"); return -1; } dev->fd = fd; if (c.sparse_mode) { if (f2fs_init_sparse_file()) return -1; } if (c.kd == -1) { c.kd = open("/proc/version", O_RDONLY); if (c.kd < 0) { MSG(0, "\tInfo: No support kernel version!\n"); c.kd = -2; } } if (fstat(fd, &stat_buf) < 0 ) { MSG(0, "\tError: Failed to get the device stat!\n"); return -1; } if (c.sparse_mode) { dev->total_sectors = c.device_size / dev->sector_size; } else if (S_ISREG(stat_buf.st_mode)) { dev->total_sectors = stat_buf.st_size / dev->sector_size; } else if (S_ISBLK(stat_buf.st_mode)) { #ifdef BLKSSZGET if (ioctl(fd, BLKSSZGET, §or_size) < 0) MSG(0, "\tError: Using the default sector size\n"); else if (dev->sector_size < sector_size) dev->sector_size = sector_size; #endif #ifdef BLKGETSIZE64 if (ioctl(fd, BLKGETSIZE64, &dev->total_sectors) < 0) { MSG(0, "\tError: Cannot get the device size\n"); return -1; } #else if (ioctl(fd, BLKGETSIZE, &total_sectors) < 0) { MSG(0, "\tError: Cannot get the device size\n"); return -1; } dev->total_sectors = total_sectors; #endif dev->total_sectors /= dev->sector_size; if (i == 0) { #ifdef HDIO_GETGIO if (ioctl(fd, HDIO_GETGEO, &geom) < 0) c.start_sector = 0; else c.start_sector = geom.start; #else c.start_sector = 0; #endif } #if !defined(WITH_ANDROID) && defined(__linux__) /* Send INQUIRY command */ memset(&io_hdr, 0, sizeof(sg_io_hdr_t)); io_hdr.interface_id = 'S'; io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; io_hdr.dxfer_len = sizeof(reply_buffer); io_hdr.dxferp = reply_buffer; io_hdr.cmd_len = sizeof(model_inq); io_hdr.cmdp = model_inq; io_hdr.timeout = 1000; if (!ioctl(fd, SG_IO, &io_hdr)) { int i = 16; MSG(0, "Info: [%s] Disk Model: ", dev->path); while (reply_buffer[i] != '`' && i < 80) printf("%c", reply_buffer[i++]); printf("\n"); } #endif } else { MSG(0, "\tError: Volume type is not supported!!!\n"); return -1; } if (!c.sector_size) { c.sector_size = dev->sector_size; c.sectors_per_blk = F2FS_BLKSIZE / c.sector_size; } else if (c.sector_size != c.devices[i].sector_size) { MSG(0, "\tError: Different sector sizes!!!\n"); return -1; } #if !defined(WITH_ANDROID) && defined(__linux__) if (S_ISBLK(stat_buf.st_mode)) f2fs_get_zoned_model(i); if (dev->zoned_model != F2FS_ZONED_NONE) { if (dev->zoned_model == F2FS_ZONED_HM) c.zoned_model = F2FS_ZONED_HM; if (f2fs_get_zone_blocks(i)) { MSG(0, "\tError: Failed to get number of blocks per zone\n"); return -1; } if (f2fs_check_zones(i)) { MSG(0, "\tError: Failed to check zone configuration\n"); return -1; } MSG(0, "Info: Host-%s zoned block device:\n", (dev->zoned_model == F2FS_ZONED_HA) ? "aware" : "managed"); MSG(0, " %u zones, %u randomly writeable zones\n", dev->nr_zones, dev->nr_rnd_zones); MSG(0, " %lu blocks per zone\n", dev->zone_blocks); } #endif c.total_sectors += dev->total_sectors; return 0; } #else #include "windows.h" #include "winioctl.h" #if (_WIN32_WINNT >= 0x0500) #define HAVE_GET_FILE_SIZE_EX 1 #endif static int win_get_device_size(const char *file, uint64_t *device_size) { 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)) { *device_size = pi.PartitionLength.QuadPart; } else if (DeviceIoControl(dev, IOCTL_DISK_GET_DRIVE_GEOMETRY, &gi, sizeof(DISK_GEOMETRY), &gi, sizeof(DISK_GEOMETRY), &retbytes, NULL)) { *device_size = gi.BytesPerSector * gi.SectorsPerTrack * gi.TracksPerCylinder * gi.Cylinders.QuadPart; #ifdef HAVE_GET_FILE_SIZE_EX } else if (GetFileSizeEx(dev, &filesize)) { *device_size = filesize.QuadPart; } #else } else { filesize = GetFileSize(dev, NULL); if (INVALID_FILE_SIZE != filesize) return -1; *device_size = filesize; } #endif /* HAVE_GET_FILE_SIZE_EX */ CloseHandle(dev); return 0; } int get_device_info(int i) { struct device_info *dev = c.devices + i; uint64_t device_size = 0; int32_t fd = 0; /* Block device target is not supported on Windows. */ if (!c.sparse_mode) { if (win_get_device_size(dev->path, &device_size)) { MSG(0, "\tError: Failed to get device size!\n"); return -1; } } else { device_size = c.device_size; } if (c.sparse_mode) { fd = open((char *)dev->path, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644); } else { fd = open((char *)dev->path, O_RDWR | O_BINARY); } if (fd < 0) { MSG(0, "\tError: Failed to open the device!\n"); return -1; } dev->fd = fd; dev->total_sectors = device_size / dev->sector_size; c.start_sector = 0; c.sector_size = dev->sector_size; c.sectors_per_blk = F2FS_BLKSIZE / c.sector_size; c.total_sectors += dev->total_sectors; return 0; } #endif int f2fs_get_device_info(void) { int i; for (i = 0; i < c.ndevs; i++) if (get_device_info(i)) return -1; if (c.wanted_total_sectors < c.total_sectors) { MSG(0, "Info: total device sectors = %"PRIu64" (in %u bytes)\n", c.total_sectors, c.sector_size); c.total_sectors = c.wanted_total_sectors; c.devices[0].total_sectors = c.total_sectors; } if (c.total_sectors * c.sector_size > (u_int64_t)F2FS_MAX_SEGMENT * 2 * 1024 * 1024) { MSG(0, "\tError: F2FS can support 16TB at most!!!\n"); return -1; } for (i = 0; i < c.ndevs; i++) { if (c.devices[i].zoned_model != F2FS_ZONED_NONE) { if (c.zone_blocks && c.zone_blocks != c.devices[i].zone_blocks) { MSG(0, "\tError: not support different zone sizes!!!\n"); return -1; } c.zone_blocks = c.devices[i].zone_blocks; } } /* * Align sections to the device zone size * and align F2FS zones to the device zones. */ if (c.zone_blocks) { c.segs_per_sec = c.zone_blocks / DEFAULT_BLOCKS_PER_SEGMENT; c.secs_per_zone = 1; } else { c.zoned_mode = 0; } c.segs_per_zone = c.segs_per_sec * c.secs_per_zone; MSG(0, "Info: Segments per section = %d\n", c.segs_per_sec); MSG(0, "Info: Sections per zone = %d\n", c.secs_per_zone); MSG(0, "Info: sector size = %u\n", c.sector_size); MSG(0, "Info: total sectors = %"PRIu64" (%"PRIu64" MB)\n", c.total_sectors, (c.total_sectors * (c.sector_size >> 9)) >> 11); return 0; } f2fs-tools-1.10.0/lib/libf2fs_io.c000066400000000000000000000172251323417143600165320ustar00rootroot00000000000000/** * libf2fs.c * * Copyright (c) 2013 Samsung Electronics Co., Ltd. * http://www.samsung.com/ * * Dual licensed under the GPL or LGPL version 2 licenses. */ #define _LARGEFILE64_SOURCE #include #include #include #include #include #include #ifdef HAVE_MNTENT_H #include #endif #include #ifndef ANDROID_WINDOWS_HOST #include #include #include #endif #ifdef HAVE_LINUX_HDREG_H #include #endif #include struct f2fs_configuration c; #ifdef WITH_ANDROID #include struct sparse_file *f2fs_sparse_file; static char **blocks; u_int64_t blocks_count; #endif static int __get_device_fd(__u64 *offset) { __u64 blk_addr = *offset >> F2FS_BLKSIZE_BITS; int i; for (i = 0; i < c.ndevs; i++) { if (c.devices[i].start_blkaddr <= blk_addr && c.devices[i].end_blkaddr >= blk_addr) { *offset -= c.devices[i].start_blkaddr << F2FS_BLKSIZE_BITS; return c.devices[i].fd; } } return -1; } #ifndef HAVE_LSEEK64 typedef off_t off64_t; static inline off64_t lseek64(int fd, __u64 offset, int set) { return lseek(fd, offset, set); } #endif /* * IO interfaces */ int dev_read_version(void *buf, __u64 offset, size_t len) { if (c.sparse_mode) return 0; if (lseek64(c.kd, (off64_t)offset, SEEK_SET) < 0) return -1; if (read(c.kd, buf, len) < 0) return -1; return 0; } #ifdef WITH_ANDROID static int sparse_read_blk(__u64 block, int count, void *buf) { int i; char *out = buf; __u64 cur_block; for (i = 0; i < count; ++i) { cur_block = block + i; if (blocks[cur_block]) memcpy(out + (i * F2FS_BLKSIZE), blocks[cur_block], F2FS_BLKSIZE); else if (blocks) memset(out + (i * F2FS_BLKSIZE), 0, F2FS_BLKSIZE); } return 0; } static int sparse_write_blk(__u64 block, int count, const void *buf) { int i; __u64 cur_block; const char *in = buf; for (i = 0; i < count; ++i) { cur_block = block + i; if (!blocks[cur_block]) { blocks[cur_block] = calloc(1, F2FS_BLKSIZE); if (!blocks[cur_block]) return -ENOMEM; } memcpy(blocks[cur_block], in + (i * F2FS_BLKSIZE), F2FS_BLKSIZE); } return 0; } static int sparse_import_segment(void *UNUSED(priv), const void *data, int len, unsigned int block, unsigned int nr_blocks) { /* Ignore chunk headers, only write the data */ if (!nr_blocks || len % F2FS_BLKSIZE) return 0; return sparse_write_blk(block, nr_blocks, data); } static int sparse_merge_blocks(uint64_t start, uint64_t num) { char *buf; uint64_t i; buf = calloc(num, F2FS_BLKSIZE); if (!buf) { fprintf(stderr, "failed to alloc %llu\n", (unsigned long long)num * F2FS_BLKSIZE); return -ENOMEM; } for (i = 0; i < num; i++) { memcpy(buf + i * F2FS_BLKSIZE, blocks[start + i], F2FS_BLKSIZE); free(blocks[start + i]); blocks[start + i] = NULL; } /* free_sparse_blocks will release this buf. */ blocks[start] = buf; return sparse_file_add_data(f2fs_sparse_file, blocks[start], F2FS_BLKSIZE * num, start); } #else static int sparse_read_blk(__u64 block, int count, void *buf) { return 0; } static int sparse_write_blk(__u64 block, int count, const void *buf) { return 0; } #endif int dev_read(void *buf, __u64 offset, size_t len) { int fd; if (c.sparse_mode) return sparse_read_blk(offset / F2FS_BLKSIZE, len / F2FS_BLKSIZE, buf); fd = __get_device_fd(&offset); if (fd < 0) return fd; if (lseek64(fd, (off64_t)offset, SEEK_SET) < 0) return -1; if (read(fd, buf, len) < 0) return -1; return 0; } #ifdef POSIX_FADV_WILLNEED int dev_readahead(__u64 offset, size_t len) #else int dev_readahead(__u64 offset, size_t UNUSED(len)) #endif { int fd = __get_device_fd(&offset); if (fd < 0) return fd; #ifdef POSIX_FADV_WILLNEED return posix_fadvise(fd, offset, len, POSIX_FADV_WILLNEED); #else return 0; #endif } int dev_write(void *buf, __u64 offset, size_t len) { int fd; if (c.dry_run) return 0; if (c.sparse_mode) return sparse_write_blk(offset / F2FS_BLKSIZE, len / F2FS_BLKSIZE, buf); fd = __get_device_fd(&offset); if (fd < 0) return fd; if (lseek64(fd, (off64_t)offset, SEEK_SET) < 0) return -1; if (write(fd, buf, len) < 0) return -1; return 0; } int dev_write_block(void *buf, __u64 blk_addr) { return dev_write(buf, blk_addr << F2FS_BLKSIZE_BITS, F2FS_BLKSIZE); } int dev_write_dump(void *buf, __u64 offset, size_t len) { if (lseek64(c.dump_fd, (off64_t)offset, SEEK_SET) < 0) return -1; if (write(c.dump_fd, buf, len) < 0) return -1; return 0; } int dev_fill(void *buf, __u64 offset, size_t len) { int fd; if (c.sparse_mode) return 0; fd = __get_device_fd(&offset); if (fd < 0) return fd; /* Only allow fill to zero */ if (*((__u8*)buf)) return -1; if (lseek64(fd, (off64_t)offset, SEEK_SET) < 0) return -1; if (write(fd, buf, len) < 0) return -1; return 0; } int dev_fill_block(void *buf, __u64 blk_addr) { return dev_fill(buf, blk_addr << F2FS_BLKSIZE_BITS, F2FS_BLKSIZE); } int dev_read_block(void *buf, __u64 blk_addr) { return dev_read(buf, blk_addr << F2FS_BLKSIZE_BITS, F2FS_BLKSIZE); } int dev_reada_block(__u64 blk_addr) { return dev_readahead(blk_addr << F2FS_BLKSIZE_BITS, F2FS_BLKSIZE); } int f2fs_fsync_device(void) { #ifndef ANDROID_WINDOWS_HOST int i; for (i = 0; i < c.ndevs; i++) { if (fsync(c.devices[i].fd) < 0) { MSG(0, "\tError: Could not conduct fsync!!!\n"); return -1; } } #endif return 0; } int f2fs_init_sparse_file(void) { #ifdef WITH_ANDROID if (c.func == MKFS) { f2fs_sparse_file = sparse_file_new(F2FS_BLKSIZE, c.device_size); } else { f2fs_sparse_file = sparse_file_import(c.devices[0].fd, true, false); if (!f2fs_sparse_file) return -1; c.device_size = sparse_file_len(f2fs_sparse_file, 0, 0); c.device_size &= (~((u_int64_t)(F2FS_BLKSIZE - 1))); } if (sparse_file_block_size(f2fs_sparse_file) != F2FS_BLKSIZE) { MSG(0, "\tError: Corrupted sparse file\n"); return -1; } blocks_count = c.device_size / F2FS_BLKSIZE; blocks = calloc(blocks_count, sizeof(char *)); return sparse_file_foreach_chunk(f2fs_sparse_file, true, false, sparse_import_segment, NULL); #else MSG(0, "\tError: Sparse mode is only supported for android\n"); return -1; #endif } int f2fs_finalize_device(void) { int i; int ret = 0; #ifdef WITH_ANDROID if (c.sparse_mode) { int64_t chunk_start = (blocks[0] == NULL) ? -1 : 0; uint64_t j; if (c.func != MKFS) { sparse_file_destroy(f2fs_sparse_file); ret = ftruncate(c.devices[0].fd, 0); ASSERT(!ret); lseek(c.devices[0].fd, 0, SEEK_SET); f2fs_sparse_file = sparse_file_new(F2FS_BLKSIZE, c.device_size); } for (j = 0; j < blocks_count; ++j) { if (!blocks[j] && chunk_start != -1) { ret = sparse_merge_blocks(chunk_start, j - chunk_start); chunk_start = -1; } else if (blocks[j] && chunk_start == -1) { chunk_start = j; } ASSERT(!ret); } if (chunk_start != -1) { ret = sparse_merge_blocks(chunk_start, blocks_count - chunk_start); ASSERT(!ret); } sparse_file_write(f2fs_sparse_file, c.devices[0].fd, /*gzip*/0, /*sparse*/1, /*crc*/0); sparse_file_destroy(f2fs_sparse_file); for (j = 0; j < blocks_count; j++) free(blocks[j]); free(blocks); blocks = NULL; f2fs_sparse_file = NULL; } #endif /* * We should call fsync() to flush out all the dirty pages * in the block device page cache. */ for (i = 0; i < c.ndevs; i++) { #ifndef ANDROID_WINDOWS_HOST ret = fsync(c.devices[i].fd); if (ret < 0) { MSG(0, "\tError: Could not conduct fsync!!!\n"); break; } #endif ret = close(c.devices[i].fd); if (ret < 0) { MSG(0, "\tError: Failed to close device file!!!\n"); break; } } close(c.kd); return ret; } f2fs-tools-1.10.0/lib/libf2fs_zoned.c000066400000000000000000000146661323417143600172500ustar00rootroot00000000000000/** * libf2fs_zoned.c * * Copyright (c) 2016 Western Digital Corporation. * Written by: Damien Le Moal * * Dual licensed under the GPL or LGPL version 2 licenses. */ #define _LARGEFILE64_SOURCE #include #include #include #include #include #include #include #ifndef ANDROID_WINDOWS_HOST #include #endif #include #include #ifdef HAVE_LINUX_BLKZONED_H void f2fs_get_zoned_model(int i) { struct device_info *dev = c.devices + i; char str[128]; FILE *file; int res; /* Check that this is a zoned block device */ snprintf(str, sizeof(str), "/sys/block/%s/queue/zoned", basename(dev->path)); file = fopen(str, "r"); if (!file) goto not_zoned; memset(str, 0, sizeof(str)); res = fscanf(file, "%s", str); fclose(file); if (res != 1) goto not_zoned; if (strcmp(str, "host-aware") == 0) { dev->zoned_model = F2FS_ZONED_HA; return; } if (strcmp(str, "host-managed") == 0) { dev->zoned_model = F2FS_ZONED_HM; return; } not_zoned: dev->zoned_model = F2FS_ZONED_NONE; } int f2fs_get_zone_blocks(int i) { struct device_info *dev = c.devices + i; uint64_t sectors; char str[128]; FILE *file; int res; /* Get zone size */ dev->zone_blocks = 0; snprintf(str, sizeof(str), "/sys/block/%s/queue/chunk_sectors", basename(dev->path)); file = fopen(str, "r"); if (!file) return -1; memset(str, 0, sizeof(str)); res = fscanf(file, "%s", str); fclose(file); if (res != 1) return -1; sectors = atol(str); if (!sectors) return -1; dev->zone_blocks = sectors >> (F2FS_BLKSIZE_BITS - 9); sectors = (sectors << 9) / c.sector_size; /* * Total number of zones: there may * be a last smaller runt zone. */ dev->nr_zones = dev->total_sectors / sectors; if (dev->total_sectors % sectors) dev->nr_zones++; return 0; } #define F2FS_REPORT_ZONES_BUFSZ 524288 int f2fs_check_zones(int j) { struct device_info *dev = c.devices + j; struct blk_zone_report *rep; struct blk_zone *blkz; unsigned int i, n = 0; u_int64_t total_sectors; u_int64_t sector; int last_is_conv = 1; int ret = -1; rep = malloc(F2FS_REPORT_ZONES_BUFSZ); if (!rep) { ERR_MSG("No memory for report zones\n"); return -ENOMEM; } dev->nr_rnd_zones = 0; sector = 0; total_sectors = (dev->total_sectors * c.sector_size) >> 9; while (sector < total_sectors) { /* Get zone info */ memset(rep, 0, F2FS_REPORT_ZONES_BUFSZ); rep->sector = sector; rep->nr_zones = (F2FS_REPORT_ZONES_BUFSZ - sizeof(struct blk_zone_report)) / sizeof(struct blk_zone); ret = ioctl(dev->fd, BLKREPORTZONE, rep); if (ret != 0) { ret = -errno; ERR_MSG("ioctl BLKREPORTZONE failed\n"); goto out; } if (!rep->nr_zones) break; blkz = (struct blk_zone *)(rep + 1); for (i = 0; i < rep->nr_zones && sector < total_sectors; i++) { if (blk_zone_cond(blkz) == BLK_ZONE_COND_READONLY || blk_zone_cond(blkz) == BLK_ZONE_COND_OFFLINE) last_is_conv = 0; if (blk_zone_conv(blkz) || blk_zone_seq_pref(blkz)) { if (last_is_conv) dev->nr_rnd_zones++; } else { last_is_conv = 0; } if (blk_zone_conv(blkz)) { DBG(2, "Zone %05u: Conventional, cond 0x%x (%s), sector %llu, %llu sectors\n", n, blk_zone_cond(blkz), blk_zone_cond_str(blkz), blk_zone_sector(blkz), blk_zone_length(blkz)); } else { DBG(2, "Zone %05u: type 0x%x (%s), cond 0x%x (%s), need_reset %d, " "non_seq %d, sector %llu, %llu sectors, wp sector %llu\n", n, blk_zone_type(blkz), blk_zone_type_str(blkz), blk_zone_cond(blkz), blk_zone_cond_str(blkz), blk_zone_need_reset(blkz), blk_zone_non_seq(blkz), blk_zone_sector(blkz), blk_zone_length(blkz), blk_zone_wp_sector(blkz)); } sector = blk_zone_sector(blkz) + blk_zone_length(blkz); n++; blkz++; } } if (sector != total_sectors) { ERR_MSG("Invalid zones: last sector reported is %llu, expected %llu\n", (unsigned long long)(sector << 9) / c.sector_size, (unsigned long long)dev->total_sectors); ret = -1; goto out; } if (n != dev->nr_zones) { ERR_MSG("Inconsistent number of zones: expected %u zones, got %u\n", dev->nr_zones, n); ret = -1; goto out; } if (dev->zoned_model == F2FS_ZONED_HM && !dev->nr_rnd_zones) { ERR_MSG("No conventional zone for super block\n"); ret = -1; } out: free(rep); return ret; } int f2fs_reset_zones(int j) { struct device_info *dev = c.devices + j; struct blk_zone_report *rep; struct blk_zone *blkz; struct blk_zone_range range; u_int64_t total_sectors; u_int64_t sector; unsigned int i; int ret = -1; rep = malloc(F2FS_REPORT_ZONES_BUFSZ); if (!rep) { ERR_MSG("No memory for report zones\n"); return -1; } sector = 0; total_sectors = (dev->total_sectors * c.sector_size) >> 9; while (sector < total_sectors) { /* Get zone info */ memset(rep, 0, F2FS_REPORT_ZONES_BUFSZ); rep->sector = sector; rep->nr_zones = (F2FS_REPORT_ZONES_BUFSZ - sizeof(struct blk_zone_report)) / sizeof(struct blk_zone); ret = ioctl(dev->fd, BLKREPORTZONE, rep); if (ret != 0) { ret = -errno; ERR_MSG("ioctl BLKREPORTZONES failed\n"); goto out; } if (!rep->nr_zones) break; blkz = (struct blk_zone *)(rep + 1); for (i = 0; i < rep->nr_zones && sector < total_sectors; i++) { if (blk_zone_seq(blkz) && !blk_zone_empty(blkz)) { /* Non empty sequential zone: reset */ range.sector = blk_zone_sector(blkz); range.nr_sectors = blk_zone_length(blkz); ret = ioctl(dev->fd, BLKRESETZONE, &range); if (ret != 0) { ret = -errno; ERR_MSG("ioctl BLKRESETZONE failed\n"); goto out; } } sector = blk_zone_sector(blkz) + blk_zone_length(blkz); blkz++; } } out: free(rep); if (!ret) MSG(0, "Info: Discarded %"PRIu64" MB\n", (sector << 9) >> 20); return ret; } #else void f2fs_get_zoned_model(int i) { struct device_info *dev = c.devices + i; c.zoned_mode = 0; dev->zoned_model = F2FS_ZONED_NONE; } int f2fs_get_zone_blocks(int i) { struct device_info *dev = c.devices + i; c.zoned_mode = 0; dev->nr_zones = 0; dev->zone_blocks = 0; dev->zoned_model = F2FS_ZONED_NONE; return 0; } int f2fs_check_zones(int i) { ERR_MSG("%d: Zoned block devices are not supported\n", i); return -1; } int f2fs_reset_zones(int i) { ERR_MSG("%d: Zoned block devices are not supported\n", i); return -1; } #endif f2fs-tools-1.10.0/m4/000077500000000000000000000000001323417143600141135ustar00rootroot00000000000000f2fs-tools-1.10.0/m4/.gitignore000066400000000000000000000000061323417143600160770ustar00rootroot00000000000000 *.m4 f2fs-tools-1.10.0/man/000077500000000000000000000000001323417143600143465ustar00rootroot00000000000000f2fs-tools-1.10.0/man/Makefile.am000066400000000000000000000001551323417143600164030ustar00rootroot00000000000000## Makefile.am dist_man_MANS = mkfs.f2fs.8 fsck.f2fs.8 dump.f2fs.8 defrag.f2fs.8 resize.f2fs.8 sload.f2fs.8 f2fs-tools-1.10.0/man/defrag.f2fs.8000066400000000000000000000031211323417143600165230ustar00rootroot00000000000000.\" Copyright (c) 2015 Jaegeuk Kim .\" .TH DEFRAG.F2FS 8 .SH NAME defrag.f2fs \- relocate blocks in a given area to the specified region .SH SYNOPSIS .B defrag.f2fs [ .B \-s .I start block address ] [ .B \-l .I number of blocks ] [ .B \-t .I target block address ] [ .B \-i .I direction ] [ .B \-d .I debugging-level ] .I device .SH DESCRIPTION .B defrag.f2fs is used to move specified number of blocks starting from a given block address to the target block address with a direction. \fIdevice\fP is the special file corresponding to the device (e.g. \fI/dev/sdXX\fP). For example, # defrag.f2fs -s 0x4000 -l 0x100 -t 0x10000 -i /dev/sdb1 This moves blocks between 0x4000 and 0x4100 to the left-hand area of 0x10000. .PP The exit code returned by .B defrag.f2fs is 0 on success and -1 on failure. .SH OPTIONS .TP .BI \-s " start block address" Specify the starting block address. .TP .BI \-l " number of blocks" Specifiy the number of blocks to move. .TP .BI \-t " target block address" Specify the destination block address. .TP .BI \-i " direction" Set the direction to left. If it is not set, the direction becomes right by default. .TP .BI \-d " debug-level" Specify the level of debugging options. The default number is 0, which shows basic debugging messages. .TP .SH AUTHOR This version of .B defrag.f2fs has been written by Jaegeuk Kim . .SH AVAILABILITY .B defrag.f2fs is available from git://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs-tools.git. .SH SEE ALSO .BR mkfs.f2fs(8), .BR dump.f2fs(8), .BR fsck.f2fs(8), .BR resize.f2fs(8), .BR sload.f2fs(8). f2fs-tools-1.10.0/man/dump.f2fs.8000066400000000000000000000030221323417143600162400ustar00rootroot00000000000000.\" Copyright (c) 2013 Samsung Electronics Co., Ltd. .\" .TH DUMP.F2FS 8 .SH NAME dump.f2fs \- retrieve directory and file entries from an F2FS-formated image .SH SYNOPSIS .B dump.f2fs [ .B \-i .I inode number ] [ .B \-s .I SIT range ] [ .B \-a .I SSA range ] [ .B \-b .I block address ] [ .B \-d .I debugging-level ] .I device .SH DESCRIPTION .B dump.f2fs is used to retrieve f2fs metadata (usually in a disk partition). \fIdevice\fP is the special file corresponding to the device (e.g. \fI/dev/sdXX\fP). Currently, it can retrieve 1) a file given its inode number, 2) SIT entries into a file, 3) SSA entries into a file, 4) reverse information from the given block address. .PP The exit code returned by .B dump.f2fs is 0 on success and -1 on failure. .SH OPTIONS .TP .BI \-i " inode number" Specify an inode number to dump out. .TP .BI \-s " SIT range" Specify a range presented by segment numbers to dump SIT entries. .TP .BI \-a " SSA range" Specify a range presented by segment numbers to dump SSA entries. .TP .BI \-b " block address" Specify a block address to retrieve its metadata information. .TP .BI \-d " debug-level" Specify the level of debugging options. The default number is 0, which shows basic debugging messages. .TP .SH AUTHOR Initial checking code was written by Byoung Geun Kim . .SH AVAILABILITY .B dump.f2fs is available from git://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs-tools.git. .SH SEE ALSO .BR mkfs.f2fs(8), .BR fsck.f2fs(8), .BR defrag.f2fs(8), .BR resize.f2fs(8), .BR sload.f2fs(8). f2fs-tools-1.10.0/man/fsck.f2fs.8000066400000000000000000000030331323417143600162230ustar00rootroot00000000000000.\" Copyright (c) 2013 Samsung Electronics Co., Ltd. .\" .TH FSCK.F2FS 8 .SH NAME fsck.f2fs \- check a Linux F2FS file system .SH SYNOPSIS .B fsck.f2fs [ .B \-a .I enable auto fix ] [ .B \-f .I enable force fix ] [ .B \-p .I enable preen mode ] [ .B \-t .I show stored directory tree ] [ .B \-d .I debugging-level ] .I device .SH DESCRIPTION .B fsck.f2fs is used to check an f2fs file system (usually in a disk partition). \fIdevice\fP is the special file corresponding to the device (e.g. \fI/dev/sdXX\fP). .PP The exit code returned by .B fsck.f2fs is 0 on success and -1 on failure. .SH OPTIONS .TP .BI \-a " enable auto fix" Enable to run file system check only if a bug was reported by the F2FS kernel module. It is disabled by default. .TP .BI \-f " enable force fix" Enable to fix all the inconsistency in the partition. .TP .BI \-p " enable preen mode" Same as "-a" to support general fsck convention. .TP .BI \-t " show stored directory tree" Enable to show every directory entries in the partition. .TP .BI \-d " debug-level" Specify the level of debugging options. The default number is 0, which shows basic debugging messages. .TP .SH AUTHOR Initial checking code was written by Byoung Geun Kim . Jaegeuk Kim reworked most parts of the codes to support fixing any corrupted images. .SH AVAILABILITY .B fsck.f2fs is available from git://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs-tools.git. .SH SEE ALSO .BR mkfs.f2fs(8), .BR dump.f2fs(8), .BR defrag.f2fs(8), .BR resize.f2fs(8), .BR sload.f2fs(8). f2fs-tools-1.10.0/man/mkfs.f2fs.8000066400000000000000000000071731323417143600162460ustar00rootroot00000000000000.\" Copyright (c) 2012 Samsung Electronics Co., Ltd. .\" http://www.samsung.com/ .\" Written by Jaegeuk Kim .\" .TH MKFS.F2FS 8 .SH NAME mkfs.f2fs \- create an F2FS file system .SH SYNOPSIS .B mkfs.f2fs [ .B \-a .I heap-based-allocation ] [ .B \-c .I device ] [ .B \-d .I debugging-level ] [ .B \-e .I extension-list ] [ .B \-f ] [ .B \-l .I volume-label ] [ .B \-m ] [ .B \-o .I overprovision-ratio-percentage ] [ .B \-O .I feature-list ] [ .B \-q ] [ .B \-s .I #-of-segments-per-section ] [ .B \-t .I nodiscard/discard ] [ .B \-z .I #-of-sections-per-zone ] .I device .I [sectors] .SH DESCRIPTION .B mkfs.f2fs is used to create a f2fs file system (usually in a disk partition). \fIdevice\fP is the special file corresponding to the device (e.g. \fI/dev/sdXX\fP). \fIsectors\fP is optionally given for specifing the filesystem size. .PP The exit code returned by .B mkfs.f2fs is 0 on success and 1 on failure. .SH OPTIONS .TP .BI \-a " heap-based-allocation" Specify 1 or 0 to enable/disable heap based block allocation policy. If the value is equal to 1, each of active log areas are initially assigned separately according to the whole volume size. The default value is 1. .TP .BI \-c " device" Build f2fs with this device additionally, so that user can see all the devices as one big volume. .TP .BI \-d " debug-level" Specify the level of debugging options. The default number is 0, which shows basic debugging messages. .TP .BI \-e " extension-list" Specify a file extension list in order f2fs to treat them as cold files. The data of files having those extensions will be stored to the cold log. The default list includes most of multimedia file extensions such as jpg, gif, mpeg, mkv, and so on. .TP .BI \-f Force overwrite when an existing filesystem is detected on the device. By default, mkfs.f2fs will not write to the device if it suspects that there is a filesystem or partition table on the device already. .TP .BI \-l " volume-label" Specify the volume label to the partition mounted as F2FS. .TP .BI \-m Specify f2fs filesystem to supports the block zoned feature. Without it, the filesystem isn't supports the feature. .TP .BI \-o " overprovision-ratio-percentage" Specify the percentage over the volume size for overprovision area. This area is hidden to users, and utilized by F2FS cleaner. If not specified, the best number will be assigned automatically accoring to the partition size. .TP .BI \-O " feature-list" Specify a feature list in order f2fs filesystem will supports. e.g "encrypt" and so on. .TP .BI \-q Quiet mode. With it, mkfs.f2fs does not show any messages include the basic messages. .TP .BI \-s " #-of-segments-per-section" Specify the number of segments per section. A section consists of multiple consecutive segments, and is the unit of garbage collection. The default number is 1, which means one segment is assigned to a section. .TP .BI \-t " nodiscard/discard" Specify 1 or 0 to enable/disable discard policy. If the value is equal to 1, discard policy is enabled, otherwise is disable. The default value is 1. .TP .BI \-z " #-of-sections-per-zone" Specify the number of sections per zone. A zone consists of multiple sections. F2FS allocates segments for active logs with separated zones as much as possible. The default number is 1, which means a zone consists of one section. .TP .SH AUTHOR This version of .B mkfs.f2fs has been written by Jaegeuk Kim . .SH AVAILABILITY .B mkfs.f2fs is available from git://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs-tools.git. .SH SEE ALSO .BR mkfs (8), .BR fsck.f2fs(8), .BR dump.f2fs(8), .BR defrag.f2fs(8), .BR resize.f2fs(8), .BR sload.f2fs(8). f2fs-tools-1.10.0/man/resize.f2fs.8000066400000000000000000000020711323417143600165770ustar00rootroot00000000000000.\" Copyright (c) 2015 Jaegeuk Kim .\" .TH RESIZE.F2FS 8 .SH NAME resize.f2fs \- resize filesystem size .SH SYNOPSIS .B resize.f2fs [ .B \-t .I target sectors ] [ .B \-d .I debugging-level ] .I device .SH DESCRIPTION .B resize.f2fs is used to resize an f2fs file system (usually in a disk partition). \fIdevice\fP is the special file corresponding to the device (e.g. \fI/dev/sdXX\fP). Current version only supports expanding the prebuilt filesystem. .PP The exit code returned by .B resize.f2fs is 0 on success and -1 on failure. .SH OPTIONS .TP .BI \-t " target sectors" Specify the size in sectors. .TP .BI \-d " debug-level" Specify the level of debugging options. The default number is 0, which shows basic debugging messages. .TP .SH AUTHOR This version of .B resize.f2fs has been written by Jaegeuk Kim . .SH AVAILABILITY .B resize.f2fs is available from git://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs-tools.git. .SH SEE ALSO .BR mkfs.f2fs(8), .BR fsck.f2fs(8), .BR dump.f2fs(8), .BR defrag.f2fs(8), .BR sload.f2fs(8). f2fs-tools-1.10.0/man/sload.f2fs.8000066400000000000000000000023161323417143600164020ustar00rootroot00000000000000.\" Copyright (C) 2015 Huawei Ltd. .\" .TH SLOAD.F2FS 8 .SH NAME sload.f2fs \- load directories and files into the device directly .SH SYNOPSIS .B sload.f2fs [ .B \-f .I source directory path ] [ .B \-t .I mount point ] [ .B \-d .I debugging-level ] .I device .SH DESCRIPTION .B sload.f2fs is used to load directories and files into a disk partition. \fIdevice\fP is the special file corresponding to the device (e.g. \fI/dev/sdXX\fP). .PP The exit code returned by .B sload.f2fs is 0 on success and -1 on failure. .SH OPTIONS .TP .BI \-f " source directory path" Specify the source directory path to be loaded. .TP .BI \-t " mount point path" Specify the mount point path in the partition to load. .TP .BI \-d " debug-level" Specify the level of debugging options. The default number is 0, which shows basic debugging messages. .TP .SH AUTHOR This version of .B sload.f2fs has been written by Hou Pengyang , Liu Shuoran , Jaegeuk Kim .SH AVAILABILITY .B sload.f2fs is available from git://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs-tools.git. .SH SEE ALSO .BR mkfs.f2fs(8), .BR fsck.f2fs(8), .BR dump.f2fs(8), .BR defrag.f2fs(8), .BR resize.f2fs(8). f2fs-tools-1.10.0/mkfs/000077500000000000000000000000001323417143600145335ustar00rootroot00000000000000f2fs-tools-1.10.0/mkfs/Makefile.am000066400000000000000000000013361323417143600165720ustar00rootroot00000000000000## Makefile.am AM_CPPFLAGS = ${libuuid_CFLAGS} ${libblkid_CFLAGS} -I$(top_srcdir)/include AM_CFLAGS = -Wall -DWITH_BLKDISCARD sbin_PROGRAMS = mkfs.f2fs noinst_HEADERS = f2fs_format_utils.h include_HEADERS = $(top_srcdir)/include/f2fs_fs.h mkfs_f2fs_SOURCES = f2fs_format_main.c f2fs_format.c f2fs_format_utils.c mkfs_f2fs_LDADD = ${libuuid_LIBS} ${libblkid_LIBS} $(top_builddir)/lib/libf2fs.la lib_LTLIBRARIES = libf2fs_format.la libf2fs_format_la_SOURCES = f2fs_format_main.c f2fs_format.c f2fs_format_utils.c libf2fs_format_la_CFLAGS = -DWITH_BLKDISCARD libf2fs_format_la_CPPFLAGS = -I$(top_srcdir)/include libf2fs_format_la_LDFLAGS = -luuid -L$(top_builddir)/lib -lf2fs \ -version-info $(FMT_CURRENT):$(FMT_REVISION):$(FMT_AGE) f2fs-tools-1.10.0/mkfs/f2fs_format.c000066400000000000000000001152541323417143600171170ustar00rootroot00000000000000/** * f2fs_format.c * * Copyright (c) 2012 Samsung Electronics Co., Ltd. * http://www.samsung.com/ * * Dual licensed under the GPL or LGPL version 2 licenses. */ #define _LARGEFILE64_SOURCE #include #include #include #include #include #ifndef ANDROID_WINDOWS_HOST #include #include #endif #include #include #include "f2fs_fs.h" #include "quota.h" #include "f2fs_format_utils.h" extern struct f2fs_configuration c; struct f2fs_super_block raw_sb; struct f2fs_super_block *sb = &raw_sb; struct f2fs_checkpoint *cp; /* Return first segment number of each area */ #define prev_zone(cur) (c.cur_seg[cur] - c.segs_per_zone) #define next_zone(cur) (c.cur_seg[cur] + c.segs_per_zone) #define last_zone(cur) ((cur - 1) * c.segs_per_zone) #define last_section(cur) (cur + (c.secs_per_zone - 1) * c.segs_per_sec) static unsigned int quotatype_bits = 0; const char *media_ext_lists[] = { "jpg", "gif", "png", "avi", "divx", "mp4", "mp3", "3gp", "wmv", "wma", "mpeg", "mkv", "mov", "asx", "asf", "wmx", "svi", "wvx", "wm", "mpg", "mpe", "rm", "ogg", "jpeg", "video", "apk", /* for android system */ "so", /* for android system */ NULL }; static bool is_extension_exist(const char *name) { int i; for (i = 0; i < F2FS_MAX_EXTENSION; i++) { char *ext = (char *)sb->extension_list[i]; if (!strcmp(ext, name)) return 1; } return 0; } static void cure_extension_list(void) { const char **extlist = media_ext_lists; char *ext_str = c.extension_list; char *ue; int name_len; int i = 0; set_sb(extension_count, 0); memset(sb->extension_list, 0, sizeof(sb->extension_list)); while (*extlist) { name_len = strlen(*extlist); memcpy(sb->extension_list[i++], *extlist, name_len); extlist++; } set_sb(extension_count, i); if (!ext_str) return; /* add user ext list */ ue = strtok(ext_str, ", "); while (ue != NULL) { name_len = strlen(ue); if (name_len >= 8) { MSG(0, "\tWarn: Extension name (%s) is too long\n", ue); goto next; } if (!is_extension_exist(ue)) memcpy(sb->extension_list[i++], ue, name_len); next: ue = strtok(NULL, ", "); if (i >= F2FS_MAX_EXTENSION) break; } set_sb(extension_count, i); free(c.extension_list); } static void verify_cur_segs(void) { int i, j; int reorder = 0; for (i = 0; i < NR_CURSEG_TYPE; i++) { for (j = i + 1; j < NR_CURSEG_TYPE; j++) { if (c.cur_seg[i] == c.cur_seg[j]) { reorder = 1; break; } } } if (!reorder) return; c.cur_seg[0] = 0; for (i = 1; i < NR_CURSEG_TYPE; i++) c.cur_seg[i] = next_zone(i - 1); } static int f2fs_prepare_super_block(void) { u_int32_t blk_size_bytes; u_int32_t log_sectorsize, log_sectors_per_block; u_int32_t log_blocksize, log_blks_per_seg; u_int32_t segment_size_bytes, zone_size_bytes; u_int32_t sit_segments; u_int32_t blocks_for_sit, blocks_for_nat, blocks_for_ssa; u_int32_t total_valid_blks_available; u_int64_t zone_align_start_offset, diff; u_int64_t total_meta_zones, total_meta_segments; u_int32_t sit_bitmap_size, max_sit_bitmap_size; u_int32_t max_nat_bitmap_size, max_nat_segments; u_int32_t total_zones; u_int32_t next_ino; enum quota_type qtype; int i; set_sb(magic, F2FS_SUPER_MAGIC); set_sb(major_ver, F2FS_MAJOR_VERSION); set_sb(minor_ver, F2FS_MINOR_VERSION); log_sectorsize = log_base_2(c.sector_size); log_sectors_per_block = log_base_2(c.sectors_per_blk); log_blocksize = log_sectorsize + log_sectors_per_block; log_blks_per_seg = log_base_2(c.blks_per_seg); set_sb(log_sectorsize, log_sectorsize); set_sb(log_sectors_per_block, log_sectors_per_block); set_sb(log_blocksize, log_blocksize); set_sb(log_blocks_per_seg, log_blks_per_seg); set_sb(segs_per_sec, c.segs_per_sec); set_sb(secs_per_zone, c.secs_per_zone); blk_size_bytes = 1 << log_blocksize; segment_size_bytes = blk_size_bytes * c.blks_per_seg; zone_size_bytes = blk_size_bytes * c.secs_per_zone * c.segs_per_sec * c.blks_per_seg; set_sb(checksum_offset, 0); set_sb(block_count, c.total_sectors >> log_sectors_per_block); zone_align_start_offset = (c.start_sector * c.sector_size + 2 * F2FS_BLKSIZE + zone_size_bytes - 1) / zone_size_bytes * zone_size_bytes - c.start_sector * c.sector_size; if (c.start_sector % c.sectors_per_blk) { MSG(1, "\t%s: Align start sector number to the page unit\n", c.zoned_mode ? "FAIL" : "WARN"); MSG(1, "\ti.e., start sector: %d, ofs:%d (sects/page: %d)\n", c.start_sector, c.start_sector % c.sectors_per_blk, c.sectors_per_blk); if (c.zoned_mode) return -1; } set_sb(segment0_blkaddr, zone_align_start_offset / blk_size_bytes); sb->cp_blkaddr = sb->segment0_blkaddr; MSG(0, "Info: zone aligned segment0 blkaddr: %u\n", get_sb(segment0_blkaddr)); if (c.zoned_mode && (get_sb(segment0_blkaddr) + c.start_sector / c.sectors_per_blk) % c.zone_blocks) { MSG(1, "\tError: Unaligned segment0 block address %u\n", get_sb(segment0_blkaddr)); return -1; } for (i = 0; i < c.ndevs; i++) { if (i == 0) { c.devices[i].total_segments = (c.devices[i].total_sectors * c.sector_size - zone_align_start_offset) / segment_size_bytes; c.devices[i].start_blkaddr = 0; c.devices[i].end_blkaddr = c.devices[i].total_segments * c.blks_per_seg - 1 + sb->segment0_blkaddr; } else { c.devices[i].total_segments = c.devices[i].total_sectors / (c.sectors_per_blk * c.blks_per_seg); c.devices[i].start_blkaddr = c.devices[i - 1].end_blkaddr + 1; c.devices[i].end_blkaddr = c.devices[i].start_blkaddr + c.devices[i].total_segments * c.blks_per_seg - 1; } if (c.ndevs > 1) { memcpy(sb->devs[i].path, c.devices[i].path, MAX_PATH_LEN); sb->devs[i].total_segments = cpu_to_le32(c.devices[i].total_segments); } c.total_segments += c.devices[i].total_segments; } set_sb(segment_count, (c.total_segments / c.segs_per_zone * c.segs_per_zone)); set_sb(segment_count_ckpt, F2FS_NUMBER_OF_CHECKPOINT_PACK); set_sb(sit_blkaddr, get_sb(segment0_blkaddr) + get_sb(segment_count_ckpt) * c.blks_per_seg); blocks_for_sit = SIZE_ALIGN(get_sb(segment_count), SIT_ENTRY_PER_BLOCK); sit_segments = SEG_ALIGN(blocks_for_sit); set_sb(segment_count_sit, sit_segments * 2); set_sb(nat_blkaddr, get_sb(sit_blkaddr) + get_sb(segment_count_sit) * c.blks_per_seg); total_valid_blks_available = (get_sb(segment_count) - (get_sb(segment_count_ckpt) + get_sb(segment_count_sit))) * c.blks_per_seg; blocks_for_nat = SIZE_ALIGN(total_valid_blks_available, NAT_ENTRY_PER_BLOCK); set_sb(segment_count_nat, SEG_ALIGN(blocks_for_nat)); /* * The number of node segments should not be exceeded a "Threshold". * This number resizes NAT bitmap area in a CP page. * So the threshold is determined not to overflow one CP page */ sit_bitmap_size = ((get_sb(segment_count_sit) / 2) << log_blks_per_seg) / 8; if (sit_bitmap_size > MAX_SIT_BITMAP_SIZE) max_sit_bitmap_size = MAX_SIT_BITMAP_SIZE; else max_sit_bitmap_size = sit_bitmap_size; /* * It should be reserved minimum 1 segment for nat. * When sit is too large, we should expand cp area. It requires more * pages for cp. */ if (max_sit_bitmap_size > MAX_SIT_BITMAP_SIZE_IN_CKPT) { max_nat_bitmap_size = CHECKSUM_OFFSET - sizeof(struct f2fs_checkpoint) + 1; set_sb(cp_payload, F2FS_BLK_ALIGN(max_sit_bitmap_size)); } else { max_nat_bitmap_size = CHECKSUM_OFFSET - sizeof(struct f2fs_checkpoint) + 1 - max_sit_bitmap_size; set_sb(cp_payload, 0); } max_nat_segments = (max_nat_bitmap_size * 8) >> log_blks_per_seg; if (get_sb(segment_count_nat) > max_nat_segments) set_sb(segment_count_nat, max_nat_segments); set_sb(segment_count_nat, get_sb(segment_count_nat) * 2); set_sb(ssa_blkaddr, get_sb(nat_blkaddr) + get_sb(segment_count_nat) * c.blks_per_seg); total_valid_blks_available = (get_sb(segment_count) - (get_sb(segment_count_ckpt) + get_sb(segment_count_sit) + get_sb(segment_count_nat))) * c.blks_per_seg; blocks_for_ssa = total_valid_blks_available / c.blks_per_seg + 1; set_sb(segment_count_ssa, SEG_ALIGN(blocks_for_ssa)); total_meta_segments = get_sb(segment_count_ckpt) + get_sb(segment_count_sit) + get_sb(segment_count_nat) + get_sb(segment_count_ssa); diff = total_meta_segments % (c.segs_per_zone); if (diff) set_sb(segment_count_ssa, get_sb(segment_count_ssa) + (c.segs_per_zone - diff)); total_meta_zones = ZONE_ALIGN(total_meta_segments * c.blks_per_seg); set_sb(main_blkaddr, get_sb(segment0_blkaddr) + total_meta_zones * c.segs_per_zone * c.blks_per_seg); if (c.zoned_mode) { /* * Make sure there is enough randomly writeable * space at the beginning of the disk. */ unsigned long main_blkzone = get_sb(main_blkaddr) / c.zone_blocks; if (c.devices[0].zoned_model == F2FS_ZONED_HM && c.devices[0].nr_rnd_zones < main_blkzone) { MSG(0, "\tError: Device does not have enough random " "write zones for F2FS volume (%lu needed)\n", main_blkzone); return -1; } } total_zones = get_sb(segment_count) / (c.segs_per_zone) - total_meta_zones; set_sb(section_count, total_zones * c.secs_per_zone); set_sb(segment_count_main, get_sb(section_count) * c.segs_per_sec); /* Let's determine the best reserved and overprovisioned space */ if (c.overprovision == 0) c.overprovision = get_best_overprovision(sb); if (c.overprovision == 0 || c.total_segments < F2FS_MIN_SEGMENTS || (c.devices[0].total_sectors * c.sector_size < zone_align_start_offset) || (get_sb(segment_count_main) - 2) < c.reserved_segments) { MSG(0, "\tError: Device size is not sufficient for F2FS volume\n"); return -1; } c.reserved_segments = (2 * (100 / c.overprovision + 1) + 6) * c.segs_per_sec; uuid_generate(sb->uuid); /* precompute checksum seed for metadata */ if (c.feature & cpu_to_le32(F2FS_FEATURE_INODE_CHKSUM)) c.chksum_seed = f2fs_cal_crc32(~0, sb->uuid, sizeof(sb->uuid)); utf8_to_utf16(sb->volume_name, (const char *)c.vol_label, MAX_VOLUME_NAME, strlen(c.vol_label)); set_sb(node_ino, 1); set_sb(meta_ino, 2); set_sb(root_ino, 3); next_ino = 4; if (c.feature & cpu_to_le32(F2FS_FEATURE_QUOTA_INO)) { quotatype_bits = QUOTA_USR_BIT | QUOTA_GRP_BIT; if (c.feature & cpu_to_le32(F2FS_FEATURE_PRJQUOTA)) quotatype_bits |= QUOTA_PRJ_BIT; } for (qtype = 0; qtype < F2FS_MAX_QUOTAS; qtype++) { if (!((1 << qtype) & quotatype_bits)) continue; sb->qf_ino[qtype] = cpu_to_le32(next_ino++); MSG(0, "Info: add quota type = %u => %u\n", qtype, next_ino - 1); } if (total_zones <= 6) { MSG(1, "\tError: %d zones: Need more zones " "by shrinking zone size\n", total_zones); return -1; } if (c.heap) { c.cur_seg[CURSEG_HOT_NODE] = last_section(last_zone(total_zones)); c.cur_seg[CURSEG_WARM_NODE] = prev_zone(CURSEG_HOT_NODE); c.cur_seg[CURSEG_COLD_NODE] = prev_zone(CURSEG_WARM_NODE); c.cur_seg[CURSEG_HOT_DATA] = prev_zone(CURSEG_COLD_NODE); c.cur_seg[CURSEG_COLD_DATA] = 0; c.cur_seg[CURSEG_WARM_DATA] = next_zone(CURSEG_COLD_DATA); } else { c.cur_seg[CURSEG_HOT_NODE] = 0; c.cur_seg[CURSEG_WARM_NODE] = next_zone(CURSEG_HOT_NODE); c.cur_seg[CURSEG_COLD_NODE] = next_zone(CURSEG_WARM_NODE); c.cur_seg[CURSEG_HOT_DATA] = next_zone(CURSEG_COLD_NODE); c.cur_seg[CURSEG_COLD_DATA] = max(last_zone((total_zones >> 2)), next_zone(CURSEG_COLD_NODE)); c.cur_seg[CURSEG_WARM_DATA] = max(last_zone((total_zones >> 1)), next_zone(CURSEG_COLD_DATA)); } /* if there is redundancy, reassign it */ verify_cur_segs(); cure_extension_list(); /* get kernel version */ if (c.kd >= 0) { dev_read_version(c.version, 0, VERSION_LEN); get_kernel_version(c.version); MSG(0, "Info: format version with\n \"%s\"\n", c.version); } else { memset(c.version, 0, VERSION_LEN); } memcpy(sb->version, c.version, VERSION_LEN); memcpy(sb->init_version, c.version, VERSION_LEN); sb->feature = c.feature; return 0; } static int f2fs_init_sit_area(void) { u_int32_t blk_size, seg_size; u_int32_t index = 0; u_int64_t sit_seg_addr = 0; u_int8_t *zero_buf = NULL; blk_size = 1 << get_sb(log_blocksize); seg_size = (1 << get_sb(log_blocks_per_seg)) * blk_size; zero_buf = calloc(sizeof(u_int8_t), seg_size); if(zero_buf == NULL) { MSG(1, "\tError: Calloc Failed for sit_zero_buf!!!\n"); return -1; } sit_seg_addr = get_sb(sit_blkaddr); sit_seg_addr *= blk_size; DBG(1, "\tFilling sit area at offset 0x%08"PRIx64"\n", sit_seg_addr); for (index = 0; index < (get_sb(segment_count_sit) / 2); index++) { if (dev_fill(zero_buf, sit_seg_addr, seg_size)) { MSG(1, "\tError: While zeroing out the sit area " "on disk!!!\n"); free(zero_buf); return -1; } sit_seg_addr += seg_size; } free(zero_buf); return 0 ; } static int f2fs_init_nat_area(void) { u_int32_t blk_size, seg_size; u_int32_t index = 0; u_int64_t nat_seg_addr = 0; u_int8_t *nat_buf = NULL; blk_size = 1 << get_sb(log_blocksize); seg_size = (1 << get_sb(log_blocks_per_seg)) * blk_size; nat_buf = calloc(sizeof(u_int8_t), seg_size); if (nat_buf == NULL) { MSG(1, "\tError: Calloc Failed for nat_zero_blk!!!\n"); return -1; } nat_seg_addr = get_sb(nat_blkaddr); nat_seg_addr *= blk_size; DBG(1, "\tFilling nat area at offset 0x%08"PRIx64"\n", nat_seg_addr); for (index = 0; index < get_sb(segment_count_nat) / 2; index++) { if (dev_fill(nat_buf, nat_seg_addr, seg_size)) { MSG(1, "\tError: While zeroing out the nat area " "on disk!!!\n"); free(nat_buf); return -1; } nat_seg_addr = nat_seg_addr + (2 * seg_size); } free(nat_buf); return 0 ; } static int f2fs_write_check_point_pack(void) { struct f2fs_summary_block *sum = NULL; struct f2fs_journal *journal; u_int32_t blk_size_bytes; u_int32_t nat_bits_bytes, nat_bits_blocks; unsigned char *nat_bits = NULL, *empty_nat_bits; u_int64_t cp_seg_blk = 0; u_int32_t crc = 0, flags; unsigned int i; char *cp_payload = NULL; char *sum_compact, *sum_compact_p; struct f2fs_summary *sum_entry; enum quota_type qtype; u_int32_t quota_inum, quota_dnum; int off; int ret = -1; cp = calloc(F2FS_BLKSIZE, 1); if (cp == NULL) { MSG(1, "\tError: Calloc Failed for f2fs_checkpoint!!!\n"); return ret; } sum = calloc(F2FS_BLKSIZE, 1); if (sum == NULL) { MSG(1, "\tError: Calloc Failed for summay_node!!!\n"); goto free_cp; } sum_compact = calloc(F2FS_BLKSIZE, 1); if (sum_compact == NULL) { MSG(1, "\tError: Calloc Failed for summay buffer!!!\n"); goto free_sum; } sum_compact_p = sum_compact; nat_bits_bytes = get_sb(segment_count_nat) << 5; nat_bits_blocks = F2FS_BYTES_TO_BLK((nat_bits_bytes << 1) + 8 + F2FS_BLKSIZE - 1); nat_bits = calloc(F2FS_BLKSIZE, nat_bits_blocks); if (nat_bits == NULL) { MSG(1, "\tError: Calloc Failed for nat bits buffer!!!\n"); goto free_sum_compact; } cp_payload = calloc(F2FS_BLKSIZE, 1); if (cp_payload == NULL) { MSG(1, "\tError: Calloc Failed for cp_payload!!!\n"); goto free_nat_bits; } /* 1. cp page 1 of checkpoint pack 1 */ srand(time(NULL)); cp->checkpoint_ver = cpu_to_le64(rand() | 0x1); set_cp(cur_node_segno[0], c.cur_seg[CURSEG_HOT_NODE]); set_cp(cur_node_segno[1], c.cur_seg[CURSEG_WARM_NODE]); set_cp(cur_node_segno[2], c.cur_seg[CURSEG_COLD_NODE]); set_cp(cur_data_segno[0], c.cur_seg[CURSEG_HOT_DATA]); set_cp(cur_data_segno[1], c.cur_seg[CURSEG_WARM_DATA]); set_cp(cur_data_segno[2], c.cur_seg[CURSEG_COLD_DATA]); for (i = 3; i < MAX_ACTIVE_NODE_LOGS; i++) { set_cp(cur_node_segno[i], 0xffffffff); set_cp(cur_data_segno[i], 0xffffffff); } quota_inum = quota_dnum = 0; for (qtype = 0; qtype < F2FS_MAX_QUOTAS; qtype++) if (sb->qf_ino[qtype]) { quota_inum++; quota_dnum += QUOTA_DATA(qtype); } set_cp(cur_node_blkoff[0], 1 + quota_inum); set_cp(cur_data_blkoff[0], 1 + quota_dnum); set_cp(valid_block_count, 2 + quota_inum + quota_dnum); set_cp(rsvd_segment_count, c.reserved_segments); set_cp(overprov_segment_count, (get_sb(segment_count_main) - get_cp(rsvd_segment_count)) * c.overprovision / 100); set_cp(overprov_segment_count, get_cp(overprov_segment_count) + get_cp(rsvd_segment_count)); MSG(0, "Info: Overprovision ratio = %.3lf%%\n", c.overprovision); MSG(0, "Info: Overprovision segments = %u (GC reserved = %u)\n", get_cp(overprov_segment_count), c.reserved_segments); /* main segments - reserved segments - (node + data segments) */ set_cp(free_segment_count, get_sb(segment_count_main) - 6); set_cp(user_block_count, ((get_cp(free_segment_count) + 6 - get_cp(overprov_segment_count)) * c.blks_per_seg)); /* cp page (2), data summaries (1), node summaries (3) */ set_cp(cp_pack_total_block_count, 6 + get_sb(cp_payload)); flags = CP_UMOUNT_FLAG | CP_COMPACT_SUM_FLAG; if (get_cp(cp_pack_total_block_count) <= (1 << get_sb(log_blocks_per_seg)) - nat_bits_blocks) flags |= CP_NAT_BITS_FLAG; if (c.trimmed) flags |= CP_TRIMMED_FLAG; set_cp(ckpt_flags, flags); set_cp(cp_pack_start_sum, 1 + get_sb(cp_payload)); set_cp(valid_node_count, 1 + quota_inum); set_cp(valid_inode_count, 1 + quota_inum); set_cp(next_free_nid, get_sb(root_ino) + 1 + quota_inum); set_cp(sit_ver_bitmap_bytesize, ((get_sb(segment_count_sit) / 2) << get_sb(log_blocks_per_seg)) / 8); set_cp(nat_ver_bitmap_bytesize, ((get_sb(segment_count_nat) / 2) << get_sb(log_blocks_per_seg)) / 8); set_cp(checksum_offset, CHECKSUM_OFFSET); crc = f2fs_cal_crc32(F2FS_SUPER_MAGIC, cp, CHECKSUM_OFFSET); *((__le32 *)((unsigned char *)cp + CHECKSUM_OFFSET)) = cpu_to_le32(crc); blk_size_bytes = 1 << get_sb(log_blocksize); if (blk_size_bytes != F2FS_BLKSIZE) { MSG(1, "\tError: Wrong block size %d / %d!!!\n", blk_size_bytes, F2FS_BLKSIZE); goto free_cp_payload; } cp_seg_blk = get_sb(segment0_blkaddr); DBG(1, "\tWriting main segments, cp at offset 0x%08"PRIx64"\n", cp_seg_blk); if (dev_write_block(cp, cp_seg_blk)) { MSG(1, "\tError: While writing the cp to disk!!!\n"); goto free_cp_payload; } for (i = 0; i < get_sb(cp_payload); i++) { cp_seg_blk++; if (dev_fill_block(cp_payload, cp_seg_blk)) { MSG(1, "\tError: While zeroing out the sit bitmap area " "on disk!!!\n"); goto free_cp_payload; } } /* Prepare and write Segment summary for HOT/WARM/COLD DATA * * The structure of compact summary * +-------------------+ * | nat_journal | * +-------------------+ * | sit_journal | * +-------------------+ * | hot data summary | * +-------------------+ * | warm data summary | * +-------------------+ * | cold data summary | * +-------------------+ */ memset(sum, 0, sizeof(struct f2fs_summary_block)); SET_SUM_TYPE((&sum->footer), SUM_TYPE_DATA); journal = &sum->journal; journal->n_nats = cpu_to_le16(1 + quota_inum); journal->nat_j.entries[0].nid = sb->root_ino; journal->nat_j.entries[0].ne.version = 0; journal->nat_j.entries[0].ne.ino = sb->root_ino; journal->nat_j.entries[0].ne.block_addr = cpu_to_le32( get_sb(main_blkaddr) + get_cp(cur_node_segno[0]) * c.blks_per_seg); for (qtype = 0, i = 1; qtype < F2FS_MAX_QUOTAS; qtype++) { if (sb->qf_ino[qtype] == 0) continue; journal->nat_j.entries[i].nid = sb->qf_ino[qtype]; journal->nat_j.entries[i].ne.version = 0; journal->nat_j.entries[i].ne.ino = sb->qf_ino[qtype]; journal->nat_j.entries[i].ne.block_addr = cpu_to_le32( get_sb(main_blkaddr) + get_cp(cur_node_segno[0]) * c.blks_per_seg + i); i++; } memcpy(sum_compact_p, &journal->n_nats, SUM_JOURNAL_SIZE); sum_compact_p += SUM_JOURNAL_SIZE; memset(sum, 0, sizeof(struct f2fs_summary_block)); /* inode sit for root */ journal->n_sits = cpu_to_le16(6); journal->sit_j.entries[0].segno = cp->cur_node_segno[0]; journal->sit_j.entries[0].se.vblocks = cpu_to_le16((CURSEG_HOT_NODE << 10) | (1 + quota_inum)); f2fs_set_bit(0, (char *)journal->sit_j.entries[0].se.valid_map); for (i = 1; i <= quota_inum; i++) f2fs_set_bit(i, (char *)journal->sit_j.entries[0].se.valid_map); journal->sit_j.entries[1].segno = cp->cur_node_segno[1]; journal->sit_j.entries[1].se.vblocks = cpu_to_le16((CURSEG_WARM_NODE << 10)); journal->sit_j.entries[2].segno = cp->cur_node_segno[2]; journal->sit_j.entries[2].se.vblocks = cpu_to_le16((CURSEG_COLD_NODE << 10)); /* data sit for root */ journal->sit_j.entries[3].segno = cp->cur_data_segno[0]; journal->sit_j.entries[3].se.vblocks = cpu_to_le16((CURSEG_HOT_DATA << 10) | (1 + quota_dnum)); f2fs_set_bit(0, (char *)journal->sit_j.entries[3].se.valid_map); for (i = 1; i <= quota_dnum; i++) f2fs_set_bit(i, (char *)journal->sit_j.entries[3].se.valid_map); journal->sit_j.entries[4].segno = cp->cur_data_segno[1]; journal->sit_j.entries[4].se.vblocks = cpu_to_le16((CURSEG_WARM_DATA << 10)); journal->sit_j.entries[5].segno = cp->cur_data_segno[2]; journal->sit_j.entries[5].se.vblocks = cpu_to_le16((CURSEG_COLD_DATA << 10)); memcpy(sum_compact_p, &journal->n_sits, SUM_JOURNAL_SIZE); sum_compact_p += SUM_JOURNAL_SIZE; /* hot data summary */ sum_entry = (struct f2fs_summary *)sum_compact_p; sum_entry->nid = sb->root_ino; sum_entry->ofs_in_node = 0; off = 1; for (qtype = 0; qtype < F2FS_MAX_QUOTAS; qtype++) { if (sb->qf_ino[qtype] == 0) continue; int j; for (j = 0; j < QUOTA_DATA(qtype); j++) { (sum_entry + off + j)->nid = sb->qf_ino[qtype]; (sum_entry + off + j)->ofs_in_node = j; } off += QUOTA_DATA(qtype); } /* warm data summary, nothing to do */ /* cold data summary, nothing to do */ cp_seg_blk++; DBG(1, "\tWriting Segment summary for HOT/WARM/COLD_DATA, at offset 0x%08"PRIx64"\n", cp_seg_blk); if (dev_write_block(sum_compact, cp_seg_blk)) { MSG(1, "\tError: While writing the sum_blk to disk!!!\n"); goto free_cp_payload; } /* Prepare and write Segment summary for HOT_NODE */ memset(sum, 0, sizeof(struct f2fs_summary_block)); SET_SUM_TYPE((&sum->footer), SUM_TYPE_NODE); sum->entries[0].nid = sb->root_ino; sum->entries[0].ofs_in_node = 0; for (qtype = i = 0; qtype < F2FS_MAX_QUOTAS; qtype++) { if (sb->qf_ino[qtype] == 0) continue; sum->entries[1 + i].nid = sb->qf_ino[qtype]; sum->entries[1 + i].ofs_in_node = 0; i++; } cp_seg_blk++; DBG(1, "\tWriting Segment summary for HOT_NODE, at offset 0x%08"PRIx64"\n", cp_seg_blk); if (dev_write_block(sum, cp_seg_blk)) { MSG(1, "\tError: While writing the sum_blk to disk!!!\n"); goto free_cp_payload; } /* Fill segment summary for WARM_NODE to zero. */ memset(sum, 0, sizeof(struct f2fs_summary_block)); SET_SUM_TYPE((&sum->footer), SUM_TYPE_NODE); cp_seg_blk++; DBG(1, "\tWriting Segment summary for WARM_NODE, at offset 0x%08"PRIx64"\n", cp_seg_blk); if (dev_write_block(sum, cp_seg_blk)) { MSG(1, "\tError: While writing the sum_blk to disk!!!\n"); goto free_cp_payload; } /* Fill segment summary for COLD_NODE to zero. */ memset(sum, 0, sizeof(struct f2fs_summary_block)); SET_SUM_TYPE((&sum->footer), SUM_TYPE_NODE); cp_seg_blk++; DBG(1, "\tWriting Segment summary for COLD_NODE, at offset 0x%08"PRIx64"\n", cp_seg_blk); if (dev_write_block(sum, cp_seg_blk)) { MSG(1, "\tError: While writing the sum_blk to disk!!!\n"); goto free_cp_payload; } /* cp page2 */ cp_seg_blk++; DBG(1, "\tWriting cp page2, at offset 0x%08"PRIx64"\n", cp_seg_blk); if (dev_write_block(cp, cp_seg_blk)) { MSG(1, "\tError: While writing the cp to disk!!!\n"); goto free_cp_payload; } /* write NAT bits, if possible */ if (flags & CP_NAT_BITS_FLAG) { uint32_t i; *(__le64 *)nat_bits = get_cp_crc(cp); empty_nat_bits = nat_bits + 8 + nat_bits_bytes; memset(empty_nat_bits, 0xff, nat_bits_bytes); test_and_clear_bit_le(0, empty_nat_bits); /* write the last blocks in cp pack */ cp_seg_blk = get_sb(segment0_blkaddr) + (1 << get_sb(log_blocks_per_seg)) - nat_bits_blocks; DBG(1, "\tWriting NAT bits pages, at offset 0x%08"PRIx64"\n", cp_seg_blk); for (i = 0; i < nat_bits_blocks; i++) { if (dev_write_block(nat_bits + i * F2FS_BLKSIZE, cp_seg_blk + i)) { MSG(1, "\tError: write NAT bits to disk!!!\n"); goto free_cp_payload; } } } /* cp page 1 of check point pack 2 * Initiatialize other checkpoint pack with version zero */ cp->checkpoint_ver = 0; crc = f2fs_cal_crc32(F2FS_SUPER_MAGIC, cp, CHECKSUM_OFFSET); *((__le32 *)((unsigned char *)cp + CHECKSUM_OFFSET)) = cpu_to_le32(crc); cp_seg_blk = get_sb(segment0_blkaddr) + c.blks_per_seg; DBG(1, "\tWriting cp page 1 of checkpoint pack 2, at offset 0x%08"PRIx64"\n", cp_seg_blk); if (dev_write_block(cp, cp_seg_blk)) { MSG(1, "\tError: While writing the cp to disk!!!\n"); goto free_cp_payload; } for (i = 0; i < get_sb(cp_payload); i++) { cp_seg_blk++; if (dev_fill_block(cp_payload, cp_seg_blk)) { MSG(1, "\tError: While zeroing out the sit bitmap area " "on disk!!!\n"); goto free_cp_payload; } } /* cp page 2 of check point pack 2 */ cp_seg_blk += (le32_to_cpu(cp->cp_pack_total_block_count) - get_sb(cp_payload) - 1); DBG(1, "\tWriting cp page 2 of checkpoint pack 2, at offset 0x%08"PRIx64"\n", cp_seg_blk); if (dev_write_block(cp, cp_seg_blk)) { MSG(1, "\tError: While writing the cp to disk!!!\n"); goto free_cp_payload; } ret = 0; free_cp_payload: free(cp_payload); free_nat_bits: free(nat_bits); free_sum_compact: free(sum_compact); free_sum: free(sum); free_cp: free(cp); return ret; } static int f2fs_write_super_block(void) { int index; u_int8_t *zero_buff; zero_buff = calloc(F2FS_BLKSIZE, 1); memcpy(zero_buff + F2FS_SUPER_OFFSET, sb, sizeof(*sb)); DBG(1, "\tWriting super block, at offset 0x%08x\n", 0); for (index = 0; index < 2; index++) { if (dev_write_block(zero_buff, index)) { MSG(1, "\tError: While while writing supe_blk " "on disk!!! index : %d\n", index); free(zero_buff); return -1; } } free(zero_buff); return 0; } #ifndef WITH_ANDROID static int discard_obsolete_dnode(struct f2fs_node *raw_node, u_int64_t offset) { u_int64_t next_blkaddr = 0; u64 end_blkaddr = (get_sb(segment_count_main) << get_sb(log_blocks_per_seg)) + get_sb(main_blkaddr); u_int64_t start_inode_pos = get_sb(main_blkaddr); u_int64_t last_inode_pos; enum quota_type qtype; u_int32_t quota_inum = 0; for (qtype = 0; qtype < F2FS_MAX_QUOTAS; qtype++) if (sb->qf_ino[qtype]) quota_inum++; /* only root inode was written before truncating dnodes */ last_inode_pos = start_inode_pos + c.cur_seg[CURSEG_HOT_NODE] * c.blks_per_seg + quota_inum; if (c.zoned_mode) return 0; do { if (offset < get_sb(main_blkaddr) || offset >= end_blkaddr) break; if (dev_read_block(raw_node, offset)) { MSG(1, "\tError: While traversing direct node!!!\n"); return -1; } next_blkaddr = le32_to_cpu(raw_node->footer.next_blkaddr); memset(raw_node, 0, F2FS_BLKSIZE); DBG(1, "\tDiscard dnode, at offset 0x%08"PRIx64"\n", offset); if (dev_write_block(raw_node, offset)) { MSG(1, "\tError: While discarding direct node!!!\n"); return -1; } offset = next_blkaddr; /* should avoid recursive chain due to stale data */ if (offset >= start_inode_pos || offset <= last_inode_pos) break; } while (1); return 0; } #endif static int f2fs_write_root_inode(void) { struct f2fs_node *raw_node = NULL; u_int64_t blk_size_bytes, data_blk_nor; u_int64_t main_area_node_seg_blk_offset = 0; raw_node = calloc(F2FS_BLKSIZE, 1); if (raw_node == NULL) { MSG(1, "\tError: Calloc Failed for raw_node!!!\n"); return -1; } raw_node->footer.nid = sb->root_ino; raw_node->footer.ino = sb->root_ino; raw_node->footer.cp_ver = cpu_to_le64(1); raw_node->footer.next_blkaddr = cpu_to_le32( get_sb(main_blkaddr) + c.cur_seg[CURSEG_HOT_NODE] * c.blks_per_seg + 1); raw_node->i.i_mode = cpu_to_le16(0x41ed); raw_node->i.i_links = cpu_to_le32(2); raw_node->i.i_uid = cpu_to_le32(getuid()); raw_node->i.i_gid = cpu_to_le32(getgid()); blk_size_bytes = 1 << get_sb(log_blocksize); raw_node->i.i_size = cpu_to_le64(1 * blk_size_bytes); /* dentry */ raw_node->i.i_blocks = cpu_to_le64(2); raw_node->i.i_atime = cpu_to_le32(time(NULL)); raw_node->i.i_atime_nsec = 0; raw_node->i.i_ctime = cpu_to_le32(time(NULL)); raw_node->i.i_ctime_nsec = 0; raw_node->i.i_mtime = cpu_to_le32(time(NULL)); raw_node->i.i_mtime_nsec = 0; raw_node->i.i_generation = 0; raw_node->i.i_xattr_nid = 0; raw_node->i.i_flags = 0; raw_node->i.i_current_depth = cpu_to_le32(1); raw_node->i.i_dir_level = DEF_DIR_LEVEL; if (c.feature & cpu_to_le32(F2FS_FEATURE_EXTRA_ATTR)) { raw_node->i.i_inline = F2FS_EXTRA_ATTR; raw_node->i.i_extra_isize = cpu_to_le16(F2FS_TOTAL_EXTRA_ATTR_SIZE); } if (c.feature & cpu_to_le32(F2FS_FEATURE_PRJQUOTA)) raw_node->i.i_projid = cpu_to_le32(F2FS_DEF_PROJID); if (c.feature & cpu_to_le32(F2FS_FEATURE_INODE_CRTIME)) { raw_node->i.i_crtime = cpu_to_le32(time(NULL)); raw_node->i.i_crtime_nsec = 0; } data_blk_nor = get_sb(main_blkaddr) + c.cur_seg[CURSEG_HOT_DATA] * c.blks_per_seg; raw_node->i.i_addr[get_extra_isize(raw_node)] = cpu_to_le32(data_blk_nor); raw_node->i.i_ext.fofs = 0; raw_node->i.i_ext.blk_addr = 0; raw_node->i.i_ext.len = 0; if (c.feature & cpu_to_le32(F2FS_FEATURE_INODE_CHKSUM)) raw_node->i.i_inode_checksum = cpu_to_le32(f2fs_inode_chksum(raw_node)); main_area_node_seg_blk_offset = get_sb(main_blkaddr); main_area_node_seg_blk_offset += c.cur_seg[CURSEG_HOT_NODE] * c.blks_per_seg; DBG(1, "\tWriting root inode (hot node), %x %x %x at offset 0x%08"PRIu64"\n", get_sb(main_blkaddr), c.cur_seg[CURSEG_HOT_NODE], c.blks_per_seg, main_area_node_seg_blk_offset); if (dev_write_block(raw_node, main_area_node_seg_blk_offset)) { MSG(1, "\tError: While writing the raw_node to disk!!!\n"); free(raw_node); return -1; } /* avoid power-off-recovery based on roll-forward policy */ main_area_node_seg_blk_offset = get_sb(main_blkaddr); main_area_node_seg_blk_offset += c.cur_seg[CURSEG_WARM_NODE] * c.blks_per_seg; #ifndef WITH_ANDROID if (discard_obsolete_dnode(raw_node, main_area_node_seg_blk_offset)) { free(raw_node); return -1; } #endif free(raw_node); return 0; } static int f2fs_write_default_quota(int qtype, unsigned int blkaddr, __le32 raw_id) { char *filebuf = calloc(F2FS_BLKSIZE, 2); int file_magics[] = INITQMAGICS; struct v2_disk_dqheader ddqheader; struct v2_disk_dqinfo ddqinfo; struct v2r1_disk_dqblk dqblk; if (filebuf == NULL) { MSG(1, "\tError: Calloc Failed for filebuf!!!\n"); return -1; } /* Write basic quota header */ ddqheader.dqh_magic = cpu_to_le32(file_magics[qtype]); /* only support QF_VFSV1 */ ddqheader.dqh_version = cpu_to_le32(1); memcpy(filebuf, &ddqheader, sizeof(ddqheader)); /* Fill Initial quota file content */ ddqinfo.dqi_bgrace = cpu_to_le32(MAX_DQ_TIME); ddqinfo.dqi_igrace = cpu_to_le32(MAX_IQ_TIME); ddqinfo.dqi_flags = cpu_to_le32(0); ddqinfo.dqi_blocks = cpu_to_le32(QT_TREEOFF + 5); ddqinfo.dqi_free_blk = cpu_to_le32(0); ddqinfo.dqi_free_entry = cpu_to_le32(5); memcpy(filebuf + V2_DQINFOOFF, &ddqinfo, sizeof(ddqinfo)); filebuf[1024] = 2; filebuf[2048] = 3; filebuf[3072] = 4; filebuf[4096] = 5; filebuf[5120 + 8] = 1; dqblk.dqb_id = raw_id; dqblk.dqb_pad = cpu_to_le32(0); dqblk.dqb_ihardlimit = cpu_to_le64(0); dqblk.dqb_isoftlimit = cpu_to_le64(0); dqblk.dqb_curinodes = cpu_to_le64(1); dqblk.dqb_bhardlimit = cpu_to_le64(0); dqblk.dqb_bsoftlimit = cpu_to_le64(0); dqblk.dqb_curspace = cpu_to_le64(4096); dqblk.dqb_btime = cpu_to_le64(0); dqblk.dqb_itime = cpu_to_le64(0); memcpy(filebuf + 5136, &dqblk, sizeof(struct v2r1_disk_dqblk)); /* Write two blocks */ if (dev_write_block(filebuf, blkaddr) || dev_write_block(filebuf + F2FS_BLKSIZE, blkaddr + 1)) { MSG(1, "\tError: While writing the quota_blk to disk!!!\n"); free(filebuf); return -1; } DBG(1, "\tWriting quota data, at offset %08x, %08x\n", blkaddr, blkaddr + 1); free(filebuf); return 0; } static int f2fs_write_qf_inode(int qtype) { struct f2fs_node *raw_node = NULL; u_int64_t data_blk_nor; u_int64_t main_area_node_seg_blk_offset = 0; __le32 raw_id; int i; raw_node = calloc(F2FS_BLKSIZE, 1); if (raw_node == NULL) { MSG(1, "\tError: Calloc Failed for raw_node!!!\n"); return -1; } raw_node->footer.nid = sb->qf_ino[qtype]; raw_node->footer.ino = sb->qf_ino[qtype]; raw_node->footer.cp_ver = cpu_to_le64(1); raw_node->footer.next_blkaddr = cpu_to_le32( get_sb(main_blkaddr) + c.cur_seg[CURSEG_HOT_NODE] * c.blks_per_seg + 1 + qtype + 1); raw_node->i.i_mode = cpu_to_le16(0x8180); raw_node->i.i_links = cpu_to_le32(1); raw_node->i.i_uid = cpu_to_le32(getuid()); raw_node->i.i_gid = cpu_to_le32(getgid()); raw_node->i.i_size = cpu_to_le64(1024 * 6); /* Hard coded */ raw_node->i.i_blocks = cpu_to_le64(1 + QUOTA_DATA(qtype)); raw_node->i.i_atime = cpu_to_le32(time(NULL)); raw_node->i.i_atime_nsec = 0; raw_node->i.i_ctime = cpu_to_le32(time(NULL)); raw_node->i.i_ctime_nsec = 0; raw_node->i.i_mtime = cpu_to_le32(time(NULL)); raw_node->i.i_mtime_nsec = 0; raw_node->i.i_generation = 0; raw_node->i.i_xattr_nid = 0; raw_node->i.i_flags = FS_IMMUTABLE_FL; raw_node->i.i_current_depth = cpu_to_le32(1); raw_node->i.i_dir_level = DEF_DIR_LEVEL; if (c.feature & cpu_to_le32(F2FS_FEATURE_EXTRA_ATTR)) { raw_node->i.i_inline = F2FS_EXTRA_ATTR; raw_node->i.i_extra_isize = cpu_to_le16(F2FS_TOTAL_EXTRA_ATTR_SIZE); } if (c.feature & cpu_to_le32(F2FS_FEATURE_PRJQUOTA)) raw_node->i.i_projid = cpu_to_le32(F2FS_DEF_PROJID); data_blk_nor = get_sb(main_blkaddr) + c.cur_seg[CURSEG_HOT_DATA] * c.blks_per_seg + 1; for (i = 0; i < qtype; i++) if (sb->qf_ino[i]) data_blk_nor += QUOTA_DATA(i); if (qtype == 0) raw_id = raw_node->i.i_uid; else if (qtype == 1) raw_id = raw_node->i.i_gid; else if (qtype == 2) raw_id = raw_node->i.i_projid; else ASSERT(0); /* write two blocks */ if (f2fs_write_default_quota(qtype, data_blk_nor, raw_id)) { free(raw_node); return -1; } for (i = 0; i < QUOTA_DATA(qtype); i++) raw_node->i.i_addr[get_extra_isize(raw_node) + i] = cpu_to_le32(data_blk_nor + i); raw_node->i.i_ext.fofs = 0; raw_node->i.i_ext.blk_addr = 0; raw_node->i.i_ext.len = 0; if (c.feature & cpu_to_le32(F2FS_FEATURE_INODE_CHKSUM)) raw_node->i.i_inode_checksum = cpu_to_le32(f2fs_inode_chksum(raw_node)); main_area_node_seg_blk_offset = get_sb(main_blkaddr); main_area_node_seg_blk_offset += c.cur_seg[CURSEG_HOT_NODE] * c.blks_per_seg + qtype + 1; DBG(1, "\tWriting quota inode (hot node), %x %x %x at offset 0x%08"PRIu64"\n", get_sb(main_blkaddr), c.cur_seg[CURSEG_HOT_NODE], c.blks_per_seg, main_area_node_seg_blk_offset); if (dev_write_block(raw_node, main_area_node_seg_blk_offset)) { MSG(1, "\tError: While writing the raw_node to disk!!!\n"); free(raw_node); return -1; } free(raw_node); return 0; } static int f2fs_update_nat_root(void) { struct f2fs_nat_block *nat_blk = NULL; u_int64_t nat_seg_blk_offset = 0; enum quota_type qtype; int i; nat_blk = calloc(F2FS_BLKSIZE, 1); if(nat_blk == NULL) { MSG(1, "\tError: Calloc Failed for nat_blk!!!\n"); return -1; } /* update quota */ for (qtype = i = 0; qtype < F2FS_MAX_QUOTAS; qtype++) { if (sb->qf_ino[qtype] == 0) continue; nat_blk->entries[sb->qf_ino[qtype]].block_addr = cpu_to_le32(get_sb(main_blkaddr) + c.cur_seg[CURSEG_HOT_NODE] * c.blks_per_seg + i + 1); nat_blk->entries[sb->qf_ino[qtype]].ino = sb->qf_ino[qtype]; i++; } /* update root */ nat_blk->entries[get_sb(root_ino)].block_addr = cpu_to_le32( get_sb(main_blkaddr) + c.cur_seg[CURSEG_HOT_NODE] * c.blks_per_seg); nat_blk->entries[get_sb(root_ino)].ino = sb->root_ino; /* update node nat */ nat_blk->entries[get_sb(node_ino)].block_addr = cpu_to_le32(1); nat_blk->entries[get_sb(node_ino)].ino = sb->node_ino; /* update meta nat */ nat_blk->entries[get_sb(meta_ino)].block_addr = cpu_to_le32(1); nat_blk->entries[get_sb(meta_ino)].ino = sb->meta_ino; nat_seg_blk_offset = get_sb(nat_blkaddr); DBG(1, "\tWriting nat root, at offset 0x%08"PRIx64"\n", nat_seg_blk_offset); if (dev_write_block(nat_blk, nat_seg_blk_offset)) { MSG(1, "\tError: While writing the nat_blk set0 to disk!\n"); free(nat_blk); return -1; } free(nat_blk); return 0; } static int f2fs_add_default_dentry_root(void) { struct f2fs_dentry_block *dent_blk = NULL; u_int64_t data_blk_offset = 0; dent_blk = calloc(F2FS_BLKSIZE, 1); if(dent_blk == NULL) { MSG(1, "\tError: Calloc Failed for dent_blk!!!\n"); return -1; } dent_blk->dentry[0].hash_code = 0; dent_blk->dentry[0].ino = sb->root_ino; dent_blk->dentry[0].name_len = cpu_to_le16(1); dent_blk->dentry[0].file_type = F2FS_FT_DIR; memcpy(dent_blk->filename[0], ".", 1); dent_blk->dentry[1].hash_code = 0; dent_blk->dentry[1].ino = sb->root_ino; dent_blk->dentry[1].name_len = cpu_to_le16(2); dent_blk->dentry[1].file_type = F2FS_FT_DIR; memcpy(dent_blk->filename[1], "..", 2); /* bitmap for . and .. */ test_and_set_bit_le(0, dent_blk->dentry_bitmap); test_and_set_bit_le(1, dent_blk->dentry_bitmap); data_blk_offset = get_sb(main_blkaddr); data_blk_offset += c.cur_seg[CURSEG_HOT_DATA] * c.blks_per_seg; DBG(1, "\tWriting default dentry root, at offset 0x%08"PRIx64"\n", data_blk_offset); if (dev_write_block(dent_blk, data_blk_offset)) { MSG(1, "\tError: While writing the dentry_blk to disk!!!\n"); free(dent_blk); return -1; } free(dent_blk); return 0; } static int f2fs_create_root_dir(void) { enum quota_type qtype; int err = 0; err = f2fs_write_root_inode(); if (err < 0) { MSG(1, "\tError: Failed to write root inode!!!\n"); goto exit; } for (qtype = 0; qtype < F2FS_MAX_QUOTAS; qtype++) { if (sb->qf_ino[qtype] == 0) continue; err = f2fs_write_qf_inode(qtype); if (err < 0) { MSG(1, "\tError: Failed to write quota inode!!!\n"); goto exit; } } err = f2fs_update_nat_root(); if (err < 0) { MSG(1, "\tError: Failed to update NAT for root!!!\n"); goto exit; } err = f2fs_add_default_dentry_root(); if (err < 0) { MSG(1, "\tError: Failed to add default dentries for root!!!\n"); goto exit; } exit: if (err) MSG(1, "\tError: Could not create the root directory!!!\n"); return err; } int f2fs_format_device(void) { int err = 0; err= f2fs_prepare_super_block(); if (err < 0) { MSG(0, "\tError: Failed to prepare a super block!!!\n"); goto exit; } if (c.trim) { err = f2fs_trim_devices(); if (err < 0) { MSG(0, "\tError: Failed to trim whole device!!!\n"); goto exit; } } err = f2fs_init_sit_area(); if (err < 0) { MSG(0, "\tError: Failed to Initialise the SIT AREA!!!\n"); goto exit; } err = f2fs_init_nat_area(); if (err < 0) { MSG(0, "\tError: Failed to Initialise the NAT AREA!!!\n"); goto exit; } err = f2fs_create_root_dir(); if (err < 0) { MSG(0, "\tError: Failed to create the root directory!!!\n"); goto exit; } err = f2fs_write_check_point_pack(); if (err < 0) { MSG(0, "\tError: Failed to write the check point pack!!!\n"); goto exit; } err = f2fs_write_super_block(); if (err < 0) { MSG(0, "\tError: Failed to write the Super Block!!!\n"); goto exit; } exit: if (err) MSG(0, "\tError: Could not format the device!!!\n"); return err; } f2fs-tools-1.10.0/mkfs/f2fs_format_main.c000066400000000000000000000200221323417143600201070ustar00rootroot00000000000000/** * f2fs_format.c * * Copyright (c) 2012 Samsung Electronics Co., Ltd. * http://www.samsung.com/ * * Dual licensed under the GPL or LGPL version 2 licenses. */ #define _LARGEFILE64_SOURCE #include #include #include #include #include #include #ifndef ANDROID_WINDOWS_HOST #include #endif #include #include #include #include "config.h" #ifdef HAVE_LIBBLKID # include #endif #include "f2fs_fs.h" #include "f2fs_format_utils.h" #ifdef WITH_ANDROID #include extern struct sparse_file *f2fs_sparse_file; #endif extern struct f2fs_configuration c; static int force_overwrite = 0; static void mkfs_usage() { MSG(0, "\nUsage: mkfs.f2fs [options] device [sectors]\n"); MSG(0, "[options]:\n"); MSG(0, " -a heap-based allocation [default:0]\n"); MSG(0, " -c [device path] up to 7 devices excepts meta device\n"); MSG(0, " -d debug level [default:0]\n"); MSG(0, " -e [extension list] e.g. \"mp3,gif,mov\"\n"); MSG(0, " -f force overwrite the exist filesystem\n"); MSG(0, " -l label\n"); MSG(0, " -m support zoned block device [default:0]\n"); MSG(0, " -o overprovision ratio [default:5]\n"); MSG(0, " -O [feature list] e.g. \"encrypt\"\n"); MSG(0, " -q quiet mode\n"); MSG(0, " -s # of segments per section [default:1]\n"); MSG(0, " -S sparse mode\n"); MSG(0, " -t 0: nodiscard, 1: discard [default:1]\n"); MSG(0, " -z # of sections per zone [default:1]\n"); MSG(0, "sectors: number of sectors. [default: determined by device size]\n"); exit(1); } static void f2fs_show_info() { MSG(0, "\n\tF2FS-tools: mkfs.f2fs Ver: %s (%s)\n\n", F2FS_TOOLS_VERSION, F2FS_TOOLS_DATE); if (c.heap == 0) MSG(0, "Info: Disable heap-based policy\n"); MSG(0, "Info: Debug level = %d\n", c.dbg_lv); if (c.extension_list) MSG(0, "Info: Add new extension list\n"); if (c.vol_label) MSG(0, "Info: Label = %s\n", c.vol_label); MSG(0, "Info: Trim is %s\n", c.trim ? "enabled": "disabled"); } static void parse_feature(const char *features) { while (*features == ' ') features++; if (!strcmp(features, "encrypt")) { c.feature |= cpu_to_le32(F2FS_FEATURE_ENCRYPT); } else if (!strcmp(features, "extra_attr")) { c.feature |= cpu_to_le32(F2FS_FEATURE_EXTRA_ATTR); } else if (!strcmp(features, "project_quota")) { c.feature |= cpu_to_le32(F2FS_FEATURE_PRJQUOTA); } else if (!strcmp(features, "inode_checksum")) { c.feature |= cpu_to_le32(F2FS_FEATURE_INODE_CHKSUM); } else if (!strcmp(features, "flexible_inline_xattr")) { c.feature |= cpu_to_le32(F2FS_FEATURE_FLEXIBLE_INLINE_XATTR); } else if (!strcmp(features, "quota")) { c.feature |= cpu_to_le32(F2FS_FEATURE_QUOTA_INO); } else if (!strcmp(features, "inode_crtime")) { c.feature |= cpu_to_le32(F2FS_FEATURE_INODE_CRTIME); } else { MSG(0, "Error: Wrong features\n"); mkfs_usage(); } } static void f2fs_parse_options(int argc, char *argv[]) { static const char *option_string = "qa:c:d:e:l:mo:O:s:S:z:t:f"; int32_t option=0; while ((option = getopt(argc,argv,option_string)) != EOF) { switch (option) { case 'q': c.dbg_lv = -1; break; case 'a': c.heap = atoi(optarg); break; case 'c': if (c.ndevs >= MAX_DEVICES) { MSG(0, "Error: Too many devices\n"); mkfs_usage(); } if (strlen(optarg) > MAX_PATH_LEN) { MSG(0, "Error: device path should be less than " "%d characters\n", MAX_PATH_LEN); mkfs_usage(); } c.devices[c.ndevs++].path = strdup(optarg); break; case 'd': c.dbg_lv = atoi(optarg); break; case 'e': c.extension_list = strdup(optarg); break; case 'l': /*v: volume label */ if (strlen(optarg) > 512) { MSG(0, "Error: Volume Label should be less than " "512 characters\n"); mkfs_usage(); } c.vol_label = optarg; break; case 'm': c.zoned_mode = 1; break; case 'o': c.overprovision = atof(optarg); break; case 'O': parse_feature(optarg); break; case 's': c.segs_per_sec = atoi(optarg); break; case 'S': c.device_size = atoll(optarg); c.device_size &= (~((u_int64_t)(F2FS_BLKSIZE - 1))); c.sparse_mode = 1; break; case 'z': c.secs_per_zone = atoi(optarg); break; case 't': c.trim = atoi(optarg); break; case 'f': force_overwrite = 1; break; default: MSG(0, "\tError: Unknown option %c\n",option); mkfs_usage(); break; } } if (!(c.feature & cpu_to_le32(F2FS_FEATURE_EXTRA_ATTR))) { if (c.feature & cpu_to_le32(F2FS_FEATURE_PRJQUOTA)) { MSG(0, "\tInfo: project quota feature should always been" "enabled with extra attr feature\n"); exit(1); } if (c.feature & cpu_to_le32(F2FS_FEATURE_INODE_CHKSUM)) { MSG(0, "\tInfo: inode checksum feature should always been" "enabled with extra attr feature\n"); exit(1); } if (c.feature & cpu_to_le32(F2FS_FEATURE_FLEXIBLE_INLINE_XATTR)) { MSG(0, "\tInfo: flexible inline xattr feature should always been" "enabled with extra attr feature\n"); exit(1); } if (c.feature & cpu_to_le32(F2FS_FEATURE_INODE_CRTIME)) { MSG(0, "\tInfo: inode crtime feature should always been" "enabled with extra attr feature\n"); exit(1); } } if (optind >= argc) { MSG(0, "\tError: Device not specified\n"); mkfs_usage(); } /* [0] : META, [1 to MAX_DEVICES - 1] : NODE/DATA */ c.devices[0].path = strdup(argv[optind]); if ((optind + 1) < argc) { if (c.ndevs > 1) { MSG(0, "\tError: Not support custom size on multi-devs.\n"); mkfs_usage(); } c.wanted_total_sectors = atoll(argv[optind+1]); } if (c.sparse_mode) c.trim = 0; if (c.zoned_mode) c.feature |= cpu_to_le32(F2FS_FEATURE_BLKZONED); } #ifdef HAVE_LIBBLKID static int f2fs_dev_is_overwrite(const char *device) { const char *type; blkid_probe pr = NULL; int ret = -1; if (!device || !*device) return 0; pr = blkid_new_probe_from_filename(device); if (!pr) goto out; ret = blkid_probe_enable_partitions(pr, 1); if (ret < 0) goto out; ret = blkid_do_fullprobe(pr); if (ret < 0) goto out; /* * Blkid returns 1 for nothing found and 0 when it finds a signature, * but we want the exact opposite, so reverse the return value here. * * In addition print some useful diagnostics about what actually is * on the device. */ if (ret) { ret = 0; goto out; } if (!blkid_probe_lookup_value(pr, "TYPE", &type, NULL)) { MSG(0, "\t%s appears to contain an existing filesystem (%s).\n", device, type); } else if (!blkid_probe_lookup_value(pr, "PTTYPE", &type, NULL)) { MSG(0, "\t%s appears to contain a partition table (%s).\n", device, type); } else { MSG(0, "\t%s appears to contain something weird according to blkid\n", device); } ret = 1; out: if (pr) blkid_free_probe(pr); if (ret == -1) MSG(0, "\tprobe of %s failed, cannot detect existing filesystem.\n", device); return ret; } static int f2fs_check_overwrite(void) { int i; for (i = 0; i < c.ndevs; i++) if (f2fs_dev_is_overwrite((char *)c.devices[i].path)) return -1; return 0; } #else static int f2fs_check_overwrite(void) { return 0; } #endif /* HAVE_LIBBLKID */ int main(int argc, char *argv[]) { f2fs_init_configuration(); f2fs_parse_options(argc, argv); f2fs_show_info(); c.func = MKFS; if (!force_overwrite && f2fs_check_overwrite()) { MSG(0, "\tUse the -f option to force overwrite.\n"); return -1; } if (f2fs_devs_are_umounted() < 0) { if (errno != EBUSY) MSG(0, "\tError: Not available on mounted device!\n"); return -1; } if (f2fs_get_device_info() < 0) return -1; /* * Some options are mandatory for host-managed * zoned block devices. */ if (c.zoned_model == F2FS_ZONED_HM && !c.zoned_mode) { MSG(0, "\tError: zoned block device feature is required\n"); return -1; } if (c.zoned_mode && !c.trim) { MSG(0, "\tError: Trim is required for zoned block devices\n"); return -1; } if (c.sparse_mode) { if (f2fs_init_sparse_file()) return -1; } if (f2fs_format_device() < 0) return -1; if (f2fs_finalize_device() < 0) return -1; MSG(0, "Info: format successful\n"); return 0; } f2fs-tools-1.10.0/mkfs/f2fs_format_utils.c000066400000000000000000000043101323417143600203250ustar00rootroot00000000000000/** * f2fs_format_utils.c * * Copyright (c) 2014 Samsung Electronics Co., Ltd. * http://www.samsung.com/ * * Dual licensed under the GPL or LGPL version 2 licenses. */ #ifndef _LARGEFILE_SOURCE #define _LARGEFILE_SOURCE #endif #ifndef _LARGEFILE64_SOURCE #define _LARGEFILE64_SOURCE #endif #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #include #include #include #ifndef ANDROID_WINDOWS_HOST #include #endif #include #include #ifdef HAVE_LINUX_FS_H #include #endif #ifdef HAVE_LINUX_FALLOC_H #include #endif #ifndef BLKDISCARD #define BLKDISCARD _IO(0x12,119) #endif #ifndef BLKSECDISCARD #define BLKSECDISCARD _IO(0x12,125) #endif static int trim_device(int i) { #ifndef ANDROID_WINDOWS_HOST unsigned long long range[2]; struct stat stat_buf; struct device_info *dev = c.devices + i; u_int64_t bytes = dev->total_sectors * dev->sector_size; int fd = dev->fd; if (fstat(fd, &stat_buf) < 0 ) { MSG(1, "\tError: Failed to get the device stat!!!\n"); return -1; } range[0] = 0; range[1] = bytes; #if defined(WITH_BLKDISCARD) && defined(BLKDISCARD) MSG(0, "Info: [%s] Discarding device\n", dev->path); if (S_ISREG(stat_buf.st_mode)) { #if defined(HAVE_FALLOCATE) && defined(FALLOC_FL_PUNCH_HOLE) if (fallocate(fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, range[0], range[1]) < 0) { MSG(0, "Info: fallocate(PUNCH_HOLE|KEEP_SIZE) is failed\n"); } #endif return 0; } else if (S_ISBLK(stat_buf.st_mode)) { if (dev->zoned_model != F2FS_ZONED_NONE) return f2fs_reset_zones(i); #ifdef BLKSECDISCARD if (ioctl(fd, BLKSECDISCARD, &range) < 0) { MSG(0, "Info: This device doesn't support BLKSECDISCARD\n"); } else { MSG(0, "Info: Secure Discarded %lu MB\n", (unsigned long)stat_buf.st_size >> 20); return 0; } #endif if (ioctl(fd, BLKDISCARD, &range) < 0) { MSG(0, "Info: This device doesn't support BLKDISCARD\n"); } else { MSG(0, "Info: Discarded %llu MB\n", range[1] >> 20); } } else return -1; #endif #endif return 0; } int f2fs_trim_devices(void) { int i; for (i = 0; i < c.ndevs; i++) if (trim_device(i)) return -1; c.trimmed = 1; return 0; } f2fs-tools-1.10.0/mkfs/f2fs_format_utils.h000066400000000000000000000005641323417143600203410ustar00rootroot00000000000000/** * f2fs_format_utils.c * * Copyright (c) 2014 Samsung Electronics Co., Ltd. * http://www.samsung.com/ * * Dual licensed under the GPL or LGPL version 2 licenses. */ #define _LARGEFILE64_SOURCE #include "f2fs_fs.h" extern struct f2fs_configuration c; int f2fs_trim_device(int, u_int64_t); int f2fs_trim_devices(void); int f2fs_format_device(void); f2fs-tools-1.10.0/scripts/000077500000000000000000000000001323417143600152625ustar00rootroot00000000000000f2fs-tools-1.10.0/scripts/dumpf2fs.sh000077500000000000000000000022331323417143600173470ustar00rootroot00000000000000#!/system/bin/sh DEV=/dev/block/mmcblk0p16 CMD=$1 BASE=0x200000 BASE_MAIN=0xac00000 case $CMD in cp1) echo dump cp1 let addr=$BASE echo $addr hexdump -s $addr -n 4096 $DEV;; cp2) echo dump cp2 let addr=$BASE+0x200000 hexdump -s $addr -n 4096 $DEV;; cp) echo dump cp1 and cp2 let addr=$BASE hexdump -s $addr -n 409 $DEV let addr=$BASE+0x200000 hexdump -s $addr -n 4096 $DEV;; cp1_all) echo dump cp1 all let addr=$BASE hexdump -s $addr -n 20480 $DEV;; cp2_all) echo dump cp2 all let addr=$BASE+0x200000 hexdump -s $addr -n 20480 $DEV;; cp_all) echo dump cp1 and cp2 all let addr=$BASE hexdump -s $addr -n 20480 $DEV let addr=$BASE+0x200000 hexdump -s $addr -n 20480 $DEV;; blk) let addr=$BASE_MAIN+$2*0x200000+$3*0x1000 hexdump -s $addr -n 4096 $DEV echo ;; inode) let addr=$BASE_MAIN+$2*0x200000+$3*0x1000 for i in `seq $3 511` do hexdump -s $addr -n 8 $DEV let end=$addr+0x0ff0 hexdump -s $end -n 16 $DEV let addr=$addr+0x1000 done echo ;; *) let addr=$1*0x1000 let segno=$addr-$BASE_MAIN let segno=$segno/0x200000 let off=$addr-$BASE_MAIN let off=$off%0x200000/0x1000 echo $segno, $off hexdump -s $addr -n 4096 $DEV echo ;; esac f2fs-tools-1.10.0/scripts/spo_test.sh000077500000000000000000000026411323417143600174640ustar00rootroot00000000000000#!/bin/bash MNT=/mnt/f2fs DEV=/dev/sdb1 USER_DIR=/home/zeus F2FS_DIR=$USER_DIR/f2fs_test check_stop() { stop=`cat /tmp/stop` if [ $stop -eq 1 ]; then exit fi } case $1 in start) echo 0 > /tmp/stop umount /mnt/* echo 3 > /proc/sys/vm/drop_caches echo 8 > /proc/sys/kernel/printk date >> $USER_DIR/por_result sync insmod $F2FS_DIR/src/fs/f2fs/f2fs.ko || exit echo Start checking F2FS without fsync check_stop fsck.f2fs $DEV -d 0 || exit mount -t f2fs -o disable_roll_forward $DEV $MNT || exit umount $MNT echo 3 > /proc/sys/vm/drop_caches echo Start checking F2FS with fsync check_stop fsck.f2fs $DEV -d 0 || exit mount -t f2fs $DEV $MNT || exit umount $MNT check_stop fsck.f2fs $DEV -d 0 || exit mount -t f2fs $DEV $MNT || exit count=`cat $USER_DIR/por_time` if [ $count -eq 20 ]; then echo Start rm all time rm -rf $MNT/* || exit echo 0 > $USER_DIR/por_time sync else echo $((count+1)) > $USER_DIR/por_time fi echo 8 > /proc/sys/kernel/printk echo Start fsstress date $F2FS_DIR/stress_test/fsstress/fsstress -z -f link=0 -f mkdir=3 -f mknod=3 -f rmdir=2 -f symlink=3 -f truncate=4 -f write=10 -f creat=10 -f unlink=5 -f rename=5 -f fsync=10 -p 10 -n 10000 -l 0 -d $MNT & RANDOM=`date '+%s'` rand=$[($RANDOM % 540) + 60] echo Start sleep: $rand seconds sleep $rand echo Reboot now check_stop echo b > /proc/sysrq-trigger ;; stop) killall -9 fsstress echo 1 > /tmp/stop ;; esac f2fs-tools-1.10.0/scripts/tracepoint.sh000077500000000000000000000045501323417143600177750ustar00rootroot00000000000000#!/system/bin/sh TRACE=/sys/kernel/debug/tracing/ dev=$(((8<<20) + 17)) # sdb1 (8,17) echo 1 > $TRACE/tracing_on # mmc tracepoints echo 0 > $TRACE/events/mmc/enable # block tracepoints #echo "dev == $dev" > $TRACE/events/block/block_rq_complete/filter echo 0 > $TRACE/events/block/block_rq_complete/enable echo 0 > $TRACE/events/block/block_bio_complete/enable # GC G=0 echo $G > $TRACE/events/f2fs/f2fs_get_victim/enable # block allocation A=0 echo $A > $TRACE/events/f2fs/f2fs_reserve_new_block/enable # block truncation T=0 echo $T > $TRACE/events/f2fs/f2fs_truncate/enable echo $T > $TRACE/events/f2fs/f2fs_truncate_inode_blocks_enter/enable echo $T > $TRACE/events/f2fs/f2fs_truncate_inode_blocks_exit/enable echo $T > $TRACE/events/f2fs/f2fs_truncate_blocks_enter/enable echo $T > $TRACE/events/f2fs/f2fs_truncate_blocks_exit/enable echo $T > $TRACE/events/f2fs/f2fs_truncate_nodes_enter/enable echo $T > $TRACE/events/f2fs/f2fs_truncate_nodes_exit/enable echo $T > $TRACE/events/f2fs/f2fs_truncate_data_blocks_range/enable echo $T > $TRACE/events/f2fs/f2fs_truncate_node/enable echo $T > $TRACE/events/f2fs/f2fs_truncate_partial_nodes/enable # syscalls S=0 echo $S > $TRACE/events/f2fs/f2fs_unlink_enter/enable echo $S > $TRACE/events/f2fs/f2fs_unlink_exit/enable echo $S > $TRACE/events/f2fs/f2fs_fallocate/enable echo $S > $TRACE/events/f2fs/f2fs_get_data_block/enable # IOs R=0 W=0 echo $R > $TRACE/events/f2fs/f2fs_readpage/enable echo $W > $TRACE/events/f2fs/f2fs_writepage/enable echo $W > $TRACE/events/f2fs/f2fs_write_begin/enable echo $W > $TRACE/events/f2fs/f2fs_write_end/enable echo 0 > $TRACE/events/f2fs/f2fs_submit_page_bio/enable echo 0 > $TRACE/events/f2fs/f2fs_submit_page_mbio/enable echo $R > $TRACE/events/f2fs/f2fs_submit_read_bio/enable echo $W > $TRACE/events/f2fs/f2fs_submit_write_bio/enable echo 0 > $TRACE/events/f2fs/f2fs_issue_discard/enable echo 0 > $TRACE/events/f2fs/f2fs_issue_flush/enable # VFS interfaces V=0 echo $V > $TRACE/events/f2fs/f2fs_iget/enable echo $V > $TRACE/events/f2fs/f2fs_iget_exit/enable echo $V > $TRACE/events/f2fs/f2fs_new_inode/enable echo $V > $TRACE/events/f2fs/f2fs_evict_inode/enable echo $V > $TRACE/events/f2fs/f2fs_sync_file_enter/enable echo $V > $TRACE/events/f2fs/f2fs_sync_file_exit/enable echo $V > $TRACE/events/f2fs/f2fs_write_checkpoint/enable echo $V > $TRACE/events/f2fs/f2fs_sync_fs/enable cat $TRACE/trace_pipe f2fs-tools-1.10.0/scripts/verify.sh000077500000000000000000000040131323417143600171230ustar00rootroot00000000000000#!/bin/bash IMG=../test.img TMP=/tmp/res XFSTESTS=~/xfstests TESTS="4 5 8 11 16 25 32 55 64" TARGET=./testdir MNT=/mnt/resize mkdir $TARGET 2>/dev/null mkdir $MNT 2>/dev/null umount $TARGET umount $MNT _check_out() { if [ $1 -ne 0 ]; then grep ASSERT $TMP echo FAIL RETURN $1 exit fi } _get_sec() { echo $(($1*1024*1024*1024/512)) } _mkfs() { echo "========== Initialize $1 GB ============" mkfs.f2fs $IMG `_get_sec $1` | grep sectors } _mount() { echo "========== mount to $1 =================" mount -t f2fs -o loop,discard,inline_data,inline_xattr $IMG $1 2>&1 _check_out $? } _fsck() { echo "========== fsck.f2fs ===================" fsck.f2fs $IMG -t 2>&1 >$TMP _check_out $? grep FSCK $TMP } _fsstress() { echo "========== fsstress $1 =================" $XFSTESTS/ltp/fsstress -x "echo 3 > /proc/sys/vm/drop_caches && sleep 1" -X 1 -r -f fsync=8 -f sync=0 -f write=8 -f dwrite=2 -f truncate=6 -f allocsp=0 -f bulkstat=0 -f bulkstat1=0 -f freesp=0 -f zero=1 -f collapse=1 -f insert=1 -f resvsp=0 -f unresvsp=0 -S t -p 10 -n $2 -d $1 >/dev/null } _resize() { echo "========== resize.f2fs $1 GB ===========" resize.f2fs -t `_get_sec $1` $IMG 2>&1 >$TMP _check_out $? _fsck } _resize_tests() { for i in $TESTS do if [ $i -ge $1 ]; then _resize $i fi done } _sload() { echo "========== sload $1 ====================" sload.f2fs -f $1 $IMG 2>&1 _check_out $? } from_mount() { echo "" echo " **** $1 GB to $2 GB with $3 *** " _mkfs $1 _mount $3 _fsstress $3 10000 umount $3 _fsck _resize_tests $2 } from_sload() { echo "" echo " **** $1 GB to $2 GB with $3 *** " _mkfs $1 _sload $3 _fsck _mount $MNT _fsstress $MNT 10000 umount $MNT _fsck _resize_tests $2 _mount $MNT _fsstress $MNT 10000 umount $MNT _fsck } test_all() { for i in $TESTS do for j in $TESTS do if [ $i -lt $j ]; then $1 $i $j $2 fi done done } test_all from_sload ~/grub rm -rf $TARGET/* _fsstress $TARGET 5000 test_all from_sload $TARGET rm -rf $TARGET 2>/dev/null test_all from_mount $MNT f2fs-tools-1.10.0/tools/000077500000000000000000000000001323417143600147335ustar00rootroot00000000000000f2fs-tools-1.10.0/tools/Makefile.am000066400000000000000000000005641323417143600167740ustar00rootroot00000000000000## Makefile.am AM_CPPFLAGS = ${libuuid_CFLAGS} -I$(top_srcdir)/include AM_CFLAGS = -Wall sbin_PROGRAMS = f2fstat fibmap.f2fs parse.f2fs f2fstat_SOURCES = f2fstat.c fibmap_f2fs_SOURCES = fibmap.c parse_f2fs_SOURCES = f2fs_io_parse.c if LINUX sbin_PROGRAMS += f2fscrypt f2fscrypt_SOURCES = f2fscrypt.c sha512.c f2fscrypt_LDFLAGS = -luuid dist_man_MANS = f2fscrypt.8 endif f2fs-tools-1.10.0/tools/f2fs_io_parse.c000066400000000000000000000127561323417143600176330ustar00rootroot00000000000000/* * f2fs IO tracer * * Copyright (c) 2014 Motorola Mobility * Copyright (c) 2014 Jaegeuk Kim * * 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. */ #define _LARGEFILE64_SOURCE #include #include #include #include #include #include #include #define P_NAMELEN 16 /* For global trace methods */ enum show_type { SHOW_PID, SHOW_FTYPE, SHOW_ALL, }; enum trace_types { TP_PID, TP_IOS, TP_MAX, }; struct tps { enum trace_types type; const char *name; }; struct tps trace_points[] = { { TP_PID, "f2fs_trace_pid" }, { TP_IOS, "f2fs_trace_ios" }, }; /* For f2fs_trace_pid and f2fs_trace_ios */ enum rw_type { READ, WRITE, MAX_RW, }; enum file_type { __NORMAL_FILE, __DIR_FILE, __NODE_FILE, __META_FILE, __ATOMIC_FILE, __VOLATILE_FILE, __MISC_FILE, __NR_FILES, }; char *file_type_string[] = { "User ", "Dir ", "Node ", "Meta ", "Atomic ", "Voltile ", "Misc ", }; struct pid_ent { int pid; char name[P_NAMELEN]; unsigned long long io[__NR_FILES][MAX_RW]; unsigned long long total_io[MAX_RW]; LIST_ENTRY(pid_ent) ptr; }; /* global variables */ int major = 0, minor = 0; int show_option = SHOW_ALL; unsigned long long total_io[__NR_FILES][MAX_RW]; LIST_HEAD(plist, pid_ent) pid_info; /* Functions */ static inline int atoh(char *str) { int val; sscanf(str, "%x", &val); return val; } static void do_init() { struct pid_ent *misc; misc = calloc(1, sizeof(struct pid_ent)); assert(misc); LIST_INIT(&pid_info); LIST_INSERT_HEAD(&pid_info, misc, ptr); } void show_usage() { printf("\nUsage: parse.f2fs [options] log_file\n"); printf("[options]:\n"); printf(" -a RW sorted by pid & file types\n"); printf(" -f RW sorted by file types\n"); printf(" -p RW sorted by pid\n"); printf(" -m major number\n"); printf(" -n minor number\n"); exit(1); } static int parse_options(int argc, char *argv[]) { const char *option_string = "fm:n:p"; int option = 0; while ((option = getopt(argc, argv, option_string)) != EOF) { switch (option) { case 'f': show_option = SHOW_FTYPE; break; case 'm': major = atoh(optarg); break; case 'n': minor = atoh(optarg); break; case 'p': show_option = SHOW_PID; break; default: printf("\tError: Unknown option %c\n", option); show_usage(); break; } } if ((optind + 1) != argc) { printf("\tError: Log file is not specified.\n"); show_usage(); } return optind; } struct pid_ent *get_pid_entry(int pid) { struct pid_ent *entry; LIST_FOREACH(entry, &pid_info, ptr) { if (entry->pid == pid) return entry; } return LIST_FIRST(&pid_info); } static void handle_tp_pid(char *ptr) { struct pid_ent *pent; pent = calloc(1, sizeof(struct pid_ent)); assert(pent); ptr = strtok(NULL, " "); pent->pid = atoh(ptr); ptr = strtok(NULL, " "); strcpy(pent->name, ptr); LIST_INSERT_HEAD(&pid_info, pent, ptr); } static void handle_tp_ios(char *ptr) { int pid, type, rw, len; struct pid_ent *p; ptr = strtok(NULL, " "); pid = atoh(ptr); ptr = strtok(NULL, " "); ptr = strtok(NULL, " "); type = atoh(ptr); ptr = strtok(NULL, " "); rw = atoh(ptr); ptr = strtok(NULL, " "); /* unsigned long long blkaddr = atoh(ptr); */ ptr = strtok(NULL, " "); len = atoh(ptr); /* update per-pid stat */ p = get_pid_entry(pid); p->io[type][rw & 0x1] += len; p->total_io[rw & 0x1] += len; /* update total stat */ total_io[type][rw & 0x1] += len; } static void do_parse(FILE *file) { char line[300]; char *ptr; int i; while (fgets(line, sizeof(line), file) != NULL) { ptr = strtok(line, ":"); ptr = strtok(NULL, " :"); for (i = 0; i < TP_MAX; i++) { if (!strcmp(ptr, trace_points[i].name)) break; } if (i == TP_MAX) continue; ptr = strtok(NULL, " :"); if (major && major != atoh(ptr)) continue; ptr = strtok(NULL, " :"); if (minor && minor != atoh(ptr)) continue; switch (i) { case TP_PID: handle_tp_pid(ptr); break; case TP_IOS: handle_tp_ios(ptr); break; } } } static void __print_pid() { struct pid_ent *entry; int i; setlocale(LC_ALL, ""); printf("%8s %16s %17s ||", "PID", "NAME", "R/W in 4KB"); for (i = 0; i < __NR_FILES; i++) printf(" %17s |", file_type_string[i]); printf("\n"); LIST_FOREACH(entry, &pid_info, ptr) { printf("%8x %16s %'8lld %'8lld ||", entry->pid, entry->name, entry->total_io[READ], entry->total_io[WRITE]); for (i = 0; i < __NR_FILES; i++) printf(" %'8lld %'8lld |", entry->io[i][READ], entry->io[i][WRITE]); printf("\n"); } } static void __print_ftype() { int i; setlocale(LC_ALL, ""); printf("\n===== Data R/W in 4KB accoring to File types =====\n"); for (i = 0; i < __NR_FILES; i++) printf(" %17s |", file_type_string[i]); printf("\n"); for (i = 0; i < __NR_FILES; i++) printf(" %'8lld %'8lld |", total_io[i][READ], total_io[i][WRITE]); printf("\n"); } static void do_print() { switch (show_option) { case SHOW_PID: __print_pid(); break; case SHOW_FTYPE: __print_ftype(); break; case SHOW_ALL: __print_pid(); printf("\n\n"); __print_ftype(); break; } } int main(int argc, char **argv) { FILE *file; int opt; opt = parse_options(argc, argv); file = fopen(argv[opt], "r"); if (!file) { perror("open log file"); exit(EXIT_FAILURE); } do_init(); do_parse(file); do_print(); fclose(file); return 0; } f2fs-tools-1.10.0/tools/f2fscrypt.8000066400000000000000000000061331323417143600167510ustar00rootroot00000000000000.TH F2FSCRYPT 8 .SH NAME f2fscrypt \- f2fs filesystem encryption utility .SH SYNOPSIS .B f2fscrypt add_key -S \fR[\fB -k \fIkeyring\fR ] [\fB-v\fR] [\fB-q\fR] [ \fI path\fR ... ] .br .B f2fscrypt new_session .br .B f2fscrypt get_policy \fIpath\fR ... .br .B f2fscrypt set_policy \fIpolicy path\fR ... .SH DESCRIPTION .B f2fscrypt performs encryption management for f2fs file systems. .SH COMMANDS .TP .B f2fscrypt add_key -S \fR[\fB -k \fIkeyring\fR ] [\fB-v\fR] [\fB-q\fR] [ \fI path\fR ... ] Prompts the user for a passphrase and inserts it into the specified keyring. If no keyring is specified, f2fscrypt will use the session keyring if it exists or the user session keyring if it does not. .IP If one or more directory paths are specified, f2fscrypt will try to set the policy of those directories to use the key just entered by the user. .TP .B f2fscrypt get_policy \fIpath\fR ... Print the policy for the directories specified on the command line. .TP .B f2fscrypt new_session Give the invoking process (typically a shell) a new session keyring, discarding its old session keyring. .TP .B f2fscrypt set_policy \fIpolicy path\fR ... Sets the policy for the directories specified on the command line. All directories must be empty to set the policy; if the directory already has a policy established, f2fscrypt will validate that the policy matches what was specified. A policy is an encryption key identifier consisting of 16 hexadecimal characters. .SH NOTES The target directory must be empty. .SH EXAMPLE .nf Formats a f2fs filesytem that supports encrypt. .ft R # mkfs.f2fs -O encrypt /dev/sdxx # mount /dev/sdxx /encrypted/ # mkdir /encrypted/dir .nf First create the key in the keyring use an simple salt (or generate a random salt). Then use it to set the policy for the directory to be encrypted. .ft R # f2fscrypt add_key -S 0x1234 Enter passphrase (echo disabled): Added key with descriptor [28e21cc0c4393da1] # f2fscrypt set_policy 28e21cc0c4393da1 /encrypted/dir Key with descriptor [28e21cc0c4393da1] applied to /encrypted/dir. # touch /encrypted/dir/test.txt # ls -l /encrypted/dir/ -rw-r--r--. 1 root root 0 Mar 5 21:41 test.txt .nf After each reboot, the same command can be used set the key for decryption of the directory and its descendants. .ft R # ls -l /encrypted/dir/ -rw-r--r--. 1 root root 0 Mar 5 21:41 zbx7tsUEMLzh+AUVMkQcnB # f2fscrypt get_policy /encrypted/dir/ /encrypted/dir/: 28e21cc0c4393da1 # f2fscrypt add_key -S 0x1234 Enter passphrase (echo disabled): Added key with descriptor [28e21cc0c4393da1] # ls -l /encrypted/dir/ -rw-r--r--. 1 root root 0 Mar 5 21:41 test.txt .nf Show process keyrings. .ft R # keyctl show Session Keyring 84022412 --alswrv 0 0 keyring: _ses 204615789 --alswrv 0 65534 \\_ keyring: _uid.0 529474961 --alsw-v 0 0 \\_ logon: f2fs:28e21cc0c4393da1 .SH AUTHOR Written by Kinglong Mee , Migrated from e4crypt that Written by Michael Halcrow , Ildar Muslukhov , and Theodore Ts'o .SH SEE ALSO .BR keyctl (1), .BR mkfs.f2fs (8), .BR mount (8). f2fs-tools-1.10.0/tools/f2fscrypt.c000066400000000000000000000556111323417143600170310ustar00rootroot00000000000000/* * f2fscrypt.c - f2fs encryption management utility * * Authors: Kinglong Mee * * Copied from e4crypt that for ext4 filesystem. * Authors: Michael Halcrow , * Ildar Muslukhov */ #ifndef _LARGEFILE_SOURCE #define _LARGEFILE_SOURCE #endif #ifndef _LARGEFILE64_SOURCE #define _LARGEFILE64_SOURCE #endif #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #include "config.h" #include #include #include #include #include #include #include #include #include #ifdef HAVE_MNTENT_H #include #endif #include #include #include #include #include #include #include #ifdef __KERNEL__ #include #endif #include #if !defined(HAVE_ADD_KEY) || !defined(HAVE_KEYCTL) #include #endif #ifdef HAVE_SYS_KEY_H #include #endif #include #define F2FS_MAX_KEY_SIZE 64 #define F2FS_MAX_PASSPHRASE_SIZE 1024 #define F2FS_MAX_SALT_SIZE 256 /* Encryption algorithms, key size and key reference len */ #define F2FS_ENCRYPTION_MODE_INVALID 0 #define F2FS_ENCRYPTION_MODE_AES_256_XTS 1 #define F2FS_ENCRYPTION_MODE_AES_256_GCM 2 #define F2FS_ENCRYPTION_MODE_AES_256_CBC 3 #define F2FS_ENCRYPTION_MODE_AES_256_CTS 4 #define F2FS_AES_256_XTS_KEY_SIZE 64 #define F2FS_AES_256_GCM_KEY_SIZE 32 #define F2FS_AES_256_CBC_KEY_SIZE 32 #define F2FS_AES_256_CTS_KEY_SIZE 32 #define F2FS_MAX_KEY_SIZE 64 /* Password derivation constants */ #define F2FS_MAX_PASSPHRASE_SIZE 1024 #define F2FS_MAX_SALT_SIZE 256 #define F2FS_PBKDF2_ITERATIONS 0xFFFF /* special process keyring shortcut IDs */ #define KEY_SPEC_THREAD_KEYRING -1 #define KEY_SPEC_PROCESS_KEYRING -2 #define KEY_SPEC_SESSION_KEYRING -3 #define KEY_SPEC_USER_KEYRING -4 #define KEY_SPEC_USER_SESSION_KEYRING -5 #define KEY_SPEC_GROUP_KEYRING -6 #define KEYCTL_GET_KEYRING_ID 0 #define KEYCTL_JOIN_SESSION_KEYRING 1 #define KEYCTL_DESCRIBE 6 #define KEYCTL_SEARCH 10 #define KEYCTL_SESSION_TO_PARENT 18 /* * File system encryption support */ /* Policy provided via an ioctl on the topmost directory */ #define F2FS_KEY_DESCRIPTOR_SIZE 8 #define F2FS_KEY_REF_STR_BUF_SIZE ((F2FS_KEY_DESCRIPTOR_SIZE * 2) + 1) struct f2fs_fscrypt_policy { __u8 version; __u8 contents_encryption_mode; __u8 filenames_encryption_mode; __u8 flags; __u8 master_key_descriptor[F2FS_KEY_DESCRIPTOR_SIZE]; } __attribute__((packed)); #define F2FS_IOC_SET_ENCRYPTION_POLICY _IOR('f', 19, struct f2fs_fscrypt_policy) #define F2FS_IOC_GET_ENCRYPTION_PWSALT _IOW('f', 20, __u8[16]) #define F2FS_IOC_GET_ENCRYPTION_POLICY _IOW('f', 21, struct f2fs_fscrypt_policy) typedef int32_t key_serial_t; #define OPT_VERBOSE 0x0001 #define OPT_QUIET 0x0002 struct f2fs_encryption_key { __u32 mode; char raw[F2FS_MAX_KEY_SIZE]; __u32 size; } __attribute__((__packed__)); int options; extern void f2fs_sha512(const unsigned char *in, unsigned long in_size, unsigned char *out); #if !defined(HAVE_KEYCTL) static long keyctl(int cmd, ...) { va_list va; unsigned long arg2, arg3, arg4, arg5; va_start(va, cmd); arg2 = va_arg(va, unsigned long); arg3 = va_arg(va, unsigned long); arg4 = va_arg(va, unsigned long); arg5 = va_arg(va, unsigned long); va_end(va); return syscall(__NR_keyctl, cmd, arg2, arg3, arg4, arg5); } #endif #if !defined(HAVE_ADD_KEY) static key_serial_t add_key(const char *type, const char *description, const void *payload, size_t plen, key_serial_t keyring) { return syscall(__NR_add_key, type, description, payload, plen, keyring); } #endif static const unsigned char *hexchars = (const unsigned char *) "0123456789abcdef"; static const size_t hexchars_size = 16; #define SHA512_LENGTH 64 #define F2FS_KEY_TYPE_LOGON "logon" #define F2FS_KEY_DESC_PREFIX "f2fs:" #define F2FS_KEY_DESC_PREFIX_SIZE 5 static int int_log2(int arg) { int l = 0; arg >>= 1; while (arg) { l++; arg >>= 1; } return l; } static void validate_paths(int argc, char *argv[], int path_start_index) { int x; int valid = 1; struct stat st; for (x = path_start_index; x < argc; x++) { int ret = access(argv[x], W_OK); if (ret) { invalid: perror(argv[x]); valid = 0; continue; } ret = stat(argv[x], &st); if (ret < 0) goto invalid; if (!S_ISDIR(st.st_mode)) { fprintf(stderr, "%s is not a directory\n", argv[x]); goto invalid; } } if (!valid) exit(1); } static int hex2byte(const char *hex, size_t hex_size, unsigned char *bytes, size_t bytes_size) { size_t x; unsigned char *h, *l; if (hex_size % 2) return -EINVAL; for (x = 0; x < hex_size; x += 2) { h = memchr(hexchars, hex[x], hexchars_size); if (!h) return -EINVAL; l = memchr(hexchars, hex[x + 1], hexchars_size); if (!l) return -EINVAL; if ((x >> 1) >= bytes_size) return -EINVAL; bytes[x >> 1] = (((unsigned char)(h - hexchars) << 4) + (unsigned char)(l - hexchars)); } return 0; } /* * Salt handling */ struct salt { unsigned char *salt; char key_ref_str[F2FS_KEY_REF_STR_BUF_SIZE]; unsigned char key_desc[F2FS_KEY_DESCRIPTOR_SIZE]; unsigned char key[F2FS_MAX_KEY_SIZE]; size_t salt_len; }; struct salt *salt_list; unsigned num_salt; unsigned max_salt; char in_passphrase[F2FS_MAX_PASSPHRASE_SIZE]; static struct salt *find_by_salt(unsigned char *salt, size_t salt_len) { unsigned int i; struct salt *p; for (i = 0, p = salt_list; i < num_salt; i++, p++) if ((p->salt_len == salt_len) && !memcmp(p->salt, salt, salt_len)) return p; return NULL; } static void add_salt(unsigned char *salt, size_t salt_len) { if (find_by_salt(salt, salt_len)) return; if (num_salt >= max_salt) { max_salt = num_salt + 10; salt_list = realloc(salt_list, max_salt * sizeof(struct salt)); if (!salt_list) { fprintf(stderr, "Couldn't allocate salt list\n"); exit(1); } } salt_list[num_salt].salt = salt; salt_list[num_salt].salt_len = salt_len; num_salt++; } static void clear_secrets(void) { if (salt_list) { memset(salt_list, 0, sizeof(struct salt) * max_salt); free(salt_list); salt_list = NULL; } memset(in_passphrase, 0, sizeof(in_passphrase)); } static void die_signal_handler(int signum, siginfo_t *siginfo, void *context) { clear_secrets(); exit(-1); } static void sigcatcher_setup(void) { struct sigaction sa; memset(&sa, 0, sizeof(struct sigaction)); sa.sa_sigaction = die_signal_handler; sa.sa_flags = SA_SIGINFO; sigaction(SIGHUP, &sa, 0); sigaction(SIGINT, &sa, 0); sigaction(SIGQUIT, &sa, 0); sigaction(SIGFPE, &sa, 0); sigaction(SIGILL, &sa, 0); sigaction(SIGBUS, &sa, 0); sigaction(SIGSEGV, &sa, 0); sigaction(SIGABRT, &sa, 0); sigaction(SIGPIPE, &sa, 0); sigaction(SIGALRM, &sa, 0); sigaction(SIGTERM, &sa, 0); sigaction(SIGUSR1, &sa, 0); sigaction(SIGUSR2, &sa, 0); sigaction(SIGPOLL, &sa, 0); sigaction(SIGPROF, &sa, 0); sigaction(SIGSYS, &sa, 0); sigaction(SIGTRAP, &sa, 0); sigaction(SIGVTALRM, &sa, 0); sigaction(SIGXCPU, &sa, 0); sigaction(SIGXFSZ, &sa, 0); } #define PARSE_FLAGS_NOTSUPP_OK 0x0001 #define PARSE_FLAGS_FORCE_FN 0x0002 static void parse_salt(char *salt_str, int flags) { unsigned char buf[F2FS_MAX_SALT_SIZE]; char *cp = salt_str; unsigned char *salt_buf; int fd, ret, salt_len = 0; if (flags & PARSE_FLAGS_FORCE_FN) goto salt_from_filename; if (strncmp(cp, "s:", 2) == 0) { cp += 2; salt_len = strlen(cp); if (salt_len >= F2FS_MAX_SALT_SIZE) goto invalid_salt; strncpy((char *) buf, cp, sizeof(buf)); } else if (cp[0] == '/') { salt_from_filename: fd = open(cp, O_RDONLY | O_DIRECTORY); if (fd == -1 && errno == ENOTDIR) fd = open(cp, O_RDONLY); if (fd == -1) { perror(cp); exit(1); } ret = ioctl(fd, F2FS_IOC_GET_ENCRYPTION_PWSALT, &buf); close(fd); if (ret < 0) { if (flags & PARSE_FLAGS_NOTSUPP_OK) return; perror("F2FS_IOC_GET_ENCRYPTION_PWSALT"); exit(1); } if (options & OPT_VERBOSE) { char tmp[80]; uuid_unparse(buf, tmp); printf("%s has pw salt %s\n", cp, tmp); } salt_len = 16; } else if (strncmp(cp, "f:", 2) == 0) { cp += 2; goto salt_from_filename; } else if (strncmp(cp, "0x", 2) == 0) { unsigned char *h, *l; cp += 2; if (strlen(cp) & 1) goto invalid_salt; while (*cp) { if (salt_len >= F2FS_MAX_SALT_SIZE) goto invalid_salt; h = memchr(hexchars, *cp++, hexchars_size); l = memchr(hexchars, *cp++, hexchars_size); if (!h || !l) goto invalid_salt; buf[salt_len++] = (((unsigned char)(h - hexchars) << 4) + (unsigned char)(l - hexchars)); } } else if (uuid_parse(cp, buf) == 0) { salt_len = 16; } else { invalid_salt: fprintf(stderr, "Invalid salt: %s\n", salt_str); exit(1); } salt_buf = malloc(salt_len); if (!salt_buf) { fprintf(stderr, "Couldn't allocate salt\n"); exit(1); } memcpy(salt_buf, buf, salt_len); add_salt(salt_buf, salt_len); } static void set_policy(struct salt *set_salt, int pad, int argc, char *argv[], int path_start_index) { struct salt *salt; struct f2fs_fscrypt_policy policy; uuid_t uu; int fd; int x; int rc; if ((pad != 4) && (pad != 8) && (pad != 16) && (pad != 32)) { fprintf(stderr, "Invalid padding %d\n", pad); exit(1); } for (x = path_start_index; x < argc; x++) { fd = open(argv[x], O_DIRECTORY); if (fd == -1) { perror(argv[x]); exit(1); } if (set_salt) salt = set_salt; else { if (ioctl(fd, F2FS_IOC_GET_ENCRYPTION_PWSALT, &uu) < 0) { perror("F2FS_IOC_GET_ENCRYPTION_PWSALT"); exit(1); } salt = find_by_salt(uu, sizeof(uu)); if (!salt) { fprintf(stderr, "Couldn't find salt!?!\n"); exit(1); } } policy.version = 0; policy.contents_encryption_mode = F2FS_ENCRYPTION_MODE_AES_256_XTS; policy.filenames_encryption_mode = F2FS_ENCRYPTION_MODE_AES_256_CTS; policy.flags = int_log2(pad >> 2); memcpy(policy.master_key_descriptor, salt->key_desc, F2FS_KEY_DESCRIPTOR_SIZE); rc = ioctl(fd, F2FS_IOC_SET_ENCRYPTION_POLICY, &policy); close(fd); if (rc) { printf("Error [%s] setting policy.\nThe key descriptor " "[%s] may not match the existing encryption " "context for directory [%s].\n", strerror(errno), salt->key_ref_str, argv[x]); continue; } printf("Key with descriptor [%s] applied to %s.\n", salt->key_ref_str, argv[x]); } } static void pbkdf2_sha512(const char *passphrase, struct salt *salt, unsigned int count, unsigned char derived_key[F2FS_MAX_KEY_SIZE]) { size_t passphrase_size = strlen(passphrase); unsigned char buf[SHA512_LENGTH + F2FS_MAX_PASSPHRASE_SIZE] = {0}; unsigned char tempbuf[SHA512_LENGTH] = {0}; char final[SHA512_LENGTH] = {0}; unsigned char saltbuf[F2FS_MAX_SALT_SIZE + F2FS_MAX_PASSPHRASE_SIZE] = {0}; int actual_buf_len = SHA512_LENGTH + passphrase_size; int actual_saltbuf_len = F2FS_MAX_SALT_SIZE + passphrase_size; unsigned int x, y; __u32 *final_u32 = (__u32 *)final; __u32 *temp_u32 = (__u32 *)tempbuf; if (passphrase_size > F2FS_MAX_PASSPHRASE_SIZE) { printf("Passphrase size is %zd; max is %d.\n", passphrase_size, F2FS_MAX_PASSPHRASE_SIZE); exit(1); } if (salt->salt_len > F2FS_MAX_SALT_SIZE) { printf("Salt size is %zd; max is %d.\n", salt->salt_len, F2FS_MAX_SALT_SIZE); exit(1); } assert(F2FS_MAX_KEY_SIZE <= SHA512_LENGTH); memcpy(saltbuf, salt->salt, salt->salt_len); memcpy(&saltbuf[F2FS_MAX_SALT_SIZE], passphrase, passphrase_size); memcpy(&buf[SHA512_LENGTH], passphrase, passphrase_size); for (x = 0; x < count; ++x) { if (x == 0) { f2fs_sha512(saltbuf, actual_saltbuf_len, tempbuf); } else { /* * buf: [previous hash || passphrase] */ memcpy(buf, tempbuf, SHA512_LENGTH); f2fs_sha512(buf, actual_buf_len, tempbuf); } for (y = 0; y < (sizeof(final) / sizeof(*final_u32)); ++y) final_u32[y] = final_u32[y] ^ temp_u32[y]; } memcpy(derived_key, final, F2FS_MAX_KEY_SIZE); } static int disable_echo(struct termios *saved_settings) { struct termios current_settings; int rc = 0; rc = tcgetattr(0, ¤t_settings); if (rc) return rc; *saved_settings = current_settings; current_settings.c_lflag &= ~ECHO; rc = tcsetattr(0, TCSANOW, ¤t_settings); return rc; } static void get_passphrase(char *passphrase, int len) { char *p; struct termios current_settings; assert(len > 0); disable_echo(¤t_settings); p = fgets(passphrase, len, stdin); tcsetattr(0, TCSANOW, ¤t_settings); printf("\n"); if (!p) { printf("Aborting.\n"); exit(1); } p = strrchr(passphrase, '\n'); if (!p) p = passphrase + len - 1; *p = '\0'; } struct keyring_map { char name[4]; size_t name_len; int code; }; static const struct keyring_map keyrings[] = { {"@us", 3, KEY_SPEC_USER_SESSION_KEYRING}, {"@u", 2, KEY_SPEC_USER_KEYRING}, {"@s", 2, KEY_SPEC_SESSION_KEYRING}, {"@g", 2, KEY_SPEC_GROUP_KEYRING}, {"@p", 2, KEY_SPEC_PROCESS_KEYRING}, {"@t", 2, KEY_SPEC_THREAD_KEYRING}, }; static int get_keyring_id(const char *keyring) { unsigned int x; char *end; /* * If no keyring is specified, by default use either the user * session key ring or the session keyring. Fetching the * session keyring will return the user session keyring if no * session keyring has been set. * * We need to do this instead of simply adding the key to * KEY_SPEC_SESSION_KEYRING since trying to add a key to a * session keyring that does not yet exist will cause the * kernel to create a session keyring --- which wil then get * garbage collected as soon as f2fscrypt exits. * * The fact that the keyctl system call and the add_key system * call treats KEY_SPEC_SESSION_KEYRING differently when a * session keyring does not exist is very unfortunate and * confusing, but so it goes... */ if (keyring == NULL) return keyctl(KEYCTL_GET_KEYRING_ID, KEY_SPEC_SESSION_KEYRING, 0); for (x = 0; x < (sizeof(keyrings) / sizeof(keyrings[0])); ++x) { if (strcmp(keyring, keyrings[x].name) == 0) { return keyrings[x].code; } } x = strtoul(keyring, &end, 10); if (*end == '\0') { if (keyctl(KEYCTL_DESCRIBE, x, NULL, 0) < 0) return 0; return x; } return 0; } static void generate_key_ref_str(struct salt *salt) { unsigned char key_ref1[SHA512_LENGTH]; unsigned char key_ref2[SHA512_LENGTH]; int x; f2fs_sha512(salt->key, F2FS_MAX_KEY_SIZE, key_ref1); f2fs_sha512(key_ref1, SHA512_LENGTH, key_ref2); memcpy(salt->key_desc, key_ref2, F2FS_KEY_DESCRIPTOR_SIZE); for (x = 0; x < F2FS_KEY_DESCRIPTOR_SIZE; ++x) { sprintf(&salt->key_ref_str[x * 2], "%02x", salt->key_desc[x]); } salt->key_ref_str[F2FS_KEY_REF_STR_BUF_SIZE - 1] = '\0'; } static void insert_key_into_keyring(const char *keyring, struct salt *salt) { int keyring_id = get_keyring_id(keyring); struct f2fs_encryption_key key; char key_ref_full[F2FS_KEY_DESC_PREFIX_SIZE + F2FS_KEY_REF_STR_BUF_SIZE]; int rc; if (keyring_id == 0) { printf("Invalid keyring [%s].\n", keyring); exit(1); } sprintf(key_ref_full, "%s%s", F2FS_KEY_DESC_PREFIX, salt->key_ref_str); rc = keyctl(KEYCTL_SEARCH, keyring_id, F2FS_KEY_TYPE_LOGON, key_ref_full, 0); if (rc != -1) { if ((options & OPT_QUIET) == 0) printf("Key with descriptor [%s] already exists\n", salt->key_ref_str); return; } else if ((rc == -1) && (errno != ENOKEY)) { printf("keyctl_search failed: %s\n", strerror(errno)); if (errno == -EINVAL) printf("Keyring [%s] is not available.\n", keyring); exit(1); } key.mode = F2FS_ENCRYPTION_MODE_AES_256_XTS; memcpy(key.raw, salt->key, F2FS_MAX_KEY_SIZE); key.size = F2FS_MAX_KEY_SIZE; rc = add_key(F2FS_KEY_TYPE_LOGON, key_ref_full, (void *)&key, sizeof(key), keyring_id); if (rc == -1) { if (errno == EDQUOT) { printf("Error adding key to keyring; quota exceeded\n"); } else { printf("Error adding key with key descriptor [%s]: " "%s\n", salt->key_ref_str, strerror(errno)); } exit(1); } else { if ((options & OPT_QUIET) == 0) printf("Added key with descriptor [%s]\n", salt->key_ref_str); } } static void get_default_salts(void) { FILE *f = setmntent("/etc/mtab", "r"); struct mntent *mnt; while (f && ((mnt = getmntent(f)) != NULL)) { if (strcmp(mnt->mnt_type, "f2fs") || access(mnt->mnt_dir, R_OK)) continue; parse_salt(mnt->mnt_dir, PARSE_FLAGS_NOTSUPP_OK); } endmntent(f); } /* Functions which implement user commands */ struct cmd_desc { const char *cmd_name; void (*cmd_func)(int, char **, const struct cmd_desc *); const char *cmd_desc; const char *cmd_help; int cmd_flags; }; #define CMD_HIDDEN 0x0001 static void do_help(int argc, char **argv, const struct cmd_desc *cmd); #define add_key_desc "adds a key to the user's keyring" #define add_key_help \ "f2fscrypt add_key -S salt [ -k keyring ] [-v] [-q] [ path ... ]\n\n" \ "Prompts the user for a passphrase and inserts it into the specified\n" \ "keyring. If no keyring is specified, f2fscrypt will use the session\n" \ "keyring if it exists or the user session keyring if it does not.\n\n" \ "If one or more directory paths are specified, f2fscrypt will try to\n" \ "set the policy of those directories to use the key just entered by\n" \ "the user.\n" static void do_add_key(int argc, char **argv, const struct cmd_desc *cmd) { struct salt *salt; char *keyring = NULL; int i, opt, pad = 4; unsigned j; while ((opt = getopt(argc, argv, "k:S:p:vq")) != -1) { switch (opt) { case 'k': /* Specify a keyring. */ keyring = optarg; break; case 'p': pad = atoi(optarg); break; case 'S': /* Salt value for passphrase. */ parse_salt(optarg, 0); break; case 'v': options |= OPT_VERBOSE; break; case 'q': options |= OPT_QUIET; break; default: fprintf(stderr, "Unrecognized option: %c\n", opt); case '?': fputs("USAGE:\n ", stderr); fputs(cmd->cmd_help, stderr); exit(1); } } if (num_salt == 0) get_default_salts(); if (num_salt == 0) { fprintf(stderr, "No salt values available\n"); exit(1); } validate_paths(argc, argv, optind); for (i = optind; i < argc; i++) parse_salt(argv[i], PARSE_FLAGS_FORCE_FN); printf("Enter passphrase (echo disabled): "); get_passphrase(in_passphrase, sizeof(in_passphrase)); for (j = 0, salt = salt_list; j < num_salt; j++, salt++) { pbkdf2_sha512(in_passphrase, salt, F2FS_PBKDF2_ITERATIONS, salt->key); generate_key_ref_str(salt); insert_key_into_keyring(keyring, salt); } if (optind != argc) set_policy(NULL, pad, argc, argv, optind); clear_secrets(); exit(0); } #define set_policy_desc "sets a policy for directories" #define set_policy_help \ "f2fscrypt set_policy policy path ... \n\n" \ "Sets the policy for the directories specified on the command line.\n" \ "All directories must be empty to set the policy; if the directory\n" \ "already has a policy established, f2fscrypt will validate that it the\n" \ "policy matches what was specified. A policy is an encryption key\n" \ "identifier consisting of 16 hexadecimal characters.\n" static void do_set_policy(int argc, char **argv, const struct cmd_desc *cmd) { struct salt saltbuf; int c, pad = 4; while ((c = getopt (argc, argv, "p:")) != EOF) { switch (c) { case 'p': pad = atoi(optarg); break; } } if (argc < optind + 2) { fprintf(stderr, "Missing required argument(s).\n\n"); fputs("USAGE:\n ", stderr); fputs(cmd->cmd_help, stderr); exit(1); } if ((strlen(argv[optind]) != (F2FS_KEY_DESCRIPTOR_SIZE * 2)) || hex2byte(argv[optind], (F2FS_KEY_DESCRIPTOR_SIZE * 2), saltbuf.key_desc, F2FS_KEY_DESCRIPTOR_SIZE)) { printf("Invalid key descriptor [%s]. Valid characters " "are 0-9 and a-f, lower case. " "Length must be %d.\n", argv[optind], (F2FS_KEY_DESCRIPTOR_SIZE * 2)); exit(1); } validate_paths(argc, argv, optind+1); strcpy(saltbuf.key_ref_str, argv[optind]); set_policy(&saltbuf, pad, argc, argv, optind+1); exit(0); } #define get_policy_desc "get the encryption for directories" #define get_policy_help \ "f2fscrypt get_policy path ... \n\n" \ "Gets the policy for the directories specified on the command line.\n" static void do_get_policy(int argc, char **argv, const struct cmd_desc *cmd) { struct f2fs_fscrypt_policy policy; struct stat st; int i, j, fd, rc; if (argc < 2) { fprintf(stderr, "Missing required argument(s).\n\n"); fputs("USAGE:\n ", stderr); fputs(cmd->cmd_help, stderr); exit(1); } for (i = 1; i < argc; i++) { if (stat(argv[i], &st) < 0) { perror(argv[i]); continue; } fd = open(argv[i], S_ISDIR(st.st_mode) ? O_DIRECTORY : O_RDONLY); if (fd == -1) { perror(argv[i]); exit(1); } rc = ioctl(fd, F2FS_IOC_GET_ENCRYPTION_POLICY, &policy); close(fd); if (rc) { printf("Error getting policy for %s: %s\n", argv[i], strerror(errno)); continue; } printf("%s: ", argv[i]); for (j = 0; j < F2FS_KEY_DESCRIPTOR_SIZE; j++) { printf("%02x", (unsigned char) policy.master_key_descriptor[j]); } fputc('\n', stdout); } exit(0); } #define new_session_desc "give the invoking process a new session keyring" #define new_session_help \ "f2fscrypt new_session\n\n" \ "Give the invoking process (typically a shell) a new session keyring,\n" \ "discarding its old session keyring.\n" static void do_new_session(int argc, char **argv, const struct cmd_desc *cmd) { long keyid, ret; if (argc > 1) { fputs("Excess arguments\n\n", stderr); fputs(cmd->cmd_help, stderr); exit(1); } keyid = keyctl(KEYCTL_JOIN_SESSION_KEYRING, NULL); if (keyid < 0) { perror("KEYCTL_JOIN_SESSION_KEYRING"); exit(1); } ret = keyctl(KEYCTL_SESSION_TO_PARENT, NULL); if (ret < 0) { perror("KEYCTL_SESSION_TO_PARENT"); exit(1); } printf("Switched invoking process to new session keyring %ld\n", keyid); exit(0); } #define CMD(name) { #name, do_##name, name##_desc, name##_help, 0 } #define _CMD(name) { #name, do_##name, NULL, NULL, CMD_HIDDEN } const struct cmd_desc cmd_list[] = { _CMD(help), CMD(add_key), CMD(get_policy), CMD(new_session), CMD(set_policy), { NULL, NULL, NULL, NULL, 0 } }; static void do_help(int argc, char **argv, const struct cmd_desc *cmd) { const struct cmd_desc *p; if (argc > 1) { for (p = cmd_list; p->cmd_name; p++) { if (p->cmd_flags & CMD_HIDDEN) continue; if (strcmp(p->cmd_name, argv[1]) == 0) { putc('\n', stdout); fputs("USAGE:\n ", stdout); fputs(p->cmd_help, stdout); exit(0); } } printf("Unknown command: %s\n\n", argv[1]); } fputs("Available commands:\n", stdout); for (p = cmd_list; p->cmd_name; p++) { if (p->cmd_flags & CMD_HIDDEN) continue; printf(" %-20s %s\n", p->cmd_name, p->cmd_desc); } printf("\nTo get more information on a command, " "type 'f2fscrypt help cmd'\n"); exit(0); } int main(int argc, char *argv[]) { const struct cmd_desc *cmd; if (argc < 2) do_help(argc, argv, cmd_list); sigcatcher_setup(); for (cmd = cmd_list; cmd->cmd_name; cmd++) { if (strcmp(cmd->cmd_name, argv[1]) == 0) { cmd->cmd_func(argc-1, argv+1, cmd); exit(0); } } printf("Unknown command: %s\n\n", argv[1]); do_help(1, argv, cmd_list); return 0; } f2fs-tools-1.10.0/tools/f2fstat.c000066400000000000000000000156101323417143600164530ustar00rootroot00000000000000#include #include #include #include #include #include #ifdef DEBUG #define dbg(fmt, args...) printf(fmt, __VA_ARGS__); #else #define dbg(fmt, args...) #endif /* * f2fs status */ #define F2FS_STATUS "/sys/kernel/debug/f2fs/status" #define KEY_NODE 0x00000001 #define KEY_META 0x00000010 unsigned long util; unsigned long used_node_blks; unsigned long used_data_blks; //unsigned long inline_inode; unsigned long free_segs; unsigned long valid_segs; unsigned long dirty_segs; unsigned long prefree_segs; unsigned long gc, bg_gc; unsigned long cp; unsigned long gc_data_blks; unsigned long gc_node_blks; //unsigned long extent_hit_ratio; unsigned long dirty_node, node_kb; unsigned long dirty_dents; unsigned long dirty_meta, meta_kb; unsigned long nat_caches; unsigned long dirty_sit; unsigned long free_nids; unsigned long ssr_blks; unsigned long lfs_blks; unsigned long memory_kb; struct options { int delay; int interval; char partname[32]; }; struct mm_table { const char *name; unsigned long *val; int flag; }; static int compare_mm_table(const void *a, const void *b) { dbg("[COMPARE] %s, %s\n", ((struct mm_table *)a)->name, ((struct mm_table *)b)->name); return strcmp(((struct mm_table *)a)->name, ((struct mm_table *)b)->name); } static inline void remove_newline(char **head) { again: if (**head == '\n') { *head = *head + 1; goto again; } } void f2fstat(struct options *opt) { int fd; int ret; char keyname[32]; char buf[4096]; struct mm_table key = { keyname, NULL }; struct mm_table *found; int f2fstat_table_cnt; char *head, *tail; int found_cnt = 0; static struct mm_table f2fstat_table[] = { { " - Data", &used_data_blks, 0 }, { " - Dirty", &dirty_segs, 0 }, { " - Free", &free_segs, 0 }, { " - NATs", &nat_caches, 0 }, { " - Node", &used_node_blks, 0 }, { " - Prefree", &prefree_segs, 0 }, { " - SITs", &dirty_sit, 0 }, { " - Valid", &valid_segs, 0 }, { " - dents", &dirty_dents, 0 }, { " - free_nids", &free_nids, 0 }, { " - meta", &dirty_meta, KEY_META }, { " - nodes", &dirty_node, KEY_NODE }, { "CP calls", &cp, 0 }, { "GC calls", &gc, 0 }, { "LFS", &lfs_blks, 0 }, { "Memory", &memory_kb, 0 }, { "SSR", &ssr_blks, 0 }, { "Utilization", &util, 0 }, }; f2fstat_table_cnt = sizeof(f2fstat_table)/sizeof(struct mm_table); fd = open(F2FS_STATUS, O_RDONLY); if (fd < 0) { perror("open " F2FS_STATUS); exit(EXIT_FAILURE); } ret = read(fd, buf, 4096); if (ret < 0) { perror("read " F2FS_STATUS); exit(EXIT_FAILURE); } buf[ret] = '\0'; head = buf; if (opt->partname[0] != '\0') { head = strstr(buf, opt->partname); if (head == NULL) exit(EXIT_FAILURE); } for (;;) { remove_newline(&head); tail = strchr(head, ':'); if (!tail) break; *tail = '\0'; if (strlen(head) >= sizeof(keyname)) { dbg("[OVER] %s\n", head); *tail = ':'; tail = strchr(head, '\n'); head = tail + 1; continue; } strcpy(keyname, head); found = bsearch(&key, f2fstat_table, f2fstat_table_cnt, sizeof(struct mm_table), compare_mm_table); dbg("[RESULT] %s (%s)\n", head, (found) ? "O" : "X"); head = tail + 1; if (!found) goto nextline; *(found->val) = strtoul(head, &tail, 10); if (found->flag) { int npages; tail = strstr(head, "in"); head = tail + 2; npages = strtoul(head, &tail, 10); switch (found->flag & (KEY_NODE | KEY_META)) { case KEY_NODE: node_kb = npages * 4; break; case KEY_META: meta_kb = npages * 4; break; } } if (++found_cnt == f2fstat_table_cnt) break; nextline: tail = strchr(head, '\n'); if (!tail) break; head = tail + 1; } close(fd); } void usage(void) { printf("Usage: f2fstat [option]\n" " -d delay (secs)\n" " -i interval of head info\n" " -p partition name (e.g. /dev/sda3)\n"); exit(EXIT_FAILURE); } void parse_option(int argc, char *argv[], struct options *opt) { int option; const char *option_string = "d:i:p:h"; while ((option = getopt(argc, argv, option_string)) != EOF) { switch (option) { case 'd': opt->delay = atoi(optarg); break; case 'i': opt->interval = atoi(optarg); break; case 'p': strcpy(opt->partname, basename(optarg)); break; default: usage(); break; } } } void __make_head(char *head, int index, int i, int len) { char name_h[5][20] = {"main segments", "page/slab caches", "cp/gc", "blks", "memory"}; int half = (len - strlen(name_h[i])) / 2; *(head + index) = '|'; index++; memset(head + index, '-', half); index += half; strcpy(head + index, name_h[i]); index += strlen(name_h[i]); memset(head + index, '-', half); } void print_head(char *res) { char *ptr, *ptr_buf; char buf[1024], head[1024]; char name[20][10] = {"util", "node", "data", "free", "valid", "dirty", "prefree", "node", "dent", "meta", "sit", "nat", "fnid", "cp", "gc", "ssr", "lfs", "total", "node", "meta"}; int i, len, prev_index = 0; ptr_buf = buf; memset(buf, ' ', 1024); memset(head, ' ', 1024); for (i = 0; i < 20; i++) { ptr = (i == 0) ? strtok(res, " ") : strtok(NULL, " "); strncpy(ptr_buf, name[i], strlen(name[i])); if (i == 1) { prev_index = ptr_buf - buf - 1; } else if (i == 7) { len = (ptr_buf - buf) - 1 - prev_index; __make_head(head, prev_index, 0, len); prev_index = ptr_buf - buf - 1; } else if (i == 13) { len = (ptr_buf - buf) - 1 - prev_index; __make_head(head, prev_index, 1, len); prev_index = ptr_buf - buf - 1; } else if (i == 15) { len = (ptr_buf - buf) - 1 - prev_index; __make_head(head, prev_index, 2, len); prev_index = ptr_buf - buf - 1; } else if (i == 17) { len = (ptr_buf - buf) - 1 - prev_index; __make_head(head, prev_index, 3, len); prev_index = ptr_buf - buf - 1; } len = strlen(ptr); ptr_buf += (len > strlen(name[i]) ? len : strlen(name[i])) + 1; } len = (ptr_buf - buf) - 1 - prev_index; __make_head(head, prev_index, 4, len); *ptr_buf = 0; *(head + (ptr_buf - buf - 1)) = '|'; *(head + (ptr_buf - buf)) = 0; fprintf(stderr, "%s\n%s\n", head, buf); } int main(int argc, char *argv[]) { char format[] = "%4ld %4ld %4ld %4ld %5ld %5ld %7ld %4ld %4ld %4ld %3ld %3ld %4ld %2ld %2ld %3ld %3ld %5ld %4ld %4ld"; char buf[1024], tmp[1024]; int head_interval; struct options opt = { .delay = 1, .interval = 20, .partname = { 0, }, }; parse_option(argc, argv, &opt); head_interval = opt.interval; while (1) { memset(buf, 0, 1024); f2fstat(&opt); sprintf(buf, format, util, used_node_blks, used_data_blks, free_segs, valid_segs, dirty_segs, prefree_segs, dirty_node, dirty_dents, dirty_meta, dirty_sit, nat_caches, free_nids, cp, gc, ssr_blks, lfs_blks, memory_kb, node_kb, meta_kb); strcpy(tmp, buf); if (head_interval == opt.interval) print_head(tmp); if (head_interval-- == 0) head_interval = opt.interval; fprintf(stderr, "%s\n", buf); sleep(opt.delay); } return 0; } f2fs-tools-1.10.0/tools/fibmap.c000066400000000000000000000113611323417143600163370ustar00rootroot00000000000000#if !defined(__FreeBSD__) && !defined(__NetBSD__) && !defined(__OpenBSD__) #define _XOPEN_SOURCE 600 #define _DARWIN_C_SOURCE #define _FILE_OFFSET_BITS 64 #ifndef _LARGEFILE_SOURCE #define _LARGEFILE_SOURCE #endif #ifndef _LARGEFILE64_SOURCE #define _LARGEFILE64_SOURCE #endif #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #endif #ifndef O_LARGEFILE #define O_LARGEFILE 0 #endif #include #include #include #include #include #include #include #include #include #ifdef HAVE_SYS_SYSMACROS_H #include #endif #include #ifdef HAVE_LINUX_HDREG_H #include #endif #ifdef HAVE_LINUX_TYPES_H #include #endif #ifdef __KERNEL__ #include #endif #include #include #ifndef FIBMAP #define FIBMAP _IO(0x00, 1) /* bmap access */ #endif struct file_ext { __u32 f_pos; __u32 start_blk; __u32 end_blk; __u32 blk_count; }; void print_ext(struct file_ext *ext) { if (ext->end_blk == 0) printf("%8d %8d %8d %8d\n", ext->f_pos, 0, 0, ext->blk_count); else printf("%8d %8d %8d %8d\n", ext->f_pos, ext->start_blk, ext->end_blk, ext->blk_count); } #if defined(HAVE_FSTAT64) && !defined(__OSX_AVAILABLE_BUT_DEPRECATED) void print_stat(struct stat64 *st) #else void print_stat(struct stat *st) #endif { printf("--------------------------------------------\n"); printf("dev [%d:%d]\n", major(st->st_dev), minor(st->st_dev)); printf("ino [0x%8"PRIx64" : %"PRIu64"]\n", st->st_ino, st->st_ino); printf("mode [0x%8x : %d]\n", st->st_mode, st->st_mode); printf("nlink [0x%8lx : %ld]\n", (unsigned long)st->st_nlink, (long)st->st_nlink); printf("uid [0x%8x : %d]\n", st->st_uid, st->st_uid); printf("gid [0x%8x : %d]\n", st->st_gid, st->st_gid); printf("size [0x%8"PRIx64" : %"PRIu64"]\n", (u64)st->st_size, (u64)st->st_size); printf("blksize [0x%8lx : %ld]\n", (unsigned long)st->st_blksize, (long)st->st_blksize); printf("blocks [0x%8"PRIx64" : %"PRIu64"]\n", (u64)st->st_blocks, (u64)st->st_blocks); printf("--------------------------------------------\n\n"); } #if defined(HAVE_FSTAT64) && !defined(__OSX_AVAILABLE_BUT_DEPRECATED) static void stat_bdev(struct stat64 *st, unsigned int *start_lba) #else static void stat_bdev(struct stat *st, unsigned int *start_lba) #endif { struct stat bdev_stat; #ifdef HDIO_GETGIO struct hd_geometry geom; #endif char devname[32] = { 0, }; char linkname[32] = { 0, }; int fd; sprintf(devname, "/dev/block/%d:%d", major(st->st_dev), minor(st->st_dev)); fd = open(devname, O_RDONLY); if (fd < 0) return; if (fstat(fd, &bdev_stat) < 0) goto out; if (S_ISBLK(bdev_stat.st_mode)) { #ifdef HDIO_GETGIO if (ioctl(fd, HDIO_GETGEO, &geom) < 0) *start_lba = 0; else *start_lba = geom.start; #else *start_lba = 0; #endif } if (readlink(devname, linkname, sizeof(linkname)) < 0) goto out; printf("----------------bdev info-------------------\n"); printf("devname = %s\n", basename(linkname)); printf("start_lba = %u\n", *start_lba); out: close(fd); } int main(int argc, char *argv[]) { int fd; int ret = 0; char *filename; #if defined(HAVE_FSTAT64) && !defined(__OSX_AVAILABLE_BUT_DEPRECATED) struct stat64 st; #else struct stat st; #endif int total_blks; unsigned int i; struct file_ext ext; __u32 start_lba; __u32 blknum; if (argc != 2) { fprintf(stderr, "No filename\n"); exit(-1); } filename = argv[1]; fd = open(filename, O_RDONLY|O_LARGEFILE); if (fd < 0) { ret = errno; perror(filename); exit(-1); } fsync(fd); #if defined(HAVE_FSTAT64) && !defined(__OSX_AVAILABLE_BUT_DEPRECATED) if (fstat64(fd, &st) < 0) { #else if (fstat(fd, &st) < 0) { #endif ret = errno; perror(filename); goto out; } stat_bdev(&st, &start_lba); total_blks = (st.st_size + st.st_blksize - 1) / st.st_blksize; printf("\n----------------file info-------------------\n"); printf("%s :\n", filename); print_stat(&st); printf("file_pos start_blk end_blk blks\n"); blknum = 0; if (ioctl(fd, FIBMAP, &blknum) < 0) { ret = errno; perror("ioctl(FIBMAP)"); goto out; } ext.f_pos = 0; ext.start_blk = blknum; ext.end_blk = blknum; ext.blk_count = 1; for (i = 1; i < total_blks; i++) { blknum = i; if (ioctl(fd, FIBMAP, &blknum) < 0) { ret = errno; perror("ioctl(FIBMAP)"); goto out; } if ((blknum == 0 && blknum == ext.end_blk) || (ext.end_blk + 1) == blknum) { ext.end_blk = blknum; ext.blk_count++; } else { print_ext(&ext); ext.f_pos = i * st.st_blksize; ext.start_blk = blknum; ext.end_blk = blknum; ext.blk_count = 1; } } print_ext(&ext); out: close(fd); return ret; } f2fs-tools-1.10.0/tools/sha512.c000066400000000000000000000243631323417143600161120ustar00rootroot00000000000000/* * sha512.c --- The sha512 algorithm * * Copyright (C) 2004 Sam Hocevar * (copied from libtomcrypt and then relicensed under GPLv2) * * %Begin-Header% * This file may be redistributed under the terms of the GNU Library * General Public License, version 2. * %End-Header% */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if HAVE_SYS_TYPES_H #include #endif #define F2FS_SHA512_LENGTH 64 /* the K array */ #define CONST64(n) n static const __u64 K[80] = { CONST64(0x428a2f98d728ae22), CONST64(0x7137449123ef65cd), CONST64(0xb5c0fbcfec4d3b2f), CONST64(0xe9b5dba58189dbbc), CONST64(0x3956c25bf348b538), CONST64(0x59f111f1b605d019), CONST64(0x923f82a4af194f9b), CONST64(0xab1c5ed5da6d8118), CONST64(0xd807aa98a3030242), CONST64(0x12835b0145706fbe), CONST64(0x243185be4ee4b28c), CONST64(0x550c7dc3d5ffb4e2), CONST64(0x72be5d74f27b896f), CONST64(0x80deb1fe3b1696b1), CONST64(0x9bdc06a725c71235), CONST64(0xc19bf174cf692694), CONST64(0xe49b69c19ef14ad2), CONST64(0xefbe4786384f25e3), CONST64(0x0fc19dc68b8cd5b5), CONST64(0x240ca1cc77ac9c65), CONST64(0x2de92c6f592b0275), CONST64(0x4a7484aa6ea6e483), CONST64(0x5cb0a9dcbd41fbd4), CONST64(0x76f988da831153b5), CONST64(0x983e5152ee66dfab), CONST64(0xa831c66d2db43210), CONST64(0xb00327c898fb213f), CONST64(0xbf597fc7beef0ee4), CONST64(0xc6e00bf33da88fc2), CONST64(0xd5a79147930aa725), CONST64(0x06ca6351e003826f), CONST64(0x142929670a0e6e70), CONST64(0x27b70a8546d22ffc), CONST64(0x2e1b21385c26c926), CONST64(0x4d2c6dfc5ac42aed), CONST64(0x53380d139d95b3df), CONST64(0x650a73548baf63de), CONST64(0x766a0abb3c77b2a8), CONST64(0x81c2c92e47edaee6), CONST64(0x92722c851482353b), CONST64(0xa2bfe8a14cf10364), CONST64(0xa81a664bbc423001), CONST64(0xc24b8b70d0f89791), CONST64(0xc76c51a30654be30), CONST64(0xd192e819d6ef5218), CONST64(0xd69906245565a910), CONST64(0xf40e35855771202a), CONST64(0x106aa07032bbd1b8), CONST64(0x19a4c116b8d2d0c8), CONST64(0x1e376c085141ab53), CONST64(0x2748774cdf8eeb99), CONST64(0x34b0bcb5e19b48a8), CONST64(0x391c0cb3c5c95a63), CONST64(0x4ed8aa4ae3418acb), CONST64(0x5b9cca4f7763e373), CONST64(0x682e6ff3d6b2b8a3), CONST64(0x748f82ee5defb2fc), CONST64(0x78a5636f43172f60), CONST64(0x84c87814a1f0ab72), CONST64(0x8cc702081a6439ec), CONST64(0x90befffa23631e28), CONST64(0xa4506cebde82bde9), CONST64(0xbef9a3f7b2c67915), CONST64(0xc67178f2e372532b), CONST64(0xca273eceea26619c), CONST64(0xd186b8c721c0c207), CONST64(0xeada7dd6cde0eb1e), CONST64(0xf57d4f7fee6ed178), CONST64(0x06f067aa72176fba), CONST64(0x0a637dc5a2c898a6), CONST64(0x113f9804bef90dae), CONST64(0x1b710b35131c471b), CONST64(0x28db77f523047d84), CONST64(0x32caab7b40c72493), CONST64(0x3c9ebe0a15c9bebc), CONST64(0x431d67c49c100d4c), CONST64(0x4cc5d4becb3e42b6), CONST64(0x597f299cfc657e2a), CONST64(0x5fcb6fab3ad6faec), CONST64(0x6c44198c4a475817) }; #define Ch(x,y,z) (z ^ (x & (y ^ z))) #define Maj(x,y,z) (((x | y) & z) | (x & y)) #define S(x, n) ROR64c(x, n) #define R(x, n) (((x)&CONST64(0xFFFFFFFFFFFFFFFF))>>((__u64)n)) #define Sigma0(x) (S(x, 28) ^ S(x, 34) ^ S(x, 39)) #define Sigma1(x) (S(x, 14) ^ S(x, 18) ^ S(x, 41)) #define Gamma0(x) (S(x, 1) ^ S(x, 8) ^ R(x, 7)) #define Gamma1(x) (S(x, 19) ^ S(x, 61) ^ R(x, 6)) #define RND(a,b,c,d,e,f,g,h,i)\ t0 = h + Sigma1(e) + Ch(e, f, g) + K[i] + W[i];\ t1 = Sigma0(a) + Maj(a, b, c);\ d += t0;\ h = t0 + t1; #define STORE64H(x, y) \ do { \ (y)[0] = (unsigned char)(((x)>>56)&255);\ (y)[1] = (unsigned char)(((x)>>48)&255);\ (y)[2] = (unsigned char)(((x)>>40)&255);\ (y)[3] = (unsigned char)(((x)>>32)&255);\ (y)[4] = (unsigned char)(((x)>>24)&255);\ (y)[5] = (unsigned char)(((x)>>16)&255);\ (y)[6] = (unsigned char)(((x)>>8)&255);\ (y)[7] = (unsigned char)((x)&255); } while(0) #define LOAD64H(x, y)\ do {x = \ (((__u64)((y)[0] & 255)) << 56) |\ (((__u64)((y)[1] & 255)) << 48) |\ (((__u64)((y)[2] & 255)) << 40) |\ (((__u64)((y)[3] & 255)) << 32) |\ (((__u64)((y)[4] & 255)) << 24) |\ (((__u64)((y)[5] & 255)) << 16) |\ (((__u64)((y)[6] & 255)) << 8) |\ (((__u64)((y)[7] & 255)));\ } while(0) #define ROR64c(x, y) \ ( ((((x)&CONST64(0xFFFFFFFFFFFFFFFF))>>((__u64)(y)&CONST64(63))) | \ ((x)<<((__u64)(64-((y)&CONST64(63)))))) & CONST64(0xFFFFFFFFFFFFFFFF)) struct sha512_state { __u64 length, state[8]; unsigned long curlen; unsigned char buf[128]; }; /* This is a highly simplified version from libtomcrypt */ struct hash_state { struct sha512_state sha512; }; static void sha512_compress(struct hash_state * md, const unsigned char *buf) { __u64 S[8], W[80], t0, t1; int i; /* copy state into S */ for (i = 0; i < 8; i++) { S[i] = md->sha512.state[i]; } /* copy the state into 1024-bits into W[0..15] */ for (i = 0; i < 16; i++) { LOAD64H(W[i], buf + (8*i)); } /* fill W[16..79] */ for (i = 16; i < 80; i++) { W[i] = Gamma1(W[i - 2]) + W[i - 7] + Gamma0(W[i - 15]) + W[i - 16]; } for (i = 0; i < 80; i += 8) { RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],i+0); RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],i+1); RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],i+2); RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],i+3); RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],i+4); RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],i+5); RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],i+6); RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],i+7); } /* feedback */ for (i = 0; i < 8; i++) { md->sha512.state[i] = md->sha512.state[i] + S[i]; } } static void sha512_init(struct hash_state * md) { md->sha512.curlen = 0; md->sha512.length = 0; md->sha512.state[0] = CONST64(0x6a09e667f3bcc908); md->sha512.state[1] = CONST64(0xbb67ae8584caa73b); md->sha512.state[2] = CONST64(0x3c6ef372fe94f82b); md->sha512.state[3] = CONST64(0xa54ff53a5f1d36f1); md->sha512.state[4] = CONST64(0x510e527fade682d1); md->sha512.state[5] = CONST64(0x9b05688c2b3e6c1f); md->sha512.state[6] = CONST64(0x1f83d9abfb41bd6b); md->sha512.state[7] = CONST64(0x5be0cd19137e2179); } static void sha512_done(struct hash_state * md, unsigned char *out) { int i; /* increase the length of the message */ md->sha512.length += md->sha512.curlen * CONST64(8); /* append the '1' bit */ md->sha512.buf[md->sha512.curlen++] = (unsigned char)0x80; /* if the length is currently above 112 bytes we append zeros then * compress. Then we can fall back to padding zeros and length encoding * like normal. */ if (md->sha512.curlen > 112) { while (md->sha512.curlen < 128) { md->sha512.buf[md->sha512.curlen++] = (unsigned char)0; } sha512_compress(md, md->sha512.buf); md->sha512.curlen = 0; } /* pad upto 120 bytes of zeroes note: that from 112 to 120 is the 64 MSB * of the length. We assume that you won't hash > 2^64 bits of data. */ while (md->sha512.curlen < 120) { md->sha512.buf[md->sha512.curlen++] = (unsigned char)0; } /* store length */ STORE64H(md->sha512.length, md->sha512.buf + 120); sha512_compress(md, md->sha512.buf); /* copy output */ for (i = 0; i < 8; i++) { STORE64H(md->sha512.state[i], out+(8 * i)); } } #define MIN(x, y) ( ((x)<(y))?(x):(y) ) #define SHA512_BLOCKSIZE 128 static void sha512_process(struct hash_state * md, const unsigned char *in, unsigned long inlen) { unsigned long n; while (inlen > 0) { if (md->sha512.curlen == 0 && inlen >= SHA512_BLOCKSIZE) { sha512_compress(md, in); md->sha512.length += SHA512_BLOCKSIZE * 8; in += SHA512_BLOCKSIZE; inlen -= SHA512_BLOCKSIZE; } else { n = MIN(inlen, (SHA512_BLOCKSIZE - md->sha512.curlen)); memcpy(md->sha512.buf + md->sha512.curlen, in, (size_t)n); md->sha512.curlen += n; in += n; inlen -= n; if (md->sha512.curlen == SHA512_BLOCKSIZE) { sha512_compress(md, md->sha512.buf); md->sha512.length += SHA512_BLOCKSIZE * 8; md->sha512.curlen = 0; } } } } void f2fs_sha512(const unsigned char *in, unsigned long in_size, unsigned char out[F2FS_SHA512_LENGTH]) { struct hash_state md; sha512_init(&md); sha512_process(&md, in, in_size); sha512_done(&md, out); } #ifdef UNITTEST static const struct { char *msg; unsigned char hash[64]; } tests[] = { { "", { 0xcf, 0x83, 0xe1, 0x35, 0x7e, 0xef, 0xb8, 0xbd, 0xf1, 0x54, 0x28, 0x50, 0xd6, 0x6d, 0x80, 0x07, 0xd6, 0x20, 0xe4, 0x05, 0x0b, 0x57, 0x15, 0xdc, 0x83, 0xf4, 0xa9, 0x21, 0xd3, 0x6c, 0xe9, 0xce, 0x47, 0xd0, 0xd1, 0x3c, 0x5d, 0x85, 0xf2, 0xb0, 0xff, 0x83, 0x18, 0xd2, 0x87, 0x7e, 0xec, 0x2f, 0x63, 0xb9, 0x31, 0xbd, 0x47, 0x41, 0x7a, 0x81, 0xa5, 0x38, 0x32, 0x7a, 0xf9, 0x27, 0xda, 0x3e } }, { "abc", { 0xdd, 0xaf, 0x35, 0xa1, 0x93, 0x61, 0x7a, 0xba, 0xcc, 0x41, 0x73, 0x49, 0xae, 0x20, 0x41, 0x31, 0x12, 0xe6, 0xfa, 0x4e, 0x89, 0xa9, 0x7e, 0xa2, 0x0a, 0x9e, 0xee, 0xe6, 0x4b, 0x55, 0xd3, 0x9a, 0x21, 0x92, 0x99, 0x2a, 0x27, 0x4f, 0xc1, 0xa8, 0x36, 0xba, 0x3c, 0x23, 0xa3, 0xfe, 0xeb, 0xbd, 0x45, 0x4d, 0x44, 0x23, 0x64, 0x3c, 0xe8, 0x0e, 0x2a, 0x9a, 0xc9, 0x4f, 0xa5, 0x4c, 0xa4, 0x9f } }, { "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu", { 0x8e, 0x95, 0x9b, 0x75, 0xda, 0xe3, 0x13, 0xda, 0x8c, 0xf4, 0xf7, 0x28, 0x14, 0xfc, 0x14, 0x3f, 0x8f, 0x77, 0x79, 0xc6, 0xeb, 0x9f, 0x7f, 0xa1, 0x72, 0x99, 0xae, 0xad, 0xb6, 0x88, 0x90, 0x18, 0x50, 0x1d, 0x28, 0x9e, 0x49, 0x00, 0xf7, 0xe4, 0x33, 0x1b, 0x99, 0xde, 0xc4, 0xb5, 0x43, 0x3a, 0xc7, 0xd3, 0x29, 0xee, 0xb6, 0xdd, 0x26, 0x54, 0x5e, 0x96, 0xe5, 0x5b, 0x87, 0x4b, 0xe9, 0x09 } }, }; int main(int argc, char **argv) { int i; int errors = 0; unsigned char tmp[64]; struct hash_state md; for (i = 0; i < (int)(sizeof(tests) / sizeof(tests[0])); i++) { unsigned char *msg = (unsigned char *) tests[i].msg; int len = strlen(tests[i].msg); f2fs_sha512(msg, len, tmp); printf("SHA512 test message %d: ", i); if (memcmp(tmp, tests[i].hash, 64) != 0) { printf("FAILED\n"); errors++; } else printf("OK\n"); } return errors; } #endif /* UNITTEST */