pax_global_header00006660000000000000000000000064133212055210014504gustar00rootroot0000000000000052 comment=b98fab3666e498c2e27ad9dcda6874c9b683f06b f2fs-tools-1.11.0/000077500000000000000000000000001332120552100135625ustar00rootroot00000000000000f2fs-tools-1.11.0/.gitignore000066400000000000000000000007411332120552100155540ustar00rootroot00000000000000.* *~ *.[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.11.0/AUTHORS000066400000000000000000000001211332120552100146240ustar00rootroot00000000000000This package has been developed by Praesto Team at Samsung Electronics Co., Ltd. f2fs-tools-1.11.0/COPYING000066400000000000000000001305111332120552100146160ustar00rootroot00000000000000The 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.11.0/ChangeLog000066400000000000000000000002351332120552100153340ustar00rootroot00000000000000f2fs-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.11.0/Makefile.am000066400000000000000000000001131332120552100156110ustar00rootroot00000000000000## Makefile.am ACLOCAL_AMFLAGS = -I m4 SUBDIRS = man lib mkfs fsck tools f2fs-tools-1.11.0/README000066400000000000000000000017471332120552100144530ustar00rootroot00000000000000F2FS 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.11.0/VERSION000066400000000000000000000000221332120552100146240ustar00rootroot000000000000001.11.0 2018-07-10 f2fs-tools-1.11.0/autogen.sh000077500000000000000000000001251332120552100155610ustar00rootroot00000000000000#!/bin/sh aclocal && \ autoheader && \ autoconf && \ libtoolize && \ automake -a -c f2fs-tools-1.11.0/configure.ac000066400000000000000000000122061332120552100160510ustar00rootroot00000000000000# -*- 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/utsname.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 tools/sg_write_buffer/Makefile ]) # export library version info for mkfs/libf2fs_format_la AC_SUBST(FMT_CURRENT, 4) AC_SUBST(FMT_REVISION, 0) AC_SUBST(FMT_AGE, 0) # export library version info for lib/libf2fs_la AC_SUBST(LIBF2FS_CURRENT, 5) AC_SUBST(LIBF2FS_REVISION, 0) AC_SUBST(LIBF2FS_AGE, 0) AC_OUTPUT f2fs-tools-1.11.0/fsck/000077500000000000000000000000001332120552100145105ustar00rootroot00000000000000f2fs-tools-1.11.0/fsck/Makefile.am000066400000000000000000000013611332120552100165450ustar00rootroot00000000000000## 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.11.0/fsck/common.h000066400000000000000000000012221332120552100161460ustar00rootroot00000000000000/** * * 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.11.0/fsck/defrag.c000066400000000000000000000045531332120552100161130ustar00rootroot00000000000000/** * 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.11.0/fsck/dict.c000066400000000000000000001052701332120552100156040ustar00rootroot00000000000000/* * 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.11.0/fsck/dict.h000066400000000000000000000107271332120552100156130ustar00rootroot00000000000000/* * 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.11.0/fsck/dir.c000066400000000000000000000426231332120552100154410ustar00rootroot00000000000000/** * 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; } /* return ino if file exists, otherwise return 0 */ nid_t f2fs_lookup(struct f2fs_sb_info *sbi, struct f2fs_node *dir, u8 *name, int len) { int err; struct dentry de = { .name = name, .len = len, }; err = f2fs_find_entry(sbi, dir, &de); if (err == 1) return de.ino; else 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. */ 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.11.0/fsck/dqblk_v2.h000066400000000000000000000014661332120552100163740ustar00rootroot00000000000000/* * 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.11.0/fsck/dump.c000066400000000000000000000461171332120552100156320ustar00rootroot00000000000000/** * 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 "node.h" #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, type, 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, &type); memset(buf, 0, BUF_SZ); switch (type) { 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 (type == SEG_TYPE_NODE || type == SEG_TYPE_DATA || type == 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); } } static bool is_sit_bitmap_set(struct f2fs_sb_info *sbi, u32 blk_addr) { struct seg_entry *se; u32 offset; se = get_seg_entry(sbi, GET_SEGNO(sbi, blk_addr)); offset = OFFSET_IN_SEG(sbi, blk_addr); return f2fs_test_bit(offset, (const char *)se->cur_valid_map) != 0; } 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"); else if (!is_sit_bitmap_set(sbi, ni.blk_addr)) MSG(force, "Invalid node blk addr\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) { print_node_info(sbi, node_blk, force); if (ni.ino == ni.nid) 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); } static int has_dirent(u32 blk_addr, int is_inline, int *enc_name) { struct f2fs_node *node_blk; int ret, is_dentry = 0; node_blk = calloc(BLOCK_SZ, 1); ASSERT(node_blk); ret = dev_read_block(node_blk, blk_addr); ASSERT(ret >= 0); if (IS_INODE(node_blk) && S_ISDIR(le16_to_cpu(node_blk->i.i_mode))) is_dentry = 1; if (is_inline && !(node_blk->i.i_inline & F2FS_INLINE_DENTRY)) is_dentry = 0; *enc_name = file_is_encrypt(&node_blk->i); free(node_blk); return is_dentry; } static void dump_dirent(u32 blk_addr, int is_inline, int enc_name) { struct f2fs_dentry_ptr d; void *inline_dentry, *blk; int ret, i = 0; blk = calloc(BLOCK_SZ, 1); ASSERT(blk); ret = dev_read_block(blk, blk_addr); ASSERT(ret >= 0); if (is_inline) { inline_dentry = inline_data_addr((struct f2fs_node *)blk); make_dentry_ptr(&d, blk, inline_dentry, 2); } else { make_dentry_ptr(&d, NULL, blk, 1); } DBG(1, "%sDentry block:\n", is_inline ? "Inline " : ""); while (i < d.max) { struct f2fs_dir_entry *de; unsigned char en[F2FS_NAME_LEN + 1]; u16 en_len, name_len; int enc; if (!test_bit_le(i, d.bitmap)) { i++; continue; } de = &d.dentry[i]; if (!de->name_len) { i++; continue; } name_len = le16_to_cpu(de->name_len); enc = enc_name; if (de->file_type == F2FS_FT_DIR) { if ((d.filename[i][0] == '.' && name_len == 1) || (d.filename[i][0] == '.' && d.filename[i][1] == '.' && name_len == 2)) { enc = 0; } } en_len = convert_encrypted_name(d.filename[i], le16_to_cpu(de->name_len), en, enc); en[en_len] = '\0'; DBG(1, "bitmap pos[0x%x] name[%s] len[0x%x] hash[0x%x] ino[0x%x] type[0x%x]\n", i, en, le16_to_cpu(de->name_len), le32_to_cpu(de->hash_code), le32_to_cpu(de->ino), de->file_type); i += GET_DENTRY_SLOTS(le16_to_cpu(de->name_len)); } free(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; int enc_name; 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; } if (!is_sit_bitmap_set(sbi, blk_addr)) 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)); if (has_dirent(ino_ni.blk_addr, 0, &enc_name)) dump_dirent(blk_addr, 0, enc_name); } 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); if (has_dirent(ino_ni.blk_addr, 1, &enc_name)) dump_dirent(blk_addr, 1, enc_name); } 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.11.0/fsck/f2fs.h000066400000000000000000000340531332120552100155260ustar00rootroot00000000000000/** * 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; block_t nat_blocks; 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 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 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 (is_set_ckpt_flags(ckpt, CP_LARGE_NAT_BITMAP_FLAG)) { offset = (flag == SIT_BITMAP) ? le32_to_cpu(ckpt->nat_ver_bitmap_bytesize) : 0; return &ckpt->sit_nat_version_bitmap + 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 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.11.0/fsck/fsck.c000066400000000000000000002020551332120552100156060ustar00rootroot00000000000000/** * 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" #include 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_clear_main_bitmap(struct f2fs_sb_info *sbi, u32 blk) { struct f2fs_fsck *fsck = F2FS_FSCK(sbi); return f2fs_clear_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; /* f2fs_iget will return -EIO if mode is not valid file type */ if (!S_ISLNK(mode) && !S_ISREG(mode) && !S_ISDIR(mode) && !S_ISCHR(mode) && !S_ISBLK(mode) && !S_ISFIFO(mode) && !S_ISSOCK(mode)) { ASSERT_MSG("inode [0x%x] unknown file type i_mode [0x%x]", nid, mode); return -1; } 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("inode [0x%x] mismatch i_mode [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); /* avoid reusing nid when reconnecting files */ f2fs_set_bit(nid, NM_I(sbi)->nid_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 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); 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; } void fsck_reada_node_block(struct f2fs_sb_info *sbi, u32 nid) { struct node_info ni; if (nid != 0 && IS_VALID_NID(sbi, nid)) { get_node_info(sbi, nid, &ni); if (IS_VALID_BLK_ADDR(sbi, ni.blk_addr)) dev_reada_block(ni.blk_addr); } } void fsck_reada_all_direct_node_blocks(struct f2fs_sb_info *sbi, struct f2fs_node *node_blk) { int i; for (i = 0; i < NIDS_PER_BLOCK; i++) { u32 nid = le32_to_cpu(node_blk->in.nid[i]); fsck_reada_node_block(sbi, nid); } } /* 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; } } /* readahead xattr node block */ fsck_reada_node_block(sbi, le32_to_cpu(node_blk->i.i_xattr_nid)); 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; } /* 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); } } } /* readahead node blocks */ for (idx = 0; idx < 5; idx++) { u32 nid = le32_to_cpu(node_blk->i.i_nid[idx]); fsck_reada_node_block(sbi, nid); } /* 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; fsck_reada_all_direct_node_blocks(sbi, node_blk); 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; fsck_reada_all_direct_node_blocks(sbi, node_blk); 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; continue; } 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 nat entry -- must be done before quota check */ 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; } } /* 6. 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; } 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); struct f2fs_sit_block *sit_blk; int i; sit_blk = calloc(BLOCK_SZ, 1); ASSERT(sit_blk); /* 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_entry *sit; struct seg_entry *se; se = get_seg_entry(sbi, curseg->segno); get_current_sit_page(sbi, curseg->segno, sit_blk); 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) continue; 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; } static struct f2fs_node *fsck_get_lpf(struct f2fs_sb_info *sbi) { struct f2fs_node *node; struct node_info ni; nid_t lpf_ino; int err; /* read root inode first */ node = calloc(F2FS_BLKSIZE, 1); ASSERT(node); get_node_info(sbi, F2FS_ROOT_INO(sbi), &ni); err = dev_read_block(node, ni.blk_addr); ASSERT(err >= 0); /* lookup lost+found in root directory */ lpf_ino = f2fs_lookup(sbi, node, (u8 *)LPF, strlen(LPF)); if (lpf_ino) { /* found */ get_node_info(sbi, lpf_ino, &ni); err = dev_read_block(node, ni.blk_addr); ASSERT(err >= 0); DBG(1, "Found lost+found 0x%x at blkaddr [0x%x]\n", lpf_ino, ni.blk_addr); if (!S_ISDIR(le16_to_cpu(node->i.i_mode))) { ASSERT_MSG("lost+found is not directory [0%o]\n", le16_to_cpu(node->i.i_mode)); /* FIXME: give up? */ goto out; } } else { /* not found, create it */ struct dentry de; memset(&de, 0, sizeof(de)); de.name = (u8 *) LPF; de.len = strlen(LPF); de.mode = 0x41c0; de.pino = F2FS_ROOT_INO(sbi), de.file_type = F2FS_FT_DIR, de.uid = getuid(); de.gid = getgid(); de.mtime = time(NULL); err = f2fs_mkdir(sbi, &de); if (err) { ASSERT_MSG("Failed create lost+found"); goto out; } get_node_info(sbi, de.ino, &ni); err = dev_read_block(node, ni.blk_addr); ASSERT(err >= 0); DBG(1, "Create lost+found 0x%x at blkaddr [0x%x]\n", de.ino, ni.blk_addr); } c.lpf_ino = le32_to_cpu(node->footer.ino); return node; out: free(node); return NULL; } static int fsck_do_reconnect_file(struct f2fs_sb_info *sbi, struct f2fs_node *lpf, struct f2fs_node *fnode) { char name[80]; size_t namelen; nid_t ino = le32_to_cpu(fnode->footer.ino); struct node_info ni; int ftype, ret; namelen = snprintf(name, 80, "%u", ino); if (namelen >= 80) /* ignore terminating '\0', should never happen */ namelen = 79; if (f2fs_lookup(sbi, lpf, (u8 *)name, namelen)) { ASSERT_MSG("Name %s already exist in lost+found", name); return -EEXIST; } get_node_info(sbi, le32_to_cpu(lpf->footer.ino), &ni); ftype = map_de_type(le16_to_cpu(fnode->i.i_mode)); ret = f2fs_add_link(sbi, lpf, (unsigned char *)name, namelen, ino, ftype, ni.blk_addr, 0); if (ret) { ASSERT_MSG("Failed to add inode [0x%x] to lost+found", ino); return -EINVAL; } /* update fnode */ memcpy(fnode->i.i_name, name, namelen); fnode->i.i_namelen = cpu_to_le32(namelen); fnode->i.i_pino = c.lpf_ino; get_node_info(sbi, le32_to_cpu(fnode->footer.ino), &ni); ret = dev_write_block(fnode, ni.blk_addr); ASSERT(ret >= 0); DBG(1, "Reconnect inode [0x%x] to lost+found\n", ino); return 0; } static void fsck_failed_reconnect_file_dnode(struct f2fs_sb_info *sbi, nid_t nid) { struct f2fs_fsck *fsck = F2FS_FSCK(sbi); struct f2fs_node *node; struct node_info ni; u32 addr; int i, err; node = calloc(F2FS_BLKSIZE, 1); ASSERT(node); get_node_info(sbi, nid, &ni); err = dev_read_block(node, ni.blk_addr); ASSERT(err >= 0); fsck->chk.valid_node_cnt--; fsck->chk.valid_blk_cnt--; f2fs_clear_main_bitmap(sbi, ni.blk_addr); for (i = 0; i < ADDRS_PER_BLOCK; i++) { addr = le32_to_cpu(node->dn.addr[i]); if (!addr) continue; fsck->chk.valid_blk_cnt--; if (addr == NEW_ADDR) continue; f2fs_clear_main_bitmap(sbi, addr); } free(node); } static void fsck_failed_reconnect_file_idnode(struct f2fs_sb_info *sbi, nid_t nid) { struct f2fs_fsck *fsck = F2FS_FSCK(sbi); struct f2fs_node *node; struct node_info ni; nid_t tmp; int i, err; node = calloc(F2FS_BLKSIZE, 1); ASSERT(node); get_node_info(sbi, nid, &ni); err = dev_read_block(node, ni.blk_addr); ASSERT(err >= 0); fsck->chk.valid_node_cnt--; fsck->chk.valid_blk_cnt--; f2fs_clear_main_bitmap(sbi, ni.blk_addr); for (i = 0; i < NIDS_PER_BLOCK; i++) { tmp = le32_to_cpu(node->in.nid[i]); if (!tmp) continue; fsck_failed_reconnect_file_dnode(sbi, tmp); } free(node); } static void fsck_failed_reconnect_file_didnode(struct f2fs_sb_info *sbi, nid_t nid) { struct f2fs_fsck *fsck = F2FS_FSCK(sbi); struct f2fs_node *node; struct node_info ni; nid_t tmp; int i, err; node = calloc(F2FS_BLKSIZE, 1); ASSERT(node); get_node_info(sbi, nid, &ni); err = dev_read_block(node, ni.blk_addr); ASSERT(err >= 0); fsck->chk.valid_node_cnt--; fsck->chk.valid_blk_cnt--; f2fs_clear_main_bitmap(sbi, ni.blk_addr); for (i = 0; i < NIDS_PER_BLOCK; i++) { tmp = le32_to_cpu(node->in.nid[i]); if (!tmp) continue; fsck_failed_reconnect_file_idnode(sbi, tmp); } free(node); } /* * Counters and main_area_bitmap are already changed during checking * inode block, so clear them. There is no need to clear new blocks * allocted to lost+found. */ static void fsck_failed_reconnect_file(struct f2fs_sb_info *sbi, nid_t ino) { struct f2fs_fsck *fsck = F2FS_FSCK(sbi); struct f2fs_node *node; struct node_info ni; nid_t nid; int ofs, i, err; node = calloc(F2FS_BLKSIZE, 1); ASSERT(node); get_node_info(sbi, ino, &ni); err = dev_read_block(node, ni.blk_addr); ASSERT(err >= 0); /* clear inode counters */ fsck->chk.valid_inode_cnt--; fsck->chk.valid_node_cnt--; fsck->chk.valid_blk_cnt--; f2fs_clear_main_bitmap(sbi, ni.blk_addr); /* clear xnid counters */ if (node->i.i_xattr_nid) { nid = le32_to_cpu(node->i.i_xattr_nid); fsck->chk.valid_node_cnt--; fsck->chk.valid_blk_cnt--; get_node_info(sbi, nid, &ni); f2fs_clear_main_bitmap(sbi, ni.blk_addr); } /* clear data counters */ if(!(node->i.i_inline & F2FS_INLINE_DATA)) { ofs = get_extra_isize(node); for (i = 0; i < ADDRS_PER_INODE(&node->i); i++) { block_t addr = le32_to_cpu(node->i.i_addr[ofs + i]); if (!addr) continue; fsck->chk.valid_blk_cnt--; if (addr == NEW_ADDR) continue; f2fs_clear_main_bitmap(sbi, addr); } } for (i = 0; i < 5; i++) { nid = le32_to_cpu(node->i.i_nid[i]); if (!nid) continue; switch (i) { case 0: /* direct node */ case 1: fsck_failed_reconnect_file_dnode(sbi, nid); break; case 2: /* indirect node */ case 3: fsck_failed_reconnect_file_idnode(sbi, nid); break; case 4: /* double indirect node */ fsck_failed_reconnect_file_didnode(sbi, nid); break; } } free(node); } /* * Scan unreachable nids and find only regular file inodes. If these files * are not corrupted, reconnect them to lost+found. * * Since all unreachable nodes are already checked, we can allocate new * blocks safely. * * This function returns the number of files been reconnected. */ static int fsck_reconnect_file(struct f2fs_sb_info *sbi) { struct f2fs_fsck *fsck = F2FS_FSCK(sbi); struct f2fs_node *lpf_node, *node; struct node_info ni; char *reconnect_bitmap; u32 blk_cnt; nid_t nid; int err, cnt = 0, ftype; node = calloc(F2FS_BLKSIZE, 1); ASSERT(node); reconnect_bitmap = calloc(fsck->nat_area_bitmap_sz, 1); ASSERT(reconnect_bitmap); for (nid = 0; nid < fsck->nr_nat_entries; nid++) { if (f2fs_test_bit(nid, fsck->nat_area_bitmap)) { if (is_qf_ino(F2FS_RAW_SUPER(sbi), nid)) { DBG(1, "Not support quota inode [0x%x]\n", nid); continue; } get_node_info(sbi, nid, &ni); err = dev_read_block(node, ni.blk_addr); ASSERT(err >= 0); /* reconnection will restore these nodes if needed */ if (node->footer.ino != node->footer.nid) { DBG(1, "Not support non-inode node [0x%x]\n", nid); continue; } if (S_ISDIR(le16_to_cpu(node->i.i_mode))) { DBG(1, "Not support directory inode [0x%x]\n", nid); continue; } ftype = map_de_type(le16_to_cpu(node->i.i_mode)); if (sanity_check_nid(sbi, nid, node, ftype, TYPE_INODE, &ni)) { ASSERT_MSG("Invalid nid [0x%x]\n", nid); continue; } DBG(1, "Check inode 0x%x\n", nid); blk_cnt = 1; fsck_chk_inode_blk(sbi, nid, ftype, node, &blk_cnt, &ni, NULL); f2fs_set_bit(nid, reconnect_bitmap); } } lpf_node = fsck_get_lpf(sbi); if (!lpf_node) goto out; for (nid = 0; nid < fsck->nr_nat_entries; nid++) { if (f2fs_test_bit(nid, reconnect_bitmap)) { get_node_info(sbi, nid, &ni); err = dev_read_block(node, ni.blk_addr); ASSERT(err >= 0); if (fsck_do_reconnect_file(sbi, lpf_node, node)) { DBG(1, "Failed to reconnect inode [0x%x]\n", nid); fsck_failed_reconnect_file(sbi, nid); continue; } quota_add_inode_usage(fsck->qctx, nid, &node->i); DBG(1, "Reconnected inode [0x%x] to lost+found\n", nid); cnt++; } } out: free(node); free(lpf_node); free(reconnect_bitmap); return cnt; } 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"); if (c.feature & cpu_to_le32(F2FS_FEATURE_LOST_FOUND)) { for (i = 0; i < fsck->nr_nat_entries; i++) if (f2fs_test_bit(i, fsck->nat_area_bitmap) != 0) break; if (i < fsck->nr_nat_entries) { i = fsck_reconnect_file(sbi); printf("[FSCK] Reconnect %u files to lost+found\n", i); } } 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) { /* flush nats to write_nit_bits below */ flush_journal_entries(sbi); 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.11.0/fsck/fsck.h000066400000000000000000000172451332120552100156200ustar00rootroot00000000000000/** * 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 void get_current_sit_page(struct f2fs_sb_info *, unsigned int, struct f2fs_sit_block *); 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 *); nid_t f2fs_lookup(struct f2fs_sb_info *, struct f2fs_node *, u8 *, int); int f2fs_add_link(struct f2fs_sb_info *, struct f2fs_node *, const unsigned char *, int, nid_t, int, block_t, int); /* xattr.c */ void *read_all_xattrs(struct f2fs_sb_info *, struct f2fs_node *); #endif /* _FSCK_H_ */ f2fs-tools-1.11.0/fsck/main.c000066400000000000000000000430741332120552100156100ustar00rootroot00000000000000/** * 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 program.\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] == '-' || !is_digits(optarg) || optind == argc) { MSG(0, "Info: Use default preen mode\n"); c.preen_mode = PREEN_MODE_0; c.auto_fix = 1; optind--; 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.11.0/fsck/mkquota.c000066400000000000000000000222541332120552100163420ustar00rootroot00000000000000/* * 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.11.0/fsck/mount.c000066400000000000000000002033011332120552100160150ustar00rootroot00000000000000/** * 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_VERITY)) { MSG(0, "%s", " verity"); } 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"); } if (f & cpu_to_le32(F2FS_FEATURE_LOST_FOUND)) { MSG(0, "%s", " lost_found"); } 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 { get_kernel_uname_version(c.version); } /* 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); nat_block = malloc(F2FS_BLKSIZE); if (!nat_block) { free(nm_i->nid_bitmap); return -ENOMEM; } 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(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); } free(nat_block); 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))); /* * Should consider new nat_blocks is larger than old * nm_i->nat_blocks, since nm_i->nat_bitmap is based on * old one. */ if (i < nm_i->nat_blocks && 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); } static int check_nat_bits(struct f2fs_sb_info *sbi, struct f2fs_super_block *sb, struct f2fs_checkpoint *cp) { 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 curseg_info *curseg = CURSEG_I(sbi, CURSEG_HOT_DATA); struct f2fs_journal *journal = &curseg->sum_blk->journal; u_int32_t i, j; block_t blkaddr; int err = 0; nat_bits = calloc(F2FS_BLKSIZE, nat_bits_blocks); ASSERT(nat_bits); full_nat_bits = nat_bits + 8; empty_nat_bits = full_nat_bits + nat_bits_bytes; blkaddr = get_sb(segment0_blkaddr) + (sbi->cur_cp << get_sb(log_blocks_per_seg)) - nat_bits_blocks; for (i = 0; i < nat_bits_blocks; i++) { if (dev_read_block(nat_bits + i * F2FS_BLKSIZE, blkaddr + i)) ASSERT_MSG("\tError: read NAT bits to disk!!!\n"); } if (*(__le64 *)nat_bits != get_cp_crc(cp) || nats_in_cursum(journal)) { /* * if there is a journal, f2fs was not shutdown cleanly. Let's * flush them with nat_bits. */ if (c.fix_on) err = -1; /* Otherwise, kernel will disable nat_bits */ goto out; } for (i = 0; i < nat_blocks; i++) { u_int32_t start_nid = i * NAT_ENTRY_PER_BLOCK; u_int32_t valid = 0; int empty = test_bit_le(i, empty_nat_bits); int full = test_bit_le(i, full_nat_bits); for (j = 0; j < NAT_ENTRY_PER_BLOCK; j++) { if (f2fs_test_bit(start_nid + j, nm_i->nid_bitmap)) valid++; } if (valid == 0) { if (!empty || full) { err = -1; goto out; } } else if (valid == NAT_ENTRY_PER_BLOCK) { if (empty || !full) { err = -1; goto out; } } else { if (empty || full) { err = -1; goto out; } } } out: free(nat_bits); if (!err) { MSG(0, "Info: Checked valid nat_bits in checkpoint\n"); } else { c.bug_on = 1; MSG(0, "Info: Corrupted valid nat_bits in checkpoint\n"); } return err; } 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; 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; nm_i->nat_blocks = nat_segs << get_sb(log_blocks_per_seg); nm_i->max_nid = NAT_ENTRY_PER_BLOCK * nm_i->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); } void get_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; 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); } 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); if (c.func == FSCK) F2FS_FSCK(sbi)->entries[nid] = nat_block->entries[entry_off]; 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])); if (ni->blk_addr) return; /* nat entry is not cached, read it */ } 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 f2fs_sit_block *sit_blk; struct seg_entry *se; struct f2fs_sit_entry sit; unsigned int i, segno; sit_blk = calloc(BLOCK_SZ, 1); ASSERT(sit_blk); for (segno = 0; segno < TOTAL_SEGS(sbi); segno++) { se = &sit_i->sentries[segno]; get_current_sit_page(sbi, segno, sit_blk); sit = sit_blk->entries[SIT_ENTRY_OFFSET(sit_i, segno)]; check_block_count(sbi, segno, &sit); seg_info_from_raw_sit(se, &sit); } free(sit_blk); 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); struct f2fs_sit_block *sit_blk; unsigned int segno = 0; struct f2fs_summary_block *sum = curseg->sum_blk; char *ptr = NULL; sit_blk = calloc(BLOCK_SZ, 1); ASSERT(sit_blk); /* remove sit journal */ sum->journal.n_sits = 0; ptr = fsck->main_area_bitmap; for (segno = 0; segno < TOTAL_SEGS(sbi); segno++) { struct f2fs_sit_entry *sit; struct seg_entry *se; u16 valid_blocks = 0; u16 type; int i; get_current_sit_page(sbi, segno, sit_blk); 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); ptr += SIT_VBLOCK_MAP_SIZE; } free(sit_blk); } 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); struct f2fs_sit_block *sit_blk; unsigned int segno; int i; sit_blk = calloc(BLOCK_SZ, 1); ASSERT(sit_blk); for (i = 0; i < sits_in_cursum(journal); i++) { struct f2fs_sit_entry *sit; struct seg_entry *se; segno = segno_in_journal(journal, i); se = get_seg_entry(sbi, segno); get_current_sit_page(sbi, segno, sit_blk); 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); struct f2fs_sit_block *sit_blk; unsigned int segno = 0; u32 free_segs = 0; sit_blk = calloc(BLOCK_SZ, 1); ASSERT(sit_blk); /* update free segments */ for (segno = 0; segno < TOTAL_SEGS(sbi); segno++) { struct f2fs_sit_entry *sit; struct seg_entry *se; se = get_seg_entry(sbi, segno); if (!se->dirty) continue; get_current_sit_page(sbi, segno, sit_blk); 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); if (se->valid_blocks == 0x0 && !IS_CUR_SEGNO(sbi, segno, NO_CHECK_TYPE)) free_segs++; } free(sit_blk); 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 == FSCK && is_set_ckpt_flags(cp, CP_NAT_BITS_FLAG)) { if (check_nat_bits(sbi, sb, cp) && c.fix_on) write_nat_bits(sbi, sb, cp, sbi->cur_cp); } 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.11.0/fsck/node.c000066400000000000000000000141271332120552100156060ustar00rootroot00000000000000/** * 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.11.0/fsck/node.h000066400000000000000000000046551332120552100156200ustar00rootroot00000000000000/** * 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.11.0/fsck/quotaio.c000066400000000000000000000115701332120552100163410ustar00rootroot00000000000000/** 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.11.0/fsck/quotaio.h000066400000000000000000000161231332120552100163450ustar00rootroot00000000000000/** 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.11.0/fsck/quotaio_tree.c000066400000000000000000000407211332120552100173600ustar00rootroot00000000000000/* * 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.11.0/fsck/quotaio_tree.h000066400000000000000000000043461332120552100173700ustar00rootroot00000000000000/* * 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.11.0/fsck/quotaio_v2.c000066400000000000000000000171331332120552100167510ustar00rootroot00000000000000/* * 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.11.0/fsck/quotaio_v2.h000066400000000000000000000033431332120552100167540ustar00rootroot00000000000000/* * * 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.11.0/fsck/resize.c000066400000000000000000000447171332120552100161720ustar00rootroot00000000000000/** * 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, nat_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); if (c.large_nat_bitmap) { nat_segments = SEG_ALIGN(blocks_for_nat) * DEFAULT_NAT_ENTRY_RATIO / 100; set_sb(segment_count_nat, nat_segments ? nat_segments : 1); max_nat_bitmap_size = (get_sb(segment_count_nat) << get_sb(log_blocks_per_seg)) / 8; set_sb(segment_count_nat, get_sb(segment_count_nat) * 2); } else { set_sb(segment_count_nat, SEG_ALIGN(blocks_for_nat)); max_nat_bitmap_size = 0; } 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; if (c.large_nat_bitmap) { /* use cp_payload if free space of f2fs_checkpoint is not enough */ if (max_sit_bitmap_size + max_nat_bitmap_size > MAX_BITMAP_SIZE_IN_CKPT) { u_int32_t diff = max_sit_bitmap_size + max_nat_bitmap_size - MAX_BITMAP_SIZE_IN_CKPT; set_sb(cp_payload, F2FS_BLK_ALIGN(diff)); } else { set_sb(cp_payload, 0); } } else { /* * 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))); /* move to set #0 */ if (f2fs_test_bit(block_off, nm_i->nat_bitmap)) { block_addr += sbi->blocks_per_seg; f2fs_clear_bit(block_off, nm_i->nat_bitmap); } 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.11.0/fsck/segment.c000066400000000000000000000206461332120552100163260ustar00rootroot00000000000000/** * 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.11.0/fsck/sload.c000066400000000000000000000173651332120552100157720ustar00rootroot00000000000000/** * 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, de->file_type == F2FS_FT_DIR, 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_IFMT|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.11.0/fsck/xattr.c000066400000000000000000000130521332120552100160170ustar00rootroot00000000000000/** * 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.11.0/fsck/xattr.h000066400000000000000000000065571332120552100160400ustar00rootroot00000000000000/** * 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.11.0/include/000077500000000000000000000000001332120552100152055ustar00rootroot00000000000000f2fs-tools-1.11.0/include/android_config.h000066400000000000000000000026501332120552100203260ustar00rootroot00000000000000#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_UTSNAME_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_UTSNAME_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.11.0/include/f2fs_fs.h000066400000000000000000001124051332120552100167110ustar00rootroot00000000000000/** * 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 #define LPF "lost+found" 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 wanted_sector_size; 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[2]; 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 */ int large_nat_bitmap; __le32 feature; /* defined features */ /* mkfs parameters */ u_int32_t next_free_nid; u_int32_t quota_inum; u_int32_t quota_dnum; u_int32_t lpf_inum; u_int32_t lpf_dnum; u_int32_t lpf_ino; /* 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 F2FS_FEATURE_LOST_FOUND 0x0200 #define F2FS_FEATURE_VERITY 0x0400 /* reserved */ #define MAX_VOLUME_NAME 512 /* * For superblock */ #pragma pack(push, 1) struct f2fs_device { __u8 path[MAX_PATH_LEN]; __le32 total_segments; } __attribute__((packed)); 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 hot_ext_count; /* # of hot file extension */ __u8 reserved[314]; /* valid reserved region */ } __attribute__((packed)); /* * For checkpoint */ #define CP_LARGE_NAT_BITMAP_FLAG 0x00000400 #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 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) #define MAX_BITMAP_SIZE_IN_CKPT \ (CHECKSUM_OFFSET - sizeof(struct f2fs_checkpoint) + 1) /* * For orphan inode management */ #define F2FS_ORPHANS_PER_BLOCK 1020 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 */ 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 FADVISE_KEEP_SIZE_BIT 0x10 #define FADVISE_HOT_BIT 0x20 #define FADVISE_VERITY_BIT 0x40 /* reserved */ #define file_is_encrypt(fi) ((fi)->i_advise & FADVISE_ENCRYPT_BIT) #define file_enc_name(fi) ((fi)->i_advise & FADVISE_ENC_NAME_BIT) 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)); struct direct_node { __le32 addr[ADDRS_PER_BLOCK]; /* array of data block address */ } __attribute__((packed)); 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) 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)); 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) #define DEFAULT_NAT_ENTRY_RATIO 20 struct f2fs_nat_entry { __u8 version; /* latest version of cached nat entry */ __le32 ino; /* inode number */ __le32 block_addr; /* block address */ } __attribute__((packed)); 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) 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)); 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 */ struct f2fs_summary { __le32 nid; /* parent node id */ union { __u8 reserved[3]; 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) 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 }; struct nat_journal_entry { __le32 nid; struct f2fs_nat_entry ne; } __attribute__((packed)); struct nat_journal { struct nat_journal_entry entries[NAT_JOURNAL_ENTRIES]; __u8 reserved[NAT_JOURNAL_RESERVED]; } __attribute__((packed)); struct sit_journal_entry { __le32 segno; struct f2fs_sit_entry se; } __attribute__((packed)); struct sit_journal { struct sit_journal_entry entries[SIT_JOURNAL_ENTRIES]; __u8 reserved[SIT_JOURNAL_RESERVED]; } __attribute__((packed)); struct f2fs_extra_info { __le64 kbytes_written; __u8 reserved[EXTRA_INFO_RESERVED]; } __attribute__((packed)); 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 */ 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 */ 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 */ 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)); #pragma pack(pop) /* 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 *); extern void get_kernel_uname_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.11.0/include/quota.h000066400000000000000000000047251332120552100165170ustar00rootroot00000000000000/* * * 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 */ #pragma pack(push, 1) 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 */ 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)); 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)); #pragma pack(pop) #endif f2fs-tools-1.11.0/lib/000077500000000000000000000000001332120552100143305ustar00rootroot00000000000000f2fs-tools-1.11.0/lib/Makefile.am000066400000000000000000000004131332120552100163620ustar00rootroot00000000000000## 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.11.0/lib/libf2fs.c000066400000000000000000000577741332120552100160470ustar00rootroot00000000000000/** * 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 #ifdef HAVE_SYS_UTSNAME_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() { #if defined(ANDROID_WINDOWS_HOST) || defined(WITH_ANDROID) 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; memset(&c, 0, sizeof(struct f2fs_configuration)); c.ndevs = 1; 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.wanted_sector_size = -1; #ifndef WITH_ANDROID c.preserve_limits = 1; #endif for (i = 0; i < MAX_DEVICES; i++) { 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.segs_per_sec = 1; c.secs_per_zone = 1; c.segs_per_zone = 1; c.vol_label = ""; c.trim = 1; c.kd = -1; 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. */ st_buf = malloc(sizeof(struct stat)); 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"); free(st_buf); return -1; } } free(st_buf); 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); } void get_kernel_uname_version(__u8 *version) { #ifdef HAVE_SYS_UTSNAME_H struct utsname buf; memset(version, 0, VERSION_LEN); if (uname(&buf)) return; snprintf((char *)version, VERSION_LEN, "%s %s", buf.release, buf.version); #else memset(version, 0, VERSION_LEN); #endif } #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) { #if !defined(WITH_ANDROID) && defined(__linux__) c.kd = open("/proc/version", O_RDONLY); #endif if (c.kd < 0) { MSG(0, "\tInfo: No support kernel version!\n"); c.kd = -2; } } stat_buf = malloc(sizeof(struct stat)); if (fstat(fd, stat_buf) < 0 ) { MSG(0, "\tError: Failed to get the device stat!\n"); free(stat_buf); 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"); free(stat_buf); return -1; } #else if (ioctl(fd, BLKGETSIZE, &total_sectors) < 0) { MSG(0, "\tError: Cannot get the device size\n"); free(stat_buf); 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"); free(stat_buf); 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"); free(stat_buf); 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"); free(stat_buf); return -1; } if (f2fs_check_zones(i)) { MSG(0, "\tError: Failed to check zone configuration\n"); free(stat_buf); 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 /* adjust wanted_total_sectors */ if (c.wanted_total_sectors != -1) { MSG(0, "Info: wanted sectors = %"PRIu64" (in %"PRIu64" bytes)\n", c.wanted_total_sectors, c.wanted_sector_size); if (c.wanted_sector_size == -1) { c.wanted_sector_size = dev->sector_size; } else if (dev->sector_size != c.wanted_sector_size) { c.wanted_total_sectors *= c.wanted_sector_size; c.wanted_total_sectors /= dev->sector_size; } } c.total_sectors += dev->total_sectors; free(stat_buf); 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.11.0/lib/libf2fs_io.c000066400000000000000000000175061332120552100165230ustar00rootroot00000000000000/** * 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; } #ifdef SPARSE_CALLBACK_USES_SIZE_T static int sparse_import_segment(void *UNUSED(priv), const void *data, size_t len, unsigned int block, unsigned int nr_blocks) #else static int sparse_import_segment(void *UNUSED(priv), const void *data, int len, unsigned int block, unsigned int nr_blocks) #endif { /* 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.11.0/lib/libf2fs_zoned.c000066400000000000000000000146661332120552100172370ustar00rootroot00000000000000/** * 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.11.0/m4/000077500000000000000000000000001332120552100141025ustar00rootroot00000000000000f2fs-tools-1.11.0/m4/.gitignore000066400000000000000000000000061332120552100160660ustar00rootroot00000000000000 *.m4 f2fs-tools-1.11.0/man/000077500000000000000000000000001332120552100143355ustar00rootroot00000000000000f2fs-tools-1.11.0/man/Makefile.am000066400000000000000000000001551332120552100163720ustar00rootroot00000000000000## 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.11.0/man/defrag.f2fs.8000066400000000000000000000031211332120552100165120ustar00rootroot00000000000000.\" 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.11.0/man/dump.f2fs.8000066400000000000000000000030221332120552100162270ustar00rootroot00000000000000.\" 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.11.0/man/fsck.f2fs.8000066400000000000000000000030331332120552100162120ustar00rootroot00000000000000.\" 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.11.0/man/mkfs.f2fs.8000066400000000000000000000075171332120552100162370ustar00rootroot00000000000000.\" 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 \-w .I specific sector_size for target sectors ] [ .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 \-w "sector-size" Specify the sector size in bytes along with given target sectors. Without it, the sectors will be calculated by device sector size. .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.11.0/man/resize.f2fs.8000066400000000000000000000020711332120552100165660ustar00rootroot00000000000000.\" 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.11.0/man/sload.f2fs.8000066400000000000000000000023161332120552100163710ustar00rootroot00000000000000.\" 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.11.0/mkfs/000077500000000000000000000000001332120552100145225ustar00rootroot00000000000000f2fs-tools-1.11.0/mkfs/Makefile.am000066400000000000000000000013361332120552100165610ustar00rootroot00000000000000## 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.11.0/mkfs/f2fs_format.c000066400000000000000000001327301332120552100171040ustar00rootroot00000000000000/** * 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 }; const char *hot_ext_lists[] = { "db", NULL }; const char **default_ext_list[] = { media_ext_lists, hot_ext_lists }; 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; char *ext_str; char *ue; int name_len; int i, pos = 0; set_sb(extension_count, 0); memset(sb->extension_list, 0, sizeof(sb->extension_list)); for (i = 0; i < 2; i++) { ext_str = c.extension_list[i]; extlist = default_ext_list[i]; while (*extlist) { name_len = strlen(*extlist); memcpy(sb->extension_list[pos++], *extlist, name_len); extlist++; } if (i == 0) set_sb(extension_count, pos); else sb->hot_ext_count = pos - get_sb(extension_count);; if (!ext_str) continue; /* 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[pos++], ue, name_len); next: ue = strtok(NULL, ", "); if (pos >= F2FS_MAX_EXTENSION) break; } if (i == 0) set_sb(extension_count, pos); else sb->hot_ext_count = pos - get_sb(extension_count); free(c.extension_list[i]); } } 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, nat_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; 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); if (c.large_nat_bitmap) { nat_segments = SEG_ALIGN(blocks_for_nat) * DEFAULT_NAT_ENTRY_RATIO / 100; set_sb(segment_count_nat, nat_segments ? nat_segments : 1); max_nat_bitmap_size = (get_sb(segment_count_nat) << log_blks_per_seg) / 8; set_sb(segment_count_nat, get_sb(segment_count_nat) * 2); } else { set_sb(segment_count_nat, SEG_ALIGN(blocks_for_nat)); max_nat_bitmap_size = 0; } /* * 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; if (c.large_nat_bitmap) { /* use cp_payload if free space of f2fs_checkpoint is not enough */ if (max_sit_bitmap_size + max_nat_bitmap_size > MAX_BITMAP_SIZE_IN_CKPT) { u_int32_t diff = max_sit_bitmap_size + max_nat_bitmap_size - MAX_BITMAP_SIZE_IN_CKPT; set_sb(cp_payload, F2FS_BLK_ALIGN(diff)); } else { set_sb(cp_payload, 0); } } else { /* * 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); c.next_free_nid = 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(c.next_free_nid++); MSG(0, "Info: add quota type = %u => %u\n", qtype, c.next_free_nid - 1); } if (c.feature & cpu_to_le32(F2FS_FEATURE_LOST_FOUND)) c.lpf_ino = c.next_free_nid++; 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 { get_kernel_uname_version(c.version); } 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; 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); } set_cp(cur_node_blkoff[0], 1 + c.quota_inum + c.lpf_inum); set_cp(cur_data_blkoff[0], 1 + c.quota_dnum + c.lpf_dnum); set_cp(valid_block_count, 2 + c.quota_inum + c.quota_dnum + c.lpf_inum + c.lpf_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; if (c.large_nat_bitmap) flags |= CP_LARGE_NAT_BITMAP_FLAG; set_cp(ckpt_flags, flags); set_cp(cp_pack_start_sum, 1 + get_sb(cp_payload)); set_cp(valid_node_count, 1 + c.quota_inum + c.lpf_inum); set_cp(valid_inode_count, 1 + c.quota_inum + c.lpf_inum); set_cp(next_free_nid, c.next_free_nid); 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 + c.quota_inum + c.lpf_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++; } if (c.lpf_inum) { journal->nat_j.entries[i].nid = cpu_to_le32(c.lpf_ino); journal->nat_j.entries[i].ne.version = 0; journal->nat_j.entries[i].ne.ino = cpu_to_le32(c.lpf_ino); 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); } 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 + c.quota_inum + c.lpf_inum)); f2fs_set_bit(0, (char *)journal->sit_j.entries[0].se.valid_map); for (i = 1; i <= c.quota_inum; i++) f2fs_set_bit(i, (char *)journal->sit_j.entries[0].se.valid_map); if (c.lpf_inum) 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 + c.quota_dnum + c.lpf_dnum)); f2fs_set_bit(0, (char *)journal->sit_j.entries[3].se.valid_map); for (i = 1; i <= c.quota_dnum; i++) f2fs_set_bit(i, (char *)journal->sit_j.entries[3].se.valid_map); if (c.lpf_dnum) 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 = cpu_to_le16(j); } off += QUOTA_DATA(qtype); } if (c.lpf_dnum) { (sum_entry + off)->nid = cpu_to_le32(c.lpf_ino); (sum_entry + off)->ofs_in_node = 0; } /* 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++; } if (c.lpf_inum) { i++; sum->entries[i].nid = cpu_to_le32(c.lpf_ino); sum->entries[i].ofs_in_node = 0; } 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 f2fs_discard_obsolete_dnode(void) { struct f2fs_node *raw_node; u_int64_t next_blkaddr = 0, offset; 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; if (c.zoned_mode) return 0; raw_node = calloc(sizeof(struct f2fs_node), 1); if (!raw_node) return -1; /* avoid power-off-recovery based on roll-forward policy */ offset = get_sb(main_blkaddr); offset += c.cur_seg[CURSEG_WARM_NODE] * c.blks_per_seg; last_inode_pos = start_inode_pos + c.cur_seg[CURSEG_HOT_NODE] * c.blks_per_seg + c.quota_inum + c.lpf_inum; 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"); free(raw_node); 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"); free(raw_node); 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); free(raw_node); 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); if (c.lpf_ino) raw_node->i.i_links = cpu_to_le32(3); else 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; } 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); if (c.lpf_ino) dqblk.dqb_curinodes = cpu_to_le64(2); else dqblk.dqb_curinodes = cpu_to_le64(1); dqblk.dqb_bhardlimit = cpu_to_le64(0); dqblk.dqb_bsoftlimit = cpu_to_le64(0); if (c.lpf_ino) dqblk.dqb_curspace = cpu_to_le64(8192); else 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); c.quota_dnum += QUOTA_DATA(qtype); 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); c.quota_inum++; 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 block_t f2fs_add_default_dentry_lpf(void) { struct f2fs_dentry_block *dent_blk; uint64_t data_blk_offset; dent_blk = calloc(F2FS_BLKSIZE, 1); if (dent_blk == NULL) { MSG(1, "\tError: Calloc Failed for dent_blk!!!\n"); return 0; } dent_blk->dentry[0].hash_code = 0; dent_blk->dentry[0].ino = cpu_to_le32(c.lpf_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); 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 + 1 + c.quota_dnum; DBG(1, "\tWriting default dentry lost+found, 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 0; } free(dent_blk); c.lpf_dnum++; return data_blk_offset; } static int f2fs_write_lpf_inode(void) { struct f2fs_node *raw_node; u_int64_t blk_size_bytes, main_area_node_seg_blk_offset; block_t data_blk_nor; int err = 0; ASSERT(c.lpf_ino); 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 = cpu_to_le32(c.lpf_ino); raw_node->footer.ino = raw_node->footer.nid; 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 + c.quota_inum + 1); raw_node->i.i_mode = cpu_to_le16(0x41c0); /* 0700 */ 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); 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_pino = le32_to_cpu(sb->root_ino); raw_node->i.i_namelen = le32_to_cpu(strlen(LPF)); memcpy(raw_node->i.i_name, LPF, strlen(LPF)); 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 = f2fs_add_default_dentry_lpf(); if (data_blk_nor == 0) { MSG(1, "\tError: Failed to add default dentries for lost+found!!!\n"); err = -1; goto exit; } raw_node->i.i_addr[get_extra_isize(raw_node)] = cpu_to_le32(data_blk_nor); 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 + c.quota_inum + 1; DBG(1, "\tWriting lost+found 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"); err = -1; goto exit; } c.lpf_inum++; exit: free(raw_node); return err; } 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); if (c.lpf_ino) { int len = strlen(LPF); f2fs_hash_t hash = f2fs_dentry_hash((unsigned char *)LPF, len); dent_blk->dentry[2].hash_code = cpu_to_le32(hash); dent_blk->dentry[2].ino = cpu_to_le32(c.lpf_ino); dent_blk->dentry[2].name_len = cpu_to_le16(len); dent_blk->dentry[2].file_type = F2FS_FT_DIR; memcpy(dent_blk->filename[2], LPF, F2FS_SLOT_LEN); memcpy(dent_blk->filename[3], LPF + F2FS_SLOT_LEN, len - F2FS_SLOT_LEN); test_and_set_bit_le(2, dent_blk->dentry_bitmap); test_and_set_bit_le(3, 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; } } if (c.feature & cpu_to_le32(F2FS_FEATURE_LOST_FOUND)) { err = f2fs_write_lpf_inode(); if (err < 0) { MSG(1, "\tError: Failed to write lost+found inode!!!\n"); goto exit; } } #ifndef WITH_ANDROID err = f2fs_discard_obsolete_dnode(); if (err < 0) { MSG(1, "\tError: Failed to discard obsolete dnode!!!\n"); goto exit; } #endif 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.11.0/mkfs/f2fs_format_main.c000066400000000000000000000212101332120552100200760ustar00rootroot00000000000000/** * 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 [cold file ext list] e.g. \"mp3,gif,mov\"\n"); MSG(0, " -E [hot file ext list] e.g. \"db\"\n"); MSG(0, " -f force overwrite the exist filesystem\n"); MSG(0, " -i extended node bitmap, node ratio is 20%% by default\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, " -w wanted sector size\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[0]) MSG(0, "Info: Add new cold file extension list\n"); if (c.extension_list[1]) MSG(0, "Info: Add new hot file 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, "verity")) { c.feature |= cpu_to_le32(F2FS_FEATURE_VERITY); } 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 if (!strcmp(features, "lost_found")) { c.feature |= cpu_to_le32(F2FS_FEATURE_LOST_FOUND); } 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:E:il:mo:O:s:S:z:t:fw:"; 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[0] = strdup(optarg); break; case 'E': c.extension_list[1] = strdup(optarg); break; case 'i': c.large_nat_bitmap = 1; 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; case 'w': c.wanted_sector_size = atoi(optarg); 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.11.0/mkfs/f2fs_format_utils.c000066400000000000000000000045771332120552100203330ustar00rootroot00000000000000/** * 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 #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; stat_buf = malloc(sizeof(struct stat)); if (fstat(fd, stat_buf) < 0 ) { MSG(1, "\tError: Failed to get the device stat!!!\n"); free(stat_buf); 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 free(stat_buf); return 0; } else if (S_ISBLK(stat_buf->st_mode)) { if (dev->zoned_model != F2FS_ZONED_NONE) { free(stat_buf); 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); free(stat_buf); 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 { free(stat_buf); return -1; } #endif free(stat_buf); #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.11.0/mkfs/f2fs_format_utils.h000066400000000000000000000005641332120552100203300ustar00rootroot00000000000000/** * 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.11.0/scripts/000077500000000000000000000000001332120552100152515ustar00rootroot00000000000000f2fs-tools-1.11.0/scripts/dumpf2fs.sh000077500000000000000000000022331332120552100173360ustar00rootroot00000000000000#!/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.11.0/scripts/spo_test.sh000077500000000000000000000026411332120552100174530ustar00rootroot00000000000000#!/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.11.0/scripts/tracepoint.sh000077500000000000000000000045501332120552100177640ustar00rootroot00000000000000#!/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.11.0/scripts/verify.sh000077500000000000000000000040131332120552100171120ustar00rootroot00000000000000#!/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.11.0/tools/000077500000000000000000000000001332120552100147225ustar00rootroot00000000000000f2fs-tools-1.11.0/tools/Makefile.am000066400000000000000000000006171332120552100167620ustar00rootroot00000000000000## 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 SUBDIRS = sg_write_buffer f2fs-tools-1.11.0/tools/f2fs_io_parse.c000066400000000000000000000127561332120552100176220ustar00rootroot00000000000000/* * 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.11.0/tools/f2fscrypt.8000066400000000000000000000061331332120552100167400ustar00rootroot00000000000000.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.11.0/tools/f2fscrypt.c000066400000000000000000000556111332120552100170200ustar00rootroot00000000000000/* * 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.11.0/tools/f2fstat.c000066400000000000000000000156101332120552100164420ustar00rootroot00000000000000#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.11.0/tools/fibmap.c000066400000000000000000000113611332120552100163260ustar00rootroot00000000000000#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 #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 #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.11.0/tools/sg_write_buffer/000077500000000000000000000000001332120552100200765ustar00rootroot00000000000000f2fs-tools-1.11.0/tools/sg_write_buffer/Android.bp000066400000000000000000000010361332120552100220010ustar00rootroot00000000000000cc_defaults { name: "sg3-utils-defaults", cflags: [ "-Wno-unused-function" ], local_include_dirs: [ "include", ], } cc_binary { name: "sg_write_buffer", defaults: [ "sg3-utils-defaults" ], srcs: [ "sg_write_buffer.c", "sg_cmds_basic.c", "sg_cmds_basic2.c", "sg_cmds_extra.c", "sg_cmds_mmc.c", "sg_io_linux.c", "sg_lib.c", "sg_lib_data.c", "sg_pt_common.c", "sg_pt_linux.c", "sg_pt_linux_nvme.c", ], } f2fs-tools-1.11.0/tools/sg_write_buffer/Makefile.am000066400000000000000000000005211332120552100221300ustar00rootroot00000000000000## Makefile.am if LINUX AM_CPPFLAGS = -I./include AM_CFLAGS = -Wall sbin_PROGRAMS = sg_write_buffer sg_write_buffer_SOURCES = sg_write_buffer.c \ sg_cmds_basic.c \ sg_cmds_basic2.c \ sg_cmds_extra.c \ sg_cmds_mmc.c \ sg_io_linux.c \ sg_lib.c \ sg_lib_data.c \ sg_pt_common.c \ sg_pt_linux.c \ sg_pt_linux_nvme.c endif f2fs-tools-1.11.0/tools/sg_write_buffer/include/000077500000000000000000000000001332120552100215215ustar00rootroot00000000000000f2fs-tools-1.11.0/tools/sg_write_buffer/include/freebsd_nvme_ioctl.h000066400000000000000000000102771332120552100255320ustar00rootroot00000000000000PROPS-END /*- * Copyright (C) 2012-2013 Intel Corporation * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include #define NVME_PASSTHROUGH_CMD _IOWR('n', 0, struct nvme_pt_command) #if __FreeBSD_version < 1100110 struct nvme_command { /* dword 0 */ uint16_t opc : 8; /* opcode */ uint16_t fuse : 2; /* fused operation */ uint16_t rsvd1 : 6; uint16_t cid; /* command identifier */ /* dword 1 */ uint32_t nsid; /* namespace identifier */ /* dword 2-3 */ uint32_t rsvd2; uint32_t rsvd3; /* dword 4-5 */ uint64_t mptr; /* metadata pointer */ /* dword 6-7 */ uint64_t prp1; /* prp entry 1 */ /* dword 8-9 */ uint64_t prp2; /* prp entry 2 */ /* dword 10-15 */ uint32_t cdw10; /* command-specific */ uint32_t cdw11; /* command-specific */ uint32_t cdw12; /* command-specific */ uint32_t cdw13; /* command-specific */ uint32_t cdw14; /* command-specific */ uint32_t cdw15; /* command-specific */ } __packed; struct nvme_status { uint16_t p : 1; /* phase tag */ uint16_t sc : 8; /* status code */ uint16_t sct : 3; /* status code type */ uint16_t rsvd2 : 2; uint16_t m : 1; /* more */ uint16_t dnr : 1; /* do not retry */ } __packed; struct nvme_completion { /* dword 0 */ uint32_t cdw0; /* command-specific */ /* dword 1 */ uint32_t rsvd1; /* dword 2 */ uint16_t sqhd; /* submission queue head pointer */ uint16_t sqid; /* submission queue identifier */ /* dword 3 */ uint16_t cid; /* command identifier */ struct nvme_status status; } __packed; struct nvme_pt_command { /* * cmd is used to specify a passthrough command to a controller or * namespace. * * The following fields from cmd may be specified by the caller: * * opc (opcode) * * nsid (namespace id) - for admin commands only * * cdw10-cdw15 * * Remaining fields must be set to 0 by the caller. */ struct nvme_command cmd; /* * cpl returns completion status for the passthrough command * specified by cmd. * * The following fields will be filled out by the driver, for * consumption by the caller: * * cdw0 * * status (except for phase) * * Remaining fields will be set to 0 by the driver. */ struct nvme_completion cpl; /* buf is the data buffer associated with this passthrough command. */ void * buf; /* * len is the length of the data buffer associated with this * passthrough command. */ uint32_t len; /* * is_read = 1 if the passthrough command will read data into the * supplied buffer from the controller. * * is_read = 0 if the passthrough command will write data from the * supplied buffer to the controller. */ uint32_t is_read; /* * driver_lock is used by the driver only. It must be set to 0 * by the caller. */ struct mtx * driver_lock; }; #else #include #endif #define nvme_completion_is_error(cpl) \ ((cpl)->status.sc != 0 || (cpl)->status.sct != 0) #define NVME_CTRLR_PREFIX "/dev/nvme" #define NVME_NS_PREFIX "ns" f2fs-tools-1.11.0/tools/sg_write_buffer/include/sg_cmds.h000066400000000000000000000015171332120552100233150ustar00rootroot00000000000000#ifndef SG_CMDS_H #define SG_CMDS_H /******************************************************************** * This header did contain wrapper declarations for many SCSI commands * up until sg3_utils version 1.22 . In that version, the command * wrappers were broken into two groups, the 'basic' ones found in the * "sg_cmds_basic.h" header and the 'extra' ones found in the * "sg_cmds_extra.h" header. This header now simply includes those two * headers. * In sg3_utils version 1.26 the sg_cmds_mmc.h header was added and * contains some MMC specific commands. * The corresponding function definitions are found in the sg_cmds_basic.c, * sg_cmds_extra.c and sg_cmds_mmc.c files. ********************************************************************/ #include "sg_cmds_basic.h" #include "sg_cmds_extra.h" #include "sg_cmds_mmc.h" #endif f2fs-tools-1.11.0/tools/sg_write_buffer/include/sg_cmds_basic.h000066400000000000000000000365121332120552100244610ustar00rootroot00000000000000#ifndef SG_CMDS_BASIC_H #define SG_CMDS_BASIC_H /* * Copyright (c) 2004-2017 Douglas Gilbert. * All rights reserved. * Use of this source code is governed by a BSD-style * license that can be found in the BSD_LICENSE file. */ /* * Error, warning and verbose output is sent to the file pointed to by * sg_warnings_strm which is declared in sg_lib.h and can be set with * the sg_set_warnings_strm() function. If not given sg_warnings_strm * defaults to stderr. * If 'noisy' is false and 'verbose' is zero then following functions should * not output anything to sg_warnings_strm. If 'noisy' is true and * 'verbose' is zero then Unit Attention, Recovered, Medium and Hardware * errors (sense keys) send output to sg_warnings_strm. Increasing values * of 'verbose' send increasing amounts of (debug) output to * sg_warnings_strm. */ #include #include #ifdef __cplusplus extern "C" { #endif /* Invokes a SCSI INQUIRY command and yields the response * Returns 0 when successful, SG_LIB_CAT_INVALID_OP -> not supported, * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, * SG_LIB_CAT_ABORTED_COMMAND, -1 -> other errors */ int sg_ll_inquiry(int sg_fd, bool cmddt, bool evpd, int pg_op, void * resp, int mx_resp_len, bool noisy, int verbose); /* Invokes a SCSI INQUIRY command and yields the response. Returns 0 when * successful, various SG_LIB_CAT_* positive values or -1 -> other errors. * The CMDDT field is obsolete in the INQUIRY cdb (since spc3r16 in 2003) so * an argument to set it has been removed (use the REPORT SUPPORTED OPERATION * CODES command instead). Adds the ability to set the command abort timeout * and the ability to report the residual count. If timeout_secs is zero * or less the default command abort timeout (60 seconds) is used. * If residp is non-NULL then the residual value is written where residp * points. A residual value of 0 implies mx_resp_len bytes have be written * where resp points. If the residual value equals mx_resp_len then no * bytes have been written. */ int sg_ll_inquiry_v2(int sg_fd, bool evpd, int pg_op, void * resp, int mx_resp_len, int timeout_secs, int * residp, bool noisy, int verbose); /* Invokes a SCSI LOG SELECT command. Return of 0 -> success, * SG_LIB_CAT_INVALID_OP -> Log Select not supported, * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION, * SG_LIB_CAT_ABORTED_COMMAND, * SG_LIB_CAT_NOT_READY -> device not ready, * -1 -> other failure */ int sg_ll_log_select(int sg_fd, bool pcr, bool sp, int pc, int pg_code, int subpg_code, unsigned char * paramp, int param_len, bool noisy, int verbose); /* Invokes a SCSI LOG SENSE command. Return of 0 -> success, * SG_LIB_CAT_INVALID_OP -> Log Sense not supported, * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION, * SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND, * -1 -> other failure */ int sg_ll_log_sense(int sg_fd, bool ppc, bool sp, int pc, int pg_code, int subpg_code, int paramp, unsigned char * resp, int mx_resp_len, bool noisy, int verbose); /* Same as sg_ll_log_sense() apart from timeout_secs and residp. See * sg_ll_inquiry_v2() for their description */ int sg_ll_log_sense_v2(int sg_fd, bool ppc, bool sp, int pc, int pg_code, int subpg_code, int paramp, unsigned char * resp, int mx_resp_len, int timeout_secs, int * residp, bool noisy, int verbose); /* Invokes a SCSI MODE SELECT (6) command. Return of 0 -> success, * SG_LIB_CAT_INVALID_OP -> invalid opcode, SG_LIB_CAT_ILLEGAL_REQ -> * bad field in cdb, * SG_LIB_CAT_NOT_READY -> device not ready, * SG_LIB_CAT_UNIT_ATTENTION, SG_LIB_CAT_ABORTED_COMMAND, * -1 -> other failure */ int sg_ll_mode_select6(int sg_fd, bool pf, bool sp, void * paramp, int param_len, bool noisy, int verbose); /* Invokes a SCSI MODE SELECT (10) command. Return of 0 -> success, * SG_LIB_CAT_INVALID_OP -> invalid opcode, SG_LIB_CAT_ILLEGAL_REQ -> * bad field in cdb, * SG_LIB_CAT_NOT_READY -> device not ready, * SG_LIB_CAT_UNIT_ATTENTION, SG_LIB_CAT_ABORTED_COMMAND, * -1 -> other failure */ int sg_ll_mode_select10(int sg_fd, bool pf, bool sp, void * paramp, int param_len, bool noisy, int verbose); /* Invokes a SCSI MODE SENSE (6) command. Return of 0 -> success, * SG_LIB_CAT_INVALID_OP -> invalid opcode, SG_LIB_CAT_ILLEGAL_REQ -> * bad field in cdb, * SG_LIB_CAT_NOT_READY -> device not ready, * SG_LIB_CAT_UNIT_ATTENTION, SG_LIB_CAT_ABORTED_COMMAND, * -1 -> other failure */ int sg_ll_mode_sense6(int sg_fd, bool dbd, int pc, int pg_code, int sub_pg_code, void * resp, int mx_resp_len, bool noisy, int verbose); /* Invokes a SCSI MODE SENSE (10) command. Return of 0 -> success, * SG_LIB_CAT_INVALID_OP -> invalid opcode, SG_LIB_CAT_ILLEGAL_REQ -> * bad field in cdb, * SG_LIB_CAT_NOT_READY -> device not ready, * SG_LIB_CAT_UNIT_ATTENTION, SG_LIB_CAT_ABORTED_COMMAND, * -1 -> other failure */ int sg_ll_mode_sense10(int sg_fd, bool llbaa, bool dbd, int pc, int pg_code, int sub_pg_code, void * resp, int mx_resp_len, bool noisy, int verbose); /* Same as sg_ll_mode_sense10() apart from timeout_secs and residp. See * sg_ll_inquiry_v2() for their description */ int sg_ll_mode_sense10_v2(int sg_fd, bool llbaa, bool dbd, int pc, int pg_code, int sub_pg_code, void * resp, int mx_resp_len, int timeout_secs, int * residp, bool noisy, int verbose); /* Invokes a SCSI PREVENT ALLOW MEDIUM REMOVAL command (SPC-3) * prevent==0 allows removal, prevent==1 prevents removal ... * Return of 0 -> success, * SG_LIB_CAT_INVALID_OP -> command not supported * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION, * SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND, * -1 -> other failure */ int sg_ll_prevent_allow(int sg_fd, int prevent, bool noisy, int verbose); /* Invokes a SCSI READ CAPACITY (10) command. Return of 0 -> success, * SG_LIB_CAT_INVALID_OP -> invalid opcode, SG_LIB_CAT_UNIT_ATTENTION * -> perhaps media changed, SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, * SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND, * -1 -> other failure */ int sg_ll_readcap_10(int sg_fd, bool pmi, unsigned int lba, void * resp, int mx_resp_len, bool noisy, int verbose); /* Invokes a SCSI READ CAPACITY (16) command. Returns 0 -> success, * SG_LIB_CAT_UNIT_ATTENTION -> media changed??, SG_LIB_CAT_INVALID_OP * -> cdb not supported, SG_LIB_CAT_IlLEGAL_REQ -> bad field in cdb * SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND, * -1 -> other failure */ int sg_ll_readcap_16(int sg_fd, bool pmi, uint64_t llba, void * resp, int mx_resp_len, bool noisy, int verbose); /* Invokes a SCSI REPORT LUNS command. Return of 0 -> success, * SG_LIB_CAT_INVALID_OP -> Report Luns not supported, * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_ABORTED_COMMAND, * SG_LIB_NOT_READY (shouldn't happen), -1 -> other failure */ int sg_ll_report_luns(int sg_fd, int select_report, void * resp, int mx_resp_len, bool noisy, int verbose); /* Invokes a SCSI REQUEST SENSE command. Return of 0 -> success, * SG_LIB_CAT_INVALID_OP -> Request Sense not supported??, * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_ABORTED_COMMAND, * -1 -> other failure */ int sg_ll_request_sense(int sg_fd, bool desc, void * resp, int mx_resp_len, bool noisy, int verbose); /* Invokes a SCSI START STOP UNIT command (SBC + MMC). * Return of 0 -> success, * SG_LIB_CAT_INVALID_OP -> Start stop unit not supported, * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION, * SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND, * -1 -> other failure * SBC-3 and MMC partially overlap on the power_condition_modifier(sbc) and * format_layer_number(mmc) fields. They also overlap on the noflush(sbc) * and fl(mmc) one bit field. This is the cause of the awkardly named * pc_mod__fl_num and noflush__fl arguments to this function. */ int sg_ll_start_stop_unit(int sg_fd, bool immed, int pc_mod__fl_num, int power_cond, bool noflush__fl, bool loej, bool start, bool noisy, int verbose); /* Invokes a SCSI SYNCHRONIZE CACHE (10) command. Return of 0 -> success, * SG_LIB_CAT_UNIT_ATTENTION, SG_LIB_CAT_ABORTED_COMMAND, * SG_LIB_CAT_INVALID_OP -> cdb not supported, * SG_LIB_CAT_IlLEGAL_REQ -> bad field in cdb * SG_LIB_CAT_NOT_READY -> device not ready, -1 -> other failure */ int sg_ll_sync_cache_10(int sg_fd, bool sync_nv, bool immed, int group, unsigned int lba, unsigned int count, bool noisy, int verbose); /* Invokes a SCSI TEST UNIT READY command. * 'pack_id' is just for diagnostics, safe to set to 0. * Return of 0 -> success, SG_LIB_CAT_UNIT_ATTENTION, * SG_LIB_CAT_NOT_READY -> device not ready, * SG_LIB_CAT_ABORTED_COMMAND, -1 -> other failure */ int sg_ll_test_unit_ready(int sg_fd, int pack_id, bool noisy, int verbose); /* Invokes a SCSI TEST UNIT READY command. * 'pack_id' is just for diagnostics, safe to set to 0. * Looks for progress indicator if 'progress' non-NULL; * if found writes value [0..65535] else write -1. * Return of 0 -> success, SG_LIB_CAT_UNIT_ATTENTION, * SG_LIB_CAT_ABORTED_COMMAND, SG_LIB_CAT_NOT_READY -> * device not ready, -1 -> other failure */ int sg_ll_test_unit_ready_progress(int sg_fd, int pack_id, int * progress, bool noisy, int verbose); struct sg_simple_inquiry_resp { unsigned char peripheral_qualifier; unsigned char peripheral_type; unsigned char byte_1; /* was 'rmb' prior to version 1.39 */ /* now rmb == !!(0x80 & byte_1) */ unsigned char version; /* as per recent drafts: whole of byte 2 */ unsigned char byte_3; unsigned char byte_5; unsigned char byte_6; unsigned char byte_7; char vendor[9]; /* T10 field is 8 bytes, NUL char appended */ char product[17]; char revision[5]; }; /* Yields most of first 36 bytes of a standard INQUIRY (evpd==0) response. * Returns 0 when successful, SG_LIB_CAT_INVALID_OP -> not supported, * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_ABORTED_COMMAND, * -1 -> other errors */ int sg_simple_inquiry(int sg_fd, struct sg_simple_inquiry_resp * inq_data, bool noisy, int verbose); /* MODE SENSE commands yield a response that has header then zero or more * block descriptors followed by mode pages. In most cases users are * interested in the first mode page. This function returns the (byte) * offset of the start of the first mode page. Set mode_sense_6 to true for * MODE SENSE (6) and false for MODE SENSE (10). Returns >= 0 is successful * or -1 if failure. If there is a failure a message is written to err_buff * if it is non-NULL and err_buff_len > 0. */ int sg_mode_page_offset(const unsigned char * resp, int resp_len, bool mode_sense_6, char * err_buff, int err_buff_len); /* MODE SENSE commands yield a response that has header then zero or more * block descriptors followed by mode pages. This functions returns the * length (in bytes) of those three components. Note that the return value * can exceed resp_len in which case the MODE SENSE command should be * re-issued with a larger response buffer. If bd_lenp is non-NULL and if * successful the block descriptor length (in bytes) is written to *bd_lenp. * Set mode_sense_6 to true for MODE SENSE (6) and false for MODE SENSE (10) * responses. Returns -1 if there is an error (e.g. response too short). */ int sg_msense_calc_length(const unsigned char * resp, int resp_len, bool mode_sense_6, int * bd_lenp); /* Fetches current, changeable, default and/or saveable modes pages as * indicated by pcontrol_arr for given pg_code and sub_pg_code. If * mode6==0 then use MODE SENSE (10) else use MODE SENSE (6). If * flexible set and mode data length seems wrong then try and * fix (compensating hack for bad device or driver). pcontrol_arr * should have 4 elements for output of current, changeable, default * and saved values respectively. Each element should be NULL or * at least mx_mpage_len bytes long. * Return of 0 -> overall success, SG_LIB_CAT_INVALID_OP -> invalid opcode, * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION, * SG_LIB_CAT_NOT_READY -> device not ready, * SG_LIB_CAT_MALFORMED -> bad response, -1 -> other failure. * If success_mask pointer is not NULL then first zeros it. Then set bits * 0, 1, 2 and/or 3 if the current, changeable, default and saved values * respectively have been fetched. If error on current page * then stops and returns that error; otherwise continues if an error is * detected but returns the first error encountered. */ int sg_get_mode_page_controls(int sg_fd, bool mode6, int pg_code, int sub_pg_code, bool dbd, bool flexible, int mx_mpage_len, int * success_mask, void * pcontrol_arr[], int * reported_lenp, int verbose); /* Returns file descriptor >= 0 if successful. If error in Unix returns negated errno. Implementation calls scsi_pt_open_device(). */ int sg_cmds_open_device(const char * device_name, bool read_only, int verbose); /* Returns file descriptor >= 0 if successful. If error in Unix returns negated errno. Implementation calls scsi_pt_open_flags(). */ int sg_cmds_open_flags(const char * device_name, int flags, int verbose); /* Returns 0 if successful. If error in Unix returns negated errno. Implementation calls scsi_pt_close_device(). */ int sg_cmds_close_device(int device_fd); const char * sg_cmds_version(); #define SG_NO_DATA_IN 0 struct sg_pt_base; /* This is a helper function used by sg_cmds_* implementations after the * call to the pass-through. pt_res is returned from do_scsi_pt(). If valid * sense data is found it is decoded and output to sg_warnings_strm (def: * stderr); depending on the 'noisy' and 'verbose' settings. Returns -2 for * sense data (may not be fatal), -1 for failed, 0, or a positive number. If * 'mx_di_len > 0' then asks pass-through for resid and returns * (mx_di_len - resid); otherwise returns 0. So for data-in it should return * the actual number of bytes received. For data-out (to device) or no data * call with 'mx_di_len' set to 0 or less. If -2 returned then sense category * output via 'o_sense_cat' pointer (if not NULL). Note that several sense * categories also have data in bytes received; -2 is still returned. */ int sg_cmds_process_resp(struct sg_pt_base * ptvp, const char * leadin, int pt_res, int mx_di_len, const unsigned char * sense_b, bool noisy, int verbose, int * o_sense_cat); /* NVMe devices use a different command set. This function will return true * if the device associated with 'pvtp' is a NVME device, else it will * return false (e.g. for SCSI devices). */ bool sg_cmds_is_nvme(const struct sg_pt_base * ptvp); #ifdef __cplusplus } #endif #endif f2fs-tools-1.11.0/tools/sg_write_buffer/include/sg_cmds_extra.h000066400000000000000000000500521332120552100245160ustar00rootroot00000000000000#ifndef SG_CMDS_EXTRA_H #define SG_CMDS_EXTRA_H /* * Copyright (c) 2004-2018 Douglas Gilbert. * All rights reserved. * Use of this source code is governed by a BSD-style * license that can be found in the BSD_LICENSE file. */ #include #ifdef __cplusplus extern "C" { #endif /* Note: all functions that have an 'int timeout_secs' argument will use * that value if it is > 0. Otherwise they will set an internal default * which is currently 60 seconds. This timeout is typically applied in the * SCSI stack above the initiator. If it goes off then the SCSI command is * aborted and there can be other unwelcome side effects. Note that some * commands (e.g. FORMAT UNIT and the Third Party copy commands) can take * a lot longer than the default timeout. */ /* Invokes a ATA PASS-THROUGH (12, 16 or 32) SCSI command (SAT). This is * selected by the cdb_len argument that can take values of 12, 16 or 32 * only (else -1 is returned). The byte at offset 0 (and bytes 0 to 9 * inclusive for ATA PT(32)) pointed to be cdbp are ignored and apart from * the control byte, the rest is copied into an internal cdb which is then * sent to the device. The control byte is byte 11 for ATA PT(12), byte 15 * for ATA PT(16) and byte 1 for ATA PT(32). If timeout_secs <= 0 then the * timeout is set to 60 seconds. For data in or out transfers set dinp or * doutp, and dlen to the number of bytes to transfer. If dlen is zero then * no data transfer is assumed. If sense buffer obtained then it is written * to sensep, else sensep[0] is set to 0x0. If ATA return descriptor is * obtained then written to ata_return_dp, else ata_return_dp[0] is set to * 0x0. Either sensep or ata_return_dp (or both) may be NULL pointers. * Returns SCSI status value (>= 0) or -1 if other error. Users are * expected to check the sense buffer themselves. If available the data in * resid is written to residp. Note in SAT-2 and later, fixed format sense * data may be placed in *sensep in which case sensep[0]==0x70, prior to * SAT-2 descriptor sense format was required (i.e. sensep[0]==0x72). */ int sg_ll_ata_pt(int sg_fd, const unsigned char * cdbp, int cdb_len, int timeout_secs, void * dinp, void * doutp, int dlen, unsigned char * sensep, int max_sense_len, unsigned char * ata_return_dp, int max_ata_return_len, int * residp, int verbose); /* Invokes a FORMAT UNIT (SBC-3) command. Return of 0 -> success, * SG_LIB_CAT_INVALID_OP -> Format unit not supported, * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION, * SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND, * -1 -> other failure. Note that sg_ll_format_unit2() and * sg_ll_format_unit_v2() are the same, both add the ffmt argument. */ int sg_ll_format_unit(int sg_fd, int fmtpinfo, bool longlist, bool fmtdata, bool cmplist, int dlist_format, int timeout_secs, void * paramp, int param_len, bool noisy, int verbose); int sg_ll_format_unit2(int sg_fd, int fmtpinfo, bool longlist, bool fmtdata, bool cmplist, int dlist_format, int ffmt, int timeout_secs, void * paramp, int param_len, bool noisy, int verbose); int sg_ll_format_unit_v2(int sg_fd, int fmtpinfo, bool longlist, bool fmtdata, bool cmplist, int dlist_format, int ffmt, int timeout_secs, void * paramp, int param_len, bool noisy, int verbose); /* Invokes a SCSI GET LBA STATUS(16) or GET LBA STATUS(32) command (SBC). * Returns 0 -> success, * SG_LIB_CAT_INVALID_OP -> GET LBA STATUS(16 or 32) not supported, * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_ABORTED_COMMAND, * SG_LIB_CAT_NOT_READY -> device not ready, -1 -> other failure. * sg_ll_get_lba_status() calls the 16 byte variant with rt=0 . */ int sg_ll_get_lba_status(int sg_fd, uint64_t start_llba, void * resp, int alloc_len, bool noisy, int verbose); int sg_ll_get_lba_status16(int sg_fd, uint64_t start_llba, uint8_t rt, void * resp, int alloc_len, bool noisy, int verbose); int sg_ll_get_lba_status32(int sg_fd, uint64_t start_llba, uint32_t scan_len, uint32_t element_id, uint8_t rt, void * resp, int alloc_len, bool noisy, int verbose); /* Invokes a SCSI PERSISTENT RESERVE IN command (SPC). Returns 0 * when successful, SG_LIB_CAT_INVALID_OP if command not supported, * SG_LIB_CAT_ILLEGAL_REQ if field in cdb not supported, * SG_LIB_CAT_UNIT_ATTENTION, SG_LIB_CAT_ABORTED_COMMAND, else -1 */ int sg_ll_persistent_reserve_in(int sg_fd, int rq_servact, void * resp, int mx_resp_len, bool noisy, int verbose); /* Invokes a SCSI PERSISTENT RESERVE OUT command (SPC). Returns 0 * when successful, SG_LIB_CAT_INVALID_OP if command not supported, * SG_LIB_CAT_ILLEGAL_REQ if field in cdb not supported, * SG_LIB_CAT_UNIT_ATTENTION, SG_LIB_CAT_ABORTED_COMMAND, else -1 */ int sg_ll_persistent_reserve_out(int sg_fd, int rq_servact, int rq_scope, unsigned int rq_type, void * paramp, int param_len, bool noisy, int verbose); /* Invokes a SCSI READ BLOCK LIMITS command. Return of 0 -> success, * SG_LIB_CAT_INVALID_OP -> READ BLOCK LIMITS not supported, * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_ABORTED_COMMAND, * SG_LIB_NOT_READY (shouldn't happen), -1 -> other failure */ int sg_ll_read_block_limits(int sg_fd, void * resp, int mx_resp_len, bool noisy, int verbose); /* Invokes a SCSI READ BUFFER command (SPC). Return of 0 -> * success, SG_LIB_CAT_INVALID_OP -> invalid opcode, * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION, * SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND, * -1 -> other failure */ int sg_ll_read_buffer(int sg_fd, int mode, int buffer_id, int buffer_offset, void * resp, int mx_resp_len, bool noisy, int verbose); /* Invokes a SCSI READ DEFECT DATA (10) command (SBC). Return of 0 -> * success, SG_LIB_CAT_INVALID_OP -> invalid opcode, * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION, * SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND, * -1 -> other failure */ int sg_ll_read_defect10(int sg_fd, bool req_plist, bool req_glist, int dl_format, void * resp, int mx_resp_len, bool noisy, int verbose); /* Invokes a SCSI READ LONG (10) command (SBC). Note that 'xfer_len' * is in bytes. Returns 0 -> success, * SG_LIB_CAT_INVALID_OP -> READ LONG(10) not supported, * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, * SG_LIB_CAT_ILLEGAL_REQ_WITH_INFO -> bad field in cdb, with info * field written to 'offsetp', SG_LIB_CAT_UNIT_ATTENTION, * SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND, * -1 -> other failure */ int sg_ll_read_long10(int sg_fd, bool pblock, bool correct, unsigned int lba, void * resp, int xfer_len, int * offsetp, bool noisy, int verbose); /* Invokes a SCSI READ LONG (16) command (SBC). Note that 'xfer_len' * is in bytes. Returns 0 -> success, * SG_LIB_CAT_INVALID_OP -> READ LONG(16) not supported, * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, * SG_LIB_CAT_ILLEGAL_REQ_WITH_INFO -> bad field in cdb, with info * field written to 'offsetp', SG_LIB_CAT_UNIT_ATTENTION, * SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND, * -1 -> other failure */ int sg_ll_read_long16(int sg_fd, bool pblock, bool correct, uint64_t llba, void * resp, int xfer_len, int * offsetp, bool noisy, int verbose); /* Invokes a SCSI READ MEDIA SERIAL NUMBER command. Return of 0 -> success, * SG_LIB_CAT_INVALID_OP -> Read media serial number not supported, * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION, * SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND, * -1 -> other failure */ int sg_ll_read_media_serial_num(int sg_fd, void * resp, int mx_resp_len, bool noisy, int verbose); /* Invokes a SCSI REASSIGN BLOCKS command. Return of 0 -> success, * SG_LIB_CAT_INVALID_OP -> invalid opcode, SG_LIB_CAT_UNIT_ATTENTION, * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_ABORTED_COMMAND, * SG_LIB_CAT_NOT_READY -> device not ready, -1 -> other failure */ int sg_ll_reassign_blocks(int sg_fd, bool longlba, bool longlist, void * paramp, int param_len, bool noisy, int verbose); /* Invokes a SCSI RECEIVE DIAGNOSTIC RESULTS command. Return of 0 -> success, * SG_LIB_CAT_INVALID_OP -> Receive diagnostic results not supported, * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION, * SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND, * -1 -> other failure */ int sg_ll_receive_diag(int sg_fd, bool pcv, int pg_code, void * resp, int mx_resp_len, bool noisy, int verbose); /* Same as sg_ll_receive_diag() but with added timeout_secs and residp * arguments. Adds the ability to set the command abort timeout * and the ability to report the residual count. If timeout_secs is zero * or less the default command abort timeout (60 seconds) is used. * If residp is non-NULL then the residual value is written where residp * points. A residual value of 0 implies mx_resp_len bytes have be written * where resp points. If the residual value equals mx_resp_len then no * bytes have been written. */ int sg_ll_receive_diag_v2(int sg_fd, bool pcv, int pg_code, void * resp, int mx_resp_len, int timeout_secs, int * residp, bool noisy, int verbose); /* Invokes a SCSI REPORT IDENTIFYING INFORMATION command. This command was * called REPORT DEVICE IDENTIFIER prior to spc4r07. Return of 0 -> success, * SG_LIB_CAT_INVALID_OP -> Report identifying information not supported, * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION, * SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND, * -1 -> other failure */ int sg_ll_report_id_info(int sg_fd, int itype, void * resp, int max_resp_len, bool noisy, int verbose); /* Invokes a SCSI REPORT TARGET PORT GROUPS command. Return of 0 -> success, * SG_LIB_CAT_INVALID_OP -> Report Target Port Groups not supported, * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_ABORTED_COMMAND, * SG_LIB_CAT_UNIT_ATTENTION, -1 -> other failure */ int sg_ll_report_tgt_prt_grp(int sg_fd, void * resp, int mx_resp_len, bool noisy, int verbose); int sg_ll_report_tgt_prt_grp2(int sg_fd, void * resp, int mx_resp_len, bool extended, bool noisy, int verbose); /* Invokes a SCSI SET TARGET PORT GROUPS command. Return of 0 -> success, * SG_LIB_CAT_INVALID_OP -> Report Target Port Groups not supported, * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_ABORTED_COMMAND, * SG_LIB_CAT_UNIT_ATTENTION, -1 -> other failure */ int sg_ll_set_tgt_prt_grp(int sg_fd, void * paramp, int param_len, bool noisy, int verbose); /* Invokes a SCSI REPORT REFERRALS command. Return of 0 -> success, * SG_LIB_CAT_INVALID_OP -> Report Referrals not supported, * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_ABORTED_COMMAND, * SG_LIB_CAT_UNIT_ATTENTION, -1 -> other failure */ int sg_ll_report_referrals(int sg_fd, uint64_t start_llba, bool one_seg, void * resp, int mx_resp_len, bool noisy, int verbose); /* Invokes a SCSI SEND DIAGNOSTIC command. Foreground, extended self tests can * take a long time, if so set long_duration flag in which case the timeout * is set to 7200 seconds; if the value of long_duration is > 7200 then that * value is taken as the timeout value in seconds. Return of 0 -> success, * SG_LIB_CAT_INVALID_OP -> Send diagnostic not supported, * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION, * SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND, * -1 -> other failure */ int sg_ll_send_diag(int sg_fd, int st_code, bool pf_bit, bool st_bit, bool devofl_bit, bool unitofl_bit, int long_duration, void * paramp, int param_len, bool noisy, int verbose); /* Invokes a SCSI SET IDENTIFYING INFORMATION command. This command was * called SET DEVICE IDENTIFIER prior to spc4r07. Return of 0 -> success, * SG_LIB_CAT_INVALID_OP -> Set identifying information not supported, * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION, * SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND, * -1 -> other failure */ int sg_ll_set_id_info(int sg_fd, int itype, void * paramp, int param_len, bool noisy, int verbose); /* Invokes a SCSI UNMAP (SBC-3) command. Return of 0 -> success, * SG_LIB_CAT_INVALID_OP -> command not supported, * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_ABORTED_COMMAND, * SG_LIB_CAT_UNIT_ATTENTION, -1 -> other failure */ int sg_ll_unmap(int sg_fd, int group_num, int timeout_secs, void * paramp, int param_len, bool noisy, int verbose); /* Invokes a SCSI UNMAP (SBC-3) command. Version 2 adds anchor field * (sbc3r22). Otherwise same as sg_ll_unmap() . */ int sg_ll_unmap_v2(int sg_fd, bool anchor, int group_num, int timeout_secs, void * paramp, int param_len, bool noisy, int verbose); /* Invokes a SCSI VERIFY (10) command (SBC and MMC). * Note that 'veri_len' is in blocks while 'data_out_len' is in bytes. * Returns of 0 -> success, * SG_LIB_CAT_INVALID_OP -> Verify(10) not supported, * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION, * SG_LIB_CAT_MEDIUM_HARD -> medium or hardware error, no valid info, * SG_LIB_CAT_MEDIUM_HARD_WITH_INFO -> as previous, with valid info, * SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND, * SG_LIB_CAT_MISCOMPARE, -1 -> other failure */ int sg_ll_verify10(int sg_fd, int vrprotect, bool dpo, int bytechk, unsigned int lba, int veri_len, void * data_out, int data_out_len, unsigned int * infop, bool noisy, int verbose); /* Invokes a SCSI VERIFY (16) command (SBC). * Note that 'veri_len' is in blocks while 'data_out_len' is in bytes. * Returns of 0 -> success, * SG_LIB_CAT_INVALID_OP -> Verify(16) not supported, * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION, * SG_LIB_CAT_MEDIUM_HARD -> medium or hardware error, no valid info, * SG_LIB_CAT_MEDIUM_HARD_WITH_INFO -> as previous, with valid info, * SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND, * SG_LIB_CAT_MISCOMPARE, -1 -> other failure */ int sg_ll_verify16(int sg_fd, int vrprotect, bool dpo, int bytechk, uint64_t llba, int veri_len, int group_num, void * data_out, int data_out_len, uint64_t * infop, bool noisy, int verbose); /* Invokes a SCSI WRITE BUFFER command (SPC). Return of 0 -> * success, SG_LIB_CAT_INVALID_OP -> invalid opcode, * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION, * SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND, * -1 -> other failure */ int sg_ll_write_buffer(int sg_fd, int mode, int buffer_id, int buffer_offset, void * paramp, int param_len, bool noisy, int verbose); /* Invokes a SCSI WRITE BUFFER command (SPC). Return of 0 -> * success, SG_LIB_CAT_INVALID_OP -> invalid opcode, * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION, * SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND, * -1 -> other failure. Adds mode specific field (spc4r32) and timeout * to command abort to override default of 60 seconds. If timeout_secs is * 0 or less then the default timeout is used instead. */ int sg_ll_write_buffer_v2(int sg_fd, int mode, int m_specific, int buffer_id, uint32_t buffer_offset, void * paramp, uint32_t param_len, int timeout_secs, bool noisy, int verbose); /* Invokes a SCSI WRITE LONG (10) command (SBC). Note that 'xfer_len' * is in bytes. Returns 0 -> success, * SG_LIB_CAT_INVALID_OP -> WRITE LONG(10) not supported, * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, * SG_LIB_CAT_ILLEGAL_REQ_WITH_INFO -> bad field in cdb, with info * field written to 'offsetp', SG_LIB_CAT_UNIT_ATTENTION, * SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND, * -1 -> other failure */ int sg_ll_write_long10(int sg_fd, bool cor_dis, bool wr_uncor, bool pblock, unsigned int lba, void * data_out, int xfer_len, int * offsetp, bool noisy, int verbose); /* Invokes a SCSI WRITE LONG (16) command (SBC). Note that 'xfer_len' * is in bytes. Returns 0 -> success, * SG_LIB_CAT_INVALID_OP -> WRITE LONG(16) not supported, * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, * SG_LIB_CAT_ILLEGAL_REQ_WITH_INFO -> bad field in cdb, with info * field written to 'offsetp', SG_LIB_CAT_UNIT_ATTENTION, * SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND, * -1 -> other failure */ int sg_ll_write_long16(int sg_fd, bool cor_dis, bool wr_uncor, bool pblock, uint64_t llba, void * data_out, int xfer_len, int * offsetp, bool noisy, int verbose); /* Invokes a SPC-3 SCSI RECEIVE COPY RESULTS command. In SPC-4 this function * supports all service action variants of the THIRD-PARTY COPY IN opcode. * SG_LIB_CAT_INVALID_OP -> Receive copy results not supported, * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION, * SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND, * -1 -> other failure */ int sg_ll_receive_copy_results(int sg_fd, int sa, int list_id, void * resp, int mx_resp_len, bool noisy, int verbose); /* Invokes a SCSI EXTENDED COPY(LID1) command. For EXTENDED COPY(LID4) * including POPULATE TOKEN and WRITE USING TOKEN use * sg_ll_3party_copy_out(). Return of 0 -> success, * SG_LIB_CAT_INVALID_OP -> Extended copy not supported, * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION, * SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND, * -1 -> other failure */ int sg_ll_extended_copy(int sg_fd, void * paramp, int param_len, bool noisy, int verbose); /* Handles various service actions associated with opcode 0x83 which is * called THIRD PARTY COPY OUT. These include the EXTENDED COPY(LID4), * POPULATE TOKEN and WRITE USING TOKEN commands. Return of 0 -> success, * SG_LIB_CAT_INVALID_OP -> opcode 0x83 not supported, * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION, * SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND, * -1 -> other failure */ int sg_ll_3party_copy_out(int sg_fd, int sa, unsigned int list_id, int group_num, int timeout_secs, void * paramp, int param_len, bool noisy, int verbose); /* Invokes a SCSI PRE-FETCH(10), PRE-FETCH(16) or SEEK(10) command (SBC). * Returns 0 -> success, 25 (SG_LIB_CAT_CONDITION_MET), various SG_LIB_CAT_* * positive values or -1 -> other errors. Note that CONDITION MET status * is returned when immed=true and num_blocks can fit in device's cache, * somewaht strangely, GOOD status (return 0) is returned if num_blocks * cannot fit in device's cache. If do_seek10==true then does a SEEK(10) * command with given lba, if that LBA is < 2**32 . Unclear what SEEK(10) * does, assume it is like PRE-FETCH. If timeout_secs is 0 (or less) then * use DEF_PT_TIMEOUT (60 seconds) as command timeout. */ int sg_ll_pre_fetch_x(int sg_fd, bool do_seek10, bool cdb16, bool immed, uint64_t lba, uint32_t num_blocks, int group_num, int timeout_secs, bool noisy, int verbose); #ifdef __cplusplus } #endif #endif f2fs-tools-1.11.0/tools/sg_write_buffer/include/sg_cmds_mmc.h000066400000000000000000000037661332120552100241610ustar00rootroot00000000000000#ifndef SG_CMDS_MMC_H #define SG_CMDS_MMC_H /* * Copyright (c) 2008-2017 Douglas Gilbert. * All rights reserved. * Use of this source code is governed by a BSD-style * license that can be found in the BSD_LICENSE file. */ #ifdef __cplusplus extern "C" { #endif /* Invokes a SCSI GET CONFIGURATION command (MMC-3...6). * Returns 0 when successful, SG_LIB_CAT_INVALID_OP if command not * supported, SG_LIB_CAT_ILLEGAL_REQ if field in cdb not supported, * SG_LIB_CAT_UNIT_ATTENTION, SG_LIB_CAT_ABORTED_COMMAND, else -1 */ int sg_ll_get_config(int sg_fd, int rt, int starting, void * resp, int mx_resp_len, bool noisy, int verbose); /* Invokes a SCSI GET PERFORMANCE command (MMC-3...6). * Returns 0 when successful, SG_LIB_CAT_INVALID_OP if command not * supported, SG_LIB_CAT_ILLEGAL_REQ if field in cdb not supported, * SG_LIB_CAT_UNIT_ATTENTION, SG_LIB_CAT_ABORTED_COMMAND, else -1 */ int sg_ll_get_performance(int sg_fd, int data_type, unsigned int starting_lba, int max_num_desc, int type, void * resp, int mx_resp_len, bool noisy, int verbose); /* Invokes a SCSI SET CD SPEED command (MMC). * Return of 0 -> success, SG_LIB_CAT_INVALID_OP -> command not supported, * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION, * SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND, * -1 -> other failure */ int sg_ll_set_cd_speed(int sg_fd, int rot_control, int drv_read_speed, int drv_write_speed, bool noisy, int verbose); /* Invokes a SCSI SET STREAMING command (MMC). Return of 0 -> success, * SG_LIB_CAT_INVALID_OP -> Set Streaming not supported, * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_ABORTED_COMMAND, * SG_LIB_CAT_UNIT_ATTENTION, SG_LIB_CAT_NOT_READY -> device not ready, * -1 -> other failure */ int sg_ll_set_streaming(int sg_fd, int type, void * paramp, int param_len, bool noisy, int verbose); #ifdef __cplusplus } #endif #endif f2fs-tools-1.11.0/tools/sg_write_buffer/include/sg_io_linux.h000066400000000000000000000146671332120552100242270ustar00rootroot00000000000000#ifndef SG_IO_LINUX_H #define SG_IO_LINUX_H /* * Copyright (c) 2004-2017 Douglas Gilbert. * All rights reserved. * Use of this source code is governed by a BSD-style * license that can be found in the BSD_LICENSE file. */ /* * Version 1.05 [20171009] */ /* * This header file contains linux specific information related to the SCSI * command pass through in the SCSI generic (sg) driver and the linux * block layer. */ #include "sg_lib.h" #include "sg_linux_inc.h" #ifdef __cplusplus extern "C" { #endif /* The following are 'host_status' codes */ #ifndef DID_OK #define DID_OK 0x00 #endif #ifndef DID_NO_CONNECT #define DID_NO_CONNECT 0x01 /* Unable to connect before timeout */ #define DID_BUS_BUSY 0x02 /* Bus remain busy until timeout */ #define DID_TIME_OUT 0x03 /* Timed out for some other reason */ #define DID_BAD_TARGET 0x04 /* Bad target (id?) */ #define DID_ABORT 0x05 /* Told to abort for some other reason */ #define DID_PARITY 0x06 /* Parity error (on SCSI bus) */ #define DID_ERROR 0x07 /* Internal error */ #define DID_RESET 0x08 /* Reset by somebody */ #define DID_BAD_INTR 0x09 /* Received an unexpected interrupt */ #define DID_PASSTHROUGH 0x0a /* Force command past mid-level */ #define DID_SOFT_ERROR 0x0b /* The low-level driver wants a retry */ #endif #ifndef DID_IMM_RETRY #define DID_IMM_RETRY 0x0c /* Retry without decrementing retry count */ #endif #ifndef DID_REQUEUE #define DID_REQUEUE 0x0d /* Requeue command (no immediate retry) also * without decrementing the retry count */ #endif #ifndef DID_TRANSPORT_DISRUPTED #define DID_TRANSPORT_DISRUPTED 0xe #endif #ifndef DID_TRANSPORT_FAILFAST #define DID_TRANSPORT_FAILFAST 0xf #endif #ifndef DID_TARGET_FAILURE #define DID_TARGET_FAILURE 0x10 #endif #ifndef DID_NEXUS_FAILURE #define DID_NEXUS_FAILURE 0x11 #endif /* These defines are to isolate applications from kernel define changes */ #define SG_LIB_DID_OK DID_OK #define SG_LIB_DID_NO_CONNECT DID_NO_CONNECT #define SG_LIB_DID_BUS_BUSY DID_BUS_BUSY #define SG_LIB_DID_TIME_OUT DID_TIME_OUT #define SG_LIB_DID_BAD_TARGET DID_BAD_TARGET #define SG_LIB_DID_ABORT DID_ABORT #define SG_LIB_DID_PARITY DID_PARITY #define SG_LIB_DID_ERROR DID_ERROR #define SG_LIB_DID_RESET DID_RESET #define SG_LIB_DID_BAD_INTR DID_BAD_INTR #define SG_LIB_DID_PASSTHROUGH DID_PASSTHROUGH #define SG_LIB_DID_SOFT_ERROR DID_SOFT_ERROR #define SG_LIB_DID_IMM_RETRY DID_IMM_RETRY #define SG_LIB_DID_REQUEUE DID_REQUEUE #define SG_LIB_TRANSPORT_DISRUPTED DID_TRANSPORT_DISRUPTED #define SG_LIB_DID_TRANSPORT_FAILFAST DID_TRANSPORT_FAILFAST #define SG_LIB_DID_TARGET_FAILURE DID_TARGET_FAILURE #define SG_LIB_DID_NEXUS_FAILURE DID_NEXUS_FAILURE /* The following are 'driver_status' codes */ #ifndef DRIVER_OK #define DRIVER_OK 0x00 #endif #ifndef DRIVER_BUSY #define DRIVER_BUSY 0x01 #define DRIVER_SOFT 0x02 #define DRIVER_MEDIA 0x03 #define DRIVER_ERROR 0x04 #define DRIVER_INVALID 0x05 #define DRIVER_TIMEOUT 0x06 #define DRIVER_HARD 0x07 #define DRIVER_SENSE 0x08 /* Sense_buffer has been set */ /* N.B. the SUGGEST_* codes are no longer used in Linux and are only kept * to stop compilation breakages. * Following "suggests" are "or-ed" with one of previous 8 entries */ #define SUGGEST_RETRY 0x10 #define SUGGEST_ABORT 0x20 #define SUGGEST_REMAP 0x30 #define SUGGEST_DIE 0x40 #define SUGGEST_SENSE 0x80 #define SUGGEST_IS_OK 0xff #endif #ifndef DRIVER_MASK #define DRIVER_MASK 0x0f #endif #ifndef SUGGEST_MASK #define SUGGEST_MASK 0xf0 #endif /* These defines are to isolate applications from kernel define changes */ #define SG_LIB_DRIVER_OK DRIVER_OK #define SG_LIB_DRIVER_BUSY DRIVER_BUSY #define SG_LIB_DRIVER_SOFT DRIVER_SOFT #define SG_LIB_DRIVER_MEDIA DRIVER_MEDIA #define SG_LIB_DRIVER_ERROR DRIVER_ERROR #define SG_LIB_DRIVER_INVALID DRIVER_INVALID #define SG_LIB_DRIVER_TIMEOUT DRIVER_TIMEOUT #define SG_LIB_DRIVER_HARD DRIVER_HARD #define SG_LIB_DRIVER_SENSE DRIVER_SENSE /* N.B. the SUGGEST_* codes are no longer used in Linux and are only kept * to stop compilation breakages. */ #define SG_LIB_SUGGEST_RETRY SUGGEST_RETRY #define SG_LIB_SUGGEST_ABORT SUGGEST_ABORT #define SG_LIB_SUGGEST_REMAP SUGGEST_REMAP #define SG_LIB_SUGGEST_DIE SUGGEST_DIE #define SG_LIB_SUGGEST_SENSE SUGGEST_SENSE #define SG_LIB_SUGGEST_IS_OK SUGGEST_IS_OK #define SG_LIB_DRIVER_MASK DRIVER_MASK #define SG_LIB_SUGGEST_MASK SUGGEST_MASK void sg_print_masked_status(int masked_status); void sg_print_host_status(int host_status); void sg_print_driver_status(int driver_status); /* sg_chk_n_print() returns 1 quietly if there are no errors/warnings else it prints errors/warnings (prefixed by 'leadin') to 'sg_warnings_fd' and returns 0. raw_sinfo indicates whether the raw sense buffer (in ASCII hex) should be printed. */ int sg_chk_n_print(const char * leadin, int masked_status, int host_status, int driver_status, const unsigned char * sense_buffer, int sb_len, bool raw_sinfo); /* The following function declaration is for the sg version 3 driver. */ struct sg_io_hdr; /* sg_chk_n_print3() returns 1 quietly if there are no errors/warnings; else it prints errors/warnings (prefixed by 'leadin') to 'sg_warnings_fd' and returns 0. */ int sg_chk_n_print3(const char * leadin, struct sg_io_hdr * hp, bool raw_sinfo); /* Calls sg_scsi_normalize_sense() after obtaining the sense buffer and its length from the struct sg_io_hdr pointer. If these cannot be obtained, false is returned. */ bool sg_normalize_sense(const struct sg_io_hdr * hp, struct sg_scsi_sense_hdr * sshp); int sg_err_category(int masked_status, int host_status, int driver_status, const unsigned char * sense_buffer, int sb_len); int sg_err_category_new(int scsi_status, int host_status, int driver_status, const unsigned char * sense_buffer, int sb_len); /* The following function declaration is for the sg version 3 driver. */ int sg_err_category3(struct sg_io_hdr * hp); /* Note about SCSI status codes found in older versions of Linux. Linux has traditionally used a 1 bit right shifted and masked version of SCSI standard status codes. Now CHECK_CONDITION and friends (in ) are deprecated. */ #ifdef __cplusplus } #endif #endif f2fs-tools-1.11.0/tools/sg_write_buffer/include/sg_lib.h000066400000000000000000000740421332120552100231400ustar00rootroot00000000000000#ifndef SG_LIB_H #define SG_LIB_H /* * Copyright (c) 2004-2018 Douglas Gilbert. * All rights reserved. * Use of this source code is governed by a BSD-style * license that can be found in the BSD_LICENSE file. */ /* * * On 5th October 2004 a FreeBSD license was added to this file. * The intention is to keep this file and the related sg_lib.c file * as open source and encourage their unencumbered use. * * Current version number is in the sg_lib.c file and can be accessed * with the sg_lib_version() function. */ /* * This header file contains defines and function declarations that may * be useful to applications that communicate with devices that use a * SCSI command set. These command sets have names like SPC-4, SBC-3, * SSC-3, SES-2 and draft standards defining them can be found at * http://www.t10.org . Virtually all devices in the Linux SCSI subsystem * utilize SCSI command sets. Many devices in other Linux device subsystems * utilize SCSI command sets either natively or via emulation (e.g. a * parallel ATA disk in a USB enclosure). */ #include #include #include #ifdef __cplusplus extern "C" { #endif /* SCSI Peripheral Device Types (PDT) [5 bit field] */ #define PDT_DISK 0x0 /* direct access block device (disk) */ #define PDT_TAPE 0x1 /* sequential access device (magnetic tape) */ #define PDT_PRINTER 0x2 /* printer device (see SSC-1) */ #define PDT_PROCESSOR 0x3 /* processor device (e.g. SAFTE device) */ #define PDT_WO 0x4 /* write once device (some optical disks) */ #define PDT_MMC 0x5 /* CD/DVD/BD (multi-media) */ #define PDT_SCANNER 0x6 /* obsolete */ #define PDT_OPTICAL 0x7 /* optical memory device (some optical disks) */ #define PDT_MCHANGER 0x8 /* media changer device (e.g. tape robot) */ #define PDT_COMMS 0x9 /* communications device (obsolete) */ #define PDT_SAC 0xc /* storage array controller device */ #define PDT_SES 0xd /* SCSI Enclosure Services (SES) device */ #define PDT_RBC 0xe /* Reduced Block Commands (simplified PDT_DISK) */ #define PDT_OCRW 0xf /* optical card read/write device */ #define PDT_BCC 0x10 /* bridge controller commands */ #define PDT_OSD 0x11 /* Object Storage Device (OSD) */ #define PDT_ADC 0x12 /* Automation/drive commands (ADC) */ #define PDT_SMD 0x13 /* Security Manager Device (SMD) */ #define PDT_ZBC 0x14 /* Zoned Block Commands (ZBC) */ #define PDT_WLUN 0x1e /* Well known logical unit (WLUN) */ #define PDT_UNKNOWN 0x1f /* Unknown or no device type */ #ifndef SAM_STAT_GOOD /* The SCSI status codes as found in SAM-4 at www.t10.org */ #define SAM_STAT_GOOD 0x0 #define SAM_STAT_CHECK_CONDITION 0x2 #define SAM_STAT_CONDITION_MET 0x4 #define SAM_STAT_BUSY 0x8 #define SAM_STAT_INTERMEDIATE 0x10 /* obsolete in SAM-4 */ #define SAM_STAT_INTERMEDIATE_CONDITION_MET 0x14 /* obsolete in SAM-4 */ #define SAM_STAT_RESERVATION_CONFLICT 0x18 #define SAM_STAT_COMMAND_TERMINATED 0x22 /* obsolete in SAM-3 */ #define SAM_STAT_TASK_SET_FULL 0x28 #define SAM_STAT_ACA_ACTIVE 0x30 #define SAM_STAT_TASK_ABORTED 0x40 #endif /* The SCSI sense key codes as found in SPC-4 at www.t10.org */ #define SPC_SK_NO_SENSE 0x0 #define SPC_SK_RECOVERED_ERROR 0x1 #define SPC_SK_NOT_READY 0x2 #define SPC_SK_MEDIUM_ERROR 0x3 #define SPC_SK_HARDWARE_ERROR 0x4 #define SPC_SK_ILLEGAL_REQUEST 0x5 #define SPC_SK_UNIT_ATTENTION 0x6 #define SPC_SK_DATA_PROTECT 0x7 #define SPC_SK_BLANK_CHECK 0x8 #define SPC_SK_VENDOR_SPECIFIC 0x9 #define SPC_SK_COPY_ABORTED 0xa #define SPC_SK_ABORTED_COMMAND 0xb #define SPC_SK_RESERVED 0xc #define SPC_SK_VOLUME_OVERFLOW 0xd #define SPC_SK_MISCOMPARE 0xe #define SPC_SK_COMPLETED 0xf /* Transport protocol identifiers or just Protocol identifiers */ #define TPROTO_FCP 0 #define TPROTO_SPI 1 #define TPROTO_SSA 2 #define TPROTO_1394 3 #define TPROTO_SRP 4 /* SCSI over RDMA */ #define TPROTO_ISCSI 5 #define TPROTO_SAS 6 #define TPROTO_ADT 7 #define TPROTO_ATA 8 #define TPROTO_UAS 9 /* USB attached SCSI */ #define TPROTO_SOP 0xa /* SCSI over PCIe */ #define TPROTO_PCIE 0xb /* includes NVMe */ #define TPROTO_NONE 0xf /* SCSI Feature Sets (sfs) */ #define SCSI_FS_SPC_DISCOVERY_2016 0x1 #define SCSI_FS_SBC_BASE_2010 0x102 #define SCSI_FS_SBC_BASE_2016 0x101 #define SCSI_FS_SBC_BASIC_PROV_2016 0x103 #define SCSI_FS_SBC_DRIVE_MAINT_2016 0x104 /* Often SCSI responses use the highest integer that can fit in a field * to indicate "unbounded" or limit does not apply. Sometimes represented * in output as "-1" for brevity */ #define SG_LIB_UNBOUNDED_16BIT 0xffff #define SG_LIB_UNBOUNDED_32BIT 0xffffffffU #define SG_LIB_UNBOUNDED_64BIT 0xffffffffffffffffULL #if (__STDC_VERSION__ >= 199901L) /* C99 or later */ typedef uintptr_t sg_uintptr_t; #else typedef unsigned long sg_uintptr_t; #endif /* The format of the version string is like this: "2.26 20170906" */ const char * sg_lib_version(); /* Returns length of SCSI command given the opcode (first byte). * Yields the wrong answer for variable length commands (opcode=0x7f) * and potentially some vendor specific commands. */ int sg_get_command_size(unsigned char cdb_byte0); /* Command name given pointer to the cdb. Certain command names * depend on peripheral type (give 0 or -1 if unknown). Places command * name into buff and will write no more than buff_len bytes. */ void sg_get_command_name(const unsigned char * cdbp, int peri_type, int buff_len, char * buff); /* Command name given only the first byte (byte 0) of a cdb and * peripheral type (give 0 or -1 if unknown). */ void sg_get_opcode_name(unsigned char cdb_byte0, int peri_type, int buff_len, char * buff); /* Command name given opcode (byte 0), service action and peripheral type. * If no service action give 0, if unknown peripheral type give 0 or -1 . */ void sg_get_opcode_sa_name(unsigned char cdb_byte0, int service_action, int peri_type, int buff_len, char * buff); /* Fetch scsi status string. */ void sg_get_scsi_status_str(int scsi_status, int buff_len, char * buff); /* This is a slightly stretched SCSI sense "descriptor" format header. * The addition is to allow the 0x70 and 0x71 response codes. The idea * is to place the salient data of both "fixed" and "descriptor" sense * format into one structure to ease application processing. * The original sense buffer should be kept around for those cases * in which more information is required (e.g. the LBA of a MEDIUM ERROR). */ struct sg_scsi_sense_hdr { unsigned char response_code; /* permit: 0x0, 0x70, 0x71, 0x72, 0x73 */ unsigned char sense_key; unsigned char asc; unsigned char ascq; unsigned char byte4; unsigned char byte5; unsigned char byte6; unsigned char additional_length; }; /* Maps the salient data from a sense buffer which is in either fixed or * descriptor format into a structure mimicking a descriptor format * header (i.e. the first 8 bytes of sense descriptor format). * If zero response code returns false. Otherwise returns true and if 'sshp' * is non-NULL then zero all fields and then set the appropriate fields in * that structure. sshp::additional_length is always 0 for response * codes 0x70 and 0x71 (fixed format). */ bool sg_scsi_normalize_sense(const unsigned char * sensep, int sense_len, struct sg_scsi_sense_hdr * sshp); /* Attempt to find the first SCSI sense data descriptor that matches the * given 'desc_type'. If found return pointer to start of sense data * descriptor; otherwise (including fixed format sense data) returns NULL. */ const unsigned char * sg_scsi_sense_desc_find(const unsigned char * sensep, int sense_len, int desc_type); /* Get sense key from sense buffer. If successful returns a sense key value * between 0 and 15. If sense buffer cannot be decode, returns -1 . */ int sg_get_sense_key(const unsigned char * sensep, int sense_len); /* Yield string associated with sense_key value. Returns 'buff'. */ char * sg_get_sense_key_str(int sense_key, int buff_len, char * buff); /* Yield string associated with ASC/ASCQ values. Returns 'buff'. */ char * sg_get_asc_ascq_str(int asc, int ascq, int buff_len, char * buff); /* Returns true if valid bit set, false if valid bit clear. Irrespective the * information field is written out via 'info_outp' (except when it is * NULL). Handles both fixed and descriptor sense formats. */ bool sg_get_sense_info_fld(const unsigned char * sensep, int sb_len, uint64_t * info_outp); /* Returns true if fixed format or command specific information descriptor * is found in the descriptor sense; else false. If available the command * specific information field (4 byte integer in fixed format, 8 byte * integer in descriptor format) is written out via 'cmd_spec_outp'. * Handles both fixed and descriptor sense formats. */ bool sg_get_sense_cmd_spec_fld(const unsigned char * sensep, int sb_len, uint64_t * cmd_spec_outp); /* Returns true if any of the 3 bits (i.e. FILEMARK, EOM or ILI) are set. * In descriptor format if the stream commands descriptor not found * then returns false. Writes true or false corresponding to these bits to * the last three arguments if they are non-NULL. */ bool sg_get_sense_filemark_eom_ili(const unsigned char * sensep, int sb_len, bool * filemark_p, bool * eom_p, bool * ili_p); /* Returns true if SKSV is set and sense key is NO_SENSE or NOT_READY. Also * returns true if progress indication sense data descriptor found. Places * progress field from sense data where progress_outp points. If progress * field is not available returns false. Handles both fixed and descriptor * sense formats. N.B. App should multiply by 100 and divide by 65536 * to get percentage completion from given value. */ bool sg_get_sense_progress_fld(const unsigned char * sensep, int sb_len, int * progress_outp); /* Closely related to sg_print_sense(). Puts decoded sense data in 'buff'. * Usually multiline with multiple '\n' including one trailing. If * 'raw_sinfo' set appends sense buffer in hex. 'leadin' is string prepended * to each line written to 'buff', NULL treated as "". Returns the number of * bytes written to 'buff' excluding the trailing '\0'. * N.B. prior to sg3_utils v 1.42 'leadin' was only prepended to the first * line output. Also this function returned type void. */ int sg_get_sense_str(const char * leadin, const unsigned char * sense_buffer, int sb_len, bool raw_sinfo, int buff_len, char * buff); /* Decode descriptor format sense descriptors (assumes sense buffer is * in descriptor format). 'leadin' is string prepended to each line written * to 'b', NULL treated as "". Returns the number of bytes written to 'b' * excluding the trailing '\0'. */ int sg_get_sense_descriptors_str(const char * leadin, const unsigned char * sense_buffer, int sb_len, int blen, char * b); /* Decodes a designation descriptor (e.g. as found in the Device * Identification VPD page (0x83)) into string 'b' whose maximum length is * blen. 'leadin' is string prepended to each line written to 'b', NULL * treated as "". Returns the number of bytes written to 'b' excluding the * trailing '\0'. */ int sg_get_designation_descriptor_str(const char * leadin, const unsigned char * ddp, int dd_len, bool print_assoc, bool do_long, int blen, char * b); /* Yield string associated with peripheral device type (pdt). Returns * 'buff'. If 'pdt' out of range yields "bad pdt" string. */ char * sg_get_pdt_str(int pdt, int buff_len, char * buff); /* Some lesser used PDTs share a lot in common with a more used PDT. * Examples are PDT_ADC decaying to PDT_TAPE and PDT_ZBC to PDT_DISK. * If such a lesser used 'pdt' is given to this function, then it will * return the more used PDT (i.e. "decays to"); otherwise 'pdt' is returned. * Valid for 'pdt' 0 to 31, for other values returns 0. */ int sg_lib_pdt_decay(int pdt); /* Yield string associated with transport protocol identifier (tpi). Returns * 'buff'. If 'tpi' out of range yields "bad tpi" string. */ char * sg_get_trans_proto_str(int tpi, int buff_len, char * buff); /* Decode TransportID pointed to by 'bp' of length 'bplen'. Place decoded * string output in 'buff' which is also the return value. Each new line * is prefixed by 'leadin'. If leadin NULL treat as "". */ char * sg_decode_transportid_str(const char * leadin, unsigned char * bp, int bplen, bool only_one, int buff_len, char * buff); /* Returns a designator's type string given 'val' (0 to 15 inclusive), * otherwise returns NULL. */ const char * sg_get_desig_type_str(int val); /* Returns a designator's code_set string given 'val' (0 to 15 inclusive), * otherwise returns NULL. */ const char * sg_get_desig_code_set_str(int val); /* Returns a designator's association string given 'val' (0 to 3 inclusive), * otherwise returns NULL. */ const char * sg_get_desig_assoc_str(int val); /* Yield SCSI Feature Set (sfs) string. When 'peri_type' is < -1 (or > 31) * returns pointer to string (same as 'buff') associated with 'sfs_code'. * When 'peri_type' is between -1 (for SPC) and 31 (inclusive) then a match * on both 'sfs_code' and 'peri_type' is required. If 'foundp' is not NULL * then where it points is set to true if a match is found else it is set to * false. If 'buff' is not NULL then in the case of a match a descriptive * string is written to 'buff' while if there is not a not then a string * ending in "Reserved" is written (and may be prefixed with SPC, SBC, SSC * or ZBC). Returns 'buff' (i.e. a pointer value) even if it is NULL. * Example: * char b[64]; * ... * printf("%s\n", sg_get_sfs_str(sfs_code, -2, sizeof(b), b, NULL, 0)); */ const char * sg_get_sfs_str(uint16_t sfs_code, int peri_type, int buff_len, char * buff, bool * foundp, int verbose); /* This is a heuristic that takes into account the command bytes and length * to decide whether the presented unstructured sequence of bytes could be * a SCSI command. If so it returns true otherwise false. Vendor specific * SCSI commands (i.e. opcodes from 0xc0 to 0xff), if presented, are assumed * to follow SCSI conventions (i.e. length of 6, 10, 12 or 16 bytes). The * only SCSI commands considered above 16 bytes of length are the Variable * Length Commands (opcode 0x7f) and the XCDB wrapped commands (opcode 0x7e). * Both have an inbuilt length field which can be cross checked with clen. * No NVMe commands (64 bytes long plus some extra added by some OSes) have * opcodes 0x7e or 0x7f yet. ATA is register based but SATA has FIS * structures that are sent across the wire. The 'FIS register' structure is * used to move a command from a SATA host to device, but the ATA 'command' * is not the first byte. So it is harder to say what will happen if a * FIS structure is presented as a SCSI command, hopfully there is a low * probability this function will yield true in that case. */ bool sg_is_scsi_cdb(const uint8_t * cdbp, int clen); /* Yield string associated with NVMe command status value in sct_sc. It * expects to decode DW3 bits 27:17 from the completion queue. Bits 27:25 * are the Status Code Type (SCT) and bits 24:17 are the Status Code (SC). * Bit 17 in DW3 should be bit 0 in sct_sc. If no status string is found * a string of the form "Reserved [0x]" is generated. * Returns 'buff'. Does nothing if buff_len<=0 or if buff is NULL.*/ char * sg_get_nvme_cmd_status_str(uint16_t sct_sc, int buff_len, char * buff); /* Attempts to map NVMe status value ((SCT << 8) | SC) n sct_sc to a SCSI * status, sense_key, asc and ascq tuple. If successful returns true and * writes to non-NULL pointer arguments; otherwise returns false. */ bool sg_nvme_status2scsi(uint16_t sct_sc, uint8_t * status_p, uint8_t * sk_p, uint8_t * asc_p, uint8_t * ascq_p); extern FILE * sg_warnings_strm; void sg_set_warnings_strm(FILE * warnings_strm); /* The following "print" functions send ACSII to 'sg_warnings_strm' file * descriptor (default value is stderr). 'leadin' is string prepended to * each line printed out, NULL treated as "". */ void sg_print_command(const unsigned char * command); void sg_print_scsi_status(int scsi_status); /* 'leadin' is string prepended to each line printed out, NULL treated as * "". N.B. prior to sg3_utils v 1.42 'leadin' was only prepended to the * first line printed. */ void sg_print_sense(const char * leadin, const unsigned char * sense_buffer, int sb_len, bool raw_info); /* Following examines exit_status and outputs a clear error message to * warnings_strm (usually stderr) if one is known and returns true. * Otherwise it doesn't print anything and returns false. Note that if * exit_status==0 then returns true but prints nothing and if * exit_status<0 ("some error occurred") false is returned. If leadin is * non-NULL is will be printed before error message. */ bool sg_if_can2stderr(const char * leadin, int exit_status); /* Utilities can use these exit status values for syntax errors and * file (device node) problems (e.g. not found or permissions). */ #define SG_LIB_SYNTAX_ERROR 1 /* command line syntax problem */ #define SG_LIB_FILE_ERROR 15 /* device or other file problem */ /* The sg_err_category_sense() function returns one of the following. * These may be used as exit status values (from a process). Notice that * some of the lower values correspond to SCSI sense key values. */ #define SG_LIB_CAT_CLEAN 0 /* No errors or other information */ /* Value 1 left unused for utilities to use SG_LIB_SYNTAX_ERROR */ #define SG_LIB_CAT_NOT_READY 2 /* sense key, unit stopped? */ /* [sk,asc,ascq: 0x2,*,*] */ #define SG_LIB_CAT_MEDIUM_HARD 3 /* medium or hardware error, blank check */ /* [sk,asc,ascq: 0x3/0x4/0x8,*,*] */ #define SG_LIB_CAT_ILLEGAL_REQ 5 /* Illegal request (other than invalid */ /* opcode): [sk,asc,ascq: 0x5,*,*] */ #define SG_LIB_CAT_UNIT_ATTENTION 6 /* sense key, device state changed */ /* [sk,asc,ascq: 0x6,*,*] */ /* was SG_LIB_CAT_MEDIA_CHANGED earlier [sk,asc,ascq: 0x6,0x28,*] */ #define SG_LIB_CAT_DATA_PROTECT 7 /* sense key, media write protected? */ /* [sk,asc,ascq: 0x7,*,*] */ #define SG_LIB_CAT_INVALID_OP 9 /* (Illegal request,) Invalid opcode: */ /* [sk,asc,ascq: 0x5,0x20,0x0] */ #define SG_LIB_CAT_COPY_ABORTED 10 /* sense key, some data transferred */ /* [sk,asc,ascq: 0xa,*,*] */ #define SG_LIB_CAT_ABORTED_COMMAND 11 /* interpreted from sense buffer */ /* [sk,asc,ascq: 0xb,! 0x10,*] */ #define SG_LIB_CAT_MISCOMPARE 14 /* sense key, probably verify */ /* [sk,asc,ascq: 0xe,*,*] */ #define SG_LIB_CAT_NO_SENSE 20 /* sense data with key of "no sense" */ /* [sk,asc,ascq: 0x0,*,*] */ #define SG_LIB_CAT_RECOVERED 21 /* Successful command after recovered err */ /* [sk,asc,ascq: 0x1,*,*] */ #define SG_LIB_CAT_RES_CONFLICT SAM_STAT_RESERVATION_CONFLICT /* 24: this is a SCSI status, not sense. */ /* It indicates reservation by another */ /* machine blocks this command */ #define SG_LIB_CAT_CONDITION_MET 25 /* SCSI status, not sense key. */ /* Only from PRE-FETCH (SBC-4) */ #define SG_LIB_CAT_BUSY 26 /* SCSI status, not sense. Invites retry */ #define SG_LIB_CAT_TS_FULL 27 /* SCSI status, not sense. Wait then retry */ #define SG_LIB_CAT_ACA_ACTIVE 28 /* SCSI status; ACA seldom used */ #define SG_LIB_CAT_TASK_ABORTED 29 /* SCSI status, this command aborted by? */ #define SG_LIB_CAT_PROTECTION 40 /* subset of aborted command (for PI, DIF) */ /* [sk,asc,ascq: 0xb,0x10,*] */ #define SG_LIB_NVME_STATUS 48 /* NVMe Status Field (SF) other than 0 */ #define SG_LIB_WILD_RESID 49 /* Residual value for data-in transfer of a */ /* SCSI command is nonsensical */ #define SG_LIB_OS_BASE_ERR 50 /* in Linux: values found in: */ /* include/uapi/asm-generic/errno-base.h */ /* Example: ENOMEM reported as 62 (=50+12) */ #define SG_LIB_CAT_MALFORMED 97 /* Response to SCSI command malformed */ #define SG_LIB_CAT_SENSE 98 /* Something else is in the sense buffer */ #define SG_LIB_CAT_OTHER 99 /* Some other error/warning has occurred */ /* (e.g. a transport or driver error) */ /* Returns a SG_LIB_CAT_* value. If cannot decode sense_buffer or a less * common sense key then return SG_LIB_CAT_SENSE .*/ int sg_err_category_sense(const unsigned char * sense_buffer, int sb_len); /* Here are some additional sense data categories that are not returned * by sg_err_category_sense() but are returned by some related functions. */ #define SG_LIB_CAT_ILLEGAL_REQ_WITH_INFO 17 /* Illegal request (other than */ /* invalid opcode) plus 'info' field: */ /* [sk,asc,ascq: 0x5,*,*] */ #define SG_LIB_CAT_MEDIUM_HARD_WITH_INFO 18 /* medium or hardware error */ /* sense key plus 'info' field: */ /* [sk,asc,ascq: 0x3/0x4,*,*] */ #define SG_LIB_CAT_PROTECTION_WITH_INFO 41 /* aborted command sense key, */ /* protection plus 'info' field: */ /* [sk,asc,ascq: 0xb,0x10,*] */ #define SG_LIB_CAT_TIMEOUT 33 /* Yield string associated with sense category. Returns 'buff' (or pointer * to "Bad sense category" if 'buff' is NULL). If sense_cat unknown then * yield "Sense category: " string. */ const char * sg_get_category_sense_str(int sense_cat, int buff_len, char * buff, int verbose); /* Iterates to next designation descriptor in the device identification * VPD page. The 'initial_desig_desc' should point to start of first * descriptor with 'page_len' being the number of valid bytes in that * and following descriptors. To start, 'off' should point to a negative * value, thereafter it should point to the value yielded by the previous * call. If 0 returned then 'initial_desig_desc + *off' should be a valid * descriptor; returns -1 if normal end condition and -2 for an abnormal * termination. Matches association, designator_type and/or code_set when * any of those values are greater than or equal to zero. */ int sg_vpd_dev_id_iter(const unsigned char * initial_desig_desc, int page_len, int * off, int m_assoc, int m_desig_type, int m_code_set); /* <<< General purpose (i.e. not SCSI specific) utility functions >>> */ /* Always returns valid string even if errnum is wild (or library problem). * If errnum is negative, flip its sign. */ char * safe_strerror(int errnum); /* Print (to stdout) 'str' of bytes in hex, 16 bytes per line optionally * followed at the right hand side of the line with an ASCII interpretation. * Each line is prefixed with an address, starting at 0 for str[0]..str[15]. * All output numbers are in hex. 'no_ascii' allows for 3 output types: * > 0 each line has address then up to 16 ASCII-hex bytes * = 0 in addition, the bytes are listed in ASCII to the right * < 0 only the ASCII-hex bytes are listed (i.e. without address) */ void dStrHex(const char * str, int len, int no_ascii); /* Print (to sg_warnings_strm (stderr)) 'str' of bytes in hex, 16 bytes per * line optionally followed at right by its ASCII interpretation. Same * logic as dStrHex() with different output stream (i.e. stderr). */ void dStrHexErr(const char * str, int len, int no_ascii); /* Read 'len' bytes from 'str' and output as ASCII-Hex bytes (space * separated) to 'b' not to exceed 'b_len' characters. Each line * starts with 'leadin' (NULL for no leadin) and there are 16 bytes * per line with an extra space between the 8th and 9th bytes. 'format' * is 0 for repeat in printable ASCII ('.' for non printable chars) to * right of each line; 1 don't (so just output ASCII hex). Returns * number of bytes written to 'b' excluding the trailing '\0'. */ int dStrHexStr(const char * str, int len, const char * leadin, int format, int cb_len, char * cbp); /* The following 3 functions are equivalent to dStrHex(), dStrHexErr() and * dStrHexStr() respectively. The difference is the type of the first of * argument: uint8_t instead of char. The name of the argument is changed * to b_str to stress it is a pointer to the start of a binary string. */ void hex2stdout(const uint8_t * b_str, int len, int no_ascii); void hex2stderr(const uint8_t * b_str, int len, int no_ascii); int hex2str(const uint8_t * b_str, int len, const char * leadin, int format, int cb_len, char * cbp); /* Returns true when executed on big endian machine; else returns false. * Useful for displaying ATA identify words (which need swapping on a * big endian machine). */ bool sg_is_big_endian(); /* Returns true if byte sequence starting at bp with a length of b_len is * all zeros (for sg_all_zeros()) or all 0xff_s (for sg_all_ffs()); * otherwise returns false. If bp is NULL ir b_len <= 0 returns false. */ bool sg_all_zeros(const uint8_t * bp, int b_len); bool sg_all_ffs(const uint8_t * bp, int b_len); /* Extract character sequence from ATA words as in the model string * in a IDENTIFY DEVICE response. Returns number of characters * written to 'ochars' before 0 character is found or 'num' words * are processed. */ int sg_ata_get_chars(const uint16_t * word_arr, int start_word, int num_words, bool is_big_endian, char * ochars); /* Print (to stdout) 16 bit 'words' in hex, 8 words per line optionally * followed at the right hand side of the line with an ASCII interpretation * (pairs of ASCII characters in big endian order (upper first)). * Each line is prefixed with an address, starting at 0. * All output numbers are in hex. 'no_ascii' allows for 3 output types: * > 0 each line has address then up to 8 ASCII-hex words * = 0 in addition, the words are listed in ASCII pairs to the right * = -1 only the ASCII-hex words are listed (i.e. without address) * = -2 only the ASCII-hex words, formatted for "hdparm --Istdin" * < -2 same as -1 * If 'swapb' is true then bytes in each word swapped. Needs to be set * for ATA IDENTIFY DEVICE response on big-endian machines. */ void dWordHex(const uint16_t * words, int num, int no_ascii, bool swapb); /* If the number in 'buf' can not be decoded or the multiplier is unknown * then -1 is returned. Accepts a hex prefix (0x or 0X) or a 'h' (or 'H') * suffix. Otherwise a decimal multiplier suffix may be given. Recognised * multipliers: c C *1; w W *2; b B *512; k K KiB *1,024; * KB *1,000; m M MiB *1,048,576; MB *1,000,000; g G GiB *1,073,741,824; * GB *1,000,000,000 and x which multiplies by . Ignore leading * spaces and tabs; accept comma, hyphen, space, tab and hash as terminator. */ int sg_get_num(const char * buf); /* If the number in 'buf' can not be decoded then -1 is returned. Accepts a * hex prefix (0x or 0X) or a 'h' (or 'H') suffix; otherwise decimal is * assumed. Does not accept multipliers. Accept a comma (","), hyphen ("-"), * a whitespace or newline as terminator. Only decimal numbers can represent * negative numbers and '-1' must be treated separately. */ int sg_get_num_nomult(const char * buf); /* If the number in 'buf' can not be decoded or the multiplier is unknown * then -1LL is returned. Accepts a hex prefix (0x or 0X) or a 'h' (or 'H') * suffix. Otherwise a decimal multiplier suffix may be given. In addition * to supporting the multipliers of sg_get_num(), this function supports: * t T TiB *(2**40); TB *(10**12); p P PiB *(2**50); PB *(10**15) . * Ignore leading spaces and tabs; accept comma, hyphen, space, tab and hash * as terminator. */ int64_t sg_get_llnum(const char * buf); /* If the number in 'buf' can not be decoded then -1 is returned. Accepts a * hex prefix (0x or 0X) or a 'h' (or 'H') suffix; otherwise decimal is * assumed. Does not accept multipliers. Accept a comma (","), hyphen ("-"), * a whitespace or newline as terminator. Only decimal numbers can represent * negative numbers and '-1' must be treated separately. */ int64_t sg_get_llnum_nomult(const char * buf); /* Returns pointer to heap (or NULL) that is aligned to a align_to byte * boundary. Sends back *buff_to_free pointer in third argument that may be * different from the return value. If it is different then the *buff_to_free * pointer should be freed (rather than the returned value) when the heap is * no longer needed. If align_to is 0 then aligns to OS's page size. Sets all * returned heap to zeros. If num_bytes is 0 then set to page size. */ uint8_t * sg_memalign(uint32_t num_bytes, uint32_t align_to, uint8_t ** buff_to_free, bool vb); /* Returns OS page size in bytes. If uncertain returns 4096. */ uint32_t sg_get_page_size(void); /* If os_err_num is within bounds then the returned value is 'os_err_num + * SG_LIB_OS_BASE_ERR' otherwise -1 is returned. If os_err_num is 0 then 0 * is returned. */ int sg_convert_errno(int os_err_num); /* <<< Architectural support functions [is there a better place?] >>> */ /* Non Unix OSes distinguish between text and binary files. * Set text mode on fd. Does nothing in Unix. Returns negative number on * failure. */ int sg_set_text_mode(int fd); /* Set binary mode on fd. Does nothing in Unix. Returns negative number on * failure. */ int sg_set_binary_mode(int fd); #ifdef __cplusplus } #endif #endif /* SG_LIB_H */ f2fs-tools-1.11.0/tools/sg_write_buffer/include/sg_lib_data.h000066400000000000000000000077711332120552100241360ustar00rootroot00000000000000#ifndef SG_LIB_DATA_H #define SG_LIB_DATA_H /* * Copyright (c) 2007-2018 Douglas Gilbert. * All rights reserved. * Use of this source code is governed by a BSD-style * license that can be found in the BSD_LICENSE file. */ /* * This header file contains some structure declarations and array name * declarations which are defined in the sg_lib_data.c . * Typically this header does not need to be exposed to users of the * sg_lib interface declared in sg_libs.h . */ #include #ifdef __cplusplus extern "C" { #endif /* Operation codes with associated service actions that change or qualify * the command name */ #define SG_EXTENDED_COPY 0x83 /* since spc4r34 became next entry */ #define SG_3PARTY_COPY_OUT 0x83 /* new in spc4r34: Third party copy out */ #define SG_RECEIVE_COPY 0x84 /* since spc4r34 became next entry */ #define SG_3PARTY_COPY_IN 0x84 /* new in spc4r34: Third party copy in */ #define SG_MAINTENANCE_IN 0xa3 #define SG_MAINTENANCE_OUT 0xa4 #define SG_PERSISTENT_RESERVE_IN 0x5e #define SG_PERSISTENT_RESERVE_OUT 0x5f #define SG_READ_ATTRIBUTE 0x8c #define SG_READ_BUFFER 0x3c /* now READ BUFFER(10) */ #define SG_READ_BUFFER_16 0x9b #define SG_READ_POSITION 0x34 /* SSC command with service actions */ #define SG_SANITIZE 0x48 #define SG_SERVICE_ACTION_BIDI 0x9d #define SG_SERVICE_ACTION_IN_12 0xab #define SG_SERVICE_ACTION_IN_16 0x9e #define SG_SERVICE_ACTION_OUT_12 0xa9 #define SG_SERVICE_ACTION_OUT_16 0x9f #define SG_VARIABLE_LENGTH_CMD 0x7f #define SG_WRITE_BUFFER 0x3b #define SG_ZONING_OUT 0x94 #define SG_ZONING_IN 0x95 struct sg_lib_simple_value_name_t { int value; const char * name; }; struct sg_lib_value_name_t { int value; int peri_dev_type; /* 0 -> SPC and/or PDT_DISK, >0 -> PDT */ const char * name; }; struct sg_lib_asc_ascq_t { unsigned char asc; /* additional sense code */ unsigned char ascq; /* additional sense code qualifier */ const char * text; }; struct sg_lib_asc_ascq_range_t { unsigned char asc; /* additional sense code (ASC) */ unsigned char ascq_min; /* ASCQ minimum in range */ unsigned char ascq_max; /* ASCQ maximum in range */ const char * text; }; /* First use: SCSI status, sense_key, asc, ascq tuple */ struct sg_lib_4tuple_u8 { uint8_t t1; uint8_t t2; uint8_t t3; uint8_t t4; }; extern const char * sg_lib_version_str; extern struct sg_lib_value_name_t sg_lib_normal_opcodes[]; extern struct sg_lib_value_name_t sg_lib_read_buff_arr[]; extern struct sg_lib_value_name_t sg_lib_write_buff_arr[]; extern struct sg_lib_value_name_t sg_lib_maint_in_arr[]; extern struct sg_lib_value_name_t sg_lib_maint_out_arr[]; extern struct sg_lib_value_name_t sg_lib_pr_in_arr[]; extern struct sg_lib_value_name_t sg_lib_pr_out_arr[]; extern struct sg_lib_value_name_t sg_lib_sanitize_sa_arr[]; extern struct sg_lib_value_name_t sg_lib_serv_in12_arr[]; extern struct sg_lib_value_name_t sg_lib_serv_out12_arr[]; extern struct sg_lib_value_name_t sg_lib_serv_in16_arr[]; extern struct sg_lib_value_name_t sg_lib_serv_out16_arr[]; extern struct sg_lib_value_name_t sg_lib_serv_bidi_arr[]; extern struct sg_lib_value_name_t sg_lib_xcopy_sa_arr[]; extern struct sg_lib_value_name_t sg_lib_rec_copy_sa_arr[]; extern struct sg_lib_value_name_t sg_lib_variable_length_arr[]; extern struct sg_lib_value_name_t sg_lib_zoning_out_arr[]; extern struct sg_lib_value_name_t sg_lib_zoning_in_arr[]; extern struct sg_lib_value_name_t sg_lib_read_attr_arr[]; extern struct sg_lib_value_name_t sg_lib_read_pos_arr[]; extern struct sg_lib_asc_ascq_range_t sg_lib_asc_ascq_range[]; extern struct sg_lib_asc_ascq_t sg_lib_asc_ascq[]; extern struct sg_lib_value_name_t sg_lib_scsi_feature_sets[]; extern const char * sg_lib_sense_key_desc[]; extern const char * sg_lib_pdt_strs[]; extern const char * sg_lib_transport_proto_strs[]; extern int sg_lib_pdt_decay_arr[]; extern struct sg_lib_value_name_t sg_lib_nvme_cmd_status_arr[]; extern struct sg_lib_4tuple_u8 sg_lib_scsi_status_sense_arr[]; #ifdef __cplusplus } #endif #endif f2fs-tools-1.11.0/tools/sg_write_buffer/include/sg_linux_inc.h000066400000000000000000000036701332120552100243610ustar00rootroot00000000000000#ifndef SG_LINUX_INC_H #define SG_LINUX_INC_H #ifdef SG_KERNEL_INCLUDES #define __user typedef unsigned char u8; #include "/usr/src/linux/include/scsi/sg.h" #include "/usr/src/linux/include/scsi/scsi.h" #else #ifdef SG_TRICK_GNU_INCLUDES #include #include #else #include #include #endif #endif #ifdef BLKGETSIZE64 #ifndef u64 #include /* C99 header for exact integer types */ typedef uint64_t u64; /* problems with BLKGETSIZE64 ioctl in lk 2.4 */ #endif #endif /* Getting the correct include files for the sg interface can be an ordeal. In a perfect world, one would just write: #include #include This would include the files found in the /usr/include/scsi directory. Those files are maintained with the GNU library which may or may not agree with the kernel and version of sg driver that is running. Any many cases this will not matter. However in some it might, for example glibc 2.1's include files match the sg driver found in the lk 2.2 series. Hence if glibc 2.1 is used with lk 2.4 then the additional sg v3 interface will not be visible. If this is a problem then defining SG_KERNEL_INCLUDES will access the kernel supplied header files (assuming they are in the normal place). The GNU library maintainers and various kernel people don't like this approach (but it does work). The technique selected by defining SG_TRICK_GNU_INCLUDES worked (and was used) prior to glibc 2.2 . Prior to that version /usr/include/linux was a symbolic link to /usr/src/linux/include/linux . There are other approaches if this include "mixup" causes pain. These would involve include files being copied or symbolic links being introduced. Sorry about the inconvenience. Typically neither SG_KERNEL_INCLUDES nor SG_TRICK_GNU_INCLUDES is defined. dpg 20010415, 20030522 */ #endif f2fs-tools-1.11.0/tools/sg_write_buffer/include/sg_pr2serr.h000066400000000000000000000007671332120552100237740ustar00rootroot00000000000000#ifndef SG_PR2SERR_H #define SG_PR2SERR_H /* * Copyright (c) 2004-2018 Douglas Gilbert. * All rights reserved. * Use of this source code is governed by a BSD-style * license that can be found in the BSD_LICENSE file. */ #include #ifdef __cplusplus extern "C" { #endif #if defined(__GNUC__) || defined(__clang__) int pr2serr(const char * fmt, ...) __attribute__ ((format (printf, 1, 2))); #else int pr2serr(const char * fmt, ...); #endif #ifdef __cplusplus } #endif #endif f2fs-tools-1.11.0/tools/sg_write_buffer/include/sg_pt.h000066400000000000000000000234421332120552100230130ustar00rootroot00000000000000#ifndef SG_PT_H #define SG_PT_H /* * Copyright (c) 2005-2018 Douglas Gilbert. * All rights reserved. * Use of this source code is governed by a BSD-style * license that can be found in the BSD_LICENSE file. */ #include #include #ifdef __cplusplus extern "C" { #endif /* This declaration hides the fact that each implementation has its own * structure "derived" (using a C++ term) from this one. It compiles * because 'struct sg_pt_base' is only referenced (by pointer: 'objp') * in this interface. An instance of this structure represents the * context of one SCSI command. */ struct sg_pt_base; /* The format of the version string is like this: "2.01 20090201". * The leading digit will be incremented if this interface changes * in a way that may impact backward compatibility. */ const char * scsi_pt_version(); /* Returns >= 0 if successful. If error in Unix returns negated errno. */ int scsi_pt_open_device(const char * device_name, bool read_only, int verbose); /* Similar to scsi_pt_open_device() but takes Unix style open flags OR-ed * together. Returns valid file descriptor( >= 0 ) if successful, otherwise * returns -1 or a negated errno. * In Win32 O_EXCL translated to equivalent. */ int scsi_pt_open_flags(const char * device_name, int flags, int verbose); /* Returns 0 if successful. If error in Unix returns negated errno. */ int scsi_pt_close_device(int device_fd); /* Assumes dev_fd is an "open" file handle associated with device_name. If * the implementation (possibly for one OS) cannot determine from dev_fd if * a SCSI or NVMe pass-through is referenced, then it might guess based on * device_name. Returns 1 if SCSI generic pass-though device, returns 2 if * secondary SCSI pass-through device (in Linux a bsg device); returns 3 is * char NVMe device (i.e. no NSID); returns 4 if block NVMe device (includes * NSID), or 0 if something else (e.g. ATA block device) or dev_fd < 0. * If error, returns negated errno (operating system) value. */ int check_pt_file_handle(int dev_fd, const char * device_name, int verbose); /* Creates an object that can be used to issue one or more SCSI commands * (or task management functions). Returns NULL if problem. * Once this object has been created it should be destroyed with * destruct_scsi_pt_obj() when it is no longer needed. */ struct sg_pt_base * construct_scsi_pt_obj(void); /* An alternate way to create an object that can be used to issue one or * more SCSI commands (or task management functions). This variant * associate a device file descriptor (handle) with the object and a * verbose argument that causes error messages if errors occur. The * reason for this is to optionally allow the detection of NVMe devices * that will cause pt_device_is_nvme() to return true. Set dev_fd to * -1 if no open device file descriptor is available. Caller should * additionally call get_scsi_pt_os_err() after this call. */ struct sg_pt_base * construct_scsi_pt_obj_with_fd(int dev_fd, int verbose); /* Forget any previous dev_fd and install the one given. May attempt to * find file type (e.g. if pass-though) from OS so there could be an error. * Returns 0 for success or the same value as get_scsi_pt_os_err() * will return. dev_fd should be >= 0 for a valid file handle or -1 . */ int set_pt_file_handle(struct sg_pt_base * objp, int dev_fd, int verbose); /* Valid file handles (which is the return value) are >= 0 . Returns -1 * if there is no valid file handle. */ int get_pt_file_handle(const struct sg_pt_base * objp); /* Clear state information held in *objp . This allows this object to be * used to issue more than one SCSI command. The dev_fd is remembered. * Use set_pt_file_handle() to change dev_fd. */ void clear_scsi_pt_obj(struct sg_pt_base * objp); /* Set the CDB (command descriptor block) */ void set_scsi_pt_cdb(struct sg_pt_base * objp, const unsigned char * cdb, int cdb_len); /* Set the sense buffer and the maximum length that it can handle */ void set_scsi_pt_sense(struct sg_pt_base * objp, unsigned char * sense, int max_sense_len); /* Set a pointer and length to be used for data transferred from device */ void set_scsi_pt_data_in(struct sg_pt_base * objp, /* from device */ unsigned char * dxferp, int dxfer_ilen); /* Set a pointer and length to be used for data transferred to device */ void set_scsi_pt_data_out(struct sg_pt_base * objp, /* to device */ const unsigned char * dxferp, int dxfer_olen); /* Set a pointer and length to be used for metadata transferred to * (out_true=true) or from (out_true-false) device */ void set_pt_metadata_xfer(struct sg_pt_base * objp, unsigned char * mdxferp, uint32_t mdxfer_len, bool out_true); /* The following "set_"s implementations may be dummies */ void set_scsi_pt_packet_id(struct sg_pt_base * objp, int pack_id); void set_scsi_pt_tag(struct sg_pt_base * objp, uint64_t tag); void set_scsi_pt_task_management(struct sg_pt_base * objp, int tmf_code); void set_scsi_pt_task_attr(struct sg_pt_base * objp, int attribute, int priority); /* Following is a guard which is defined when set_scsi_pt_flags() is * present. Older versions of this library may not have this function. */ #define SCSI_PT_FLAGS_FUNCTION 1 /* If neither QUEUE_AT_HEAD nor QUEUE_AT_TAIL are given, or both * are given, use the pass-through default. */ #define SCSI_PT_FLAGS_QUEUE_AT_TAIL 0x10 #define SCSI_PT_FLAGS_QUEUE_AT_HEAD 0x20 /* Set (potentially OS dependent) flags for pass-through mechanism. * Apart from contradictions, flags can be OR-ed together. */ void set_scsi_pt_flags(struct sg_pt_base * objp, int flags); #define SCSI_PT_DO_START_OK 0 #define SCSI_PT_DO_BAD_PARAMS 1 #define SCSI_PT_DO_TIMEOUT 2 #define SCSI_PT_DO_NVME_STATUS 48 /* == SG_LIB_NVME_STATUS */ /* If OS error prior to or during command submission then returns negated * error value (e.g. Unix '-errno'). This includes interrupted system calls * (e.g. by a signal) in which case -EINTR would be returned. Note that * system call errors also can be fetched with get_scsi_pt_os_err(). * Return 0 if okay (i.e. at the very least: command sent). Positive * return values are errors (see SCSI_PT_DO_* defines). If a file descriptor * has already been provided by construct_scsi_pt_obj_with_fd() then the * given 'fd' can be -1 or the same value as given to the constructor. */ int do_scsi_pt(struct sg_pt_base * objp, int fd, int timeout_secs, int verbose); #define SCSI_PT_RESULT_GOOD 0 #define SCSI_PT_RESULT_STATUS 1 /* other than GOOD and CHECK CONDITION */ #define SCSI_PT_RESULT_SENSE 2 #define SCSI_PT_RESULT_TRANSPORT_ERR 3 #define SCSI_PT_RESULT_OS_ERR 4 /* highest numbered applicable category returned */ int get_scsi_pt_result_category(const struct sg_pt_base * objp); /* If not available return 0 which implies there is no residual * value. If supported the number of bytes actually sent back by * the device is 'dxfer_ilen - get_scsi_pt_len()' bytes. */ int get_scsi_pt_resid(const struct sg_pt_base * objp); /* Returns SCSI status value (from device that received the command). If an * NVMe command was issued directly (i.e. through do_scsi_pt() then return * NVMe status (i.e. ((SCT << 8) | SC)) */ int get_scsi_pt_status_response(const struct sg_pt_base * objp); /* Returns SCSI status value or, if NVMe command given to do_scsi_pt(), * then returns NVMe result (i.e. DWord(0) from completion queue). If * 'objp' is NULL then returns 0xffffffff. */ uint32_t get_pt_result(const struct sg_pt_base * objp); /* Actual sense length returned. If sense data is present but actual sense length is not known, return 'max_sense_len' */ int get_scsi_pt_sense_len(const struct sg_pt_base * objp); /* If not available return 0 (for success). */ int get_scsi_pt_os_err(const struct sg_pt_base * objp); char * get_scsi_pt_os_err_str(const struct sg_pt_base * objp, int max_b_len, char * b); /* If not available return 0 (for success) */ int get_scsi_pt_transport_err(const struct sg_pt_base * objp); void set_scsi_pt_transport_err(struct sg_pt_base * objp, int err); char * get_scsi_pt_transport_err_str(const struct sg_pt_base * objp, int max_b_len, char * b); /* If not available return -1 */ int get_scsi_pt_duration_ms(const struct sg_pt_base * objp); /* Return true if device associated with 'objp' uses NVMe command set. To * be useful (in modifying the type of command sent (SCSI or NVMe) then * construct_scsi_pt_obj_with_fd() should be used followed by an invocation * of this function. */ bool pt_device_is_nvme(const struct sg_pt_base * objp); /* If a NVMe block device (which includes the NSID) handle is associated * with 'objp', then its NSID is returned (values range from 0x1 to * 0xffffffe). Otherwise 0 is returned. */ uint32_t get_pt_nvme_nsid(const struct sg_pt_base * objp); /* Should be invoked once per objp after other processing is complete in * order to clean up resources. For ever successful construct_scsi_pt_obj() * call there should be one destruct_scsi_pt_obj(). If the * construct_scsi_pt_obj_with_fd() function was used to create this object * then the dev_fd provided to that constructor is not altered by this * destructor. So the user should still close dev_fd (perhaps with * scsi_pt_close_device() ). */ void destruct_scsi_pt_obj(struct sg_pt_base * objp); #ifdef SG_LIB_WIN32 #define SG_LIB_WIN32_DIRECT 1 /* Request SPT direct interface when state_direct is 1, state_direct set * to 0 for the SPT indirect interface. Default setting selected by build * (i.e. library compile time) and is usually indirect. */ void scsi_pt_win32_direct(int state_direct); /* Returns current SPT interface state, 1 for direct, 0 for indirect */ int scsi_pt_win32_spt_state(void); #endif #ifdef __cplusplus } #endif #endif f2fs-tools-1.11.0/tools/sg_write_buffer/include/sg_pt_linux.h000066400000000000000000000142441332120552100242320ustar00rootroot00000000000000#ifndef SG_PT_LINUX_H #define SG_PT_LINUX_H /* * Copyright (c) 2017 Douglas Gilbert. * All rights reserved. * Use of this source code is governed by a BSD-style * license that can be found in the BSD_LICENSE file. */ #include #include #include #include "sg_pt_nvme.h" /* This header is for internal use by the sg3_utils library (libsgutils) * and is Linux specific. Best not to include it directly in code that * is meant to be OS independent. */ #ifdef __cplusplus extern "C" { #endif #ifndef HAVE_LINUX_BSG_H #define BSG_PROTOCOL_SCSI 0 #define BSG_SUB_PROTOCOL_SCSI_CMD 0 #define BSG_SUB_PROTOCOL_SCSI_TMF 1 #define BSG_SUB_PROTOCOL_SCSI_TRANSPORT 2 /* * For flag constants below: * sg.h sg_io_hdr also has bits defined for it's flags member. These * two flag values (0x10 and 0x20) have the same meaning in sg.h . For * bsg the BSG_FLAG_Q_AT_HEAD flag is ignored since it is the default. */ #define BSG_FLAG_Q_AT_TAIL 0x10 /* default is Q_AT_HEAD */ #define BSG_FLAG_Q_AT_HEAD 0x20 struct sg_io_v4 { __s32 guard; /* [i] 'Q' to differentiate from v3 */ __u32 protocol; /* [i] 0 -> SCSI , .... */ __u32 subprotocol; /* [i] 0 -> SCSI command, 1 -> SCSI task management function, .... */ __u32 request_len; /* [i] in bytes */ __u64 request; /* [i], [*i] {SCSI: cdb} */ __u64 request_tag; /* [i] {SCSI: task tag (only if flagged)} */ __u32 request_attr; /* [i] {SCSI: task attribute} */ __u32 request_priority; /* [i] {SCSI: task priority} */ __u32 request_extra; /* [i] {spare, for padding} */ __u32 max_response_len; /* [i] in bytes */ __u64 response; /* [i], [*o] {SCSI: (auto)sense data} */ /* "dout_": data out (to device); "din_": data in (from device) */ __u32 dout_iovec_count; /* [i] 0 -> "flat" dout transfer else dout_xfer points to array of iovec */ __u32 dout_xfer_len; /* [i] bytes to be transferred to device */ __u32 din_iovec_count; /* [i] 0 -> "flat" din transfer */ __u32 din_xfer_len; /* [i] bytes to be transferred from device */ __u64 dout_xferp; /* [i], [*i] */ __u64 din_xferp; /* [i], [*o] */ __u32 timeout; /* [i] units: millisecond */ __u32 flags; /* [i] bit mask */ __u64 usr_ptr; /* [i->o] unused internally */ __u32 spare_in; /* [i] */ __u32 driver_status; /* [o] 0 -> ok */ __u32 transport_status; /* [o] 0 -> ok */ __u32 device_status; /* [o] {SCSI: command completion status} */ __u32 retry_delay; /* [o] {SCSI: status auxiliary information} */ __u32 info; /* [o] additional information */ __u32 duration; /* [o] time to complete, in milliseconds */ __u32 response_len; /* [o] bytes of response actually written */ __s32 din_resid; /* [o] din_xfer_len - actual_din_xfer_len */ __s32 dout_resid; /* [o] dout_xfer_len - actual_dout_xfer_len */ __u64 generated_tag; /* [o] {SCSI: transport generated task tag} */ __u32 spare_out; /* [o] */ __u32 padding; }; #else #include #endif struct sg_pt_linux_scsi { struct sg_io_v4 io_hdr; /* use v4 header as it is more general */ /* Leave io_hdr in first place of this structure */ bool is_sg; bool is_bsg; bool is_nvme; /* OS device type, if false ignore nvme_direct */ bool nvme_direct; /* false: our SNTL; true: received NVMe command */ bool mdxfer_out; /* direction of metadata xfer, true->data-out */ bool scsi_dsense; /* SCSI descriptor sense active when true */ int dev_fd; /* -1 if not given (yet) */ int in_err; int os_err; uint32_t nvme_nsid; /* 1 to 0xfffffffe are possibly valid, 0 * implies dev_fd is not a NVMe device * (is_nvme=false) or it is a NVMe char * device (e.g. /dev/nvme0 ) */ uint32_t nvme_result; /* DW0 from completion queue */ uint32_t nvme_status; /* SCT|SC: DW3 27:17 from completion queue, * note: the DNR+More bit are not there. * The whole 16 byte completion q entry is * sent back as sense data */ uint32_t mdxfer_len; void * mdxferp; uint8_t * nvme_id_ctlp; /* cached response to controller IDENTIFY */ uint8_t * free_nvme_id_ctlp; unsigned char tmf_request[4]; }; struct sg_pt_base { struct sg_pt_linux_scsi impl; }; #ifndef sg_nvme_admin_cmd #define sg_nvme_admin_cmd sg_nvme_passthru_cmd #endif /* Linux NVMe related ioctls */ #ifndef NVME_IOCTL_ID #define NVME_IOCTL_ID _IO('N', 0x40) #endif #ifndef NVME_IOCTL_ADMIN_CMD #define NVME_IOCTL_ADMIN_CMD _IOWR('N', 0x41, struct sg_nvme_admin_cmd) #endif #ifndef NVME_IOCTL_SUBMIT_IO #define NVME_IOCTL_SUBMIT_IO _IOW('N', 0x42, struct sg_nvme_user_io) #endif #ifndef NVME_IOCTL_IO_CMD #define NVME_IOCTL_IO_CMD _IOWR('N', 0x43, struct sg_nvme_passthru_cmd) #endif #ifndef NVME_IOCTL_RESET #define NVME_IOCTL_RESET _IO('N', 0x44) #endif #ifndef NVME_IOCTL_SUBSYS_RESET #define NVME_IOCTL_SUBSYS_RESET _IO('N', 0x45) #endif extern bool sg_bsg_nvme_char_major_checked; extern int sg_bsg_major; extern volatile int sg_nvme_char_major; extern long sg_lin_page_size; void sg_find_bsg_nvme_char_major(int verbose); int sg_do_nvme_pt(struct sg_pt_base * vp, int fd, int time_secs, int vb); /* This trims given NVMe block device name in Linux (e.g. /dev/nvme0n1p5) * to the name of its associated char device (e.g. /dev/nvme0). If this * occurs true is returned and the char device name is placed in 'b' (as * long as b_len is sufficient). Otherwise false is returned. */ bool sg_get_nvme_char_devname(const char * nvme_block_devname, uint32_t b_len, char * b); #ifdef __cplusplus } #endif #endif /* end of SG_PT_LINUX_H */ f2fs-tools-1.11.0/tools/sg_write_buffer/include/sg_pt_nvme.h000066400000000000000000000131371332120552100240400ustar00rootroot00000000000000#ifndef SG_PT_NVME_H #define SG_PT_NVME_H /* * Copyright (c) 2017-2018 Douglas Gilbert. * All rights reserved. * Use of this source code is governed by a BSD-style * license that can be found in the BSD_LICENSE file. */ #include #include #ifdef __cplusplus extern "C" { #endif /* structures copied and slightly modified from which * is Copyright (c) 2011-2014, Intel Corporation. */ /* Note that the command input structure is in (packed) "cpu" format. That * means, for example, if the CPU is little endian (most are) then so is the * structure. However what comes out in the data-in buffer (e.g. for the * Admin Identify command response) is almost all little endian following ATA * (but no SCSI and IP which are big endian) and Intel's preference. There * are exceptions, for example the EUI-64 identifiers in the Admin Identify * response are big endian. * * Code online (e.g. nvme-cli at github.com) seems to like packed strcutures, * the author prefers byte offset plus a range of unaligned integer builders * such as those in sg_unaligned.h . */ #ifdef __GNUC__ #ifndef __clang__ struct __attribute__((__packed__)) sg_nvme_user_io #else struct sg_nvme_user_io #endif #else struct sg_nvme_user_io #endif { uint8_t opcode; uint8_t flags; uint16_t control; uint16_t nblocks; uint16_t rsvd; uint64_t metadata; uint64_t addr; uint64_t slba; uint32_t dsmgmt; uint32_t reftag; uint16_t apptag; uint16_t appmask; } #ifdef SG_LIB_FREEBSD __packed; #else ; #endif /* Using byte offsets and unaligned be/le copies safer than packed * structures. These are for sg_nvme_user_io . */ #define SG_NVME_IO_OPCODE 0 #define SG_NVME_IO_FLAGS 1 #define SG_NVME_IO_CONTROL 2 #define SG_NVME_IO_NBLOCKS 4 #define SG_NVME_IO_RSVD 6 #define SG_NVME_IO_METADATA 8 #define SG_NVME_IO_ADDR 16 #define SG_NVME_IO_SLBA 24 #define SG_NVME_IO_DSMGMT 32 #define SG_NVME_IO_REFTAG 36 #define SG_NVME_IO_APPTAG 40 #define SG_NVME_IO_APPMASK 42 #ifdef __GNUC__ #ifndef __clang__ struct __attribute__((__packed__)) sg_nvme_passthru_cmd #else struct sg_nvme_passthru_cmd #endif #else struct sg_nvme_passthru_cmd #endif { uint8_t opcode; uint8_t flags; uint16_t rsvd1; uint32_t nsid; uint32_t cdw2; uint32_t cdw3; uint64_t metadata; uint64_t addr; uint32_t metadata_len; uint32_t data_len; uint32_t cdw10; uint32_t cdw11; uint32_t cdw12; uint32_t cdw13; uint32_t cdw14; uint32_t cdw15; #ifdef SG_LIB_LINUX uint32_t timeout_ms; uint32_t result; /* out: DWord(0) from completion queue */ #endif } #ifdef SG_LIB_FREEBSD __packed; #else ; #endif /* Using byte offsets and unaligned be/le copies safer than packed * structures. These are for sg_nvme_passthru_cmd . */ #define SG_NVME_PT_OPCODE 0 /* length: 1 byte */ #define SG_NVME_PT_FLAGS 1 /* length: 1 byte */ #define SG_NVME_PT_RSVD1 2 /* length: 2 bytes */ #define SG_NVME_PT_NSID 4 /* length: 4 bytes */ #define SG_NVME_PT_CDW2 8 /* length: 4 bytes */ #define SG_NVME_PT_CDW3 12 /* length: 4 bytes */ #define SG_NVME_PT_METADATA 16 /* length: 8 bytes */ #define SG_NVME_PT_ADDR 24 /* length: 8 bytes */ #define SG_NVME_PT_METADATA_LEN 32 /* length: 4 bytes */ #define SG_NVME_PT_DATA_LEN 36 /* length: 4 bytes */ #define SG_NVME_PT_CDW10 40 /* length: 4 bytes */ #define SG_NVME_PT_CDW11 44 /* length: 4 bytes */ #define SG_NVME_PT_CDW12 48 /* length: 4 bytes */ #define SG_NVME_PT_CDW13 52 /* length: 4 bytes */ #define SG_NVME_PT_CDW14 56 /* length: 4 bytes */ #define SG_NVME_PT_CDW15 60 /* length: 4 bytes */ #ifdef SG_LIB_LINUX /* General references state that "all NVMe commands are 64 bytes long". If * so then the following are add-ons by Linux, go to the OS and not the * the NVMe device. */ #define SG_NVME_PT_TIMEOUT_MS 64 /* length: 4 bytes */ #define SG_NVME_PT_RESULT 68 /* length: 4 bytes */ #endif /* Byte offset of Result and Status (plus phase bit) in CQ */ #define SG_NVME_PT_CQ_RESULT 0 /* CDW0, length: 4 bytes */ #define SG_NVME_PT_CQ_DW0 0 /* CDW0, length: 4 bytes */ #define SG_NVME_PT_CQ_DW1 4 /* CDW1, length: 4 bytes */ #define SG_NVME_PT_CQ_DW2 8 /* CDW2, length: 4 bytes */ #define SG_NVME_PT_CQ_DW3 12 /* CDW3, length: 4 bytes */ #define SG_NVME_PT_CQ_STATUS_P 14 /* CDW3 31:16, length: 2 bytes */ /* Valid namespace IDs (nsid_s) range from 1 to 0xfffffffe, leaving: */ #define SG_NVME_BROADCAST_NSID 0xffffffff /* all namespaces */ #define SG_NVME_CTL_NSID 0x0 /* the "controller's" namespace */ /* Given the NVMe Identify Controller response and optionally the NVMe * Identify Namespace response (NULL otherwise), generate the SCSI VPD * page 0x83 (device identification) descriptor(s) in dop. Return the * number of bytes written which will not exceed max_do_len. Probably use * Peripheral Device Type (pdt) of 0 (disk) for don't know. Transport * protocol (tproto) should be -1 if not known, else SCSI value. * N.B. Does not write total VPD page length into dop[2:3] . */ int sg_make_vpd_devid_for_nvme(const uint8_t * nvme_id_ctl_p, const uint8_t * nvme_id_ns_p, int pdt, int tproto, uint8_t * dop, int max_do_len); #ifdef __cplusplus } #endif #endif /* SG_PT_NVME_H */ f2fs-tools-1.11.0/tools/sg_write_buffer/include/sg_pt_win32.h000066400000000000000000000354171332120552100240420ustar00rootroot00000000000000#ifndef SG_PT_WIN32_H #define SG_PT_WIN32_H /* * The information in this file was obtained from scsi-wnt.h by * Richard Stemmer, rs@epost.de . He in turn gives credit to * Jay A. Key (for scsipt.c). * The plscsi program (by Pat LaVarre ) has * also been used as a reference. * Much of the information in this header can also be obtained * from msdn.microsoft.com . * Updated for cygwin version 1.7.17 changes 20121026 */ /* WIN32_LEAN_AND_MEAN may be required to prevent inclusion of */ #define WIN32_LEAN_AND_MEAN #include #ifdef __cplusplus extern "C" { #endif #define SCSI_MAX_SENSE_LEN 64 #define SCSI_MAX_CDB_LEN 16 #define SCSI_MAX_INDIRECT_DATA 16384 typedef struct { USHORT Length; UCHAR ScsiStatus; UCHAR PathId; UCHAR TargetId; UCHAR Lun; UCHAR CdbLength; UCHAR SenseInfoLength; UCHAR DataIn; ULONG DataTransferLength; ULONG TimeOutValue; ULONG_PTR DataBufferOffset; /* was ULONG; problem in 64 bit */ ULONG SenseInfoOffset; UCHAR Cdb[SCSI_MAX_CDB_LEN]; } SCSI_PASS_THROUGH, *PSCSI_PASS_THROUGH; typedef struct { USHORT Length; UCHAR ScsiStatus; UCHAR PathId; UCHAR TargetId; UCHAR Lun; UCHAR CdbLength; UCHAR SenseInfoLength; UCHAR DataIn; ULONG DataTransferLength; ULONG TimeOutValue; PVOID DataBuffer; ULONG SenseInfoOffset; UCHAR Cdb[SCSI_MAX_CDB_LEN]; } SCSI_PASS_THROUGH_DIRECT, *PSCSI_PASS_THROUGH_DIRECT; typedef struct { SCSI_PASS_THROUGH spt; /* plscsi shows a follow on 16 bytes allowing 32 byte cdb */ ULONG Filler; UCHAR ucSenseBuf[SCSI_MAX_SENSE_LEN]; UCHAR ucDataBuf[SCSI_MAX_INDIRECT_DATA]; } SCSI_PASS_THROUGH_WITH_BUFFERS, *PSCSI_PASS_THROUGH_WITH_BUFFERS; typedef struct { SCSI_PASS_THROUGH_DIRECT spt; ULONG Filler; UCHAR ucSenseBuf[SCSI_MAX_SENSE_LEN]; } SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER, *PSCSI_PASS_THROUGH_DIRECT_WITH_BUFFER; typedef struct { UCHAR NumberOfLogicalUnits; UCHAR InitiatorBusId; ULONG InquiryDataOffset; } SCSI_BUS_DATA, *PSCSI_BUS_DATA; typedef struct { UCHAR NumberOfBusses; SCSI_BUS_DATA BusData[1]; } SCSI_ADAPTER_BUS_INFO, *PSCSI_ADAPTER_BUS_INFO; typedef struct { UCHAR PathId; UCHAR TargetId; UCHAR Lun; BOOLEAN DeviceClaimed; ULONG InquiryDataLength; ULONG NextInquiryDataOffset; UCHAR InquiryData[1]; } SCSI_INQUIRY_DATA, *PSCSI_INQUIRY_DATA; typedef struct { ULONG Length; UCHAR PortNumber; UCHAR PathId; UCHAR TargetId; UCHAR Lun; } SCSI_ADDRESS, *PSCSI_ADDRESS; /* * Standard IOCTL define */ #ifndef CTL_CODE #define CTL_CODE(DevType, Function, Method, Access) \ (((DevType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method)) #endif /* * file access values */ #ifndef FILE_ANY_ACCESS #define FILE_ANY_ACCESS 0 #endif #ifndef FILE_READ_ACCESS #define FILE_READ_ACCESS 0x0001 #endif #ifndef FILE_WRITE_ACCESS #define FILE_WRITE_ACCESS 0x0002 #endif // IOCTL_STORAGE_QUERY_PROPERTY #define FILE_DEVICE_MASS_STORAGE 0x0000002d #define IOCTL_STORAGE_BASE FILE_DEVICE_MASS_STORAGE #define FILE_ANY_ACCESS 0 // #define METHOD_BUFFERED 0 #define IOCTL_STORAGE_QUERY_PROPERTY \ CTL_CODE(IOCTL_STORAGE_BASE, 0x0500, METHOD_BUFFERED, FILE_ANY_ACCESS) #ifndef _DEVIOCTL_ typedef enum _STORAGE_BUS_TYPE { BusTypeUnknown = 0x00, BusTypeScsi = 0x01, BusTypeAtapi = 0x02, BusTypeAta = 0x03, BusType1394 = 0x04, BusTypeSsa = 0x05, BusTypeFibre = 0x06, BusTypeUsb = 0x07, BusTypeRAID = 0x08, BusTypeiScsi = 0x09, BusTypeSas = 0x0A, BusTypeSata = 0x0B, BusTypeSd = 0x0C, BusTypeMmc = 0x0D, BusTypeVirtual = 0xE, BusTypeFileBackedVirtual = 0xF, BusTypeSpaces = 0x10, BusTypeNvme = 0x11, BusTypeSCM = 0x12, BusTypeUfs = 0x13, BusTypeMax = 0x14, BusTypeMaxReserved = 0x7F } STORAGE_BUS_TYPE, *PSTORAGE_BUS_TYPE; typedef enum _STORAGE_PROTOCOL_TYPE { ProtocolTypeUnknown = 0, ProtocolTypeScsi, ProtocolTypeAta, ProtocolTypeNvme, ProtocolTypeSd } STORAGE_PROTOCOL_TYPE; typedef enum _STORAGE_PROTOCOL_NVME_DATA_TYPE { NVMeDataTypeUnknown = 0, NVMeDataTypeIdentify, NVMeDataTypeLogPage, NVMeDataTypeFeature } STORAGE_PROTOCOL_NVME_DATA_TYPE; typedef struct _STORAGE_PROTOCOL_SPECIFIC_DATA { STORAGE_PROTOCOL_TYPE ProtocolType; ULONG DataType; ULONG ProtocolDataRequestValue; ULONG ProtocolDataRequestSubValue; ULONG ProtocolDataOffset; ULONG ProtocolDataLength; ULONG FixedProtocolReturnData; ULONG Reserved[3]; } STORAGE_PROTOCOL_SPECIFIC_DATA; typedef struct _STORAGE_DEVICE_DESCRIPTOR { ULONG Version; ULONG Size; UCHAR DeviceType; UCHAR DeviceTypeModifier; BOOLEAN RemovableMedia; BOOLEAN CommandQueueing; ULONG VendorIdOffset; /* 0 if not available */ ULONG ProductIdOffset; /* 0 if not available */ ULONG ProductRevisionOffset;/* 0 if not available */ ULONG SerialNumberOffset; /* -1 if not available ?? */ STORAGE_BUS_TYPE BusType; ULONG RawPropertiesLength; UCHAR RawDeviceProperties[1]; } STORAGE_DEVICE_DESCRIPTOR, *PSTORAGE_DEVICE_DESCRIPTOR; #define STORAGE_PROTOCOL_STRUCTURE_VERSION 0x1 #define IOCTL_STORAGE_PROTOCOL_COMMAND \ CTL_CODE(IOCTL_STORAGE_BASE, 0x04F0, METHOD_BUFFERED, \ FILE_READ_ACCESS | FILE_WRITE_ACCESS) typedef struct _STORAGE_PROTOCOL_COMMAND { DWORD Version; /* STORAGE_PROTOCOL_STRUCTURE_VERSION */ DWORD Length; STORAGE_PROTOCOL_TYPE ProtocolType; DWORD Flags; DWORD ReturnStatus; DWORD ErrorCode; DWORD CommandLength; DWORD ErrorInfoLength; DWORD DataToDeviceTransferLength; DWORD DataFromDeviceTransferLength; DWORD TimeOutValue; DWORD ErrorInfoOffset; DWORD DataToDeviceBufferOffset; DWORD DataFromDeviceBufferOffset; DWORD CommandSpecific; DWORD Reserved0; DWORD FixedProtocolReturnData; DWORD Reserved1[3]; BYTE Command[1]; /* has CommandLength elements */ } STORAGE_PROTOCOL_COMMAND, *PSTORAGE_PROTOCOL_COMMAND; #endif /* _DEVIOCTL_ */ typedef struct _STORAGE_DEVICE_UNIQUE_IDENTIFIER { ULONG Version; ULONG Size; ULONG StorageDeviceIdOffset; ULONG StorageDeviceOffset; ULONG DriveLayoutSignatureOffset; } STORAGE_DEVICE_UNIQUE_IDENTIFIER, *PSTORAGE_DEVICE_UNIQUE_IDENTIFIER; // Use CompareStorageDuids(PSTORAGE_DEVICE_UNIQUE_IDENTIFIER duid1, duid2) // to test for equality #ifndef _DEVIOCTL_ typedef enum _STORAGE_QUERY_TYPE { PropertyStandardQuery = 0, PropertyExistsQuery, PropertyMaskQuery, PropertyQueryMaxDefined } STORAGE_QUERY_TYPE, *PSTORAGE_QUERY_TYPE; typedef enum _STORAGE_PROPERTY_ID { StorageDeviceProperty = 0, StorageAdapterProperty, StorageDeviceIdProperty, StorageDeviceUniqueIdProperty, StorageDeviceWriteCacheProperty, StorageMiniportProperty, StorageAccessAlignmentProperty, /* Identify controller goes to adapter; Identify namespace to device */ StorageAdapterProtocolSpecificProperty = 49, StorageDeviceProtocolSpecificProperty = 50 } STORAGE_PROPERTY_ID, *PSTORAGE_PROPERTY_ID; typedef struct _STORAGE_PROPERTY_QUERY { STORAGE_PROPERTY_ID PropertyId; STORAGE_QUERY_TYPE QueryType; UCHAR AdditionalParameters[1]; } STORAGE_PROPERTY_QUERY, *PSTORAGE_PROPERTY_QUERY; typedef struct _STORAGE_PROTOCOL_DATA_DESCRIPTOR { DWORD Version; DWORD Size; STORAGE_PROTOCOL_SPECIFIC_DATA ProtocolSpecificData; } STORAGE_PROTOCOL_DATA_DESCRIPTOR, *PSTORAGE_PROTOCOL_DATA_DESCRIPTOR; // Command completion status // The "Phase Tag" field and "Status Field" are separated in spec. We define // them in the same data structure to ease the memory access from software. // typedef union { struct { USHORT P : 1; // Phase Tag (P) USHORT SC : 8; // Status Code (SC) USHORT SCT : 3; // Status Code Type (SCT) USHORT Reserved : 2; USHORT M : 1; // More (M) USHORT DNR : 1; // Do Not Retry (DNR) } DUMMYSTRUCTNAME; USHORT AsUshort; } NVME_COMMAND_STATUS, *PNVME_COMMAND_STATUS; // Information of log: NVME_LOG_PAGE_ERROR_INFO. Size: 64 bytes // typedef struct { ULONGLONG ErrorCount; USHORT SQID; // Submission Queue ID USHORT CMDID; // Command ID NVME_COMMAND_STATUS Status; // Status Field: This field indicates the // Status Field for the command that // completed. The Status Field is located in // bits 15:01, bit 00 corresponds to the Phase // Tag posted for the command. struct { USHORT Byte : 8; // Byte in command that contained error USHORT Bit : 3; // Bit in command that contained error USHORT Reserved : 5; } ParameterErrorLocation; ULONGLONG Lba; // LBA: This field indicates the first LBA // that experienced the error condition, if // applicable. ULONG NameSpace; // Namespace: This field indicates the nsid // that the error is associated with, if // applicable. UCHAR VendorInfoAvailable; // Vendor Specific Information Available UCHAR Reserved0[3]; ULONGLONG CommandSpecificInfo; // This field contains command specific // information. If used, the command // definition specifies the information // returned. UCHAR Reserved1[24]; } NVME_ERROR_INFO_LOG, *PNVME_ERROR_INFO_LOG; typedef struct { ULONG DW0; ULONG Reserved; union { struct { USHORT SQHD; // SQ Head Pointer (SQHD) USHORT SQID; // SQ Identifier (SQID) } DUMMYSTRUCTNAME; ULONG AsUlong; } DW2; union { struct { USHORT CID; // Command Identifier (CID) NVME_COMMAND_STATUS Status; } DUMMYSTRUCTNAME; ULONG AsUlong; } DW3; } NVME_COMPLETION_ENTRY, *PNVME_COMPLETION_ENTRY; // Bit-mask values for STORAGE_PROTOCOL_COMMAND - "Flags" field. // // Flag indicates the request targeting to adapter instead of device. #define STORAGE_PROTOCOL_COMMAND_FLAG_ADAPTER_REQUEST 0x80000000 // // Status values for STORAGE_PROTOCOL_COMMAND - "ReturnStatus" field. // #define STORAGE_PROTOCOL_STATUS_PENDING 0x0 #define STORAGE_PROTOCOL_STATUS_SUCCESS 0x1 #define STORAGE_PROTOCOL_STATUS_ERROR 0x2 #define STORAGE_PROTOCOL_STATUS_INVALID_REQUEST 0x3 #define STORAGE_PROTOCOL_STATUS_NO_DEVICE 0x4 #define STORAGE_PROTOCOL_STATUS_BUSY 0x5 #define STORAGE_PROTOCOL_STATUS_DATA_OVERRUN 0x6 #define STORAGE_PROTOCOL_STATUS_INSUFFICIENT_RESOURCES 0x7 #define STORAGE_PROTOCOL_STATUS_NOT_SUPPORTED 0xFF // Command Length for Storage Protocols. // // NVMe commands are always 64 bytes. #define STORAGE_PROTOCOL_COMMAND_LENGTH_NVME 0x40 // Command Specific Information for Storage Protocols - CommandSpecific field // #define STORAGE_PROTOCOL_SPECIFIC_NVME_ADMIN_COMMAND 0x01 #define STORAGE_PROTOCOL_SPECIFIC_NVME_NVM_COMMAND 0x02 #endif /* _DEVIOCTL_ */ // NVME_PASS_THROUGH #ifndef STB_IO_CONTROL typedef struct _SRB_IO_CONTROL { ULONG HeaderLength; UCHAR Signature[8]; ULONG Timeout; ULONG ControlCode; ULONG ReturnCode; ULONG Length; } SRB_IO_CONTROL, *PSRB_IO_CONTROL; #endif #ifndef NVME_PASS_THROUGH_SRB_IO_CODE #define NVME_SIG_STR "NvmeMini" #define NVME_STORPORT_DRIVER 0xe000 #define NVME_PASS_THROUGH_SRB_IO_CODE \ CTL_CODE(NVME_STORPORT_DRIVER, 0x0800, METHOD_BUFFERED, FILE_ANY_ACCESS) #pragma pack(1) /* Following is pre-Win10; used with DeviceIoControl(IOCTL_SCSI_MINIPORT), * in Win10 need DeviceIoControl(IOCTL_STORAGE_PROTOCOL_COMMAND) for pure * pass-through. Win10 also has "Protocol specific queries" for things like * Identify and Get feature. */ typedef struct _NVME_PASS_THROUGH_IOCTL { SRB_IO_CONTROL SrbIoCtrl; ULONG VendorSpecific[6]; ULONG NVMeCmd[16]; /* Command DW[0...15] */ ULONG CplEntry[4]; /* Completion DW[0...3] */ ULONG Direction; /* 0=None, 1=Out, 2=In, 3=I/O */ ULONG QueueId; /* 0=AdminQ */ ULONG DataBufferLen; /* sizeof(DataBuffer) if Data In */ ULONG MetaDataLen; ULONG ReturnBufferLen; /* offsetof(DataBuffer), plus * sizeof(DataBuffer) if Data Out */ UCHAR DataBuffer[1]; } NVME_PASS_THROUGH_IOCTL; #pragma pack() #endif // NVME_PASS_THROUGH_SRB_IO_CODE /* * method codes */ #define METHOD_BUFFERED 0 #define METHOD_IN_DIRECT 1 #define METHOD_OUT_DIRECT 2 #define METHOD_NEITHER 3 #define IOCTL_SCSI_BASE 0x00000004 /* * constants for DataIn member of SCSI_PASS_THROUGH* structures */ #define SCSI_IOCTL_DATA_OUT 0 #define SCSI_IOCTL_DATA_IN 1 #define SCSI_IOCTL_DATA_UNSPECIFIED 2 #define IOCTL_SCSI_PASS_THROUGH CTL_CODE(IOCTL_SCSI_BASE, 0x0401, \ METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) #define IOCTL_SCSI_MINIPORT CTL_CODE(IOCTL_SCSI_BASE, 0x0402, \ METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) #define IOCTL_SCSI_GET_INQUIRY_DATA CTL_CODE(IOCTL_SCSI_BASE, 0x0403, \ METHOD_BUFFERED, FILE_ANY_ACCESS) #define IOCTL_SCSI_GET_CAPABILITIES CTL_CODE(IOCTL_SCSI_BASE, 0x0404, \ METHOD_BUFFERED, FILE_ANY_ACCESS) #define IOCTL_SCSI_PASS_THROUGH_DIRECT CTL_CODE(IOCTL_SCSI_BASE, 0x0405, \ METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) #define IOCTL_SCSI_GET_ADDRESS CTL_CODE(IOCTL_SCSI_BASE, 0x0406, \ METHOD_BUFFERED, FILE_ANY_ACCESS) #ifdef __cplusplus } #endif #endif f2fs-tools-1.11.0/tools/sg_write_buffer/include/sg_unaligned.h000066400000000000000000000215551332120552100243410ustar00rootroot00000000000000#ifndef SG_UNALIGNED_H #define SG_UNALIGNED_H /* * Copyright (c) 2014-2017 Douglas Gilbert. * All rights reserved. * Use of this source code is governed by a BSD-style * license that can be found in the BSD_LICENSE file. */ #include #ifdef __cplusplus extern "C" { #endif /* Borrowed from the Linux kernel, via mhvtl */ /* In the first section below, functions that copy unsigned integers in a * computer's native format, to and from an unaligned big endian sequence of * bytes. Big endian byte format "on the wire" is the default used by SCSI * standards (www.t10.org). Big endian is also the network byte order. */ static inline uint16_t __get_unaligned_be16(const uint8_t *p) { return p[0] << 8 | p[1]; } static inline uint32_t __get_unaligned_be32(const uint8_t *p) { return p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3]; } /* Assume 48 bit value placed in uint64_t */ static inline uint64_t __get_unaligned_be48(const uint8_t *p) { return (uint64_t)__get_unaligned_be16(p) << 32 | __get_unaligned_be32(p + 2); } static inline uint64_t __get_unaligned_be64(const uint8_t *p) { return (uint64_t)__get_unaligned_be32(p) << 32 | __get_unaligned_be32(p + 4); } static inline void __put_unaligned_be16(uint16_t val, uint8_t *p) { *p++ = val >> 8; *p++ = val; } static inline void __put_unaligned_be32(uint32_t val, uint8_t *p) { __put_unaligned_be16(val >> 16, p); __put_unaligned_be16(val, p + 2); } /* Assume 48 bit value placed in uint64_t */ static inline void __put_unaligned_be48(uint64_t val, uint8_t *p) { __put_unaligned_be16(val >> 32, p); __put_unaligned_be32(val, p + 2); } static inline void __put_unaligned_be64(uint64_t val, uint8_t *p) { __put_unaligned_be32(val >> 32, p); __put_unaligned_be32(val, p + 4); } static inline uint16_t sg_get_unaligned_be16(const void *p) { return __get_unaligned_be16((const uint8_t *)p); } static inline uint32_t sg_get_unaligned_be24(const void *p) { return ((const uint8_t *)p)[0] << 16 | ((const uint8_t *)p)[1] << 8 | ((const uint8_t *)p)[2]; } static inline uint32_t sg_get_unaligned_be32(const void *p) { return __get_unaligned_be32((const uint8_t *)p); } /* Assume 48 bit value placed in uint64_t */ static inline uint64_t sg_get_unaligned_be48(const void *p) { return __get_unaligned_be48((const uint8_t *)p); } static inline uint64_t sg_get_unaligned_be64(const void *p) { return __get_unaligned_be64((const uint8_t *)p); } /* Returns 0 if 'num_bytes' is less than or equal to 0 or greater than * 8 (i.e. sizeof(uint64_t)). Else returns result in uint64_t which is * an 8 byte unsigned integer. */ static inline uint64_t sg_get_unaligned_be(int num_bytes, const void *p) { if ((num_bytes <= 0) || (num_bytes > (int)sizeof(uint64_t))) return 0; else { const uint8_t * xp = (const uint8_t *)p; uint64_t res = *xp; for (++xp; num_bytes > 1; ++xp, --num_bytes) res = (res << 8) | *xp; return res; } } static inline void sg_put_unaligned_be16(uint16_t val, void *p) { __put_unaligned_be16(val, (uint8_t *)p); } static inline void sg_put_unaligned_be24(uint32_t val, void *p) { ((uint8_t *)p)[0] = (val >> 16) & 0xff; ((uint8_t *)p)[1] = (val >> 8) & 0xff; ((uint8_t *)p)[2] = val & 0xff; } static inline void sg_put_unaligned_be32(uint32_t val, void *p) { __put_unaligned_be32(val, (uint8_t *)p); } /* Assume 48 bit value placed in uint64_t */ static inline void sg_put_unaligned_be48(uint64_t val, void *p) { __put_unaligned_be48(val, (uint8_t *)p); } static inline void sg_put_unaligned_be64(uint64_t val, void *p) { __put_unaligned_be64(val, (uint8_t *)p); } /* Since cdb and parameter blocks are often memset to zero before these * unaligned function partially fill them, then check for a val of zero * and ignore if it is with these variants. */ static inline void sg_nz_put_unaligned_be16(uint16_t val, void *p) { if (val) __put_unaligned_be16(val, (uint8_t *)p); } static inline void sg_nz_put_unaligned_be24(uint32_t val, void *p) { if (val) { ((uint8_t *)p)[0] = (val >> 16) & 0xff; ((uint8_t *)p)[1] = (val >> 8) & 0xff; ((uint8_t *)p)[2] = val & 0xff; } } static inline void sg_nz_put_unaligned_be32(uint32_t val, void *p) { if (val) __put_unaligned_be32(val, (uint8_t *)p); } static inline void sg_nz_put_unaligned_be64(uint64_t val, void *p) { if (val) __put_unaligned_be64(val, (uint8_t *)p); } /* Below are the little endian equivalents of the big endian functions * above. Little endian is used by ATA, PCI and NVMe. */ static inline uint16_t __get_unaligned_le16(const uint8_t *p) { return p[1] << 8 | p[0]; } static inline uint32_t __get_unaligned_le32(const uint8_t *p) { return p[3] << 24 | p[2] << 16 | p[1] << 8 | p[0]; } static inline uint64_t __get_unaligned_le64(const uint8_t *p) { return (uint64_t)__get_unaligned_le32(p + 4) << 32 | __get_unaligned_le32(p); } static inline void __put_unaligned_le16(uint16_t val, uint8_t *p) { *p++ = val; *p++ = val >> 8; } static inline void __put_unaligned_le32(uint32_t val, uint8_t *p) { __put_unaligned_le16(val >> 16, p + 2); __put_unaligned_le16(val, p); } static inline void __put_unaligned_le64(uint64_t val, uint8_t *p) { __put_unaligned_le32(val >> 32, p + 4); __put_unaligned_le32(val, p); } static inline uint16_t sg_get_unaligned_le16(const void *p) { return __get_unaligned_le16((const uint8_t *)p); } static inline uint32_t sg_get_unaligned_le24(const void *p) { return (uint32_t)__get_unaligned_le16((const uint8_t *)p) | ((const uint8_t *)p)[2] << 16; } static inline uint32_t sg_get_unaligned_le32(const void *p) { return __get_unaligned_le32((const uint8_t *)p); } /* Assume 48 bit value placed in uint64_t */ static inline uint64_t sg_get_unaligned_le48(const void *p) { return (uint64_t)__get_unaligned_le16((const uint8_t *)p + 4) << 32 | __get_unaligned_le32((const uint8_t *)p); } static inline uint64_t sg_get_unaligned_le64(const void *p) { return __get_unaligned_le64((const uint8_t *)p); } /* Returns 0 if 'num_bytes' is less than or equal to 0 or greater than * 8 (i.e. sizeof(uint64_t)). Else returns result in uint64_t which is * an 8 byte unsigned integer. */ static inline uint64_t sg_get_unaligned_le(int num_bytes, const void *p) { if ((num_bytes <= 0) || (num_bytes > (int)sizeof(uint64_t))) return 0; else { const uint8_t * xp = (const uint8_t *)p + (num_bytes - 1); uint64_t res = *xp; for (--xp; num_bytes > 1; --xp, --num_bytes) res = (res << 8) | *xp; return res; } } static inline void sg_put_unaligned_le16(uint16_t val, void *p) { __put_unaligned_le16(val, (uint8_t *)p); } static inline void sg_put_unaligned_le24(uint32_t val, void *p) { ((uint8_t *)p)[2] = (val >> 16) & 0xff; ((uint8_t *)p)[1] = (val >> 8) & 0xff; ((uint8_t *)p)[0] = val & 0xff; } static inline void sg_put_unaligned_le32(uint32_t val, void *p) { __put_unaligned_le32(val, (uint8_t *)p); } /* Assume 48 bit value placed in uint64_t */ static inline void sg_put_unaligned_le48(uint64_t val, void *p) { ((uint8_t *)p)[5] = (val >> 40) & 0xff; ((uint8_t *)p)[4] = (val >> 32) & 0xff; ((uint8_t *)p)[3] = (val >> 24) & 0xff; ((uint8_t *)p)[2] = (val >> 16) & 0xff; ((uint8_t *)p)[1] = (val >> 8) & 0xff; ((uint8_t *)p)[0] = val & 0xff; } static inline void sg_put_unaligned_le64(uint64_t val, void *p) { __put_unaligned_le64(val, (uint8_t *)p); } /* Since cdb and parameter blocks are often memset to zero before these * unaligned function partially fill them, then check for a val of zero * and ignore if it is with these variants. */ static inline void sg_nz_put_unaligned_le16(uint16_t val, void *p) { if (val) __put_unaligned_le16(val, (uint8_t *)p); } static inline void sg_nz_put_unaligned_le24(uint32_t val, void *p) { if (val) { ((uint8_t *)p)[2] = (val >> 16) & 0xff; ((uint8_t *)p)[1] = (val >> 8) & 0xff; ((uint8_t *)p)[0] = val & 0xff; } } static inline void sg_nz_put_unaligned_le32(uint32_t val, void *p) { if (val) __put_unaligned_le32(val, (uint8_t *)p); } static inline void sg_nz_put_unaligned_le64(uint64_t val, void *p) { if (val) __put_unaligned_le64(val, (uint8_t *)p); } #ifdef __cplusplus } #endif #endif /* SG_UNALIGNED_H */ f2fs-tools-1.11.0/tools/sg_write_buffer/sg_cmds_basic.c000066400000000000000000000533351332120552100230330ustar00rootroot00000000000000/* * Copyright (c) 1999-2018 Douglas Gilbert. * All rights reserved. * Use of this source code is governed by a BSD-style * license that can be found in the BSD_LICENSE file. */ /* * CONTENTS * Some SCSI commands are executed in many contexts and hence began * to appear in several sg3_utils utilities. This files centralizes * some of the low level command execution code. In most cases the * interpretation of the command response is left to the each * utility. */ #include #include #include #include #include #include #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "sg_lib.h" #include "sg_cmds_basic.h" #include "sg_pt.h" #include "sg_unaligned.h" /* Needs to be after config.h */ #ifdef SG_LIB_LINUX #include #endif static const char * const version_str = "1.83 20180204"; #define SENSE_BUFF_LEN 64 /* Arbitrary, could be larger */ #define EBUFF_SZ 256 #define DEF_PT_TIMEOUT 60 /* 60 seconds */ #define START_PT_TIMEOUT 120 /* 120 seconds == 2 minutes */ #define LONG_PT_TIMEOUT 7200 /* 7,200 seconds == 120 minutes */ #define INQUIRY_CMD 0x12 #define INQUIRY_CMDLEN 6 #define REQUEST_SENSE_CMD 0x3 #define REQUEST_SENSE_CMDLEN 6 #define REPORT_LUNS_CMD 0xa0 #define REPORT_LUNS_CMDLEN 12 #define TUR_CMD 0x0 #define TUR_CMDLEN 6 #define SAFE_STD_INQ_RESP_LEN 36 /* other lengths lock up some devices */ const char * sg_cmds_version() { return version_str; } #if defined(__GNUC__) || defined(__clang__) static int pr2ws(const char * fmt, ...) __attribute__ ((format (printf, 1, 2))); #else static int pr2ws(const char * fmt, ...); #endif static int pr2ws(const char * fmt, ...) { va_list args; int n; va_start(args, fmt); n = vfprintf(sg_warnings_strm ? sg_warnings_strm : stderr, fmt, args); va_end(args); return n; } /* Returns file descriptor >= 0 if successful. If error in Unix returns negated errno. */ int sg_cmds_open_device(const char * device_name, bool read_only, int verbose) { /* The following 2 lines are temporary. It is to avoid a NULL pointer * crash when an old utility is used with a newer library built after * the sg_warnings_strm cleanup */ if (NULL == sg_warnings_strm) sg_warnings_strm = stderr; return scsi_pt_open_device(device_name, read_only, verbose); } /* Returns file descriptor >= 0 if successful. If error in Unix returns negated errno. */ int sg_cmds_open_flags(const char * device_name, int flags, int verbose) { return scsi_pt_open_flags(device_name, flags, verbose); } /* Returns 0 if successful. If error in Unix returns negated errno. */ int sg_cmds_close_device(int device_fd) { return scsi_pt_close_device(device_fd); } static const char * const pass_through_s = "pass-through"; static int sg_cmds_process_helper(const char * leadin, int mx_di_len, int resid, const unsigned char * sbp, int slen, bool noisy, int verbose, int * o_sense_cat) { int scat, got; bool n = false; bool check_data_in = false; char b[512]; scat = sg_err_category_sense(sbp, slen); switch (scat) { case SG_LIB_CAT_NOT_READY: case SG_LIB_CAT_INVALID_OP: case SG_LIB_CAT_ILLEGAL_REQ: case SG_LIB_CAT_ABORTED_COMMAND: case SG_LIB_CAT_COPY_ABORTED: case SG_LIB_CAT_DATA_PROTECT: case SG_LIB_CAT_PROTECTION: case SG_LIB_CAT_NO_SENSE: case SG_LIB_CAT_MISCOMPARE: n = false; break; case SG_LIB_CAT_RECOVERED: case SG_LIB_CAT_MEDIUM_HARD: check_data_in = true; #if defined(__GNUC__) #if (__GNUC__ >= 7) __attribute__((fallthrough)); /* FALL THROUGH */ #endif #endif case SG_LIB_CAT_UNIT_ATTENTION: case SG_LIB_CAT_SENSE: default: n = noisy; break; } if (verbose || n) { if (leadin && (strlen(leadin) > 0)) pr2ws("%s:\n", leadin); sg_get_sense_str(NULL, sbp, slen, (verbose > 1), sizeof(b), b); pr2ws("%s", b); if ((mx_di_len > 0) && (resid > 0)) { got = mx_di_len - resid; if ((verbose > 2) || check_data_in || (got > 0)) pr2ws(" %s requested %d bytes (data-in) but got %d " "bytes\n", pass_through_s, mx_di_len, got); } } if (o_sense_cat) *o_sense_cat = scat; return -2; } /* This is a helper function used by sg_cmds_* implementations after the * call to the pass-through. pt_res is returned from do_scsi_pt(). If valid * sense data is found it is decoded and output to sg_warnings_strm (def: * stderr); depending on the 'noisy' and 'verbose' settings. Returns -2 for * "sense" category (may not be fatal), -1 for failed, 0, or a positive * number. If 'mx_di_len > 0' then asks pass-through for resid and returns * (mx_di_len - resid); otherwise returns 0. So for data-in it should return * the actual number of bytes received. For data-out (to device) or no data * call with 'mx_di_len' set to 0 or less. If -2 returned then sense category * output via 'o_sense_cat' pointer (if not NULL). Note that several sense * categories also have data in bytes received; -2 is still returned. */ int sg_cmds_process_resp(struct sg_pt_base * ptvp, const char * leadin, int pt_res, int mx_di_len, const unsigned char * sbp, bool noisy, int verbose, int * o_sense_cat) { int got, cat, duration, slen, resid, resp_code, sstat; bool transport_sense; char b[1024]; if (NULL == leadin) leadin = ""; if (pt_res < 0) { #ifdef SG_LIB_LINUX if (verbose) pr2ws("%s: %s os error: %s\n", leadin, pass_through_s, safe_strerror(-pt_res)); if ((-ENXIO == pt_res) && o_sense_cat) { if (verbose > 2) pr2ws("map ENXIO to SG_LIB_CAT_NOT_READY\n"); *o_sense_cat = SG_LIB_CAT_NOT_READY; return -2; } else if (noisy && (0 == verbose)) pr2ws("%s: %s os error: %s\n", leadin, pass_through_s, safe_strerror(-pt_res)); #else if (noisy || verbose) pr2ws("%s: %s os error: %s\n", leadin, pass_through_s, safe_strerror(-pt_res)); #endif return -1; } else if (SCSI_PT_DO_BAD_PARAMS == pt_res) { pr2ws("%s: bad %s setup\n", leadin, pass_through_s); return -1; } else if (SCSI_PT_DO_TIMEOUT == pt_res) { pr2ws("%s: %s timeout\n", leadin, pass_through_s); return -1; } if ((verbose > 2) && ((duration = get_scsi_pt_duration_ms(ptvp)) >= 0)) pr2ws(" duration=%d ms\n", duration); resid = (mx_di_len > 0) ? get_scsi_pt_resid(ptvp) : 0; slen = get_scsi_pt_sense_len(ptvp); switch ((cat = get_scsi_pt_result_category(ptvp))) { case SCSI_PT_RESULT_GOOD: if (sbp && (slen > 7)) { resp_code = sbp[0] & 0x7f; /* SBC referrals can have status=GOOD and sense_key=COMPLETED */ if (resp_code >= 0x70) { if (resp_code < 0x72) { if (SPC_SK_NO_SENSE != (0xf & sbp[2])) sg_err_category_sense(sbp, slen); } else if (resp_code < 0x74) { if (SPC_SK_NO_SENSE != (0xf & sbp[1])) sg_err_category_sense(sbp, slen); } } } if (mx_di_len > 0) { got = mx_di_len - resid; if ((verbose > 1) && (resid != 0)) pr2ws(" %s: %s requested %d bytes (data-in) but got %d " "bytes\n", leadin, pass_through_s, mx_di_len, got); if (got >= 0) return got; else { if (verbose) pr2ws(" %s: %s can't get negative bytes, say it got " "none\n", leadin, pass_through_s); return 0; } } else return 0; case SCSI_PT_RESULT_STATUS: /* other than GOOD and CHECK CONDITION */ sstat = get_scsi_pt_status_response(ptvp); if (o_sense_cat) { switch (sstat) { case SAM_STAT_RESERVATION_CONFLICT: *o_sense_cat = SG_LIB_CAT_RES_CONFLICT; return -2; case SAM_STAT_CONDITION_MET: *o_sense_cat = SG_LIB_CAT_CONDITION_MET; return -2; case SAM_STAT_BUSY: *o_sense_cat = SG_LIB_CAT_BUSY; return -2; case SAM_STAT_TASK_SET_FULL: *o_sense_cat = SG_LIB_CAT_TS_FULL; return -2; case SAM_STAT_ACA_ACTIVE: *o_sense_cat = SG_LIB_CAT_ACA_ACTIVE; return -2; case SAM_STAT_TASK_ABORTED: *o_sense_cat = SG_LIB_CAT_TASK_ABORTED; return -2; default: break; } } if (verbose || noisy) { sg_get_scsi_status_str(sstat, sizeof(b), b); pr2ws("%s: scsi status: %s\n", leadin, b); } return -1; case SCSI_PT_RESULT_SENSE: return sg_cmds_process_helper(leadin, mx_di_len, resid, sbp, slen, noisy, verbose, o_sense_cat); case SCSI_PT_RESULT_TRANSPORT_ERR: if (verbose || noisy) { get_scsi_pt_transport_err_str(ptvp, sizeof(b), b); pr2ws("%s: transport: %s\n", leadin, b); } #ifdef SG_LIB_LINUX transport_sense = (slen > 0); #else transport_sense = ((SAM_STAT_CHECK_CONDITION == get_scsi_pt_status_response(ptvp)) && (slen > 0)); #endif if (transport_sense) return sg_cmds_process_helper(leadin, mx_di_len, resid, sbp, slen, noisy, verbose, o_sense_cat); else return -1; case SCSI_PT_RESULT_OS_ERR: if (verbose || noisy) { get_scsi_pt_os_err_str(ptvp, sizeof(b), b); pr2ws("%s: os: %s\n", leadin, b); } return -1; default: pr2ws("%s: unknown %s result category (%d)\n", leadin, pass_through_s, cat); return -1; } } bool sg_cmds_is_nvme(const struct sg_pt_base * ptvp) { return pt_device_is_nvme(ptvp); } static struct sg_pt_base * create_pt_obj(const char * cname) { struct sg_pt_base * ptvp = construct_scsi_pt_obj(); if (NULL == ptvp) pr2ws("%s: out of memory\n", cname); return ptvp; } static const char * const inquiry_s = "inquiry"; static int sg_ll_inquiry_com(int sg_fd, bool cmddt, bool evpd, int pg_op, void * resp, int mx_resp_len, int timeout_secs, int * residp, bool noisy, int verbose) { int res, ret, k, sense_cat, resid; unsigned char inq_cdb[INQUIRY_CMDLEN] = {INQUIRY_CMD, 0, 0, 0, 0, 0}; unsigned char sense_b[SENSE_BUFF_LEN]; unsigned char * up; struct sg_pt_base * ptvp; if (cmddt) inq_cdb[1] |= 0x2; if (evpd) inq_cdb[1] |= 0x1; inq_cdb[2] = (unsigned char)pg_op; /* 16 bit allocation length (was 8, increased in spc3r09, 200209) */ sg_put_unaligned_be16((uint16_t)mx_resp_len, inq_cdb + 3); if (verbose) { pr2ws(" %s cdb: ", inquiry_s); for (k = 0; k < INQUIRY_CMDLEN; ++k) pr2ws("%02x ", inq_cdb[k]); pr2ws("\n"); } if (resp && (mx_resp_len > 0)) { up = (unsigned char *)resp; up[0] = 0x7f; /* defensive prefill */ if (mx_resp_len > 4) up[4] = 0; } if (timeout_secs <= 0) timeout_secs = DEF_PT_TIMEOUT; ptvp = construct_scsi_pt_obj(); if (NULL == ptvp) { pr2ws("%s: out of memory\n", __func__); if (residp) *residp = 0; return -1; } set_scsi_pt_cdb(ptvp, inq_cdb, sizeof(inq_cdb)); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); set_scsi_pt_data_in(ptvp, (unsigned char *)resp, mx_resp_len); res = do_scsi_pt(ptvp, sg_fd, timeout_secs, verbose); ret = sg_cmds_process_resp(ptvp, inquiry_s, res, mx_resp_len, sense_b, noisy, verbose, &sense_cat); resid = get_scsi_pt_resid(ptvp); if (residp) *residp = resid; if (-1 == ret) ret = sg_convert_errno(get_scsi_pt_os_err(ptvp)); else if (-2 == ret) { switch (sense_cat) { case SG_LIB_CAT_RECOVERED: case SG_LIB_CAT_NO_SENSE: ret = 0; break; default: ret = sense_cat; break; } } else if (ret < 4) { if (verbose) pr2ws("%s: got too few bytes (%d)\n", __func__, ret); ret = SG_LIB_CAT_MALFORMED; } else ret = 0; destruct_scsi_pt_obj(ptvp); if (resid > 0) { if (resid > mx_resp_len) { pr2ws("%s resid (%d) should never exceed requested " "len=%d\n", inquiry_s, resid, mx_resp_len); return ret ? ret : SG_LIB_CAT_MALFORMED; } /* zero unfilled section of response buffer, based on resid */ memset((unsigned char *)resp + (mx_resp_len - resid), 0, resid); } return ret; } /* Invokes a SCSI INQUIRY command and yields the response. Returns 0 when * successful, various SG_LIB_CAT_* positive values or -1 -> other errors. * The CMDDT field is obsolete in the INQUIRY cdb. */ int sg_ll_inquiry(int sg_fd, bool cmddt, bool evpd, int pg_op, void * resp, int mx_resp_len, bool noisy, int verbose) { return sg_ll_inquiry_com(sg_fd, cmddt, evpd, pg_op, resp, mx_resp_len, 0 /* timeout_sec */, NULL, noisy, verbose); } /* Yields most of first 36 bytes of a standard INQUIRY (evpd==0) response. * Returns 0 when successful, various SG_LIB_CAT_* positive values or * -1 -> other errors */ int sg_simple_inquiry(int sg_fd, struct sg_simple_inquiry_resp * inq_data, bool noisy, int verbose) { int ret; unsigned char inq_resp[SAFE_STD_INQ_RESP_LEN]; if (inq_data) { memset(inq_data, 0, sizeof(* inq_data)); inq_data->peripheral_qualifier = 0x3; inq_data->peripheral_type = 0x1f; } ret = sg_ll_inquiry_com(sg_fd, false, false, 0, inq_resp, sizeof(inq_resp), 0, NULL, noisy, verbose); if (inq_data && (0 == ret)) { inq_data->peripheral_qualifier = (inq_resp[0] >> 5) & 0x7; inq_data->peripheral_type = inq_resp[0] & 0x1f; inq_data->byte_1 = inq_resp[1]; inq_data->version = inq_resp[2]; inq_data->byte_3 = inq_resp[3]; inq_data->byte_5 = inq_resp[5]; inq_data->byte_6 = inq_resp[6]; inq_data->byte_7 = inq_resp[7]; memcpy(inq_data->vendor, inq_resp + 8, 8); memcpy(inq_data->product, inq_resp + 16, 16); memcpy(inq_data->revision, inq_resp + 32, 4); } return ret; } /* Invokes a SCSI INQUIRY command and yields the response. Returns 0 when * successful, various SG_LIB_CAT_* positive values or -1 -> other errors. * The CMDDT field is obsolete in the INQUIRY cdb (since spc3r16 in 2003) so * an argument to set it has been removed (use the REPORT SUPPORTED OPERATION * CODES command instead). Adds the ability to set the command abort timeout * and the ability to report the residual count. If timeout_secs is zero * or less the default command abort timeout (60 seconds) is used. * If residp is non-NULL then the residual value is written where residp * points. A residual value of 0 implies mx_resp_len bytes have be written * where resp points. If the residual value equals mx_resp_len then no * bytes have been written. */ int sg_ll_inquiry_v2(int sg_fd, bool evpd, int pg_op, void * resp, int mx_resp_len, int timeout_secs, int * residp, bool noisy, int verbose) { return sg_ll_inquiry_com(sg_fd, false, evpd, pg_op, resp, mx_resp_len, timeout_secs, residp, noisy, verbose); } /* Invokes a SCSI TEST UNIT READY command. * 'pack_id' is just for diagnostics, safe to set to 0. * Looks for progress indicator if 'progress' non-NULL; * if found writes value [0..65535] else write -1. * Returns 0 when successful, various SG_LIB_CAT_* positive values or * -1 -> other errors */ int sg_ll_test_unit_ready_progress(int sg_fd, int pack_id, int * progress, bool noisy, int verbose) { static const char * const tur_s = "test unit ready"; int res, ret, k, sense_cat; unsigned char tur_cdb[TUR_CMDLEN] = {TUR_CMD, 0, 0, 0, 0, 0}; unsigned char sense_b[SENSE_BUFF_LEN]; struct sg_pt_base * ptvp; if (verbose) { pr2ws(" %s cdb: ", tur_s); for (k = 0; k < TUR_CMDLEN; ++k) pr2ws("%02x ", tur_cdb[k]); pr2ws("\n"); } if (NULL == ((ptvp = create_pt_obj(tur_s)))) return -1; set_scsi_pt_cdb(ptvp, tur_cdb, sizeof(tur_cdb)); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); set_scsi_pt_packet_id(ptvp, pack_id); res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose); ret = sg_cmds_process_resp(ptvp, tur_s, res, SG_NO_DATA_IN, sense_b, noisy, verbose, &sense_cat); if (-1 == ret) { int os_err = get_scsi_pt_os_err(ptvp); if ((os_err > 0) && (os_err < 47)) ret = SG_LIB_OS_BASE_ERR + os_err; } else if (-2 == ret) { if (progress) { int slen = get_scsi_pt_sense_len(ptvp); if (! sg_get_sense_progress_fld(sense_b, slen, progress)) *progress = -1; } switch (sense_cat) { case SG_LIB_CAT_RECOVERED: case SG_LIB_CAT_NO_SENSE: ret = 0; break; default: ret = sense_cat; break; } } else ret = 0; destruct_scsi_pt_obj(ptvp); return ret; } /* Invokes a SCSI TEST UNIT READY command. * 'pack_id' is just for diagnostics, safe to set to 0. * Returns 0 when successful, various SG_LIB_CAT_* positive values or * -1 -> other errors */ int sg_ll_test_unit_ready(int sg_fd, int pack_id, bool noisy, int verbose) { return sg_ll_test_unit_ready_progress(sg_fd, pack_id, NULL, noisy, verbose); } /* Invokes a SCSI REQUEST SENSE command. Returns 0 when successful, various * SG_LIB_CAT_* positive values or -1 -> other errors */ int sg_ll_request_sense(int sg_fd, bool desc, void * resp, int mx_resp_len, bool noisy, int verbose) { static const char * const rq_s = "request sense"; int k, ret, res, sense_cat; unsigned char rs_cdb[REQUEST_SENSE_CMDLEN] = {REQUEST_SENSE_CMD, 0, 0, 0, 0, 0}; unsigned char sense_b[SENSE_BUFF_LEN]; struct sg_pt_base * ptvp; if (desc) rs_cdb[1] |= 0x1; if (mx_resp_len > 0xff) { pr2ws("mx_resp_len cannot exceed 255\n"); return -1; } rs_cdb[4] = mx_resp_len & 0xff; if (verbose) { pr2ws(" %s cmd: ", rq_s); for (k = 0; k < REQUEST_SENSE_CMDLEN; ++k) pr2ws("%02x ", rs_cdb[k]); pr2ws("\n"); } if (NULL == ((ptvp = create_pt_obj(rq_s)))) return -1; set_scsi_pt_cdb(ptvp, rs_cdb, sizeof(rs_cdb)); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); set_scsi_pt_data_in(ptvp, (unsigned char *)resp, mx_resp_len); res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose); ret = sg_cmds_process_resp(ptvp, rq_s, res, mx_resp_len, sense_b, noisy, verbose, &sense_cat); if (-1 == ret) { int os_err = get_scsi_pt_os_err(ptvp); if ((os_err > 0) && (os_err < 47)) ret = SG_LIB_OS_BASE_ERR + os_err; } else if (-2 == ret) { switch (sense_cat) { case SG_LIB_CAT_RECOVERED: case SG_LIB_CAT_NO_SENSE: ret = 0; break; default: ret = sense_cat; break; } } else { if ((mx_resp_len >= 8) && (ret < 8)) { if (verbose) pr2ws(" %s: got %d bytes in response, too short\n", rq_s, ret); ret = -1; } else ret = 0; } destruct_scsi_pt_obj(ptvp); return ret; } /* Invokes a SCSI REPORT LUNS command. Return of 0 -> success, * various SG_LIB_CAT_* positive values or -1 -> other errors */ int sg_ll_report_luns(int sg_fd, int select_report, void * resp, int mx_resp_len, bool noisy, int verbose) { static const char * const report_luns_s = "report luns"; int k, ret, res, sense_cat; unsigned char rl_cdb[REPORT_LUNS_CMDLEN] = {REPORT_LUNS_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; unsigned char sense_b[SENSE_BUFF_LEN]; struct sg_pt_base * ptvp; rl_cdb[2] = select_report & 0xff; sg_put_unaligned_be32((uint32_t)mx_resp_len, rl_cdb + 6); if (verbose) { pr2ws(" %s cdb: ", report_luns_s); for (k = 0; k < REPORT_LUNS_CMDLEN; ++k) pr2ws("%02x ", rl_cdb[k]); pr2ws("\n"); } if (NULL == ((ptvp = create_pt_obj(report_luns_s)))) return -1; set_scsi_pt_cdb(ptvp, rl_cdb, sizeof(rl_cdb)); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); set_scsi_pt_data_in(ptvp, (unsigned char *)resp, mx_resp_len); res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose); ret = sg_cmds_process_resp(ptvp, report_luns_s, res, mx_resp_len, sense_b, noisy, verbose, &sense_cat); if (-1 == ret) { int os_err = get_scsi_pt_os_err(ptvp); if ((os_err > 0) && (os_err < 47)) ret = SG_LIB_OS_BASE_ERR + os_err; } else if (-2 == ret) { switch (sense_cat) { case SG_LIB_CAT_RECOVERED: case SG_LIB_CAT_NO_SENSE: ret = 0; break; default: ret = sense_cat; break; } } else ret = 0; destruct_scsi_pt_obj(ptvp); return ret; } f2fs-tools-1.11.0/tools/sg_write_buffer/sg_cmds_basic2.c000066400000000000000000001112641332120552100231110ustar00rootroot00000000000000/* * Copyright (c) 1999-2018 Douglas Gilbert. * All rights reserved. * Use of this source code is governed by a BSD-style * license that can be found in the BSD_LICENSE file. */ /* * CONTENTS * Some SCSI commands are executed in many contexts and hence began * to appear in several sg3_utils utilities. This files centralizes * some of the low level command execution code. In most cases the * interpretation of the command response is left to the each * utility. */ #include #include #include #include #include #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "sg_lib.h" #include "sg_cmds_basic.h" #include "sg_pt.h" #include "sg_unaligned.h" #define SENSE_BUFF_LEN 64 /* Arbitrary, could be larger */ #define EBUFF_SZ 256 #define DEF_PT_TIMEOUT 60 /* 60 seconds */ #define START_PT_TIMEOUT 120 /* 120 seconds == 2 minutes */ #define LONG_PT_TIMEOUT 7200 /* 7,200 seconds == 120 minutes */ #define SYNCHRONIZE_CACHE_CMD 0x35 #define SYNCHRONIZE_CACHE_CMDLEN 10 #define SERVICE_ACTION_IN_16_CMD 0x9e #define SERVICE_ACTION_IN_16_CMDLEN 16 #define READ_CAPACITY_16_SA 0x10 #define READ_CAPACITY_10_CMD 0x25 #define READ_CAPACITY_10_CMDLEN 10 #define MODE_SENSE6_CMD 0x1a #define MODE_SENSE6_CMDLEN 6 #define MODE_SENSE10_CMD 0x5a #define MODE_SENSE10_CMDLEN 10 #define MODE_SELECT6_CMD 0x15 #define MODE_SELECT6_CMDLEN 6 #define MODE_SELECT10_CMD 0x55 #define MODE_SELECT10_CMDLEN 10 #define LOG_SENSE_CMD 0x4d #define LOG_SENSE_CMDLEN 10 #define LOG_SELECT_CMD 0x4c #define LOG_SELECT_CMDLEN 10 #define START_STOP_CMD 0x1b #define START_STOP_CMDLEN 6 #define PREVENT_ALLOW_CMD 0x1e #define PREVENT_ALLOW_CMDLEN 6 #define MODE6_RESP_HDR_LEN 4 #define MODE10_RESP_HDR_LEN 8 #define MODE_RESP_ARB_LEN 1024 #define INQUIRY_RESP_INITIAL_LEN 36 #if defined(__GNUC__) || defined(__clang__) static int pr2ws(const char * fmt, ...) __attribute__ ((format (printf, 1, 2))); #else static int pr2ws(const char * fmt, ...); #endif static int pr2ws(const char * fmt, ...) { va_list args; int n; va_start(args, fmt); n = vfprintf(sg_warnings_strm ? sg_warnings_strm : stderr, fmt, args); va_end(args); return n; } static struct sg_pt_base * create_pt_obj(const char * cname) { struct sg_pt_base * ptvp = construct_scsi_pt_obj(); if (NULL == ptvp) pr2ws("%s: out of memory\n", cname); return ptvp; } /* Invokes a SCSI SYNCHRONIZE CACHE (10) command. Return of 0 -> success, * various SG_LIB_CAT_* positive values or -1 -> other errors */ int sg_ll_sync_cache_10(int sg_fd, bool sync_nv, bool immed, int group, unsigned int lba, unsigned int count, bool noisy, int verbose) { static const char * const cdb_name_s = "synchronize cache(10)"; int res, ret, k, sense_cat; unsigned char sc_cdb[SYNCHRONIZE_CACHE_CMDLEN] = {SYNCHRONIZE_CACHE_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0}; unsigned char sense_b[SENSE_BUFF_LEN]; struct sg_pt_base * ptvp; if (sync_nv) sc_cdb[1] |= 4; if (immed) sc_cdb[1] |= 2; sg_put_unaligned_be32((uint32_t)lba, sc_cdb + 2); sc_cdb[6] = group & 0x1f; if (count > 0xffff) { pr2ws("count too big\n"); return -1; } sg_put_unaligned_be16((int16_t)count, sc_cdb + 7); if (verbose) { pr2ws(" %s cdb: ", cdb_name_s); for (k = 0; k < SYNCHRONIZE_CACHE_CMDLEN; ++k) pr2ws("%02x ", sc_cdb[k]); pr2ws("\n"); } if (NULL == ((ptvp = create_pt_obj(cdb_name_s)))) return -1; set_scsi_pt_cdb(ptvp, sc_cdb, sizeof(sc_cdb)); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose); ret = sg_cmds_process_resp(ptvp, cdb_name_s, res, SG_NO_DATA_IN, sense_b, noisy, verbose, &sense_cat); if (-1 == ret) { int os_err = get_scsi_pt_os_err(ptvp); if ((os_err > 0) && (os_err < 47)) ret = SG_LIB_OS_BASE_ERR + os_err; } else if (-2 == ret) { switch (sense_cat) { case SG_LIB_CAT_RECOVERED: case SG_LIB_CAT_NO_SENSE: ret = 0; break; default: ret = sense_cat; break; } } else ret = 0; destruct_scsi_pt_obj(ptvp); return ret; } /* Invokes a SCSI READ CAPACITY (16) command. Returns 0 -> success, * various SG_LIB_CAT_* positive values or -1 -> other errors */ int sg_ll_readcap_16(int sg_fd, bool pmi, uint64_t llba, void * resp, int mx_resp_len, bool noisy, int verbose) { static const char * const cdb_name_s = "read capacity(16)"; int k, ret, res, sense_cat; unsigned char rc_cdb[SERVICE_ACTION_IN_16_CMDLEN] = {SERVICE_ACTION_IN_16_CMD, READ_CAPACITY_16_SA, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; unsigned char sense_b[SENSE_BUFF_LEN]; struct sg_pt_base * ptvp; if (pmi) { /* lbs only valid when pmi set */ rc_cdb[14] |= 1; sg_put_unaligned_be64(llba, rc_cdb + 2); } /* Allocation length, no guidance in SBC-2 rev 15b */ sg_put_unaligned_be32((uint32_t)mx_resp_len, rc_cdb + 10); if (verbose) { pr2ws(" %s cdb: ", cdb_name_s); for (k = 0; k < SERVICE_ACTION_IN_16_CMDLEN; ++k) pr2ws("%02x ", rc_cdb[k]); pr2ws("\n"); } if (NULL == ((ptvp = create_pt_obj(cdb_name_s)))) return -1; set_scsi_pt_cdb(ptvp, rc_cdb, sizeof(rc_cdb)); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); set_scsi_pt_data_in(ptvp, (unsigned char *)resp, mx_resp_len); res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose); ret = sg_cmds_process_resp(ptvp, cdb_name_s, res, mx_resp_len, sense_b, noisy, verbose, &sense_cat); if (-1 == ret) { int os_err = get_scsi_pt_os_err(ptvp); if ((os_err > 0) && (os_err < 47)) ret = SG_LIB_OS_BASE_ERR + os_err; } else if (-2 == ret) { switch (sense_cat) { case SG_LIB_CAT_RECOVERED: case SG_LIB_CAT_NO_SENSE: ret = 0; break; default: ret = sense_cat; break; } } else ret = 0; destruct_scsi_pt_obj(ptvp); return ret; } /* Invokes a SCSI READ CAPACITY (10) command. Returns 0 -> success, * various SG_LIB_CAT_* positive values or -1 -> other errors */ int sg_ll_readcap_10(int sg_fd, bool pmi, unsigned int lba, void * resp, int mx_resp_len, bool noisy, int verbose) { static const char * const cdb_name_s = "read capacity(10)"; int k, ret, res, sense_cat; unsigned char rc_cdb[READ_CAPACITY_10_CMDLEN] = {READ_CAPACITY_10_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0}; unsigned char sense_b[SENSE_BUFF_LEN]; struct sg_pt_base * ptvp; if (pmi) { /* lbs only valid when pmi set */ rc_cdb[8] |= 1; sg_put_unaligned_be32((uint32_t)lba, rc_cdb + 2); } if (verbose) { pr2ws(" %s cdb: ", cdb_name_s); for (k = 0; k < READ_CAPACITY_10_CMDLEN; ++k) pr2ws("%02x ", rc_cdb[k]); pr2ws("\n"); } if (NULL == ((ptvp = create_pt_obj(cdb_name_s)))) return -1; set_scsi_pt_cdb(ptvp, rc_cdb, sizeof(rc_cdb)); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); set_scsi_pt_data_in(ptvp, (unsigned char *)resp, mx_resp_len); res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose); ret = sg_cmds_process_resp(ptvp, cdb_name_s, res, mx_resp_len, sense_b, noisy, verbose, &sense_cat); if (-1 == ret) { int os_err = get_scsi_pt_os_err(ptvp); if ((os_err > 0) && (os_err < 47)) ret = SG_LIB_OS_BASE_ERR + os_err; } else if (-2 == ret) { switch (sense_cat) { case SG_LIB_CAT_RECOVERED: case SG_LIB_CAT_NO_SENSE: ret = 0; break; default: ret = sense_cat; break; } } else ret = 0; destruct_scsi_pt_obj(ptvp); return ret; } /* Invokes a SCSI MODE SENSE (6) command. Return of 0 -> success, * various SG_LIB_CAT_* positive values or -1 -> other errors */ int sg_ll_mode_sense6(int sg_fd, bool dbd, int pc, int pg_code, int sub_pg_code, void * resp, int mx_resp_len, bool noisy, int verbose) { static const char * const cdb_name_s = "mode sense(6)"; int res, ret, k, sense_cat, resid; unsigned char modes_cdb[MODE_SENSE6_CMDLEN] = {MODE_SENSE6_CMD, 0, 0, 0, 0, 0}; unsigned char sense_b[SENSE_BUFF_LEN]; struct sg_pt_base * ptvp; modes_cdb[1] = (unsigned char)(dbd ? 0x8 : 0); modes_cdb[2] = (unsigned char)(((pc << 6) & 0xc0) | (pg_code & 0x3f)); modes_cdb[3] = (unsigned char)(sub_pg_code & 0xff); modes_cdb[4] = (unsigned char)(mx_resp_len & 0xff); if (mx_resp_len > 0xff) { pr2ws("mx_resp_len too big\n"); return -1; } if (verbose) { pr2ws(" %s cdb: ", cdb_name_s); for (k = 0; k < MODE_SENSE6_CMDLEN; ++k) pr2ws("%02x ", modes_cdb[k]); pr2ws("\n"); } if (NULL == ((ptvp = create_pt_obj(cdb_name_s)))) return -1; set_scsi_pt_cdb(ptvp, modes_cdb, sizeof(modes_cdb)); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); set_scsi_pt_data_in(ptvp, (unsigned char *)resp, mx_resp_len); res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose); ret = sg_cmds_process_resp(ptvp, cdb_name_s, res, mx_resp_len, sense_b, noisy, verbose, &sense_cat); resid = get_scsi_pt_resid(ptvp); if (-1 == ret) { int os_err = get_scsi_pt_os_err(ptvp); if ((os_err > 0) && (os_err < 47)) ret = SG_LIB_OS_BASE_ERR + os_err; } else if (-2 == ret) { switch (sense_cat) { case SG_LIB_CAT_RECOVERED: case SG_LIB_CAT_NO_SENSE: ret = 0; break; default: ret = sense_cat; break; } } else { if ((verbose > 2) && (ret > 0)) { pr2ws(" %s: response", cdb_name_s); if (3 == verbose) { pr2ws("%s:\n", (ret > 256 ? ", first 256 bytes" : "")); hex2stderr((const uint8_t *)resp, (ret > 256 ? 256 : ret), -1); } else { pr2ws(":\n"); hex2stderr((const uint8_t *)resp, ret, 0); } } ret = 0; } destruct_scsi_pt_obj(ptvp); if (resid > 0) { if (resid > mx_resp_len) { pr2ws("%s: resid (%d) should never exceed requested len=%d\n", cdb_name_s, resid, mx_resp_len); return ret ? ret : SG_LIB_CAT_MALFORMED; } /* zero unfilled section of response buffer */ memset((unsigned char *)resp + (mx_resp_len - resid), 0, resid); } return ret; } /* Invokes a SCSI MODE SENSE (10) command. Return of 0 -> success, * various SG_LIB_CAT_* positive values or -1 -> other errors */ int sg_ll_mode_sense10(int sg_fd, bool llbaa, bool dbd, int pc, int pg_code, int sub_pg_code, void * resp, int mx_resp_len, bool noisy, int verbose) { return sg_ll_mode_sense10_v2(sg_fd, llbaa, dbd, pc, pg_code, sub_pg_code, resp, mx_resp_len, 0, NULL, noisy, verbose); } /* Invokes a SCSI MODE SENSE (10) command. Return of 0 -> success, * various SG_LIB_CAT_* positive values or -1 -> other errors. * Adds the ability to set the command abort timeout * and the ability to report the residual count. If timeout_secs is zero * or less the default command abort timeout (60 seconds) is used. * If residp is non-NULL then the residual value is written where residp * points. A residual value of 0 implies mx_resp_len bytes have be written * where resp points. If the residual value equals mx_resp_len then no * bytes have been written. */ int sg_ll_mode_sense10_v2(int sg_fd, bool llbaa, bool dbd, int pc, int pg_code, int sub_pg_code, void * resp, int mx_resp_len, int timeout_secs, int * residp, bool noisy, int verbose) { int res, ret, k, sense_cat, resid; static const char * const cdb_name_s = "mode sense(10)"; struct sg_pt_base * ptvp; unsigned char modes_cdb[MODE_SENSE10_CMDLEN] = {MODE_SENSE10_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0}; unsigned char sense_b[SENSE_BUFF_LEN]; modes_cdb[1] = (unsigned char)((dbd ? 0x8 : 0) | (llbaa ? 0x10 : 0)); modes_cdb[2] = (unsigned char)(((pc << 6) & 0xc0) | (pg_code & 0x3f)); modes_cdb[3] = (unsigned char)(sub_pg_code & 0xff); sg_put_unaligned_be16((int16_t)mx_resp_len, modes_cdb + 7); if (mx_resp_len > 0xffff) { pr2ws("mx_resp_len too big\n"); goto gen_err; } if (verbose) { pr2ws(" %s cdb: ", cdb_name_s); for (k = 0; k < MODE_SENSE10_CMDLEN; ++k) pr2ws("%02x ", modes_cdb[k]); pr2ws("\n"); } if (timeout_secs <= 0) timeout_secs = DEF_PT_TIMEOUT; if (NULL == ((ptvp = create_pt_obj(cdb_name_s)))) goto gen_err; set_scsi_pt_cdb(ptvp, modes_cdb, sizeof(modes_cdb)); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); set_scsi_pt_data_in(ptvp, (unsigned char *)resp, mx_resp_len); res = do_scsi_pt(ptvp, sg_fd, timeout_secs, verbose); ret = sg_cmds_process_resp(ptvp, cdb_name_s, res, mx_resp_len, sense_b, noisy, verbose, &sense_cat); resid = get_scsi_pt_resid(ptvp); if (residp) *residp = resid; if (-1 == ret) { int os_err = get_scsi_pt_os_err(ptvp); if ((os_err > 0) && (os_err < 47)) ret = SG_LIB_OS_BASE_ERR + os_err; } else if (-2 == ret) { switch (sense_cat) { case SG_LIB_CAT_RECOVERED: case SG_LIB_CAT_NO_SENSE: ret = 0; break; default: ret = sense_cat; break; } } else { if ((verbose > 2) && (ret > 0)) { pr2ws(" %s: response", cdb_name_s); if (3 == verbose) { pr2ws("%s:\n", (ret > 256 ? ", first 256 bytes" : "")); hex2stderr((const uint8_t *)resp, (ret > 256 ? 256 : ret), -1); } else { pr2ws(":\n"); hex2stderr((const uint8_t *)resp, ret, 0); } } ret = 0; } destruct_scsi_pt_obj(ptvp); if (resid > 0) { if (resid > mx_resp_len) { pr2ws("%s: resid (%d) should never exceed requested len=%d\n", cdb_name_s, resid, mx_resp_len); return ret ? ret : SG_LIB_CAT_MALFORMED; } /* zero unfilled section of response buffer */ memset((unsigned char *)resp + (mx_resp_len - resid), 0, resid); } return ret; gen_err: if (residp) *residp = 0; return -1; } /* Invokes a SCSI MODE SELECT (6) command. Return of 0 -> success, * various SG_LIB_CAT_* positive values or -1 -> other errors */ int sg_ll_mode_select6(int sg_fd, bool pf, bool sp, void * paramp, int param_len, bool noisy, int verbose) { static const char * const cdb_name_s = "mode select(6)"; int res, ret, k, sense_cat; unsigned char modes_cdb[MODE_SELECT6_CMDLEN] = {MODE_SELECT6_CMD, 0, 0, 0, 0, 0}; unsigned char sense_b[SENSE_BUFF_LEN]; struct sg_pt_base * ptvp; modes_cdb[1] = (unsigned char)((pf ? 0x10 : 0x0) | (sp ? 0x1 : 0x0)); modes_cdb[4] = (unsigned char)(param_len & 0xff); if (param_len > 0xff) { pr2ws("%s: param_len too big\n", cdb_name_s); return -1; } if (verbose) { pr2ws(" %s cdb: ", cdb_name_s); for (k = 0; k < MODE_SELECT6_CMDLEN; ++k) pr2ws("%02x ", modes_cdb[k]); pr2ws("\n"); } if (verbose > 1) { pr2ws(" %s parameter list\n", cdb_name_s); hex2stderr((const uint8_t *)paramp, param_len, -1); } if (NULL == ((ptvp = create_pt_obj(cdb_name_s)))) return -1; set_scsi_pt_cdb(ptvp, modes_cdb, sizeof(modes_cdb)); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); set_scsi_pt_data_out(ptvp, (unsigned char *)paramp, param_len); res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose); ret = sg_cmds_process_resp(ptvp, cdb_name_s, res, SG_NO_DATA_IN, sense_b, noisy, verbose, &sense_cat); if (-1 == ret) { int os_err = get_scsi_pt_os_err(ptvp); if ((os_err > 0) && (os_err < 47)) ret = SG_LIB_OS_BASE_ERR + os_err; } else if (-2 == ret) { switch (sense_cat) { case SG_LIB_CAT_RECOVERED: case SG_LIB_CAT_NO_SENSE: ret = 0; break; default: ret = sense_cat; break; } } else ret = 0; destruct_scsi_pt_obj(ptvp); return ret; } /* Invokes a SCSI MODE SELECT (10) command. Return of 0 -> success, * various SG_LIB_CAT_* positive values or -1 -> other errors */ int sg_ll_mode_select10(int sg_fd, bool pf, bool sp, void * paramp, int param_len, bool noisy, int verbose) { static const char * const cdb_name_s = "mode select(10)"; int res, ret, k, sense_cat; unsigned char modes_cdb[MODE_SELECT10_CMDLEN] = {MODE_SELECT10_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0}; unsigned char sense_b[SENSE_BUFF_LEN]; struct sg_pt_base * ptvp; modes_cdb[1] = (unsigned char)((pf ? 0x10 : 0x0) | (sp ? 0x1 : 0x0)); sg_put_unaligned_be16((int16_t)param_len, modes_cdb + 7); if (param_len > 0xffff) { pr2ws("%s: param_len too big\n", cdb_name_s); return -1; } if (verbose) { pr2ws(" %s cdb: ", cdb_name_s); for (k = 0; k < MODE_SELECT10_CMDLEN; ++k) pr2ws("%02x ", modes_cdb[k]); pr2ws("\n"); } if (verbose > 1) { pr2ws(" %s parameter list\n", cdb_name_s); hex2stderr((const uint8_t *)paramp, param_len, -1); } if (NULL == ((ptvp = create_pt_obj(cdb_name_s)))) return -1; set_scsi_pt_cdb(ptvp, modes_cdb, sizeof(modes_cdb)); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); set_scsi_pt_data_out(ptvp, (unsigned char *)paramp, param_len); res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose); ret = sg_cmds_process_resp(ptvp, cdb_name_s, res, SG_NO_DATA_IN, sense_b, noisy, verbose, &sense_cat); if (-1 == ret) { int os_err = get_scsi_pt_os_err(ptvp); if ((os_err > 0) && (os_err < 47)) ret = SG_LIB_OS_BASE_ERR + os_err; } else if (-2 == ret) { switch (sense_cat) { case SG_LIB_CAT_RECOVERED: case SG_LIB_CAT_NO_SENSE: ret = 0; break; default: ret = sense_cat; break; } } else ret = 0; destruct_scsi_pt_obj(ptvp); return ret; } /* MODE SENSE commands yield a response that has header then zero or more * block descriptors followed by mode pages. In most cases users are * interested in the first mode page. This function returns the (byte) * offset of the start of the first mode page. Set mode_sense_6 to true for * MODE SENSE (6) and false for MODE SENSE (10). Returns >= 0 is successful * or -1 if failure. If there is a failure a message is written to err_buff * if it is non-NULL and err_buff_len > 0. */ int sg_mode_page_offset(const unsigned char * resp, int resp_len, bool mode_sense_6, char * err_buff, int err_buff_len) { int bd_len, calc_len, offset; bool err_buff_ok = ((err_buff_len > 0) && err_buff); if ((NULL == resp) || (resp_len < 4)) goto too_short; if (mode_sense_6) { calc_len = resp[0] + 1; bd_len = resp[3]; offset = bd_len + MODE6_RESP_HDR_LEN; } else { /* Mode sense(10) */ if (resp_len < 8) goto too_short; calc_len = sg_get_unaligned_be16(resp) + 2; bd_len = sg_get_unaligned_be16(resp + 6); /* LongLBA doesn't change this calculation */ offset = bd_len + MODE10_RESP_HDR_LEN; } if ((offset + 2) > calc_len) { if (err_buff_ok) snprintf(err_buff, err_buff_len, "calculated response " "length too small, offset=%d calc_len=%d bd_len=%d\n", offset, calc_len, bd_len); offset = -1; } return offset; too_short: if (err_buff_ok) snprintf(err_buff, err_buff_len, "given MS(%d) response length (%d) " "too short\n", (mode_sense_6 ? 6 : 10), resp_len); return -1; } /* MODE SENSE commands yield a response that has header then zero or more * block descriptors followed by mode pages. This functions returns the * length (in bytes) of those three components. Note that the return value * can exceed resp_len in which case the MODE SENSE command should be * re-issued with a larger response buffer. If bd_lenp is non-NULL and if * successful the block descriptor length (in bytes) is written to *bd_lenp. * Set mode_sense_6 to true for MODE SENSE (6) and false for MODE SENSE (10) * responses. Returns -1 if there is an error (e.g. response too short). */ int sg_msense_calc_length(const unsigned char * resp, int resp_len, bool mode_sense_6, int * bd_lenp) { int calc_len; if (NULL == resp) goto an_err; if (mode_sense_6) { if (resp_len < 4) goto an_err; calc_len = resp[0] + 1; } else { if (resp_len < 8) goto an_err; calc_len = sg_get_unaligned_be16(resp + 0) + 2; } if (bd_lenp) *bd_lenp = mode_sense_6 ? resp[3] : sg_get_unaligned_be16(resp + 6); return calc_len; an_err: if (bd_lenp) *bd_lenp = 0; return -1; } /* Fetches current, changeable, default and/or saveable modes pages as * indicated by pcontrol_arr for given pg_code and sub_pg_code. If * mode6==false then use MODE SENSE (10) else use MODE SENSE (6). If * flexible set and mode data length seems wrong then try and * fix (compensating hack for bad device or driver). pcontrol_arr * should have 4 elements for output of current, changeable, default * and saved values respectively. Each element should be NULL or * at least mx_mpage_len bytes long. * Return of 0 -> overall success, various SG_LIB_CAT_* positive values or * -1 -> other errors. * If success_mask pointer is not NULL then first zeros it. Then set bits * 0, 1, 2 and/or 3 if the current, changeable, default and saved values * respectively have been fetched. If error on current page * then stops and returns that error; otherwise continues if an error is * detected but returns the first error encountered. */ int sg_get_mode_page_controls(int sg_fd, bool mode6, int pg_code, int sub_pg_code, bool dbd, bool flexible, int mx_mpage_len, int * success_mask, void * pcontrol_arr[], int * reported_lenp, int verbose) { bool resp_mode6; int k, n, res, offset, calc_len, xfer_len; int resid = 0; const int msense10_hlen = MODE10_RESP_HDR_LEN; unsigned char buff[MODE_RESP_ARB_LEN]; char ebuff[EBUFF_SZ]; int first_err = 0; if (success_mask) *success_mask = 0; if (reported_lenp) *reported_lenp = 0; if (mx_mpage_len < 4) return 0; memset(ebuff, 0, sizeof(ebuff)); /* first try to find length of current page response */ memset(buff, 0, msense10_hlen); if (mode6) /* want first 8 bytes just in case */ res = sg_ll_mode_sense6(sg_fd, dbd, 0 /* pc */, pg_code, sub_pg_code, buff, msense10_hlen, true, verbose); else /* MODE SENSE(10) obviously */ res = sg_ll_mode_sense10_v2(sg_fd, false /* llbaa */, dbd, 0 /* pc */, pg_code, sub_pg_code, buff, msense10_hlen, 0, &resid, true, verbose); if (0 != res) return res; n = buff[0]; if (reported_lenp) { int m; m = sg_msense_calc_length(buff, msense10_hlen, mode6, NULL) - resid; if (m < 0) /* Grrr, this should not happen */ m = 0; *reported_lenp = m; } resp_mode6 = mode6; if (flexible) { if (mode6 && (n < 3)) { resp_mode6 = false; if (verbose) pr2ws(">>> msense(6) but resp[0]=%d so try msense(10) " "response processing\n", n); } if ((! mode6) && (n > 5)) { if ((n > 11) && (0 == (n % 2)) && (0 == buff[4]) && (0 == buff[5]) && (0 == buff[6])) { buff[1] = n; buff[0] = 0; if (verbose) pr2ws(">>> msense(10) but resp[0]=%d and not msense(6) " "response so fix length\n", n); } else resp_mode6 = true; } } if (verbose && (resp_mode6 != mode6)) pr2ws(">>> msense(%d) but resp[0]=%d so switch response " "processing\n", (mode6 ? 6 : 10), buff[0]); calc_len = sg_msense_calc_length(buff, msense10_hlen, resp_mode6, NULL); if (calc_len > MODE_RESP_ARB_LEN) calc_len = MODE_RESP_ARB_LEN; offset = sg_mode_page_offset(buff, calc_len, resp_mode6, ebuff, EBUFF_SZ); if (offset < 0) { if (('\0' != ebuff[0]) && (verbose > 0)) pr2ws("%s: %s\n", __func__, ebuff); return SG_LIB_CAT_MALFORMED; } xfer_len = calc_len - offset; if (xfer_len > mx_mpage_len) xfer_len = mx_mpage_len; for (k = 0; k < 4; ++k) { if (NULL == pcontrol_arr[k]) continue; memset(pcontrol_arr[k], 0, mx_mpage_len); resid = 0; if (mode6) res = sg_ll_mode_sense6(sg_fd, dbd, k /* pc */, pg_code, sub_pg_code, buff, calc_len, true, verbose); else res = sg_ll_mode_sense10_v2(sg_fd, false /* llbaa */, dbd, k /* pc */, pg_code, sub_pg_code, buff, calc_len, 0, &resid, true, verbose); if (res || resid) { if (0 == first_err) { if (res) first_err = res; else { first_err = -49; /* unexpected resid != 0 */ if (verbose) pr2ws("%s: unexpected resid=%d, page=0x%x, " "pcontrol=%d\n", __func__, resid, pg_code, k); } } if (0 == k) break; /* if problem on current page, it won't improve */ else continue; } if (xfer_len > 0) memcpy(pcontrol_arr[k], buff + offset, xfer_len); if (success_mask) *success_mask |= (1 << k); } return first_err; } /* Invokes a SCSI LOG SENSE command. Return of 0 -> success, * various SG_LIB_CAT_* positive values or -1 -> other errors. */ int sg_ll_log_sense(int sg_fd, bool ppc, bool sp, int pc, int pg_code, int subpg_code, int paramp, unsigned char * resp, int mx_resp_len, bool noisy, int verbose) { return sg_ll_log_sense_v2(sg_fd, ppc, sp, pc, pg_code, subpg_code, paramp, resp, mx_resp_len, 0, NULL, noisy, verbose); } /* Invokes a SCSI LOG SENSE command. Return of 0 -> success, * various SG_LIB_CAT_* positive values or -1 -> other errors. * Adds the ability to set the command abort timeout * and the ability to report the residual count. If timeout_secs is zero * or less the default command abort timeout (60 seconds) is used. * If residp is non-NULL then the residual value is written where residp * points. A residual value of 0 implies mx_resp_len bytes have be written * where resp points. If the residual value equals mx_resp_len then no * bytes have been written. */ int sg_ll_log_sense_v2(int sg_fd, bool ppc, bool sp, int pc, int pg_code, int subpg_code, int paramp, unsigned char * resp, int mx_resp_len, int timeout_secs, int * residp, bool noisy, int verbose) { static const char * const cdb_name_s = "log sense"; int res, ret, k, sense_cat, resid; unsigned char logs_cdb[LOG_SENSE_CMDLEN] = {LOG_SENSE_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0}; unsigned char sense_b[SENSE_BUFF_LEN]; struct sg_pt_base * ptvp; if (mx_resp_len > 0xffff) { pr2ws("mx_resp_len too big\n"); goto gen_err; } logs_cdb[1] = (unsigned char)((ppc ? 2 : 0) | (sp ? 1 : 0)); logs_cdb[2] = (unsigned char)(((pc << 6) & 0xc0) | (pg_code & 0x3f)); logs_cdb[3] = (unsigned char)(subpg_code & 0xff); sg_put_unaligned_be16((int16_t)paramp, logs_cdb + 5); sg_put_unaligned_be16((int16_t)mx_resp_len, logs_cdb + 7); if (verbose) { pr2ws(" %s cdb: ", cdb_name_s); for (k = 0; k < LOG_SENSE_CMDLEN; ++k) pr2ws("%02x ", logs_cdb[k]); pr2ws("\n"); } if (timeout_secs <= 0) timeout_secs = DEF_PT_TIMEOUT; if (NULL == ((ptvp = create_pt_obj(cdb_name_s)))) goto gen_err; set_scsi_pt_cdb(ptvp, logs_cdb, sizeof(logs_cdb)); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); set_scsi_pt_data_in(ptvp, resp, mx_resp_len); res = do_scsi_pt(ptvp, sg_fd, timeout_secs, verbose); ret = sg_cmds_process_resp(ptvp, cdb_name_s, res, mx_resp_len, sense_b, noisy, verbose, &sense_cat); resid = get_scsi_pt_resid(ptvp); if (residp) *residp = resid; if (-1 == ret) { int os_err = get_scsi_pt_os_err(ptvp); if ((os_err > 0) && (os_err < 47)) ret = SG_LIB_OS_BASE_ERR + os_err; } else if (-2 == ret) { switch (sense_cat) { case SG_LIB_CAT_RECOVERED: case SG_LIB_CAT_NO_SENSE: ret = 0; break; default: ret = sense_cat; break; } } else { if ((mx_resp_len > 3) && (ret < 4)) { /* resid indicates LOG SENSE response length bad, so zero it */ resp[2] = 0; resp[3] = 0; } ret = 0; } destruct_scsi_pt_obj(ptvp); if (resid > 0) { if (resid > mx_resp_len) { pr2ws("%s: resid (%d) should never exceed requested len=%d\n", cdb_name_s, resid, mx_resp_len); return ret ? ret : SG_LIB_CAT_MALFORMED; } /* zero unfilled section of response buffer */ memset((unsigned char *)resp + (mx_resp_len - resid), 0, resid); } return ret; gen_err: if (residp) *residp = 0; return -1; } /* Invokes a SCSI LOG SELECT command. Return of 0 -> success, * various SG_LIB_CAT_* positive values or -1 -> other errors */ int sg_ll_log_select(int sg_fd, bool pcr, bool sp, int pc, int pg_code, int subpg_code, unsigned char * paramp, int param_len, bool noisy, int verbose) { static const char * const cdb_name_s = "log select"; int res, ret, k, sense_cat; unsigned char logs_cdb[LOG_SELECT_CMDLEN] = {LOG_SELECT_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0}; unsigned char sense_b[SENSE_BUFF_LEN]; struct sg_pt_base * ptvp; if (param_len > 0xffff) { pr2ws("%s: param_len too big\n", cdb_name_s); return -1; } logs_cdb[1] = (unsigned char)((pcr ? 2 : 0) | (sp ? 1 : 0)); logs_cdb[2] = (unsigned char)(((pc << 6) & 0xc0) | (pg_code & 0x3f)); logs_cdb[3] = (unsigned char)(subpg_code & 0xff); sg_put_unaligned_be16((int16_t)param_len, logs_cdb + 7); if (verbose) { pr2ws(" %s cdb: ", cdb_name_s); for (k = 0; k < LOG_SELECT_CMDLEN; ++k) pr2ws("%02x ", logs_cdb[k]); pr2ws("\n"); } if ((verbose > 1) && (param_len > 0)) { pr2ws(" %s parameter list\n", cdb_name_s); hex2stderr(paramp, param_len, -1); } if (NULL == ((ptvp = create_pt_obj(cdb_name_s)))) return -1; set_scsi_pt_cdb(ptvp, logs_cdb, sizeof(logs_cdb)); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); set_scsi_pt_data_out(ptvp, paramp, param_len); res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose); ret = sg_cmds_process_resp(ptvp, cdb_name_s, res, SG_NO_DATA_IN, sense_b, noisy, verbose, &sense_cat); if (-1 == ret) { int os_err = get_scsi_pt_os_err(ptvp); if ((os_err > 0) && (os_err < 47)) ret = SG_LIB_OS_BASE_ERR + os_err; } else if (-2 == ret) { switch (sense_cat) { case SG_LIB_CAT_RECOVERED: case SG_LIB_CAT_NO_SENSE: ret = 0; break; default: ret = sense_cat; break; } } else ret = 0; destruct_scsi_pt_obj(ptvp); return ret; } /* Invokes a SCSI START STOP UNIT command (SBC + MMC). * Return of 0 -> success, * various SG_LIB_CAT_* positive values or -1 -> other errors. * SBC-3 and MMC partially overlap on the power_condition_modifier(sbc) and * format_layer_number(mmc) fields. They also overlap on the noflush(sbc) * and fl(mmc) one bit field. This is the cause of the awkardly named * pc_mod__fl_num and noflush__fl arguments to this function. * */ int sg_ll_start_stop_unit(int sg_fd, bool immed, int pc_mod__fl_num, int power_cond, bool noflush__fl, bool loej, bool start, bool noisy, int verbose) { static const char * const cdb_name_s = "start stop unit"; int k, res, ret, sense_cat; struct sg_pt_base * ptvp; unsigned char ssuBlk[START_STOP_CMDLEN] = {START_STOP_CMD, 0, 0, 0, 0, 0}; unsigned char sense_b[SENSE_BUFF_LEN]; if (immed) ssuBlk[1] = 0x1; ssuBlk[3] = pc_mod__fl_num & 0xf; /* bits 2 and 3 are reserved in MMC */ ssuBlk[4] = ((power_cond & 0xf) << 4); if (noflush__fl) ssuBlk[4] |= 0x4; if (loej) ssuBlk[4] |= 0x2; if (start) ssuBlk[4] |= 0x1; if (verbose) { pr2ws(" %s command:", cdb_name_s); for (k = 0; k < (int)sizeof(ssuBlk); ++k) pr2ws(" %02x", ssuBlk[k]); pr2ws("\n"); } if (NULL == ((ptvp = create_pt_obj(cdb_name_s)))) return -1; set_scsi_pt_cdb(ptvp, ssuBlk, sizeof(ssuBlk)); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); res = do_scsi_pt(ptvp, sg_fd, START_PT_TIMEOUT, verbose); ret = sg_cmds_process_resp(ptvp, cdb_name_s, res, SG_NO_DATA_IN, sense_b, noisy, verbose, &sense_cat); if (-1 == ret) { int os_err = get_scsi_pt_os_err(ptvp); if ((os_err > 0) && (os_err < 47)) ret = SG_LIB_OS_BASE_ERR + os_err; } else if (-2 == ret) { switch (sense_cat) { case SG_LIB_CAT_RECOVERED: case SG_LIB_CAT_NO_SENSE: ret = 0; break; default: ret = sense_cat; break; } } else ret = 0; destruct_scsi_pt_obj(ptvp); return ret; } /* Invokes a SCSI PREVENT ALLOW MEDIUM REMOVAL command * [was in SPC-3 but displaced from SPC-4 into SBC-3, MMC-5, SSC-3] * prevent==0 allows removal, prevent==1 prevents removal ... * Return of 0 -> success, * various SG_LIB_CAT_* positive values or -1 -> other errors */ int sg_ll_prevent_allow(int sg_fd, int prevent, bool noisy, int verbose) { static const char * const cdb_name_s = "prevent allow medium removal"; int k, res, ret, sense_cat; unsigned char p_cdb[PREVENT_ALLOW_CMDLEN] = {PREVENT_ALLOW_CMD, 0, 0, 0, 0, 0}; unsigned char sense_b[SENSE_BUFF_LEN]; struct sg_pt_base * ptvp; if ((prevent < 0) || (prevent > 3)) { pr2ws("prevent argument should be 0, 1, 2 or 3\n"); return -1; } p_cdb[4] |= (prevent & 0x3); if (verbose) { pr2ws(" %s cdb: ", cdb_name_s); for (k = 0; k < PREVENT_ALLOW_CMDLEN; ++k) pr2ws("%02x ", p_cdb[k]); pr2ws("\n"); } if (NULL == ((ptvp = create_pt_obj(cdb_name_s)))) return -1; set_scsi_pt_cdb(ptvp, p_cdb, sizeof(p_cdb)); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose); ret = sg_cmds_process_resp(ptvp, cdb_name_s, res, SG_NO_DATA_IN, sense_b, noisy, verbose, &sense_cat); if (-1 == ret) { int os_err = get_scsi_pt_os_err(ptvp); if ((os_err > 0) && (os_err < 47)) ret = SG_LIB_OS_BASE_ERR + os_err; } else if (-2 == ret) { switch (sense_cat) { case SG_LIB_CAT_RECOVERED: case SG_LIB_CAT_NO_SENSE: ret = 0; break; default: ret = sense_cat; break; } } else ret = 0; destruct_scsi_pt_obj(ptvp); return ret; } f2fs-tools-1.11.0/tools/sg_write_buffer/sg_cmds_extra.c000066400000000000000000002563121332120552100230750ustar00rootroot00000000000000/* * Copyright (c) 1999-2018 Douglas Gilbert. * All rights reserved. * Use of this source code is governed by a BSD-style * license that can be found in the BSD_LICENSE file. */ #include #include #include #include #include #define __STDC_FORMAT_MACROS 1 #include #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "sg_lib.h" #include "sg_lib_data.h" #include "sg_cmds_basic.h" #include "sg_cmds_extra.h" #include "sg_pt.h" #include "sg_unaligned.h" #define SENSE_BUFF_LEN 64 /* Arbitrary, could be larger */ #define DEF_PT_TIMEOUT 60 /* 60 seconds */ #define LONG_PT_TIMEOUT 7200 /* 7,200 seconds == 120 minutes */ #define SERVICE_ACTION_IN_16_CMD 0x9e #define SERVICE_ACTION_IN_16_CMDLEN 16 #define SERVICE_ACTION_OUT_16_CMD 0x9f #define SERVICE_ACTION_OUT_16_CMDLEN 16 #define MAINTENANCE_IN_CMD 0xa3 #define MAINTENANCE_IN_CMDLEN 12 #define MAINTENANCE_OUT_CMD 0xa4 #define MAINTENANCE_OUT_CMDLEN 12 #define ATA_PT_12_CMD 0xa1 #define ATA_PT_12_CMDLEN 12 #define ATA_PT_16_CMD 0x85 #define ATA_PT_16_CMDLEN 16 #define ATA_PT_32_SA 0x1ff0 #define ATA_PT_32_CMDLEN 32 #define FORMAT_UNIT_CMD 0x4 #define FORMAT_UNIT_CMDLEN 6 #define PERSISTENT_RESERVE_IN_CMD 0x5e #define PERSISTENT_RESERVE_IN_CMDLEN 10 #define PERSISTENT_RESERVE_OUT_CMD 0x5f #define PERSISTENT_RESERVE_OUT_CMDLEN 10 #define READ_BLOCK_LIMITS_CMD 0x5 #define READ_BLOCK_LIMITS_CMDLEN 6 #define READ_BUFFER_CMD 0x3c #define READ_BUFFER_CMDLEN 10 #define READ_DEFECT10_CMD 0x37 #define READ_DEFECT10_CMDLEN 10 #define REASSIGN_BLKS_CMD 0x7 #define REASSIGN_BLKS_CMDLEN 6 #define RECEIVE_DIAGNOSTICS_CMD 0x1c #define RECEIVE_DIAGNOSTICS_CMDLEN 6 #define THIRD_PARTY_COPY_OUT_CMD 0x83 /* was EXTENDED_COPY_CMD */ #define THIRD_PARTY_COPY_OUT_CMDLEN 16 #define THIRD_PARTY_COPY_IN_CMD 0x84 /* was RECEIVE_COPY_RESULTS_CMD */ #define THIRD_PARTY_COPY_IN_CMDLEN 16 #define SEND_DIAGNOSTIC_CMD 0x1d #define SEND_DIAGNOSTIC_CMDLEN 6 #define SERVICE_ACTION_IN_12_CMD 0xab #define SERVICE_ACTION_IN_12_CMDLEN 12 #define READ_LONG10_CMD 0x3e #define READ_LONG10_CMDLEN 10 #define UNMAP_CMD 0x42 #define UNMAP_CMDLEN 10 #define VERIFY10_CMD 0x2f #define VERIFY10_CMDLEN 10 #define VERIFY16_CMD 0x8f #define VERIFY16_CMDLEN 16 #define WRITE_LONG10_CMD 0x3f #define WRITE_LONG10_CMDLEN 10 #define WRITE_BUFFER_CMD 0x3b #define WRITE_BUFFER_CMDLEN 10 #define PRE_FETCH10_CMD 0x34 #define PRE_FETCH10_CMDLEN 10 #define PRE_FETCH16_CMD 0x90 #define PRE_FETCH16_CMDLEN 16 #define SEEK10_CMD 0x2b #define SEEK10_CMDLEN 10 #define GET_LBA_STATUS16_SA 0x12 #define GET_LBA_STATUS32_SA 0x12 #define READ_LONG_16_SA 0x11 #define READ_MEDIA_SERIAL_NUM_SA 0x1 #define REPORT_IDENTIFYING_INFORMATION_SA 0x5 #define REPORT_TGT_PRT_GRP_SA 0xa #define SET_IDENTIFYING_INFORMATION_SA 0x6 #define SET_TGT_PRT_GRP_SA 0xa #define WRITE_LONG_16_SA 0x11 #define REPORT_REFERRALS_SA 0x13 #define EXTENDED_COPY_LID1_SA 0x0 #if defined(__GNUC__) || defined(__clang__) static int pr2ws(const char * fmt, ...) __attribute__ ((format (printf, 1, 2))); #else static int pr2ws(const char * fmt, ...); #endif static int pr2ws(const char * fmt, ...) { va_list args; int n; va_start(args, fmt); n = vfprintf(sg_warnings_strm ? sg_warnings_strm : stderr, fmt, args); va_end(args); return n; } static struct sg_pt_base * create_pt_obj(const char * cname) { struct sg_pt_base * ptvp = construct_scsi_pt_obj(); if (NULL == ptvp) pr2ws("%s: out of memory\n", cname); return ptvp; } /* Invokes a SCSI GET LBA STATUS(16) command (SBC). Returns 0 -> success, * various SG_LIB_CAT_* positive values or -1 -> other errors */ int sg_ll_get_lba_status16(int sg_fd, uint64_t start_llba, uint8_t rt, void * resp, int alloc_len, bool noisy, int verbose) { static const char * const cdb_name_s = "Get LBA status(16)"; int k, res, sense_cat, ret; unsigned char getLbaStatCmd[SERVICE_ACTION_IN_16_CMDLEN]; unsigned char sense_b[SENSE_BUFF_LEN]; struct sg_pt_base * ptvp; memset(getLbaStatCmd, 0, sizeof(getLbaStatCmd)); getLbaStatCmd[0] = SERVICE_ACTION_IN_16_CMD; getLbaStatCmd[1] = GET_LBA_STATUS16_SA; sg_put_unaligned_be64(start_llba, getLbaStatCmd + 2); sg_put_unaligned_be32((uint32_t)alloc_len, getLbaStatCmd + 10); getLbaStatCmd[14] = rt; if (verbose) { pr2ws(" %s cdb: ", cdb_name_s); for (k = 0; k < SERVICE_ACTION_IN_16_CMDLEN; ++k) pr2ws("%02x ", getLbaStatCmd[k]); pr2ws("\n"); } if (NULL == ((ptvp = create_pt_obj(cdb_name_s)))) return -1; set_scsi_pt_cdb(ptvp, getLbaStatCmd, sizeof(getLbaStatCmd)); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); set_scsi_pt_data_in(ptvp, (unsigned char *)resp, alloc_len); res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose); ret = sg_cmds_process_resp(ptvp, cdb_name_s, res, alloc_len, sense_b, noisy, verbose, &sense_cat); if (-1 == ret) { int os_err = get_scsi_pt_os_err(ptvp); if ((os_err > 0) && (os_err < 47)) ret = SG_LIB_OS_BASE_ERR + os_err; } else if (-2 == ret) { switch (sense_cat) { case SG_LIB_CAT_RECOVERED: case SG_LIB_CAT_NO_SENSE: ret = 0; break; default: ret = sense_cat; break; } } else { if ((verbose > 2) && (ret > 0)) { pr2ws(" %s: response\n", cdb_name_s); if (3 == verbose) { pr2ws("%s:\n", (ret > 256 ? ", first 256 bytes" : "")); hex2stderr((const uint8_t *)resp, (ret > 256 ? 256 : ret), -1); } else { pr2ws(":\n"); hex2stderr((const uint8_t *)resp, ret, 0); } } ret = 0; } destruct_scsi_pt_obj(ptvp); return ret; } int sg_ll_get_lba_status(int sg_fd, uint64_t start_llba, void * resp, int alloc_len, bool noisy, int verbose) { return sg_ll_get_lba_status16(sg_fd, start_llba, /* rt = */ 0x0, resp, alloc_len, noisy, verbose); } #define GLS32_CMD_LEN 32 int sg_ll_get_lba_status32(int sg_fd, uint64_t start_llba, uint32_t scan_len, uint32_t element_id, uint8_t rt, void * resp, int alloc_len, bool noisy, int verbose) { static const char * const cdb_name_s = "Get LBA status(32)"; int k, res, sense_cat, ret; unsigned char gls32_cmd[GLS32_CMD_LEN]; unsigned char sense_b[SENSE_BUFF_LEN]; struct sg_pt_base * ptvp; memset(gls32_cmd, 0, sizeof(gls32_cmd)); gls32_cmd[0] = SG_VARIABLE_LENGTH_CMD; gls32_cmd[7] = GLS32_CMD_LEN - 8; sg_put_unaligned_be16((uint16_t)GET_LBA_STATUS32_SA, gls32_cmd + 8); gls32_cmd[10] = rt; sg_put_unaligned_be64(start_llba, gls32_cmd + 12); sg_put_unaligned_be32(scan_len, gls32_cmd + 20); sg_put_unaligned_be32(element_id, gls32_cmd + 24); sg_put_unaligned_be32((uint32_t)alloc_len, gls32_cmd + 28); if (verbose) { pr2ws(" %s cdb: ", cdb_name_s); for (k = 0; k < GLS32_CMD_LEN; ++k) pr2ws("%02x ", gls32_cmd[k]); pr2ws("\n"); } if (NULL == ((ptvp = create_pt_obj(cdb_name_s)))) return -1; set_scsi_pt_cdb(ptvp, gls32_cmd, sizeof(gls32_cmd)); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); set_scsi_pt_data_in(ptvp, (unsigned char *)resp, alloc_len); res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose); ret = sg_cmds_process_resp(ptvp, cdb_name_s, res, alloc_len, sense_b, noisy, verbose, &sense_cat); if (-1 == ret) { int os_err = get_scsi_pt_os_err(ptvp); if ((os_err > 0) && (os_err < 47)) ret = SG_LIB_OS_BASE_ERR + os_err; } else if (-2 == ret) { switch (sense_cat) { case SG_LIB_CAT_RECOVERED: case SG_LIB_CAT_NO_SENSE: ret = 0; break; default: ret = sense_cat; break; } } else { if ((verbose > 2) && (ret > 0)) { pr2ws(" %s: response\n", cdb_name_s); if (3 == verbose) { pr2ws("%s:\n", (ret > 256 ? ", first 256 bytes" : "")); hex2stderr((const uint8_t *)resp, (ret > 256 ? 256 : ret), -1); } else { pr2ws(":\n"); hex2stderr((const uint8_t *)resp, ret, 0); } } ret = 0; } destruct_scsi_pt_obj(ptvp); return ret; } int sg_ll_report_tgt_prt_grp(int sg_fd, void * resp, int mx_resp_len, bool noisy, int verbose) { return sg_ll_report_tgt_prt_grp2(sg_fd, resp, mx_resp_len, false, noisy, verbose); } /* Invokes a SCSI REPORT TARGET PORT GROUPS command. Return of 0 -> success, * various SG_LIB_CAT_* positive values or -1 -> other errors */ int sg_ll_report_tgt_prt_grp2(int sg_fd, void * resp, int mx_resp_len, bool extended, bool noisy, int verbose) { static const char * const cdb_name_s = "Report target port groups"; int k, res, ret, sense_cat; unsigned char rtpg_cdb[MAINTENANCE_IN_CMDLEN] = {MAINTENANCE_IN_CMD, REPORT_TGT_PRT_GRP_SA, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; unsigned char sense_b[SENSE_BUFF_LEN]; struct sg_pt_base * ptvp; if (extended) rtpg_cdb[1] |= 0x20; sg_put_unaligned_be32((uint32_t)mx_resp_len, rtpg_cdb + 6); if (verbose) { pr2ws(" %s cdb: ", cdb_name_s); for (k = 0; k < MAINTENANCE_IN_CMDLEN; ++k) pr2ws("%02x ", rtpg_cdb[k]); pr2ws("\n"); } if (NULL == ((ptvp = create_pt_obj(cdb_name_s)))) return -1; set_scsi_pt_cdb(ptvp, rtpg_cdb, sizeof(rtpg_cdb)); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); set_scsi_pt_data_in(ptvp, (unsigned char *)resp, mx_resp_len); res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose); ret = sg_cmds_process_resp(ptvp, cdb_name_s, res, mx_resp_len, sense_b, noisy, verbose, &sense_cat); if (-1 == ret) { int os_err = get_scsi_pt_os_err(ptvp); if ((os_err > 0) && (os_err < 47)) ret = SG_LIB_OS_BASE_ERR + os_err; } else if (-2 == ret) { switch (sense_cat) { case SG_LIB_CAT_RECOVERED: case SG_LIB_CAT_NO_SENSE: ret = 0; break; default: ret = sense_cat; break; } } else { if ((verbose > 2) && (ret > 0)) { pr2ws(" %s: response", cdb_name_s); if (3 == verbose) { pr2ws("%s:\n", (ret > 256 ? ", first 256 bytes" : "")); hex2stderr((const uint8_t *)resp, (ret > 256 ? 256 : ret), -1); } else { pr2ws(":\n"); hex2stderr((const uint8_t *)resp, ret, 0); } } ret = 0; } destruct_scsi_pt_obj(ptvp); return ret; } /* Invokes a SCSI SET TARGET PORT GROUPS command. Return of 0 -> success, * various SG_LIB_CAT_* positive values or -1 -> other errors */ int sg_ll_set_tgt_prt_grp(int sg_fd, void * paramp, int param_len, bool noisy, int verbose) { static const char * const cdb_name_s = "Set target port groups"; int k, res, ret, sense_cat; unsigned char stpg_cdb[MAINTENANCE_OUT_CMDLEN] = {MAINTENANCE_OUT_CMD, SET_TGT_PRT_GRP_SA, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; unsigned char sense_b[SENSE_BUFF_LEN]; struct sg_pt_base * ptvp; sg_put_unaligned_be32((uint32_t)param_len, stpg_cdb + 6); if (verbose) { pr2ws(" %s cdb: ", cdb_name_s); for (k = 0; k < MAINTENANCE_OUT_CMDLEN; ++k) pr2ws("%02x ", stpg_cdb[k]); pr2ws("\n"); if ((verbose > 1) && paramp && param_len) { pr2ws(" %s parameter list:\n", cdb_name_s); hex2stderr((const uint8_t *)paramp, param_len, -1); } } if (NULL == ((ptvp = create_pt_obj(cdb_name_s)))) return -1; set_scsi_pt_cdb(ptvp, stpg_cdb, sizeof(stpg_cdb)); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); set_scsi_pt_data_out(ptvp, (unsigned char *)paramp, param_len); res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose); ret = sg_cmds_process_resp(ptvp, cdb_name_s, res, SG_NO_DATA_IN, sense_b, noisy, verbose, &sense_cat); if (-1 == ret) { int os_err = get_scsi_pt_os_err(ptvp); if ((os_err > 0) && (os_err < 47)) ret = SG_LIB_OS_BASE_ERR + os_err; } else if (-2 == ret) { switch (sense_cat) { case SG_LIB_CAT_RECOVERED: case SG_LIB_CAT_NO_SENSE: ret = 0; break; default: ret = sense_cat; break; } } else ret = 0; destruct_scsi_pt_obj(ptvp); return ret; } /* Invokes a SCSI REPORT REFERRALS command. Return of 0 -> success, * various SG_LIB_CAT_* positive values or -1 -> other errors */ int sg_ll_report_referrals(int sg_fd, uint64_t start_llba, bool one_seg, void * resp, int mx_resp_len, bool noisy, int verbose) { static const char * const cdb_name_s = "Report referrals"; int k, res, ret, sense_cat; unsigned char repRef_cdb[SERVICE_ACTION_IN_16_CMDLEN] = {SERVICE_ACTION_IN_16_CMD, REPORT_REFERRALS_SA, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; unsigned char sense_b[SENSE_BUFF_LEN]; struct sg_pt_base * ptvp; sg_put_unaligned_be64(start_llba, repRef_cdb + 2); sg_put_unaligned_be32((uint32_t)mx_resp_len, repRef_cdb + 10); if (one_seg) repRef_cdb[14] = 0x1; if (verbose) { pr2ws(" %s cdb: ", cdb_name_s); for (k = 0; k < SERVICE_ACTION_IN_16_CMDLEN; ++k) pr2ws("%02x ", repRef_cdb[k]); pr2ws("\n"); } if (NULL == ((ptvp = create_pt_obj(cdb_name_s)))) return -1; set_scsi_pt_cdb(ptvp, repRef_cdb, sizeof(repRef_cdb)); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); set_scsi_pt_data_in(ptvp, (unsigned char *)resp, mx_resp_len); res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose); ret = sg_cmds_process_resp(ptvp, cdb_name_s, res, mx_resp_len, sense_b, noisy, verbose, &sense_cat); if (-1 == ret) { int os_err = get_scsi_pt_os_err(ptvp); if ((os_err > 0) && (os_err < 47)) ret = SG_LIB_OS_BASE_ERR + os_err; } else if (-2 == ret) { switch (sense_cat) { case SG_LIB_CAT_RECOVERED: case SG_LIB_CAT_NO_SENSE: ret = 0; break; default: ret = sense_cat; break; } } else { if ((verbose > 2) && (ret > 0)) { pr2ws(" %s: response", cdb_name_s); if (3 == verbose) { pr2ws("%s:\n", (ret > 256 ? ", first 256 bytes" : "")); hex2stderr((const uint8_t *)resp, (ret > 256 ? 256 : ret), -1); } else { pr2ws(":\n"); hex2stderr((const uint8_t *)resp, ret, 0); } } ret = 0; } destruct_scsi_pt_obj(ptvp); return ret; } /* Invokes a SCSI SEND DIAGNOSTIC command. Foreground, extended self tests can * take a long time, if so set long_duration flag in which case the timeout * is set to 7200 seconds; if the value of long_duration is > 7200 then that * value is taken as the timeout value in seconds. Return of 0 -> success, * various SG_LIB_CAT_* positive values or -1 -> other errors */ int sg_ll_send_diag(int sg_fd, int st_code, bool pf_bit, bool st_bit, bool devofl_bit, bool unitofl_bit, int long_duration, void * paramp, int param_len, bool noisy, int verbose) { static const char * const cdb_name_s = "Send diagnostic"; int k, res, ret, sense_cat, tmout; unsigned char senddiag_cdb[SEND_DIAGNOSTIC_CMDLEN] = {SEND_DIAGNOSTIC_CMD, 0, 0, 0, 0, 0}; unsigned char sense_b[SENSE_BUFF_LEN]; struct sg_pt_base * ptvp; senddiag_cdb[1] = (unsigned char)(st_code << 5); if (pf_bit) senddiag_cdb[1] |= 0x10; if (st_bit) senddiag_cdb[1] |= 0x4; if (devofl_bit) senddiag_cdb[1] |= 0x2; if (unitofl_bit) senddiag_cdb[1] |= 0x1; sg_put_unaligned_be16((uint16_t)param_len, senddiag_cdb + 3); if (long_duration > LONG_PT_TIMEOUT) tmout = long_duration; else tmout = long_duration ? LONG_PT_TIMEOUT : DEF_PT_TIMEOUT; if (verbose) { pr2ws(" %s cdb: ", cdb_name_s); for (k = 0; k < SEND_DIAGNOSTIC_CMDLEN; ++k) pr2ws("%02x ", senddiag_cdb[k]); pr2ws("\n"); if (verbose > 1) { if (paramp && param_len) { pr2ws(" %s parameter list:\n", cdb_name_s); hex2stderr((const uint8_t *)paramp, param_len, -1); } pr2ws(" %s timeout: %d seconds\n", cdb_name_s, tmout); } } if (NULL == ((ptvp = create_pt_obj(cdb_name_s)))) return -1; set_scsi_pt_cdb(ptvp, senddiag_cdb, sizeof(senddiag_cdb)); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); set_scsi_pt_data_out(ptvp, (unsigned char *)paramp, param_len); res = do_scsi_pt(ptvp, sg_fd, tmout, verbose); ret = sg_cmds_process_resp(ptvp, cdb_name_s, res, SG_NO_DATA_IN, sense_b, noisy, verbose, &sense_cat); if (-1 == ret) { int os_err = get_scsi_pt_os_err(ptvp); if ((os_err > 0) && (os_err < 47)) ret = SG_LIB_OS_BASE_ERR + os_err; } else if (-2 == ret) { switch (sense_cat) { case SG_LIB_CAT_RECOVERED: case SG_LIB_CAT_NO_SENSE: ret = 0; break; default: ret = sense_cat; break; } } else ret = 0; destruct_scsi_pt_obj(ptvp); return ret; } /* Invokes a SCSI RECEIVE DIAGNOSTIC RESULTS command. Return of 0 -> success, * various SG_LIB_CAT_* positive values or -1 -> other errors */ int sg_ll_receive_diag(int sg_fd, bool pcv, int pg_code, void * resp, int mx_resp_len, bool noisy, int verbose) { return sg_ll_receive_diag_v2(sg_fd, pcv, pg_code, resp, mx_resp_len, 0, NULL, noisy, verbose); } /* Invokes a SCSI RECEIVE DIAGNOSTIC RESULTS command. Return of 0 -> success, * various SG_LIB_CAT_* positive values or -1 -> other errors */ int sg_ll_receive_diag_v2(int sg_fd, bool pcv, int pg_code, void * resp, int mx_resp_len, int timeout_secs, int * residp, bool noisy, int verbose) { int resid = 0; int k, res, ret, sense_cat; static const char * const cdb_name_s = "Receive diagnostic results"; struct sg_pt_base * ptvp; unsigned char rcvdiag_cdb[RECEIVE_DIAGNOSTICS_CMDLEN] = {RECEIVE_DIAGNOSTICS_CMD, 0, 0, 0, 0, 0}; unsigned char sense_b[SENSE_BUFF_LEN]; if (pcv) rcvdiag_cdb[1] = 0x1; rcvdiag_cdb[2] = (unsigned char)(pg_code); sg_put_unaligned_be16((uint16_t)mx_resp_len, rcvdiag_cdb + 3); if (verbose) { pr2ws(" %s cdb: ", cdb_name_s); for (k = 0; k < RECEIVE_DIAGNOSTICS_CMDLEN; ++k) pr2ws("%02x ", rcvdiag_cdb[k]); pr2ws("\n"); } if (timeout_secs <= 0) timeout_secs = DEF_PT_TIMEOUT; if (NULL == ((ptvp = create_pt_obj(cdb_name_s)))) { if (residp) *residp = 0; return -1; } set_scsi_pt_cdb(ptvp, rcvdiag_cdb, sizeof(rcvdiag_cdb)); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); set_scsi_pt_data_in(ptvp, (unsigned char *)resp, mx_resp_len); res = do_scsi_pt(ptvp, sg_fd, timeout_secs, verbose); ret = sg_cmds_process_resp(ptvp, cdb_name_s, res, mx_resp_len, sense_b, noisy, verbose, &sense_cat); resid = get_scsi_pt_resid(ptvp); if (residp) *residp = resid; if (-1 == ret) { int os_err = get_scsi_pt_os_err(ptvp); if ((os_err > 0) && (os_err < 47)) ret = SG_LIB_OS_BASE_ERR + os_err; } else if (-2 == ret) { switch (sense_cat) { case SG_LIB_CAT_RECOVERED: case SG_LIB_CAT_NO_SENSE: ret = 0; break; default: ret = sense_cat; break; } } else { if ((verbose > 2) && (ret > 0)) { pr2ws(" %s: response", cdb_name_s); if (3 == verbose) { pr2ws("%s:\n", (ret > 256 ? ", first 256 bytes" : "")); hex2stderr((const uint8_t *)resp, (ret > 256 ? 256 : ret), -1); } else { pr2ws(":\n"); hex2stderr((const uint8_t *)resp, ret, 0); } } ret = 0; } destruct_scsi_pt_obj(ptvp); return ret; } /* Invokes a SCSI READ DEFECT DATA (10) command (SBC). Return of 0 -> success * various SG_LIB_CAT_* positive values or -1 -> other errors */ int sg_ll_read_defect10(int sg_fd, bool req_plist, bool req_glist, int dl_format, void * resp, int mx_resp_len, bool noisy, int verbose) { static const char * const cdb_name_s = "Read defect(10)"; int res, k, ret, sense_cat; unsigned char rdef_cdb[READ_DEFECT10_CMDLEN] = {READ_DEFECT10_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0}; unsigned char sense_b[SENSE_BUFF_LEN]; struct sg_pt_base * ptvp; rdef_cdb[2] = (dl_format & 0x7); if (req_plist) rdef_cdb[2] |= 0x10; if (req_glist) rdef_cdb[2] |= 0x8; sg_put_unaligned_be16((uint16_t)mx_resp_len, rdef_cdb + 7); if (mx_resp_len > 0xffff) { pr2ws("mx_resp_len too big\n"); return -1; } if (verbose) { pr2ws(" %s cdb: ", cdb_name_s); for (k = 0; k < READ_DEFECT10_CMDLEN; ++k) pr2ws("%02x ", rdef_cdb[k]); pr2ws("\n"); } if (NULL == ((ptvp = create_pt_obj(cdb_name_s)))) return -1; set_scsi_pt_cdb(ptvp, rdef_cdb, sizeof(rdef_cdb)); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); set_scsi_pt_data_in(ptvp, (unsigned char *)resp, mx_resp_len); res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose); ret = sg_cmds_process_resp(ptvp, cdb_name_s, res, mx_resp_len, sense_b, noisy, verbose, &sense_cat); if (-1 == ret) { int os_err = get_scsi_pt_os_err(ptvp); if ((os_err > 0) && (os_err < 47)) ret = SG_LIB_OS_BASE_ERR + os_err; } else if (-2 == ret) { switch (sense_cat) { case SG_LIB_CAT_RECOVERED: case SG_LIB_CAT_NO_SENSE: ret = 0; break; default: ret = sense_cat; break; } } else { if ((verbose > 2) && (ret > 0)) { pr2ws(" %s: response\n", cdb_name_s); if (3 == verbose) { pr2ws("%s:\n", (ret > 256 ? ", first 256 bytes" : "")); hex2stderr((const uint8_t *)resp, (ret > 256 ? 256 : ret), -1); } else { pr2ws(":\n"); hex2stderr((const uint8_t *)resp, ret, 0); } } ret = 0; } destruct_scsi_pt_obj(ptvp); return ret; } /* Invokes a SCSI READ MEDIA SERIAL NUMBER command. Return of 0 -> success, * various SG_LIB_CAT_* positive values or -1 -> other errors */ int sg_ll_read_media_serial_num(int sg_fd, void * resp, int mx_resp_len, bool noisy, int verbose) { static const char * const cdb_name_s = "Read media serial number"; int k, res, ret, sense_cat; unsigned char rmsn_cdb[SERVICE_ACTION_IN_12_CMDLEN] = {SERVICE_ACTION_IN_12_CMD, READ_MEDIA_SERIAL_NUM_SA, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; unsigned char sense_b[SENSE_BUFF_LEN]; struct sg_pt_base * ptvp; sg_put_unaligned_be32((uint32_t)mx_resp_len, rmsn_cdb + 6); if (verbose) { pr2ws(" %s cdb: ", cdb_name_s); for (k = 0; k < SERVICE_ACTION_IN_12_CMDLEN; ++k) pr2ws("%02x ", rmsn_cdb[k]); pr2ws("\n"); } if (NULL == ((ptvp = create_pt_obj(cdb_name_s)))) return -1; set_scsi_pt_cdb(ptvp, rmsn_cdb, sizeof(rmsn_cdb)); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); set_scsi_pt_data_in(ptvp, (unsigned char *)resp, mx_resp_len); res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose); ret = sg_cmds_process_resp(ptvp, cdb_name_s, res, mx_resp_len, sense_b, noisy, verbose, &sense_cat); if (-1 == ret) { int os_err = get_scsi_pt_os_err(ptvp); if ((os_err > 0) && (os_err < 47)) ret = SG_LIB_OS_BASE_ERR + os_err; } else if (-2 == ret) { switch (sense_cat) { case SG_LIB_CAT_RECOVERED: case SG_LIB_CAT_NO_SENSE: ret = 0; break; default: ret = sense_cat; break; } } else { if ((verbose > 2) && (ret > 0)) { pr2ws(" %s: response", cdb_name_s); if (3 == verbose) { pr2ws("%s:\n", (ret > 256 ? ", first 256 bytes" : "")); hex2stderr((const uint8_t *)resp, (ret > 256 ? 256 : ret), -1); } else { pr2ws(":\n"); hex2stderr((const uint8_t *)resp, ret, 0); } } ret = 0; } destruct_scsi_pt_obj(ptvp); return ret; } /* Invokes a SCSI REPORT IDENTIFYING INFORMATION command. This command was * called REPORT DEVICE IDENTIFIER prior to spc4r07. Return of 0 -> success, * various SG_LIB_CAT_* positive values or -1 -> other errors */ int sg_ll_report_id_info(int sg_fd, int itype, void * resp, int max_resp_len, bool noisy, int verbose) { static const char * const cdb_name_s = "Report identifying information"; int k, res, ret, sense_cat; unsigned char rii_cdb[MAINTENANCE_IN_CMDLEN] = {MAINTENANCE_IN_CMD, REPORT_IDENTIFYING_INFORMATION_SA, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; unsigned char sense_b[SENSE_BUFF_LEN]; struct sg_pt_base * ptvp; sg_put_unaligned_be32((uint32_t)max_resp_len, rii_cdb + 6); rii_cdb[10] |= (itype << 1) & 0xfe; if (verbose) { pr2ws(" %s cdb: ", cdb_name_s); for (k = 0; k < MAINTENANCE_IN_CMDLEN; ++k) pr2ws("%02x ", rii_cdb[k]); pr2ws("\n"); } if (NULL == ((ptvp = create_pt_obj(cdb_name_s)))) return -1; set_scsi_pt_cdb(ptvp, rii_cdb, sizeof(rii_cdb)); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); set_scsi_pt_data_in(ptvp, (unsigned char *)resp, max_resp_len); res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose); ret = sg_cmds_process_resp(ptvp, cdb_name_s, res, max_resp_len, sense_b, noisy, verbose, &sense_cat); if (-1 == ret) { int os_err = get_scsi_pt_os_err(ptvp); if ((os_err > 0) && (os_err < 47)) ret = SG_LIB_OS_BASE_ERR + os_err; } else if (-2 == ret) { switch (sense_cat) { case SG_LIB_CAT_RECOVERED: case SG_LIB_CAT_NO_SENSE: ret = 0; break; default: ret = sense_cat; break; } } else { if ((verbose > 2) && (ret > 0)) { pr2ws(" %s: response", cdb_name_s); if (3 == verbose) { pr2ws("%s:\n", (ret > 256 ? ", first 256 bytes" : "")); hex2stderr((const uint8_t *)resp, (ret > 256 ? 256 : ret), -1); } else { pr2ws(":\n"); hex2stderr((const uint8_t *)resp, ret, 0); } } ret = 0; } destruct_scsi_pt_obj(ptvp); return ret; } /* Invokes a SCSI SET IDENTIFYING INFORMATION command. This command was * called SET DEVICE IDENTIFIER prior to spc4r07. Return of 0 -> success, * various SG_LIB_CAT_* positive values or -1 -> other errors */ int sg_ll_set_id_info(int sg_fd, int itype, void * paramp, int param_len, bool noisy, int verbose) { static const char * const cdb_name_s = "Set identifying information"; int k, res, ret, sense_cat; unsigned char sii_cdb[MAINTENANCE_OUT_CMDLEN] = {MAINTENANCE_OUT_CMD, SET_IDENTIFYING_INFORMATION_SA, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; unsigned char sense_b[SENSE_BUFF_LEN]; struct sg_pt_base * ptvp; sg_put_unaligned_be32((uint32_t)param_len, sii_cdb + 6); sii_cdb[10] |= (itype << 1) & 0xfe; if (verbose) { pr2ws(" %s cdb: ", cdb_name_s); for (k = 0; k < MAINTENANCE_OUT_CMDLEN; ++k) pr2ws("%02x ", sii_cdb[k]); pr2ws("\n"); if ((verbose > 1) && paramp && param_len) { pr2ws(" %s parameter list:\n", cdb_name_s); hex2stderr((const uint8_t *)paramp, param_len, -1); } } if (NULL == ((ptvp = create_pt_obj(cdb_name_s)))) return -1; set_scsi_pt_cdb(ptvp, sii_cdb, sizeof(sii_cdb)); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); set_scsi_pt_data_out(ptvp, (unsigned char *)paramp, param_len); res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose); ret = sg_cmds_process_resp(ptvp, cdb_name_s, res, SG_NO_DATA_IN, sense_b, noisy, verbose, &sense_cat); if (-1 == ret) { int os_err = get_scsi_pt_os_err(ptvp); if ((os_err > 0) && (os_err < 47)) ret = SG_LIB_OS_BASE_ERR + os_err; } else if (-2 == ret) { switch (sense_cat) { case SG_LIB_CAT_RECOVERED: case SG_LIB_CAT_NO_SENSE: ret = 0; break; default: ret = sense_cat; break; } } else ret = 0; destruct_scsi_pt_obj(ptvp); return ret; } /* Invokes a FORMAT UNIT (SBC-3) command. Return of 0 -> success, * various SG_LIB_CAT_* positive values or -1 -> other errors */ int sg_ll_format_unit(int sg_fd, int fmtpinfo, bool longlist, bool fmtdata, bool cmplst, int dlist_format, int timeout_secs, void * paramp, int param_len, bool noisy, int verbose) { return sg_ll_format_unit_v2(sg_fd, fmtpinfo, longlist, fmtdata, cmplst, dlist_format, 0, timeout_secs, paramp, param_len, noisy, verbose); } /* Invokes a FORMAT UNIT (SBC-3) command. Return of 0 -> success, * various SG_LIB_CAT_* positive values or -1 -> other errors */ int sg_ll_format_unit2(int sg_fd, int fmtpinfo, bool longlist, bool fmtdata, bool cmplst, int dlist_format, int ffmt, int timeout_secs, void * paramp, int param_len, bool noisy, int verbose) { return sg_ll_format_unit_v2(sg_fd, fmtpinfo, longlist, fmtdata, cmplst, dlist_format, ffmt, timeout_secs, paramp, param_len, noisy, verbose); } /* Invokes a FORMAT UNIT (SBC-4) command. Return of 0 -> success, * various SG_LIB_CAT_* positive values or -1 -> other errors. * FFMT field added in sbc4r10 [20160121] */ int sg_ll_format_unit_v2(int sg_fd, int fmtpinfo, bool longlist, bool fmtdata, bool cmplst, int dlist_format, int ffmt, int timeout_secs, void * paramp, int param_len, bool noisy, int verbose) { static const char * const cdb_name_s = "Format unit"; int k, res, ret, sense_cat, tmout; unsigned char fu_cdb[FORMAT_UNIT_CMDLEN] = {FORMAT_UNIT_CMD, 0, 0, 0, 0, 0}; unsigned char sense_b[SENSE_BUFF_LEN]; struct sg_pt_base * ptvp; if (fmtpinfo) fu_cdb[1] |= (fmtpinfo << 6); if (longlist) fu_cdb[1] |= 0x20; if (fmtdata) fu_cdb[1] |= 0x10; if (cmplst) fu_cdb[1] |= 0x8; if (dlist_format) fu_cdb[1] |= (dlist_format & 0x7); if (ffmt) fu_cdb[4] |= (ffmt & 0x3); tmout = (timeout_secs > 0) ? timeout_secs : DEF_PT_TIMEOUT; if (verbose) { pr2ws(" %s cdb: ", cdb_name_s); for (k = 0; k < 6; ++k) pr2ws("%02x ", fu_cdb[k]); pr2ws("\n"); if (verbose > 1) { if (param_len > 0) { pr2ws(" %s parameter list:\n", cdb_name_s); hex2stderr((const uint8_t *)paramp, param_len, -1); } pr2ws(" %s timeout: %d seconds\n", cdb_name_s, tmout); } } if (NULL == ((ptvp = create_pt_obj(cdb_name_s)))) return -1; set_scsi_pt_cdb(ptvp, fu_cdb, sizeof(fu_cdb)); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); set_scsi_pt_data_out(ptvp, (unsigned char *)paramp, param_len); res = do_scsi_pt(ptvp, sg_fd, tmout, verbose); ret = sg_cmds_process_resp(ptvp, cdb_name_s, res, SG_NO_DATA_IN, sense_b, noisy, verbose, &sense_cat); if (-1 == ret) { int os_err = get_scsi_pt_os_err(ptvp); if ((os_err > 0) && (os_err < 47)) ret = SG_LIB_OS_BASE_ERR + os_err; } else if (-2 == ret) { switch (sense_cat) { case SG_LIB_CAT_RECOVERED: case SG_LIB_CAT_NO_SENSE: ret = 0; break; default: ret = sense_cat; break; } } else ret = 0; destruct_scsi_pt_obj(ptvp); return ret; } /* Invokes a SCSI REASSIGN BLOCKS command. Return of 0 -> success, * various SG_LIB_CAT_* positive values or -1 -> other errors */ int sg_ll_reassign_blocks(int sg_fd, bool longlba, bool longlist, void * paramp, int param_len, bool noisy, int verbose) { static const char * const cdb_name_s = "Reassign blocks"; int res, k, ret, sense_cat; unsigned char reass_cdb[REASSIGN_BLKS_CMDLEN] = {REASSIGN_BLKS_CMD, 0, 0, 0, 0, 0}; unsigned char sense_b[SENSE_BUFF_LEN]; struct sg_pt_base * ptvp; if (longlba) reass_cdb[1] = 0x2; if (longlist) reass_cdb[1] |= 0x1; if (verbose) { pr2ws(" %s cdb: ", cdb_name_s); for (k = 0; k < REASSIGN_BLKS_CMDLEN; ++k) pr2ws("%02x ", reass_cdb[k]); pr2ws("\n"); } if (verbose > 1) { pr2ws(" %s parameter list\n", cdb_name_s); hex2stderr((const uint8_t *)paramp, param_len, -1); } if (NULL == ((ptvp = create_pt_obj(cdb_name_s)))) return -1; set_scsi_pt_cdb(ptvp, reass_cdb, sizeof(reass_cdb)); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); set_scsi_pt_data_out(ptvp, (unsigned char *)paramp, param_len); res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose); ret = sg_cmds_process_resp(ptvp, cdb_name_s, res, SG_NO_DATA_IN, sense_b, noisy, verbose, &sense_cat); if (-1 == ret) { int os_err = get_scsi_pt_os_err(ptvp); if ((os_err > 0) && (os_err < 47)) ret = SG_LIB_OS_BASE_ERR + os_err; } else if (-2 == ret) { switch (sense_cat) { case SG_LIB_CAT_RECOVERED: case SG_LIB_CAT_NO_SENSE: ret = 0; break; default: ret = sense_cat; break; } } else ret = 0; destruct_scsi_pt_obj(ptvp); return ret; } /* Invokes a SCSI PERSISTENT RESERVE IN command (SPC). Returns 0 * when successful, various SG_LIB_CAT_* positive values or * -1 -> other errors */ int sg_ll_persistent_reserve_in(int sg_fd, int rq_servact, void * resp, int mx_resp_len, bool noisy, int verbose) { static const char * const cdb_name_s = "Persistent reservation in"; int res, k, ret, sense_cat; unsigned char prin_cdb[PERSISTENT_RESERVE_IN_CMDLEN] = {PERSISTENT_RESERVE_IN_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0}; unsigned char sense_b[SENSE_BUFF_LEN]; struct sg_pt_base * ptvp; if (rq_servact > 0) prin_cdb[1] = (unsigned char)(rq_servact & 0x1f); sg_put_unaligned_be16((uint16_t)mx_resp_len, prin_cdb + 7); if (verbose) { pr2ws(" %s cdb: ", cdb_name_s); for (k = 0; k < PERSISTENT_RESERVE_IN_CMDLEN; ++k) pr2ws("%02x ", prin_cdb[k]); pr2ws("\n"); } if (NULL == ((ptvp = create_pt_obj(cdb_name_s)))) return -1; set_scsi_pt_cdb(ptvp, prin_cdb, sizeof(prin_cdb)); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); set_scsi_pt_data_in(ptvp, (unsigned char *)resp, mx_resp_len); res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose); ret = sg_cmds_process_resp(ptvp, cdb_name_s, res, mx_resp_len, sense_b, noisy, verbose, &sense_cat); if (-1 == ret) { int os_err = get_scsi_pt_os_err(ptvp); if ((os_err > 0) && (os_err < 47)) ret = SG_LIB_OS_BASE_ERR + os_err; } else if (-2 == ret) { switch (sense_cat) { case SG_LIB_CAT_RECOVERED: case SG_LIB_CAT_NO_SENSE: ret = 0; break; default: ret = sense_cat; break; } } else { if ((verbose > 2) && (ret > 0)) { pr2ws(" %s: response", cdb_name_s); if (3 == verbose) { pr2ws("%s:\n", (ret > 256 ? ", first 256 bytes" : "")); hex2stderr((const uint8_t *)resp, (ret > 256 ? 256 : ret), -1); } else { pr2ws(":\n"); hex2stderr((const uint8_t *)resp, ret, 0); } } ret = 0; } destruct_scsi_pt_obj(ptvp); return ret; } /* Invokes a SCSI PERSISTENT RESERVE OUT command (SPC). Returns 0 * when successful, various SG_LIB_CAT_* positive values or * -1 -> other errors */ int sg_ll_persistent_reserve_out(int sg_fd, int rq_servact, int rq_scope, unsigned int rq_type, void * paramp, int param_len, bool noisy, int verbose) { static const char * const cdb_name_s = "Persistent reservation out"; int res, k, ret, sense_cat; unsigned char prout_cdb[PERSISTENT_RESERVE_OUT_CMDLEN] = {PERSISTENT_RESERVE_OUT_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0}; unsigned char sense_b[SENSE_BUFF_LEN]; struct sg_pt_base * ptvp; if (rq_servact > 0) prout_cdb[1] = (unsigned char)(rq_servact & 0x1f); prout_cdb[2] = (((rq_scope & 0xf) << 4) | (rq_type & 0xf)); sg_put_unaligned_be16((uint16_t)param_len, prout_cdb + 7); if (verbose) { pr2ws(" %s cdb: ", cdb_name_s); for (k = 0; k < PERSISTENT_RESERVE_OUT_CMDLEN; ++k) pr2ws("%02x ", prout_cdb[k]); pr2ws("\n"); if (verbose > 1) { pr2ws(" %s parameters:\n", cdb_name_s); hex2stderr((const uint8_t *)paramp, param_len, 0); } } if (NULL == ((ptvp = create_pt_obj(cdb_name_s)))) return -1; set_scsi_pt_cdb(ptvp, prout_cdb, sizeof(prout_cdb)); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); set_scsi_pt_data_out(ptvp, (unsigned char *)paramp, param_len); res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose); ret = sg_cmds_process_resp(ptvp, cdb_name_s, res, SG_NO_DATA_IN, sense_b, noisy, verbose, &sense_cat); if (-1 == ret) { int os_err = get_scsi_pt_os_err(ptvp); if ((os_err > 0) && (os_err < 47)) ret = SG_LIB_OS_BASE_ERR + os_err; } else if (-2 == ret) { switch (sense_cat) { case SG_LIB_CAT_RECOVERED: case SG_LIB_CAT_NO_SENSE: ret = 0; break; default: ret = sense_cat; break; } } else ret = 0; destruct_scsi_pt_obj(ptvp); return ret; } static bool has_blk_ili(unsigned char * sensep, int sb_len) { int resp_code; const unsigned char * cup; if (sb_len < 8) return false; resp_code = (0x7f & sensep[0]); if (resp_code >= 0x72) { /* descriptor format */ /* find block command descriptor */ if ((cup = sg_scsi_sense_desc_find(sensep, sb_len, 0x5))) return (cup[3] & 0x20); } else /* fixed */ return (sensep[2] & 0x20); return false; } /* Invokes a SCSI READ LONG (10) command (SBC). Note that 'xfer_len' * is in bytes. Returns 0 -> success, * various SG_LIB_CAT_* positive values or -1 -> other errors */ int sg_ll_read_long10(int sg_fd, bool pblock, bool correct, unsigned int lba, void * resp, int xfer_len, int * offsetp, bool noisy, int verbose) { static const char * const cdb_name_s = "read long(10)"; int k, res, sense_cat, ret; unsigned char readLong_cdb[READ_LONG10_CMDLEN]; unsigned char sense_b[SENSE_BUFF_LEN]; struct sg_pt_base * ptvp; memset(readLong_cdb, 0, READ_LONG10_CMDLEN); readLong_cdb[0] = READ_LONG10_CMD; if (pblock) readLong_cdb[1] |= 0x4; if (correct) readLong_cdb[1] |= 0x2; sg_put_unaligned_be32((uint32_t)lba, readLong_cdb + 2); sg_put_unaligned_be16((uint16_t)xfer_len, readLong_cdb + 7); if (verbose) { pr2ws(" %s cdb: ", cdb_name_s); for (k = 0; k < READ_LONG10_CMDLEN; ++k) pr2ws("%02x ", readLong_cdb[k]); pr2ws("\n"); } if (NULL == ((ptvp = create_pt_obj(cdb_name_s)))) return -1; set_scsi_pt_cdb(ptvp, readLong_cdb, sizeof(readLong_cdb)); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); set_scsi_pt_data_in(ptvp, (unsigned char *)resp, xfer_len); res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose); ret = sg_cmds_process_resp(ptvp, cdb_name_s, res, xfer_len, sense_b, noisy, verbose, &sense_cat); if (-1 == ret) { int os_err = get_scsi_pt_os_err(ptvp); if ((os_err > 0) && (os_err < 47)) ret = SG_LIB_OS_BASE_ERR + os_err; } else if (-2 == ret) { switch (sense_cat) { case SG_LIB_CAT_RECOVERED: case SG_LIB_CAT_NO_SENSE: ret = 0; break; case SG_LIB_CAT_ILLEGAL_REQ: { bool valid, ili; int slen; uint64_t ull = 0; slen = get_scsi_pt_sense_len(ptvp); valid = sg_get_sense_info_fld(sense_b, slen, &ull); ili = has_blk_ili(sense_b, slen); if (valid && ili) { if (offsetp) *offsetp = (int)(int64_t)ull; ret = SG_LIB_CAT_ILLEGAL_REQ_WITH_INFO; } else { if (verbose > 1) pr2ws(" info field: 0x%" PRIx64 ", valid: %d, " "ili: %d\n", ull, valid, ili); ret = SG_LIB_CAT_ILLEGAL_REQ; } } break; default: ret = sense_cat; break; } } else { if ((verbose > 2) && (ret > 0)) { pr2ws(" %s: response", cdb_name_s); if (3 == verbose) { pr2ws("%s:\n", (ret > 256 ? ", first 256 bytes" : "")); hex2stderr((const uint8_t *)resp, (ret > 256 ? 256 : ret), -1); } else { pr2ws(":\n"); hex2stderr((const uint8_t *)resp, ret, 0); } } ret = 0; } destruct_scsi_pt_obj(ptvp); return ret; } /* Invokes a SCSI READ LONG (16) command (SBC). Note that 'xfer_len' * is in bytes. Returns 0 -> success, * various SG_LIB_CAT_* positive values or -1 -> other errors */ int sg_ll_read_long16(int sg_fd, bool pblock, bool correct, uint64_t llba, void * resp, int xfer_len, int * offsetp, bool noisy, int verbose) { static const char * const cdb_name_s = "read long(16)"; int k, res, sense_cat, ret; unsigned char readLong_cdb[SERVICE_ACTION_IN_16_CMDLEN]; unsigned char sense_b[SENSE_BUFF_LEN]; struct sg_pt_base * ptvp; memset(readLong_cdb, 0, sizeof(readLong_cdb)); readLong_cdb[0] = SERVICE_ACTION_IN_16_CMD; readLong_cdb[1] = READ_LONG_16_SA; if (pblock) readLong_cdb[14] |= 0x2; if (correct) readLong_cdb[14] |= 0x1; sg_put_unaligned_be64(llba, readLong_cdb + 2); sg_put_unaligned_be16((uint16_t)xfer_len, readLong_cdb + 12); if (verbose) { pr2ws(" %s cdb: ", cdb_name_s); for (k = 0; k < SERVICE_ACTION_IN_16_CMDLEN; ++k) pr2ws("%02x ", readLong_cdb[k]); pr2ws("\n"); } if (NULL == ((ptvp = create_pt_obj(cdb_name_s)))) return -1; set_scsi_pt_cdb(ptvp, readLong_cdb, sizeof(readLong_cdb)); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); set_scsi_pt_data_in(ptvp, (unsigned char *)resp, xfer_len); res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose); ret = sg_cmds_process_resp(ptvp, cdb_name_s, res, xfer_len, sense_b, noisy, verbose, &sense_cat); if (-1 == ret) { int os_err = get_scsi_pt_os_err(ptvp); if ((os_err > 0) && (os_err < 47)) ret = SG_LIB_OS_BASE_ERR + os_err; } else if (-2 == ret) { switch (sense_cat) { case SG_LIB_CAT_RECOVERED: case SG_LIB_CAT_NO_SENSE: ret = 0; break; case SG_LIB_CAT_ILLEGAL_REQ: { bool valid, ili; int slen; uint64_t ull = 0; slen = get_scsi_pt_sense_len(ptvp); valid = sg_get_sense_info_fld(sense_b, slen, &ull); ili = has_blk_ili(sense_b, slen); if (valid && ili) { if (offsetp) *offsetp = (int)(int64_t)ull; ret = SG_LIB_CAT_ILLEGAL_REQ_WITH_INFO; } else { if (verbose > 1) pr2ws(" info field: 0x%" PRIx64 ", valid: %d, " "ili: %d\n", ull, (int)valid, (int)ili); ret = SG_LIB_CAT_ILLEGAL_REQ; } } break; default: ret = sense_cat; break; } } else { if ((verbose > 2) && (ret > 0)) { pr2ws(" %s: response", cdb_name_s); if (3 == verbose) { pr2ws("%s:\n", (ret > 256 ? ", first 256 bytes" : "")); hex2stderr((const uint8_t *)resp, (ret > 256 ? 256 : ret), -1); } else { pr2ws(":\n"); hex2stderr((const uint8_t *)resp, ret, 0); } } ret = 0; } destruct_scsi_pt_obj(ptvp); return ret; } /* Invokes a SCSI WRITE LONG (10) command (SBC). Note that 'xfer_len' * is in bytes. Returns 0 -> success, * various SG_LIB_CAT_* positive values or -1 -> other errors */ int sg_ll_write_long10(int sg_fd, bool cor_dis, bool wr_uncor, bool pblock, unsigned int lba, void * data_out, int xfer_len, int * offsetp, bool noisy, int verbose) { static const char * const cdb_name_s = "write long(10)"; int k, res, sense_cat, ret; unsigned char writeLong_cdb[WRITE_LONG10_CMDLEN]; unsigned char sense_b[SENSE_BUFF_LEN]; struct sg_pt_base * ptvp; memset(writeLong_cdb, 0, WRITE_LONG10_CMDLEN); writeLong_cdb[0] = WRITE_LONG10_CMD; if (cor_dis) writeLong_cdb[1] |= 0x80; if (wr_uncor) writeLong_cdb[1] |= 0x40; if (pblock) writeLong_cdb[1] |= 0x20; sg_put_unaligned_be32((uint32_t)lba, writeLong_cdb + 2); sg_put_unaligned_be16((uint16_t)xfer_len, writeLong_cdb + 7); if (verbose) { pr2ws(" %s cdb: ", cdb_name_s); for (k = 0; k < (int)sizeof(writeLong_cdb); ++k) pr2ws("%02x ", writeLong_cdb[k]); pr2ws("\n"); } if (NULL == ((ptvp = create_pt_obj(cdb_name_s)))) return -1; set_scsi_pt_cdb(ptvp, writeLong_cdb, sizeof(writeLong_cdb)); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); set_scsi_pt_data_out(ptvp, (unsigned char *)data_out, xfer_len); res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose); ret = sg_cmds_process_resp(ptvp, cdb_name_s, res, SG_NO_DATA_IN, sense_b, noisy, verbose, &sense_cat); if (-1 == ret) ; else if (-2 == ret) { switch (sense_cat) { case SG_LIB_CAT_RECOVERED: case SG_LIB_CAT_NO_SENSE: ret = 0; break; case SG_LIB_CAT_ILLEGAL_REQ: { int valid, slen, ili; uint64_t ull = 0; slen = get_scsi_pt_sense_len(ptvp); valid = sg_get_sense_info_fld(sense_b, slen, &ull); ili = has_blk_ili(sense_b, slen); if (valid && ili) { if (offsetp) *offsetp = (int)(int64_t)ull; ret = SG_LIB_CAT_ILLEGAL_REQ_WITH_INFO; } else { if (verbose > 1) pr2ws(" info field: 0x%" PRIx64 ", valid: %d, " "ili: %d\n", ull, (int)valid, (int)ili); ret = SG_LIB_CAT_ILLEGAL_REQ; } } break; default: ret = sense_cat; break; } } else ret = 0; destruct_scsi_pt_obj(ptvp); return ret; } /* Invokes a SCSI WRITE LONG (16) command (SBC). Note that 'xfer_len' * is in bytes. Returns 0 -> success, * various SG_LIB_CAT_* positive values or -1 -> other errors */ int sg_ll_write_long16(int sg_fd, bool cor_dis, bool wr_uncor, bool pblock, uint64_t llba, void * data_out, int xfer_len, int * offsetp, bool noisy, int verbose) { static const char * const cdb_name_s = "write long(16)"; int k, res, sense_cat, ret; unsigned char writeLong_cdb[SERVICE_ACTION_OUT_16_CMDLEN]; unsigned char sense_b[SENSE_BUFF_LEN]; struct sg_pt_base * ptvp; memset(writeLong_cdb, 0, sizeof(writeLong_cdb)); writeLong_cdb[0] = SERVICE_ACTION_OUT_16_CMD; writeLong_cdb[1] = WRITE_LONG_16_SA; if (cor_dis) writeLong_cdb[1] |= 0x80; if (wr_uncor) writeLong_cdb[1] |= 0x40; if (pblock) writeLong_cdb[1] |= 0x20; sg_put_unaligned_be64(llba, writeLong_cdb + 2); sg_put_unaligned_be16((uint16_t)xfer_len, writeLong_cdb + 12); if (verbose) { pr2ws(" %s cdb: ", cdb_name_s); for (k = 0; k < SERVICE_ACTION_OUT_16_CMDLEN; ++k) pr2ws("%02x ", writeLong_cdb[k]); pr2ws("\n"); } if (NULL == ((ptvp = create_pt_obj(cdb_name_s)))) return -1; set_scsi_pt_cdb(ptvp, writeLong_cdb, sizeof(writeLong_cdb)); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); set_scsi_pt_data_out(ptvp, (unsigned char *)data_out, xfer_len); res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose); ret = sg_cmds_process_resp(ptvp, cdb_name_s, res, SG_NO_DATA_IN, sense_b, noisy, verbose, &sense_cat); if (-1 == ret) { int os_err = get_scsi_pt_os_err(ptvp); if ((os_err > 0) && (os_err < 47)) ret = SG_LIB_OS_BASE_ERR + os_err; } else if (-2 == ret) { switch (sense_cat) { case SG_LIB_CAT_RECOVERED: case SG_LIB_CAT_NO_SENSE: ret = 0; break; case SG_LIB_CAT_ILLEGAL_REQ: { bool valid, ili; int slen; uint64_t ull = 0; slen = get_scsi_pt_sense_len(ptvp); valid = sg_get_sense_info_fld(sense_b, slen, &ull); ili = has_blk_ili(sense_b, slen); if (valid && ili) { if (offsetp) *offsetp = (int)(int64_t)ull; ret = SG_LIB_CAT_ILLEGAL_REQ_WITH_INFO; } else { if (verbose > 1) pr2ws(" info field: 0x%" PRIx64 ", valid: %d, " "ili: %d\n", ull, (int)valid, (int)ili); ret = SG_LIB_CAT_ILLEGAL_REQ; } } break; default: ret = sense_cat; break; } } else ret = 0; destruct_scsi_pt_obj(ptvp); return ret; } /* Invokes a SCSI VERIFY (10) command (SBC and MMC). * Note that 'veri_len' is in blocks while 'data_out_len' is in bytes. * Returns of 0 -> success, * various SG_LIB_CAT_* positive values or * -1 -> other errors */ int sg_ll_verify10(int sg_fd, int vrprotect, bool dpo, int bytchk, unsigned int lba, int veri_len, void * data_out, int data_out_len, unsigned int * infop, bool noisy, int verbose) { static const char * const cdb_name_s = "verify(10)"; int k, res, ret, sense_cat, slen; unsigned char v_cdb[VERIFY10_CMDLEN] = {VERIFY10_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0}; unsigned char sense_b[SENSE_BUFF_LEN]; struct sg_pt_base * ptvp; /* N.B. BYTCHK field expanded to 2 bits sbc3r34 */ v_cdb[1] = (((vrprotect & 0x7) << 5) | ((bytchk & 0x3) << 1)) ; if (dpo) v_cdb[1] |= 0x10; sg_put_unaligned_be32((uint32_t)lba, v_cdb + 2); sg_put_unaligned_be16((uint16_t)veri_len, v_cdb + 7); if (verbose > 1) { pr2ws(" %s cdb: ", cdb_name_s); for (k = 0; k < VERIFY10_CMDLEN; ++k) pr2ws("%02x ", v_cdb[k]); pr2ws("\n"); if ((verbose > 3) && bytchk && data_out && (data_out_len > 0)) { k = data_out_len > 4104 ? 4104 : data_out_len; pr2ws(" data_out buffer%s\n", (data_out_len > 4104 ? ", first 4104 bytes" : "")); hex2stderr((const uint8_t *)data_out, k, verbose < 5); } } if (NULL == ((ptvp = create_pt_obj(cdb_name_s)))) return -1; set_scsi_pt_cdb(ptvp, v_cdb, sizeof(v_cdb)); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); if (data_out_len > 0) set_scsi_pt_data_out(ptvp, (unsigned char *)data_out, data_out_len); res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose); ret = sg_cmds_process_resp(ptvp, cdb_name_s, res, SG_NO_DATA_IN, sense_b, noisy, verbose, &sense_cat); if (-1 == ret) { int os_err = get_scsi_pt_os_err(ptvp); if ((os_err > 0) && (os_err < 47)) ret = SG_LIB_OS_BASE_ERR + os_err; } else if (-2 == ret) { switch (sense_cat) { case SG_LIB_CAT_RECOVERED: case SG_LIB_CAT_NO_SENSE: ret = 0; break; case SG_LIB_CAT_MEDIUM_HARD: { bool valid; uint64_t ull = 0; slen = get_scsi_pt_sense_len(ptvp); valid = sg_get_sense_info_fld(sense_b, slen, &ull); if (valid) { if (infop) *infop = (unsigned int)ull; ret = SG_LIB_CAT_MEDIUM_HARD_WITH_INFO; } else ret = SG_LIB_CAT_MEDIUM_HARD; } break; default: ret = sense_cat; break; } } else ret = 0; destruct_scsi_pt_obj(ptvp); return ret; } /* Invokes a SCSI VERIFY (16) command (SBC and MMC). * Note that 'veri_len' is in blocks while 'data_out_len' is in bytes. * Returns of 0 -> success, * various SG_LIB_CAT_* positive values or -1 -> other errors */ int sg_ll_verify16(int sg_fd, int vrprotect, bool dpo, int bytchk, uint64_t llba, int veri_len, int group_num, void * data_out, int data_out_len, uint64_t * infop, bool noisy, int verbose) { static const char * const cdb_name_s = "verify(16)"; int k, res, ret, sense_cat, slen; unsigned char v_cdb[VERIFY16_CMDLEN] = {VERIFY16_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; unsigned char sense_b[SENSE_BUFF_LEN]; struct sg_pt_base * ptvp; /* N.B. BYTCHK field expanded to 2 bits sbc3r34 */ v_cdb[1] = (((vrprotect & 0x7) << 5) | ((bytchk & 0x3) << 1)) ; if (dpo) v_cdb[1] |= 0x10; sg_put_unaligned_be64(llba, v_cdb + 2); sg_put_unaligned_be32((uint32_t)veri_len, v_cdb + 10); v_cdb[14] = group_num & 0x1f; if (verbose > 1) { pr2ws(" %s cdb: ", cdb_name_s); for (k = 0; k < VERIFY16_CMDLEN; ++k) pr2ws("%02x ", v_cdb[k]); pr2ws("\n"); if ((verbose > 3) && bytchk && data_out && (data_out_len > 0)) { k = data_out_len > 4104 ? 4104 : data_out_len; pr2ws(" data_out buffer%s\n", (data_out_len > 4104 ? ", first 4104 bytes" : "")); hex2stderr((const uint8_t *)data_out, k, verbose < 5); } } if (NULL == ((ptvp = create_pt_obj(cdb_name_s)))) return -1; set_scsi_pt_cdb(ptvp, v_cdb, sizeof(v_cdb)); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); if (data_out_len > 0) set_scsi_pt_data_out(ptvp, (unsigned char *)data_out, data_out_len); res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose); ret = sg_cmds_process_resp(ptvp, cdb_name_s, res, SG_NO_DATA_IN, sense_b, noisy, verbose, &sense_cat); if (-1 == ret) { int os_err = get_scsi_pt_os_err(ptvp); if ((os_err > 0) && (os_err < 47)) ret = SG_LIB_OS_BASE_ERR + os_err; } else if (-2 == ret) { switch (sense_cat) { case SG_LIB_CAT_RECOVERED: case SG_LIB_CAT_NO_SENSE: ret = 0; break; case SG_LIB_CAT_MEDIUM_HARD: { bool valid; uint64_t ull = 0; slen = get_scsi_pt_sense_len(ptvp); valid = sg_get_sense_info_fld(sense_b, slen, &ull); if (valid) { if (infop) *infop = ull; ret = SG_LIB_CAT_MEDIUM_HARD_WITH_INFO; } else ret = SG_LIB_CAT_MEDIUM_HARD; } break; default: ret = sense_cat; break; } } else ret = 0; destruct_scsi_pt_obj(ptvp); return ret; } /* Invokes a ATA PASS-THROUGH (12, 16 or 32) SCSI command (SAT). This is * selected by the cdb_len argument that can take values of 12, 16 or 32 * only (else -1 is returned). The byte at offset 0 (and bytes 0 to 9 * inclusive for ATA PT(32)) pointed to be cdbp are ignored and apart from * the control byte, the rest is copied into an internal cdb which is then * sent to the device. The control byte is byte 11 for ATA PT(12), byte 15 * for ATA PT(16) and byte 1 for ATA PT(32). If timeout_secs <= 0 then the * timeout is set to 60 seconds. For data in or out transfers set dinp or * doutp, and dlen to the number of bytes to transfer. If dlen is zero then * no data transfer is assumed. If sense buffer obtained then it is written * to sensep, else sensep[0] is set to 0x0. If ATA return descriptor is * obtained then written to ata_return_dp, else ata_return_dp[0] is set to * 0x0. Either sensep or ata_return_dp (or both) may be NULL pointers. * Returns SCSI status value (>= 0) or -1 if other error. Users are * expected to check the sense buffer themselves. If available the data in * resid is written to residp. Note in SAT-2 and later, fixed format sense * data may be placed in *sensep in which case sensep[0]==0x70, prior to * SAT-2 descriptor sense format was required (i.e. sensep[0]==0x72). */ int sg_ll_ata_pt(int sg_fd, const unsigned char * cdbp, int cdb_len, int timeout_secs, void * dinp, void * doutp, int dlen, unsigned char * sensep, int max_sense_len, unsigned char * ata_return_dp, int max_ata_return_len, int * residp, int verbose) { int k, res, slen, duration; int ret = -1; unsigned char apt_cdb[ATA_PT_32_CMDLEN]; unsigned char sense_b[SENSE_BUFF_LEN]; unsigned char * sp; const unsigned char * bp; struct sg_pt_base * ptvp; const char * cnamep; char b[256]; memset(apt_cdb, 0, sizeof(apt_cdb)); b[0] = '\0'; switch (cdb_len) { case 12: cnamep = "ATA pass-through(12)"; apt_cdb[0] = ATA_PT_12_CMD; memcpy(apt_cdb + 1, cdbp + 1, 10); /* control byte at cdb[11] left at zero */ break; case 16: cnamep = "ATA pass-through(16)"; apt_cdb[0] = ATA_PT_16_CMD; memcpy(apt_cdb + 1, cdbp + 1, 14); /* control byte at cdb[15] left at zero */ break; case 32: cnamep = "ATA pass-through(32)"; apt_cdb[0] = SG_VARIABLE_LENGTH_CMD; /* control byte at cdb[1] left at zero */ apt_cdb[7] = 0x18; /* length starting at next byte */ sg_put_unaligned_be16(ATA_PT_32_SA, apt_cdb + 8); memcpy(apt_cdb + 10, cdbp + 10, 32 - 10); break; default: pr2ws("cdb_len must be 12, 16 or 32\n"); return -1; } if (NULL == cdbp) { if (verbose) pr2ws("%s NULL cdb pointer\n", cnamep); return -1; } if (sensep && (max_sense_len >= (int)sizeof(sense_b))) { sp = sensep; slen = max_sense_len; } else { sp = sense_b; slen = sizeof(sense_b); } if (verbose) { pr2ws(" %s cdb: ", cnamep); if (cdb_len < 32) { for (k = 0; k < cdb_len; ++k) pr2ws("%02x ", apt_cdb[k]); pr2ws("\n"); } else { pr2ws("\n"); hex2stderr(apt_cdb, cdb_len, -1); } } if (NULL == ((ptvp = create_pt_obj(cnamep)))) return -1; set_scsi_pt_cdb(ptvp, apt_cdb, cdb_len); set_scsi_pt_sense(ptvp, sp, slen); if (dlen > 0) { if (dinp) set_scsi_pt_data_in(ptvp, (unsigned char *)dinp, dlen); else if (doutp) set_scsi_pt_data_out(ptvp, (unsigned char *)doutp, dlen); } res = do_scsi_pt(ptvp, sg_fd, ((timeout_secs > 0) ? timeout_secs : DEF_PT_TIMEOUT), verbose); if (SCSI_PT_DO_BAD_PARAMS == res) { if (verbose) pr2ws("%s: bad parameters\n", cnamep); goto out; } else if (SCSI_PT_DO_TIMEOUT == res) { if (verbose) pr2ws("%s: timeout\n", cnamep); goto out; } else if (res > 2) { if (verbose) pr2ws("%s: do_scsi_pt: errno=%d\n", cnamep, -res); } if ((verbose > 2) && ((duration = get_scsi_pt_duration_ms(ptvp)) >= 0)) pr2ws(" duration=%d ms\n", duration); switch (get_scsi_pt_result_category(ptvp)) { case SCSI_PT_RESULT_GOOD: if ((sensep) && (max_sense_len > 0)) *sensep = 0; if ((ata_return_dp) && (max_ata_return_len > 0)) *ata_return_dp = 0; if (residp && (dlen > 0)) *residp = get_scsi_pt_resid(ptvp); ret = 0; break; case SCSI_PT_RESULT_STATUS: /* other than GOOD + CHECK CONDITION */ if ((sensep) && (max_sense_len > 0)) *sensep = 0; if ((ata_return_dp) && (max_ata_return_len > 0)) *ata_return_dp = 0; ret = get_scsi_pt_status_response(ptvp); break; case SCSI_PT_RESULT_SENSE: if (sensep && (sp != sensep)) { k = get_scsi_pt_sense_len(ptvp); k = (k > max_sense_len) ? max_sense_len : k; memcpy(sensep, sp, k); } if (ata_return_dp && (max_ata_return_len > 0)) { /* search for ATA return descriptor */ bp = sg_scsi_sense_desc_find(sp, slen, 0x9); if (bp) { k = bp[1] + 2; k = (k > max_ata_return_len) ? max_ata_return_len : k; memcpy(ata_return_dp, bp, k); } else ata_return_dp[0] = 0x0; } if (residp && (dlen > 0)) *residp = get_scsi_pt_resid(ptvp); ret = get_scsi_pt_status_response(ptvp); break; case SCSI_PT_RESULT_TRANSPORT_ERR: if (verbose) pr2ws("%s: transport error: %s\n", cnamep, get_scsi_pt_transport_err_str(ptvp, sizeof(b), b)); break; case SCSI_PT_RESULT_OS_ERR: if (verbose) pr2ws("%s: os error: %s\n", cnamep, get_scsi_pt_os_err_str(ptvp, sizeof(b) , b)); break; default: if (verbose) pr2ws("%s: unknown pt_result_category=%d\n", cnamep, get_scsi_pt_result_category(ptvp)); break; } out: destruct_scsi_pt_obj(ptvp); return ret; } /* Invokes a SCSI READ BUFFER(10) command (SPC). Return of 0 -> success * various SG_LIB_CAT_* positive values or -1 -> other errors */ int sg_ll_read_buffer(int sg_fd, int mode, int buffer_id, int buffer_offset, void * resp, int mx_resp_len, bool noisy, int verbose) { static const char * const cdb_name_s = "read buffer(10)"; int res, k, ret, sense_cat; unsigned char rbuf_cdb[READ_BUFFER_CMDLEN] = {READ_BUFFER_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0}; unsigned char sense_b[SENSE_BUFF_LEN]; struct sg_pt_base * ptvp; rbuf_cdb[1] = (unsigned char)(mode & 0x1f); rbuf_cdb[2] = (unsigned char)(buffer_id & 0xff); sg_put_unaligned_be24((uint32_t)buffer_offset, rbuf_cdb + 3); sg_put_unaligned_be24((uint32_t)mx_resp_len, rbuf_cdb + 6); if (verbose) { pr2ws(" %s cdb: ", cdb_name_s); for (k = 0; k < READ_BUFFER_CMDLEN; ++k) pr2ws("%02x ", rbuf_cdb[k]); pr2ws("\n"); } if (NULL == ((ptvp = create_pt_obj(cdb_name_s)))) return -1; set_scsi_pt_cdb(ptvp, rbuf_cdb, sizeof(rbuf_cdb)); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); set_scsi_pt_data_in(ptvp, (unsigned char *)resp, mx_resp_len); res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose); ret = sg_cmds_process_resp(ptvp, cdb_name_s, res, mx_resp_len, sense_b, noisy, verbose, &sense_cat); if (-1 == ret) { int os_err = get_scsi_pt_os_err(ptvp); if ((os_err > 0) && (os_err < 47)) ret = SG_LIB_OS_BASE_ERR + os_err; } else if (-2 == ret) { switch (sense_cat) { case SG_LIB_CAT_RECOVERED: case SG_LIB_CAT_NO_SENSE: ret = 0; break; default: ret = sense_cat; break; } } else { if ((verbose > 2) && (ret > 0)) { pr2ws(" %s: response", cdb_name_s); if (3 == verbose) { pr2ws("%s:\n", (ret > 256 ? ", first 256 bytes" : "")); hex2stderr((const uint8_t *)resp, (ret > 256 ? 256 : ret), -1); } else { pr2ws(":\n"); hex2stderr((const uint8_t *)resp, ret, 0); } } ret = 0; } destruct_scsi_pt_obj(ptvp); return ret; } /* Invokes a SCSI WRITE BUFFER command (SPC). Return of 0 -> success * various SG_LIB_CAT_* positive values or -1 -> other errors */ int sg_ll_write_buffer(int sg_fd, int mode, int buffer_id, int buffer_offset, void * paramp, int param_len, bool noisy, int verbose) { static const char * const cdb_name_s = "write buffer"; int k, res, ret, sense_cat; unsigned char wbuf_cdb[WRITE_BUFFER_CMDLEN] = {WRITE_BUFFER_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0}; unsigned char sense_b[SENSE_BUFF_LEN]; struct sg_pt_base * ptvp; wbuf_cdb[1] = (unsigned char)(mode & 0x1f); wbuf_cdb[2] = (unsigned char)(buffer_id & 0xff); sg_put_unaligned_be24((uint32_t)buffer_offset, wbuf_cdb + 3); sg_put_unaligned_be24((uint32_t)param_len, wbuf_cdb + 6); if (verbose) { pr2ws(" %s cdb: ", cdb_name_s); for (k = 0; k < WRITE_BUFFER_CMDLEN; ++k) pr2ws("%02x ", wbuf_cdb[k]); pr2ws("\n"); if ((verbose > 1) && paramp && param_len) { pr2ws(" %s parameter list", cdb_name_s); if (2 == verbose) { pr2ws("%s:\n", (param_len > 256 ? ", first 256 bytes" : "")); hex2stderr((const uint8_t *)paramp, (param_len > 256 ? 256 : param_len), -1); } else { pr2ws(":\n"); hex2stderr((const uint8_t *)paramp, param_len, 0); } } } if (NULL == ((ptvp = create_pt_obj(cdb_name_s)))) return -1; set_scsi_pt_cdb(ptvp, wbuf_cdb, sizeof(wbuf_cdb)); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); set_scsi_pt_data_out(ptvp, (unsigned char *)paramp, param_len); res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose); ret = sg_cmds_process_resp(ptvp, cdb_name_s, res, SG_NO_DATA_IN, sense_b, noisy, verbose, &sense_cat); if (-1 == ret) { int os_err = get_scsi_pt_os_err(ptvp); if ((os_err > 0) && (os_err < 47)) ret = SG_LIB_OS_BASE_ERR + os_err; } else if (-2 == ret) { switch (sense_cat) { case SG_LIB_CAT_RECOVERED: case SG_LIB_CAT_NO_SENSE: ret = 0; break; default: ret = sense_cat; break; } } else ret = 0; destruct_scsi_pt_obj(ptvp); return ret; } /* Invokes a SCSI WRITE BUFFER command (SPC). Return of 0 -> * success, SG_LIB_CAT_INVALID_OP -> invalid opcode, * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION, * SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND, * -1 -> other failure. Adds mode specific field (spc4r32) and timeout * to command abort to override default of 60 seconds. If timeout_secs is * 0 or less then the default timeout is used instead. */ int sg_ll_write_buffer_v2(int sg_fd, int mode, int m_specific, int buffer_id, uint32_t buffer_offset, void * paramp, uint32_t param_len, int timeout_secs, bool noisy, int verbose) { int k, res, ret, sense_cat; uint8_t wbuf_cdb[WRITE_BUFFER_CMDLEN] = {WRITE_BUFFER_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0}; uint8_t sense_b[SENSE_BUFF_LEN]; struct sg_pt_base * ptvp; if (buffer_offset > 0xffffff) { pr2ws("%s: buffer_offset value too large for 24 bits\n", __func__); return -1; } if (param_len > 0xffffff) { pr2ws("%s: param_len value too large for 24 bits\n", __func__); return -1; } wbuf_cdb[1] = (uint8_t)(mode & 0x1f); wbuf_cdb[1] |= (uint8_t)((m_specific & 0x7) << 5); wbuf_cdb[2] = (uint8_t)(buffer_id & 0xff); sg_put_unaligned_be24(buffer_offset, wbuf_cdb + 3); sg_put_unaligned_be24(param_len, wbuf_cdb + 6); if (verbose) { pr2ws(" Write buffer cdb: "); for (k = 0; k < WRITE_BUFFER_CMDLEN; ++k) pr2ws("%02x ", wbuf_cdb[k]); pr2ws("\n"); if ((verbose > 1) && paramp && param_len) { pr2ws(" Write buffer parameter list%s:\n", ((param_len > 256) ? " (first 256 bytes)" : "")); hex2stderr((const uint8_t *)paramp, ((param_len > 256) ? 256 : param_len), -1); } } if (timeout_secs <= 0) timeout_secs = DEF_PT_TIMEOUT; ptvp = construct_scsi_pt_obj(); if (NULL == ptvp) { pr2ws("%s: out of memory\n", __func__); return -1; } set_scsi_pt_cdb(ptvp, wbuf_cdb, sizeof(wbuf_cdb)); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); set_scsi_pt_data_out(ptvp, (uint8_t *)paramp, param_len); res = do_scsi_pt(ptvp, sg_fd, timeout_secs, verbose); ret = sg_cmds_process_resp(ptvp, "Write buffer", res, SG_NO_DATA_IN, sense_b, noisy, verbose, &sense_cat); if (-1 == ret) { int os_err = get_scsi_pt_os_err(ptvp); if ((os_err > 0) && (os_err < 47)) ret = SG_LIB_OS_BASE_ERR + os_err; } else if (-2 == ret) { switch (sense_cat) { case SG_LIB_CAT_RECOVERED: case SG_LIB_CAT_NO_SENSE: ret = 0; break; default: ret = sense_cat; break; } } else ret = 0; destruct_scsi_pt_obj(ptvp); return ret; } /* Invokes a SCSI UNMAP command. Return of 0 -> success, * various SG_LIB_CAT_* positive values or -1 -> other errors */ int sg_ll_unmap(int sg_fd, int group_num, int timeout_secs, void * paramp, int param_len, bool noisy, int verbose) { return sg_ll_unmap_v2(sg_fd, false, group_num, timeout_secs, paramp, param_len, noisy, verbose); } /* Invokes a SCSI UNMAP (SBC-3) command. Version 2 adds anchor field * (sbc3r22). Otherwise same as sg_ll_unmap() . */ int sg_ll_unmap_v2(int sg_fd, bool anchor, int group_num, int timeout_secs, void * paramp, int param_len, bool noisy, int verbose) { static const char * const cdb_name_s = "unmap"; int k, res, ret, sense_cat, tmout; unsigned char u_cdb[UNMAP_CMDLEN] = {UNMAP_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0}; unsigned char sense_b[SENSE_BUFF_LEN]; struct sg_pt_base * ptvp; if (anchor) u_cdb[1] |= 0x1; tmout = (timeout_secs > 0) ? timeout_secs : DEF_PT_TIMEOUT; u_cdb[6] = group_num & 0x1f; sg_put_unaligned_be16((uint16_t)param_len, u_cdb + 7); if (verbose) { pr2ws(" %s cdb: ", cdb_name_s); for (k = 0; k < UNMAP_CMDLEN; ++k) pr2ws("%02x ", u_cdb[k]); pr2ws("\n"); if ((verbose > 1) && paramp && param_len) { pr2ws(" %s parameter list:\n", cdb_name_s); hex2stderr((const uint8_t *)paramp, param_len, -1); } } if (NULL == ((ptvp = create_pt_obj(cdb_name_s)))) return -1; set_scsi_pt_cdb(ptvp, u_cdb, sizeof(u_cdb)); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); set_scsi_pt_data_out(ptvp, (unsigned char *)paramp, param_len); res = do_scsi_pt(ptvp, sg_fd, tmout, verbose); ret = sg_cmds_process_resp(ptvp, cdb_name_s, res, SG_NO_DATA_IN, sense_b, noisy, verbose, &sense_cat); if (-1 == ret) { int os_err = get_scsi_pt_os_err(ptvp); if ((os_err > 0) && (os_err < 47)) ret = SG_LIB_OS_BASE_ERR + os_err; } else if (-2 == ret) { switch (sense_cat) { case SG_LIB_CAT_RECOVERED: case SG_LIB_CAT_NO_SENSE: ret = 0; break; default: ret = sense_cat; break; } } else ret = 0; destruct_scsi_pt_obj(ptvp); return ret; } /* Invokes a SCSI READ BLOCK LIMITS command. Return of 0 -> success, * various SG_LIB_CAT_* positive values or -1 -> other errors */ int sg_ll_read_block_limits(int sg_fd, void * resp, int mx_resp_len, bool noisy, int verbose) { static const char * const cdb_name_s = "read block limits"; int k, ret, res, sense_cat; unsigned char rl_cdb[READ_BLOCK_LIMITS_CMDLEN] = {READ_BLOCK_LIMITS_CMD, 0, 0, 0, 0, 0}; unsigned char sense_b[SENSE_BUFF_LEN]; struct sg_pt_base * ptvp; if (verbose) { pr2ws(" %s cdb: ", cdb_name_s); for (k = 0; k < READ_BLOCK_LIMITS_CMDLEN; ++k) pr2ws("%02x ", rl_cdb[k]); pr2ws("\n"); } if (NULL == ((ptvp = create_pt_obj(cdb_name_s)))) return -1; set_scsi_pt_cdb(ptvp, rl_cdb, sizeof(rl_cdb)); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); set_scsi_pt_data_in(ptvp, (unsigned char *)resp, mx_resp_len); res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose); ret = sg_cmds_process_resp(ptvp, cdb_name_s, res, mx_resp_len, sense_b, noisy, verbose, &sense_cat); if (-1 == ret) { int os_err = get_scsi_pt_os_err(ptvp); if ((os_err > 0) && (os_err < 47)) ret = SG_LIB_OS_BASE_ERR + os_err; } else if (-2 == ret) { switch (sense_cat) { case SG_LIB_CAT_RECOVERED: case SG_LIB_CAT_NO_SENSE: ret = 0; break; default: ret = sense_cat; break; } } else { if ((verbose > 2) && (ret > 0)) { pr2ws(" %s: response", cdb_name_s); if (3 == verbose) { pr2ws("%s:\n", (ret > 256 ? ", first 256 bytes" : "")); hex2stderr((const uint8_t *)resp, (ret > 256 ? 256 : ret), -1); } else { pr2ws(":\n"); hex2stderr((const uint8_t *)resp, ret, 0); } } ret = 0; } destruct_scsi_pt_obj(ptvp); return ret; } /* Invokes a SCSI RECEIVE COPY RESULTS command. Actually cover all current * uses of opcode 0x84 (Third-party copy IN). Return of 0 -> success, * various SG_LIB_CAT_* positive values or -1 -> other errors */ int sg_ll_receive_copy_results(int sg_fd, int sa, int list_id, void * resp, int mx_resp_len, bool noisy, int verbose) { int k, res, ret, sense_cat; unsigned char rcvcopyres_cdb[THIRD_PARTY_COPY_IN_CMDLEN] = {THIRD_PARTY_COPY_IN_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; unsigned char sense_b[SENSE_BUFF_LEN]; struct sg_pt_base * ptvp; char b[64]; sg_get_opcode_sa_name(THIRD_PARTY_COPY_IN_CMD, sa, 0, (int)sizeof(b), b); rcvcopyres_cdb[1] = (unsigned char)(sa & 0x1f); if (sa <= 4) /* LID1 variants */ rcvcopyres_cdb[2] = (unsigned char)(list_id); else if ((sa >= 5) && (sa <= 7)) /* LID4 variants */ sg_put_unaligned_be32((uint32_t)list_id, rcvcopyres_cdb + 2); sg_put_unaligned_be32((uint32_t)mx_resp_len, rcvcopyres_cdb + 10); if (verbose) { pr2ws(" %s cdb: ", b); for (k = 0; k < THIRD_PARTY_COPY_IN_CMDLEN; ++k) pr2ws("%02x ", rcvcopyres_cdb[k]); pr2ws("\n"); } if (NULL == ((ptvp = create_pt_obj(b)))) return -1; set_scsi_pt_cdb(ptvp, rcvcopyres_cdb, sizeof(rcvcopyres_cdb)); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); set_scsi_pt_data_in(ptvp, (unsigned char *)resp, mx_resp_len); res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose); ret = sg_cmds_process_resp(ptvp, b, res, mx_resp_len, sense_b, noisy, verbose, &sense_cat); if (-1 == ret) { int os_err = get_scsi_pt_os_err(ptvp); if ((os_err > 0) && (os_err < 47)) ret = SG_LIB_OS_BASE_ERR + os_err; } else if (-2 == ret) { switch (sense_cat) { case SG_LIB_CAT_RECOVERED: case SG_LIB_CAT_NO_SENSE: ret = 0; break; default: ret = sense_cat; break; } } else ret = 0; destruct_scsi_pt_obj(ptvp); return ret; } /* SPC-4 rev 35 and later calls this opcode (0x83) "Third-party copy OUT" * The original EXTENDED COPY command (now called EXTENDED COPY (LID1)) * is the only one supported by sg_ll_extended_copy(). See function * sg_ll_3party_copy_out() for the other service actions ( > 0 ). */ /* Invokes a SCSI EXTENDED COPY (LID1) command. Return of 0 -> success, * various SG_LIB_CAT_* positive values or -1 -> other errors */ int sg_ll_extended_copy(int sg_fd, void * paramp, int param_len, bool noisy, int verbose) { int k, res, ret, sense_cat; unsigned char xcopy_cdb[THIRD_PARTY_COPY_OUT_CMDLEN] = {THIRD_PARTY_COPY_OUT_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; unsigned char sense_b[SENSE_BUFF_LEN]; struct sg_pt_base * ptvp; const char * opcode_name = "Extended copy (LID1)"; xcopy_cdb[1] = (unsigned char)(EXTENDED_COPY_LID1_SA & 0x1f); sg_put_unaligned_be32((uint32_t)param_len, xcopy_cdb + 10); if (verbose) { pr2ws(" %s cdb: ", opcode_name); for (k = 0; k < THIRD_PARTY_COPY_OUT_CMDLEN; ++k) pr2ws("%02x ", xcopy_cdb[k]); pr2ws("\n"); if ((verbose > 1) && paramp && param_len) { pr2ws(" %s parameter list:\n", opcode_name); hex2stderr((const uint8_t *)paramp, param_len, -1); } } if (NULL == ((ptvp = create_pt_obj(opcode_name)))) return -1; set_scsi_pt_cdb(ptvp, xcopy_cdb, sizeof(xcopy_cdb)); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); set_scsi_pt_data_out(ptvp, (unsigned char *)paramp, param_len); res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose); ret = sg_cmds_process_resp(ptvp, opcode_name, res, SG_NO_DATA_IN, sense_b, noisy, verbose, &sense_cat); if (-1 == ret) { int os_err = get_scsi_pt_os_err(ptvp); if ((os_err > 0) && (os_err < 47)) ret = SG_LIB_OS_BASE_ERR + os_err; } else if (-2 == ret) { switch (sense_cat) { case SG_LIB_CAT_RECOVERED: case SG_LIB_CAT_NO_SENSE: ret = 0; break; default: ret = sense_cat; break; } } else ret = 0; destruct_scsi_pt_obj(ptvp); return ret; } /* Handles various service actions associated with opcode 0x83 which is * called THIRD PARTY COPY OUT. These include the EXTENDED COPY(LID1 and * LID4), POPULATE TOKEN and WRITE USING TOKEN commands. * Return of 0 -> success, * various SG_LIB_CAT_* positive values or -1 -> other errors */ int sg_ll_3party_copy_out(int sg_fd, int sa, unsigned int list_id, int group_num, int timeout_secs, void * paramp, int param_len, bool noisy, int verbose) { int k, res, ret, sense_cat, tmout; unsigned char xcopy_cdb[THIRD_PARTY_COPY_OUT_CMDLEN] = {THIRD_PARTY_COPY_OUT_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; unsigned char sense_b[SENSE_BUFF_LEN]; struct sg_pt_base * ptvp; char cname[80]; sg_get_opcode_sa_name(THIRD_PARTY_COPY_OUT_CMD, sa, 0, sizeof(cname), cname); xcopy_cdb[1] = (unsigned char)(sa & 0x1f); switch (sa) { case 0x0: /* XCOPY(LID1) */ case 0x1: /* XCOPY(LID4) */ sg_put_unaligned_be32((uint32_t)param_len, xcopy_cdb + 10); break; case 0x10: /* POPULATE TOKEN (SBC-3) */ case 0x11: /* WRITE USING TOKEN (SBC-3) */ sg_put_unaligned_be32((uint32_t)list_id, xcopy_cdb + 6); sg_put_unaligned_be32((uint32_t)param_len, xcopy_cdb + 10); xcopy_cdb[14] = (unsigned char)(group_num & 0x1f); break; case 0x1c: /* COPY OPERATION ABORT */ sg_put_unaligned_be32((uint32_t)list_id, xcopy_cdb + 2); break; default: pr2ws("%s: unknown service action 0x%x\n", __func__, sa); return -1; } tmout = (timeout_secs > 0) ? timeout_secs : DEF_PT_TIMEOUT; if (verbose) { pr2ws(" %s cdb: ", cname); for (k = 0; k < THIRD_PARTY_COPY_OUT_CMDLEN; ++k) pr2ws("%02x ", xcopy_cdb[k]); pr2ws("\n"); if ((verbose > 1) && paramp && param_len) { pr2ws(" %s parameter list:\n", cname); hex2stderr((const uint8_t *)paramp, param_len, -1); } } if (NULL == ((ptvp = create_pt_obj(cname)))) return -1; set_scsi_pt_cdb(ptvp, xcopy_cdb, sizeof(xcopy_cdb)); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); set_scsi_pt_data_out(ptvp, (unsigned char *)paramp, param_len); res = do_scsi_pt(ptvp, sg_fd, tmout, verbose); ret = sg_cmds_process_resp(ptvp, cname, res, SG_NO_DATA_IN, sense_b, noisy, verbose, &sense_cat); if (-1 == ret) { int os_err = get_scsi_pt_os_err(ptvp); if ((os_err > 0) && (os_err < 47)) ret = SG_LIB_OS_BASE_ERR + os_err; } else if (-2 == ret) { switch (sense_cat) { case SG_LIB_CAT_RECOVERED: case SG_LIB_CAT_NO_SENSE: ret = 0; break; default: ret = sense_cat; break; } } else ret = 0; destruct_scsi_pt_obj(ptvp); return ret; } /* Invokes a SCSI PRE-FETCH(10), PRE-FETCH(16) or SEEK(10) command (SBC). * Returns 0 -> success, 25 (SG_LIB_CAT_CONDITION_MET), various SG_LIB_CAT_* * positive values or -1 -> other errors. Note that CONDITION MET status * is returned when immed=true and num_blocks can fit in device's cache, * somewaht strangely, GOOD status (return 0) is returned if num_blocks * cannot fit in device's cache. If do_seek10==true then does a SEEK(10) * command with given lba, if that LBA is < 2**32 . Unclear what SEEK(10) * does, assume it is like PRE-FETCH. If timeout_secs is 0 (or less) then * use DEF_PT_TIMEOUT (60 seconds) as command timeout. */ int sg_ll_pre_fetch_x(int sg_fd, bool do_seek10, bool cdb16, bool immed, uint64_t lba, uint32_t num_blocks, int group_num, int timeout_secs, bool noisy, int verbose) { static const char * const cdb10_name_s = "Pre-fetch(10)"; static const char * const cdb16_name_s = "Pre-fetch(16)"; static const char * const cdb_seek_name_s = "Seek(10)"; int k, res, sense_cat, ret, cdb_len, tmout; const char *cdb_name_s; unsigned char preFetchCdb[PRE_FETCH16_CMDLEN]; /* all use longest cdb */ unsigned char sense_b[SENSE_BUFF_LEN]; struct sg_pt_base * ptvp; memset(preFetchCdb, 0, sizeof(preFetchCdb)); if (do_seek10) { if (lba > UINT32_MAX) { if (verbose) pr2ws("%s: LBA exceeds 2**32 in %s\n", __func__, cdb_seek_name_s); return -1; } preFetchCdb[0] = SEEK10_CMD; cdb_len = SEEK10_CMDLEN; cdb_name_s = cdb_seek_name_s; sg_put_unaligned_be32((uint32_t)lba, preFetchCdb + 2); } else { if ((! cdb16) && ((lba > UINT32_MAX) || (num_blocks > UINT16_MAX))) { cdb16 = true; if (noisy || verbose) pr2ws("%s: do %s due to %s size\n", __func__, cdb16_name_s, (lba > UINT32_MAX) ? "LBA" : "NUM_BLOCKS"); } if (cdb16) { preFetchCdb[0] = PRE_FETCH16_CMD; cdb_len = PRE_FETCH16_CMDLEN; cdb_name_s = cdb16_name_s; if (immed) preFetchCdb[1] = 0x2; sg_put_unaligned_be64(lba, preFetchCdb + 2); sg_put_unaligned_be32(num_blocks, preFetchCdb + 10); preFetchCdb[14] = 0x3f & group_num; } else { preFetchCdb[0] = PRE_FETCH10_CMD; cdb_len = PRE_FETCH10_CMDLEN; cdb_name_s = cdb10_name_s; if (immed) preFetchCdb[1] = 0x2; sg_put_unaligned_be32((uint32_t)lba, preFetchCdb + 2); preFetchCdb[6] = 0x3f & group_num; sg_put_unaligned_be16((uint16_t)num_blocks, preFetchCdb + 7); } } tmout = (timeout_secs > 0) ? timeout_secs : DEF_PT_TIMEOUT; if (verbose) { pr2ws(" %s cdb: ", cdb_name_s); for (k = 0; k < cdb_len; ++k) pr2ws("%02x ", preFetchCdb[k]); pr2ws("\n"); } if (NULL == ((ptvp = create_pt_obj(cdb_name_s)))) return -1; set_scsi_pt_cdb(ptvp, preFetchCdb, cdb_len); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); res = do_scsi_pt(ptvp, sg_fd, tmout, verbose); if (0 == res) { int sstat = get_scsi_pt_status_response(ptvp); if (SG_LIB_CAT_CONDITION_MET == sstat) { ret = SG_LIB_CAT_CONDITION_MET; if (verbose > 2) pr2ws("%s: returns SG_LIB_CAT_CONDITION_MET\n", __func__); goto fini; } } ret = sg_cmds_process_resp(ptvp, cdb_name_s, res, SG_NO_DATA_IN, sense_b, noisy, verbose, &sense_cat); if (-1 == ret) { int os_err = get_scsi_pt_os_err(ptvp); if ((os_err > 0) && (os_err < 47)) ret = SG_LIB_OS_BASE_ERR + os_err; } else if (-2 == ret) { switch (sense_cat) { case SG_LIB_CAT_RECOVERED: case SG_LIB_CAT_NO_SENSE: ret = 0; break; default: ret = sense_cat; break; } } else ret = 0; fini: destruct_scsi_pt_obj(ptvp); return ret; } f2fs-tools-1.11.0/tools/sg_write_buffer/sg_cmds_mmc.c000066400000000000000000000304171332120552100225220ustar00rootroot00000000000000/* * Copyright (c) 2008-2018 Douglas Gilbert. * All rights reserved. * Use of this source code is governed by a BSD-style * license that can be found in the BSD_LICENSE file. */ #include #include #include #include #include #define __STDC_FORMAT_MACROS 1 #include #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "sg_lib.h" #include "sg_cmds_basic.h" #include "sg_cmds_mmc.h" #include "sg_pt.h" #include "sg_unaligned.h" #define SENSE_BUFF_LEN 64 /* Arbitrary, could be larger */ #define DEF_PT_TIMEOUT 60 /* 60 seconds */ #define GET_CONFIG_CMD 0x46 #define GET_CONFIG_CMD_LEN 10 #define GET_PERFORMANCE_CMD 0xac #define GET_PERFORMANCE_CMD_LEN 12 #define SET_CD_SPEED_CMD 0xbb #define SET_CD_SPEED_CMDLEN 12 #define SET_STREAMING_CMD 0xb6 #define SET_STREAMING_CMDLEN 12 #if defined(__GNUC__) || defined(__clang__) static int pr2ws(const char * fmt, ...) __attribute__ ((format (printf, 1, 2))); #else static int pr2ws(const char * fmt, ...); #endif static int pr2ws(const char * fmt, ...) { va_list args; int n; va_start(args, fmt); n = vfprintf(sg_warnings_strm ? sg_warnings_strm : stderr, fmt, args); va_end(args); return n; } static struct sg_pt_base * create_pt_obj(const char * cname) { struct sg_pt_base * ptvp = construct_scsi_pt_obj(); if (NULL == ptvp) pr2ws("%s: out of memory\n", cname); return ptvp; } /* Invokes a SCSI SET CD SPEED command (MMC). * Return of 0 -> success, SG_LIB_CAT_INVALID_OP -> command not supported, * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION, * SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND, * -1 -> other failure */ int sg_ll_set_cd_speed(int sg_fd, int rot_control, int drv_read_speed, int drv_write_speed, bool noisy, int verbose) { static const char * const cdb_name_s = "set cd speed"; int res, ret, k, sense_cat; unsigned char scsCmdBlk[SET_CD_SPEED_CMDLEN] = {SET_CD_SPEED_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ,0}; unsigned char sense_b[SENSE_BUFF_LEN]; struct sg_pt_base * ptvp; scsCmdBlk[1] |= (rot_control & 0x3); sg_put_unaligned_be16((uint16_t)drv_read_speed, scsCmdBlk + 2); sg_put_unaligned_be16((uint16_t)drv_write_speed, scsCmdBlk + 4); if (verbose) { pr2ws(" %s cdb: ", cdb_name_s); for (k = 0; k < SET_CD_SPEED_CMDLEN; ++k) pr2ws("%02x ", scsCmdBlk[k]); pr2ws("\n"); } if (NULL == ((ptvp = create_pt_obj(cdb_name_s)))) return -1; set_scsi_pt_cdb(ptvp, scsCmdBlk, sizeof(scsCmdBlk)); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose); ret = sg_cmds_process_resp(ptvp, cdb_name_s, res, SG_NO_DATA_IN, sense_b, noisy, verbose, &sense_cat); if (-1 == ret) { int os_err = get_scsi_pt_os_err(ptvp); if ((os_err > 0) && (os_err < 47)) ret = SG_LIB_OS_BASE_ERR + os_err; } else if (-2 == ret) { switch (sense_cat) { case SG_LIB_CAT_NOT_READY: case SG_LIB_CAT_UNIT_ATTENTION: case SG_LIB_CAT_INVALID_OP: case SG_LIB_CAT_ILLEGAL_REQ: case SG_LIB_CAT_ABORTED_COMMAND: ret = sense_cat; break; case SG_LIB_CAT_RECOVERED: case SG_LIB_CAT_NO_SENSE: ret = 0; break; default: ret = -1; break; } } else ret = 0; destruct_scsi_pt_obj(ptvp); return ret; } /* Invokes a SCSI GET CONFIGURATION command (MMC-3,4,5). * Returns 0 when successful, SG_LIB_CAT_INVALID_OP if command not * supported, SG_LIB_CAT_ILLEGAL_REQ if field in cdb not supported, * SG_LIB_CAT_UNIT_ATTENTION, SG_LIB_CAT_ABORTED_COMMAND, else -1 */ int sg_ll_get_config(int sg_fd, int rt, int starting, void * resp, int mx_resp_len, bool noisy, int verbose) { static const char * const cdb_name_s = "get configuration"; int res, k, ret, sense_cat; unsigned char gcCmdBlk[GET_CONFIG_CMD_LEN] = {GET_CONFIG_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0}; unsigned char sense_b[SENSE_BUFF_LEN]; struct sg_pt_base * ptvp; if ((rt < 0) || (rt > 3)) { pr2ws("Bad rt value: %d\n", rt); return -1; } gcCmdBlk[1] = (rt & 0x3); if ((starting < 0) || (starting > 0xffff)) { pr2ws("Bad starting field number: 0x%x\n", starting); return -1; } sg_put_unaligned_be16((uint16_t)starting, gcCmdBlk + 2); if ((mx_resp_len < 0) || (mx_resp_len > 0xffff)) { pr2ws("Bad mx_resp_len: 0x%x\n", starting); return -1; } sg_put_unaligned_be16((uint16_t)mx_resp_len, gcCmdBlk + 7); if (verbose) { pr2ws(" %s cdb: ", cdb_name_s); for (k = 0; k < GET_CONFIG_CMD_LEN; ++k) pr2ws("%02x ", gcCmdBlk[k]); pr2ws("\n"); } if (NULL == ((ptvp = create_pt_obj(cdb_name_s)))) return -1; set_scsi_pt_cdb(ptvp, gcCmdBlk, sizeof(gcCmdBlk)); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); set_scsi_pt_data_in(ptvp, (unsigned char *)resp, mx_resp_len); res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose); ret = sg_cmds_process_resp(ptvp, cdb_name_s, res, mx_resp_len, sense_b, noisy, verbose, &sense_cat); if (-1 == ret) { int os_err = get_scsi_pt_os_err(ptvp); if ((os_err > 0) && (os_err < 47)) ret = SG_LIB_OS_BASE_ERR + os_err; } else if (-2 == ret) { switch (sense_cat) { case SG_LIB_CAT_INVALID_OP: case SG_LIB_CAT_ILLEGAL_REQ: case SG_LIB_CAT_UNIT_ATTENTION: case SG_LIB_CAT_ABORTED_COMMAND: ret = sense_cat; break; case SG_LIB_CAT_RECOVERED: case SG_LIB_CAT_NO_SENSE: ret = 0; break; default: ret = -1; break; } } else { if ((verbose > 2) && (ret > 3)) { unsigned char * bp; int len; bp = (unsigned char *)resp; len = sg_get_unaligned_be32(bp + 0); if (len < 0) len = 0; len = (ret < len) ? ret : len; pr2ws(" %s: response:\n", cdb_name_s); if (3 == verbose) { pr2ws("%s:\n", (len > 256 ? ", first 256 bytes" : "")); hex2stderr((const uint8_t *)resp, (len > 256 ? 256 : len), -1); } else { pr2ws(":\n"); hex2stderr((const uint8_t *)resp, len, 0); } } ret = 0; } destruct_scsi_pt_obj(ptvp); return ret; } /* Invokes a SCSI GET PERFORMANCE command (MMC-3...6). * Returns 0 when successful, SG_LIB_CAT_INVALID_OP if command not * supported, SG_LIB_CAT_ILLEGAL_REQ if field in cdb not supported, * SG_LIB_CAT_UNIT_ATTENTION, SG_LIB_CAT_ABORTED_COMMAND, else -1 */ int sg_ll_get_performance(int sg_fd, int data_type, unsigned int starting_lba, int max_num_desc, int ttype, void * resp, int mx_resp_len, bool noisy, int verbose) { static const char * const cdb_name_s = "get performance"; int res, k, ret, sense_cat; unsigned char gpCmdBlk[GET_PERFORMANCE_CMD_LEN] = {GET_PERFORMANCE_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; unsigned char sense_b[SENSE_BUFF_LEN]; struct sg_pt_base * ptvp; if ((data_type < 0) || (data_type > 0x1f)) { pr2ws("Bad data_type value: %d\n", data_type); return -1; } gpCmdBlk[1] = (data_type & 0x1f); sg_put_unaligned_be32((uint32_t)starting_lba, gpCmdBlk + 2); if ((max_num_desc < 0) || (max_num_desc > 0xffff)) { pr2ws("Bad max_num_desc: 0x%x\n", max_num_desc); return -1; } sg_put_unaligned_be16((uint16_t)max_num_desc, gpCmdBlk + 8); if ((ttype < 0) || (ttype > 0xff)) { pr2ws("Bad type: 0x%x\n", ttype); return -1; } gpCmdBlk[10] = (unsigned char)ttype; if (verbose) { pr2ws(" %s cdb: ", cdb_name_s); for (k = 0; k < GET_PERFORMANCE_CMD_LEN; ++k) pr2ws("%02x ", gpCmdBlk[k]); pr2ws("\n"); } if (NULL == ((ptvp = create_pt_obj(cdb_name_s)))) return -1; set_scsi_pt_cdb(ptvp, gpCmdBlk, sizeof(gpCmdBlk)); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); set_scsi_pt_data_in(ptvp, (unsigned char *)resp, mx_resp_len); res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose); ret = sg_cmds_process_resp(ptvp, cdb_name_s, res, mx_resp_len, sense_b, noisy, verbose, &sense_cat); if (-1 == ret) { int os_err = get_scsi_pt_os_err(ptvp); if ((os_err > 0) && (os_err < 47)) ret = SG_LIB_OS_BASE_ERR + os_err; } else if (-2 == ret) { switch (sense_cat) { case SG_LIB_CAT_INVALID_OP: case SG_LIB_CAT_ILLEGAL_REQ: case SG_LIB_CAT_UNIT_ATTENTION: case SG_LIB_CAT_ABORTED_COMMAND: ret = sense_cat; break; case SG_LIB_CAT_RECOVERED: case SG_LIB_CAT_NO_SENSE: ret = 0; break; default: ret = -1; break; } } else { if ((verbose > 2) && (ret > 3)) { unsigned char * bp; int len; bp = (unsigned char *)resp; len = sg_get_unaligned_be32(bp + 0); if (len < 0) len = 0; len = (ret < len) ? ret : len; pr2ws(" %s: response", cdb_name_s); if (3 == verbose) { pr2ws("%s:\n", (len > 256 ? ", first 256 bytes" : "")); hex2stderr((const uint8_t *)resp, (len > 256 ? 256 : len), -1); } else { pr2ws(":\n"); hex2stderr((const uint8_t *)resp, len, 0); } } ret = 0; } destruct_scsi_pt_obj(ptvp); return ret; } /* Invokes a SCSI SET STREAMING command (MMC). Return of 0 -> success, * SG_LIB_CAT_INVALID_OP -> Set Streaming not supported, * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_ABORTED_COMMAND, * SG_LIB_CAT_UNIT_ATTENTION, SG_LIB_CAT_NOT_READY -> device not ready, * -1 -> other failure */ int sg_ll_set_streaming(int sg_fd, int type, void * paramp, int param_len, bool noisy, int verbose) { static const char * const cdb_name_s = "set streaming"; int k, res, ret, sense_cat; unsigned char ssCmdBlk[SET_STREAMING_CMDLEN] = {SET_STREAMING_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; unsigned char sense_b[SENSE_BUFF_LEN]; struct sg_pt_base * ptvp; ssCmdBlk[8] = type; sg_put_unaligned_be16((uint16_t)param_len, ssCmdBlk + 9); if (verbose) { pr2ws(" %s cdb: ", cdb_name_s); for (k = 0; k < SET_STREAMING_CMDLEN; ++k) pr2ws("%02x ", ssCmdBlk[k]); pr2ws("\n"); if ((verbose > 1) && paramp && param_len) { pr2ws(" %s parameter list:\n", cdb_name_s); hex2stderr((const uint8_t *)paramp, param_len, -1); } } if (NULL == ((ptvp = create_pt_obj(cdb_name_s)))) return -1; set_scsi_pt_cdb(ptvp, ssCmdBlk, sizeof(ssCmdBlk)); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); set_scsi_pt_data_out(ptvp, (unsigned char *)paramp, param_len); res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose); ret = sg_cmds_process_resp(ptvp, cdb_name_s, res, SG_NO_DATA_IN, sense_b, noisy, verbose, &sense_cat); if (-1 == ret) { int os_err = get_scsi_pt_os_err(ptvp); if ((os_err > 0) && (os_err < 47)) ret = SG_LIB_OS_BASE_ERR + os_err; } else if (-2 == ret) { switch (sense_cat) { case SG_LIB_CAT_NOT_READY: case SG_LIB_CAT_INVALID_OP: case SG_LIB_CAT_ILLEGAL_REQ: case SG_LIB_CAT_UNIT_ATTENTION: case SG_LIB_CAT_ABORTED_COMMAND: ret = sense_cat; break; case SG_LIB_CAT_RECOVERED: case SG_LIB_CAT_NO_SENSE: ret = 0; break; default: ret = -1; break; } } else ret = 0; destruct_scsi_pt_obj(ptvp); return ret; } f2fs-tools-1.11.0/tools/sg_write_buffer/sg_io_linux.c000066400000000000000000000166551332120552100225760ustar00rootroot00000000000000/* * Copyright (c) 1999-2018 Douglas Gilbert. * All rights reserved. * Use of this source code is governed by a BSD-style * license that can be found in the BSD_LICENSE file. */ #include #include #include #include #include #include #ifdef HAVE_CONFIG_H #include "config.h" #endif #ifdef SG_LIB_LINUX #include "sg_io_linux.h" /* Version 1.07 20160405 */ #if defined(__GNUC__) || defined(__clang__) static int pr2ws(const char * fmt, ...) __attribute__ ((format (printf, 1, 2))); #else static int pr2ws(const char * fmt, ...); #endif static int pr2ws(const char * fmt, ...) { va_list args; int n; va_start(args, fmt); n = vfprintf(sg_warnings_strm ? sg_warnings_strm : stderr, fmt, args); va_end(args); return n; } void sg_print_masked_status(int masked_status) { int scsi_status = (masked_status << 1) & 0x7e; sg_print_scsi_status(scsi_status); } static const char * linux_host_bytes[] = { "DID_OK", "DID_NO_CONNECT", "DID_BUS_BUSY", "DID_TIME_OUT", "DID_BAD_TARGET", "DID_ABORT", "DID_PARITY", "DID_ERROR", "DID_RESET", "DID_BAD_INTR", "DID_PASSTHROUGH", "DID_SOFT_ERROR", "DID_IMM_RETRY", "DID_REQUEUE", "DID_TRANSPORT_DISRUPTED", "DID_TRANSPORT_FAILFAST", "DID_TARGET_FAILURE", "DID_NEXUS_FAILURE", "DID_ALLOC_FAILURE", "DID_MEDIUM_ERROR", }; #define LINUX_HOST_BYTES_SZ \ (int)(sizeof(linux_host_bytes) / sizeof(linux_host_bytes[0])) void sg_print_host_status(int host_status) { pr2ws("Host_status=0x%02x ", host_status); if ((host_status < 0) || (host_status >= LINUX_HOST_BYTES_SZ)) pr2ws("is invalid "); else pr2ws("[%s] ", linux_host_bytes[host_status]); } static const char * linux_driver_bytes[] = { "DRIVER_OK", "DRIVER_BUSY", "DRIVER_SOFT", "DRIVER_MEDIA", "DRIVER_ERROR", "DRIVER_INVALID", "DRIVER_TIMEOUT", "DRIVER_HARD", "DRIVER_SENSE" }; #define LINUX_DRIVER_BYTES_SZ \ (int)(sizeof(linux_driver_bytes) / sizeof(linux_driver_bytes[0])) #if 0 static const char * linux_driver_suggests[] = { "SUGGEST_OK", "SUGGEST_RETRY", "SUGGEST_ABORT", "SUGGEST_REMAP", "SUGGEST_DIE", "UNKNOWN","UNKNOWN","UNKNOWN", "SUGGEST_SENSE" }; #define LINUX_DRIVER_SUGGESTS_SZ \ (int)(sizeof(linux_driver_suggests) / sizeof(linux_driver_suggests[0])) #endif void sg_print_driver_status(int driver_status) { int driv; const char * driv_cp = "invalid"; driv = driver_status & SG_LIB_DRIVER_MASK; if (driv < LINUX_DRIVER_BYTES_SZ) driv_cp = linux_driver_bytes[driv]; #if 0 sugg = (driver_status & SG_LIB_SUGGEST_MASK) >> 4; if (sugg < LINUX_DRIVER_SUGGESTS_SZ) sugg_cp = linux_driver_suggests[sugg]; #endif pr2ws("Driver_status=0x%02x", driver_status); pr2ws(" [%s] ", driv_cp); } /* Returns 1 if no errors found and thus nothing printed; otherwise prints error/warning (prefix by 'leadin') and returns 0. */ static int sg_linux_sense_print(const char * leadin, int scsi_status, int host_status, int driver_status, const unsigned char * sense_buffer, int sb_len, bool raw_sinfo) { bool done_leadin = false; bool done_sense = false; scsi_status &= 0x7e; /*sanity */ if ((0 == scsi_status) && (0 == host_status) && (0 == driver_status)) return 1; /* No problems */ if (0 != scsi_status) { if (leadin) pr2ws("%s: ", leadin); done_leadin = true; pr2ws("SCSI status: "); sg_print_scsi_status(scsi_status); pr2ws("\n"); if (sense_buffer && ((scsi_status == SAM_STAT_CHECK_CONDITION) || (scsi_status == SAM_STAT_COMMAND_TERMINATED))) { /* SAM_STAT_COMMAND_TERMINATED is obsolete */ sg_print_sense(0, sense_buffer, sb_len, raw_sinfo); done_sense = true; } } if (0 != host_status) { if (leadin && (! done_leadin)) pr2ws("%s: ", leadin); if (done_leadin) pr2ws("plus...: "); else done_leadin = true; sg_print_host_status(host_status); pr2ws("\n"); } if (0 != driver_status) { if (done_sense && (SG_LIB_DRIVER_SENSE == (SG_LIB_DRIVER_MASK & driver_status))) return 0; if (leadin && (! done_leadin)) pr2ws("%s: ", leadin); if (done_leadin) pr2ws("plus...: "); sg_print_driver_status(driver_status); pr2ws("\n"); if (sense_buffer && (! done_sense) && (SG_LIB_DRIVER_SENSE == (SG_LIB_DRIVER_MASK & driver_status))) sg_print_sense(0, sense_buffer, sb_len, raw_sinfo); } return 0; } #ifdef SG_IO bool sg_normalize_sense(const struct sg_io_hdr * hp, struct sg_scsi_sense_hdr * sshp) { if ((NULL == hp) || (0 == hp->sb_len_wr)) { if (sshp) memset(sshp, 0, sizeof(struct sg_scsi_sense_hdr)); return 0; } return sg_scsi_normalize_sense(hp->sbp, hp->sb_len_wr, sshp); } /* Returns 1 if no errors found and thus nothing printed; otherwise returns 0. */ int sg_chk_n_print3(const char * leadin, struct sg_io_hdr * hp, bool raw_sinfo) { return sg_linux_sense_print(leadin, hp->status, hp->host_status, hp->driver_status, hp->sbp, hp->sb_len_wr, raw_sinfo); } #endif /* Returns 1 if no errors found and thus nothing printed; otherwise returns 0. */ int sg_chk_n_print(const char * leadin, int masked_status, int host_status, int driver_status, const unsigned char * sense_buffer, int sb_len, bool raw_sinfo) { int scsi_status = (masked_status << 1) & 0x7e; return sg_linux_sense_print(leadin, scsi_status, host_status, driver_status, sense_buffer, sb_len, raw_sinfo); } #ifdef SG_IO int sg_err_category3(struct sg_io_hdr * hp) { return sg_err_category_new(hp->status, hp->host_status, hp->driver_status, hp->sbp, hp->sb_len_wr); } #endif int sg_err_category(int masked_status, int host_status, int driver_status, const unsigned char * sense_buffer, int sb_len) { int scsi_status = (masked_status << 1) & 0x7e; return sg_err_category_new(scsi_status, host_status, driver_status, sense_buffer, sb_len); } int sg_err_category_new(int scsi_status, int host_status, int driver_status, const unsigned char * sense_buffer, int sb_len) { int masked_driver_status = (SG_LIB_DRIVER_MASK & driver_status); scsi_status &= 0x7e; if ((0 == scsi_status) && (0 == host_status) && (0 == masked_driver_status)) return SG_LIB_CAT_CLEAN; if ((SAM_STAT_CHECK_CONDITION == scsi_status) || (SAM_STAT_COMMAND_TERMINATED == scsi_status) || (SG_LIB_DRIVER_SENSE == masked_driver_status)) return sg_err_category_sense(sense_buffer, sb_len); if (0 != host_status) { if ((SG_LIB_DID_NO_CONNECT == host_status) || (SG_LIB_DID_BUS_BUSY == host_status) || (SG_LIB_DID_TIME_OUT == host_status)) return SG_LIB_CAT_TIMEOUT; if (SG_LIB_DID_NEXUS_FAILURE == host_status) return SG_LIB_CAT_RES_CONFLICT; } if (SG_LIB_DRIVER_TIMEOUT == masked_driver_status) return SG_LIB_CAT_TIMEOUT; return SG_LIB_CAT_OTHER; } #endif /* if SG_LIB_LINUX defined */ f2fs-tools-1.11.0/tools/sg_write_buffer/sg_lib.c000066400000000000000000003626761332120552100215250ustar00rootroot00000000000000/* * Copyright (c) 1999-2018 Douglas Gilbert. * All rights reserved. * Use of this source code is governed by a BSD-style * license that can be found in the BSD_LICENSE file. */ /* NOTICE: * On 5th October 2004 (v1.00) this file name was changed from sg_err.c * to sg_lib.c and the previous GPL was changed to a FreeBSD license. * The intention is to maintain this file and the related sg_lib.h file * as open source and encourage their unencumbered use. * * CONTRIBUTIONS: * This file started out as a copy of SCSI opcodes, sense keys and * additional sense codes (ASC/ASCQ) kept in the Linux SCSI subsystem * in the kernel source file: drivers/scsi/constant.c . That file * bore this notice: "Copyright (C) 1993, 1994, 1995 Eric Youngdale" * and a GPL notice. * * Much of the data in this file is derived from SCSI draft standards * found at http://www.t10.org with the "SCSI Primary Commands-4" (SPC-4) * being the central point of reference. * * Contributions: * sense key specific field decoding [Trent Piepho 20031116] * */ #define _POSIX_C_SOURCE 200809L /* for posix_memalign() */ #define __STDC_FORMAT_MACROS 1 #include #include #include #include #include #include #include #include #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "sg_lib.h" #include "sg_lib_data.h" #include "sg_unaligned.h" #include "sg_pr2serr.h" /* sg_lib_version_str (and datestamp) defined in sg_lib_data.c file */ #define ASCQ_ATA_PT_INFO_AVAILABLE 0x1d /* corresponding ASC is 0 */ FILE * sg_warnings_strm = NULL; /* would like to default to stderr */ #if defined(__GNUC__) || defined(__clang__) static int pr2ws(const char * fmt, ...) __attribute__ ((format (printf, 1, 2))); #else static int pr2ws(const char * fmt, ...); #endif static int pr2ws(const char * fmt, ...) { va_list args; int n; va_start(args, fmt); n = vfprintf(sg_warnings_strm ? sg_warnings_strm : stderr, fmt, args); va_end(args); return n; } #if defined(__GNUC__) || defined(__clang__) static int scnpr(char * cp, int cp_max_len, const char * fmt, ...) __attribute__ ((format (printf, 3, 4))); #else static int scnpr(char * cp, int cp_max_len, const char * fmt, ...); #endif /* Want safe, 'n += snprintf(b + n, blen - n, ...)' style sequence of * functions. Returns number of chars placed in cp excluding the * trailing null char. So for cp_max_len > 0 the return value is always * < cp_max_len; for cp_max_len <= 1 the return value is 0 and no chars are * written to cp. Note this means that when cp_max_len = 1, this function * assumes that cp[0] is the null character and does nothing (and returns * 0). Linux kernel has a similar function called scnprintf(). */ static int scnpr(char * cp, int cp_max_len, const char * fmt, ...) { va_list args; int n; if (cp_max_len < 2) return 0; va_start(args, fmt); n = vsnprintf(cp, cp_max_len, fmt, args); va_end(args); return (n < cp_max_len) ? n : (cp_max_len - 1); } /* Simple ASCII printable (does not use locale), includes space and excludes * DEL (0x7f). */ static inline int my_isprint(int ch) { return ((ch >= ' ') && (ch < 0x7f)); } /* Searches 'arr' for match on 'value' then 'peri_type'. If matches 'value' but not 'peri_type' then yields first 'value' match entry. Last element of 'arr' has NULL 'name'. If no match returns NULL. */ static const struct sg_lib_value_name_t * get_value_name(const struct sg_lib_value_name_t * arr, int value, int peri_type) { const struct sg_lib_value_name_t * vp = arr; const struct sg_lib_value_name_t * holdp; if (peri_type < 0) peri_type = 0; for (; vp->name; ++vp) { if (value == vp->value) { if (peri_type == vp->peri_dev_type) return vp; holdp = vp; while ((vp + 1)->name && (value == (vp + 1)->value)) { ++vp; if (peri_type == vp->peri_dev_type) return vp; } return holdp; } } return NULL; } /* If this function is not called, sg_warnings_strm will be NULL and all users * (mainly fprintf() ) need to check and substitute stderr as required */ void sg_set_warnings_strm(FILE * warnings_strm) { sg_warnings_strm = warnings_strm; } #define CMD_NAME_LEN 128 void sg_print_command(const unsigned char * command) { int k, sz; char buff[CMD_NAME_LEN]; sg_get_command_name(command, 0, CMD_NAME_LEN, buff); buff[CMD_NAME_LEN - 1] = '\0'; pr2ws("%s [", buff); if (SG_VARIABLE_LENGTH_CMD == command[0]) sz = command[7] + 8; else sz = sg_get_command_size(command[0]); for (k = 0; k < sz; ++k) pr2ws("%02x ", command[k]); pr2ws("]\n"); } void sg_get_scsi_status_str(int scsi_status, int buff_len, char * buff) { const char * ccp = NULL; bool unknown = false; if ((NULL == buff) || (buff_len < 1)) return; else if (1 == buff_len) { buff[0] = '\0'; return; } scsi_status &= 0x7e; /* sanitize as much as possible */ switch (scsi_status) { case 0: ccp = "Good"; break; case 0x2: ccp = "Check Condition"; break; case 0x4: ccp = "Condition Met"; break; case 0x8: ccp = "Busy"; break; case 0x10: ccp = "Intermediate (obsolete)"; break; case 0x14: ccp = "Intermediate-Condition Met (obsolete)"; break; case 0x18: ccp = "Reservation Conflict"; break; case 0x22: ccp = "Command Terminated (obsolete)"; break; case 0x28: ccp = "Task set Full"; break; case 0x30: ccp = "ACA Active"; break; case 0x40: ccp = "Task Aborted"; break; default: unknown = true; break; } if (unknown) scnpr(buff, buff_len, "Unknown status [0x%x]", scsi_status); else scnpr(buff, buff_len, "%s", ccp); } void sg_print_scsi_status(int scsi_status) { char buff[128]; sg_get_scsi_status_str(scsi_status, sizeof(buff) - 1, buff); buff[sizeof(buff) - 1] = '\0'; pr2ws("%s ", buff); } /* Get sense key from sense buffer. If successful returns a sense key value * between 0 and 15. If sense buffer cannot be decode, returns -1 . */ int sg_get_sense_key(const unsigned char * sbp, int sb_len) { if ((NULL == sbp) || (sb_len < 2)) return -1; switch (sbp[0] & 0x7f) { case 0x70: case 0x71: return (sb_len < 3) ? -1 : (sbp[2] & 0xf); case 0x72: case 0x73: return sbp[1] & 0xf; default: return -1; } } /* Yield string associated with sense_key value. Returns 'buff'. */ char * sg_get_sense_key_str(int sense_key, int buff_len, char * buff) { if (1 == buff_len) { buff[0] = '\0'; return buff; } if ((sense_key >= 0) && (sense_key < 16)) scnpr(buff, buff_len, "%s", sg_lib_sense_key_desc[sense_key]); else scnpr(buff, buff_len, "invalid value: 0x%x", sense_key); return buff; } /* Yield string associated with ASC/ASCQ values. Returns 'buff'. */ char * sg_get_asc_ascq_str(int asc, int ascq, int buff_len, char * buff) { int k, num, rlen; bool found = false; struct sg_lib_asc_ascq_t * eip; struct sg_lib_asc_ascq_range_t * ei2p; if (1 == buff_len) { buff[0] = '\0'; return buff; } for (k = 0; sg_lib_asc_ascq_range[k].text; ++k) { ei2p = &sg_lib_asc_ascq_range[k]; if ((ei2p->asc == asc) && (ascq >= ei2p->ascq_min) && (ascq <= ei2p->ascq_max)) { found = true; num = scnpr(buff, buff_len, "Additional sense: "); rlen = buff_len - num; scnpr(buff + num, ((rlen > 0) ? rlen : 0), ei2p->text, ascq); } } if (found) return buff; for (k = 0; sg_lib_asc_ascq[k].text; ++k) { eip = &sg_lib_asc_ascq[k]; if (eip->asc == asc && eip->ascq == ascq) { found = true; scnpr(buff, buff_len, "Additional sense: %s", eip->text); } } if (! found) { if (asc >= 0x80) scnpr(buff, buff_len, "vendor specific ASC=%02x, ASCQ=%02x " "(hex)", asc, ascq); else if (ascq >= 0x80) scnpr(buff, buff_len, "ASC=%02x, vendor specific qualification " "ASCQ=%02x (hex)", asc, ascq); else scnpr(buff, buff_len, "ASC=%02x, ASCQ=%02x (hex)", asc, ascq); } return buff; } /* Attempt to find the first SCSI sense data descriptor that matches the * given 'desc_type'. If found return pointer to start of sense data * descriptor; otherwise (including fixed format sense data) returns NULL. */ const unsigned char * sg_scsi_sense_desc_find(const unsigned char * sbp, int sb_len, int desc_type) { int add_sb_len, add_d_len, desc_len, k; const unsigned char * descp; if ((sb_len < 8) || (0 == (add_sb_len = sbp[7]))) return NULL; if ((sbp[0] < 0x72) || (sbp[0] > 0x73)) return NULL; add_sb_len = (add_sb_len < (sb_len - 8)) ? add_sb_len : (sb_len - 8); descp = &sbp[8]; for (desc_len = 0, k = 0; k < add_sb_len; k += desc_len) { descp += desc_len; add_d_len = (k < (add_sb_len - 1)) ? descp[1]: -1; desc_len = add_d_len + 2; if (descp[0] == desc_type) return descp; if (add_d_len < 0) /* short descriptor ?? */ break; } return NULL; } /* Returns true if valid bit set, false if valid bit clear. Irrespective the * information field is written out via 'info_outp' (except when it is * NULL). Handles both fixed and descriptor sense formats. */ bool sg_get_sense_info_fld(const unsigned char * sbp, int sb_len, uint64_t * info_outp) { const unsigned char * bp; uint64_t ull; if (info_outp) *info_outp = 0; if (sb_len < 7) return false; switch (sbp[0] & 0x7f) { case 0x70: case 0x71: if (info_outp) *info_outp = sg_get_unaligned_be32(sbp + 3); return !!(sbp[0] & 0x80); case 0x72: case 0x73: bp = sg_scsi_sense_desc_find(sbp, sb_len, 0 /* info desc */); if (bp && (0xa == bp[1])) { ull = sg_get_unaligned_be64(bp + 4); if (info_outp) *info_outp = ull; return !!(bp[2] & 0x80); /* since spc3r23 should be set */ } else return false; default: return false; } } /* Returns true if fixed format or command specific information descriptor * is found in the descriptor sense; else false. If available the command * specific information field (4 byte integer in fixed format, 8 byte * integer in descriptor format) is written out via 'cmd_spec_outp'. * Handles both fixed and descriptor sense formats. */ bool sg_get_sense_cmd_spec_fld(const unsigned char * sbp, int sb_len, uint64_t * cmd_spec_outp) { const unsigned char * bp; if (cmd_spec_outp) *cmd_spec_outp = 0; if (sb_len < 7) return false; switch (sbp[0] & 0x7f) { case 0x70: case 0x71: if (cmd_spec_outp) *cmd_spec_outp = sg_get_unaligned_be32(sbp + 8); return true; case 0x72: case 0x73: bp = sg_scsi_sense_desc_find(sbp, sb_len, 1 /* command specific info desc */); if (bp && (0xa == bp[1])) { if (cmd_spec_outp) *cmd_spec_outp = sg_get_unaligned_be64(bp + 4); return true; } else return false; default: return false; } } /* Returns true if any of the 3 bits (i.e. FILEMARK, EOM or ILI) are set. * In descriptor format if the stream commands descriptor not found * then returns false. Writes true or false corresponding to these bits to * the last three arguments if they are non-NULL. */ bool sg_get_sense_filemark_eom_ili(const unsigned char * sbp, int sb_len, bool * filemark_p, bool * eom_p, bool * ili_p) { const unsigned char * bp; if (sb_len < 7) return false; switch (sbp[0] & 0x7f) { case 0x70: case 0x71: if (sbp[2] & 0xe0) { if (filemark_p) *filemark_p = !!(sbp[2] & 0x80); if (eom_p) *eom_p = !!(sbp[2] & 0x40); if (ili_p) *ili_p = !!(sbp[2] & 0x20); return true; } else return false; case 0x72: case 0x73: /* Look for stream commands sense data descriptor */ bp = sg_scsi_sense_desc_find(sbp, sb_len, 4); if (bp && (bp[1] >= 2)) { if (bp[3] & 0xe0) { if (filemark_p) *filemark_p = !!(bp[3] & 0x80); if (eom_p) *eom_p = !!(bp[3] & 0x40); if (ili_p) *ili_p = !!(bp[3] & 0x20); return true; } } return false; default: return false; } } /* Returns true if SKSV is set and sense key is NO_SENSE or NOT_READY. Also * returns true if progress indication sense data descriptor found. Places * progress field from sense data where progress_outp points. If progress * field is not available returns false and *progress_outp is unaltered. * Handles both fixed and descriptor sense formats. * Hint: if true is returned *progress_outp may be multiplied by 100 then * divided by 65536 to get the percentage completion. */ bool sg_get_sense_progress_fld(const unsigned char * sbp, int sb_len, int * progress_outp) { const unsigned char * bp; int sk, sk_pr; if (sb_len < 7) return false; switch (sbp[0] & 0x7f) { case 0x70: case 0x71: sk = (sbp[2] & 0xf); if ((sb_len < 18) || ((SPC_SK_NO_SENSE != sk) && (SPC_SK_NOT_READY != sk))) return false; if (sbp[15] & 0x80) { /* SKSV bit set */ if (progress_outp) *progress_outp = sg_get_unaligned_be16(sbp + 16); return true; } else return false; case 0x72: case 0x73: /* sense key specific progress (0x2) or progress descriptor (0xa) */ sk = (sbp[1] & 0xf); sk_pr = (SPC_SK_NO_SENSE == sk) || (SPC_SK_NOT_READY == sk); if (sk_pr && ((bp = sg_scsi_sense_desc_find(sbp, sb_len, 2))) && (0x6 == bp[1]) && (0x80 & bp[4])) { if (progress_outp) *progress_outp = sg_get_unaligned_be16(bp + 5); return true; } else if (((bp = sg_scsi_sense_desc_find(sbp, sb_len, 0xa))) && ((0x6 == bp[1]))) { if (progress_outp) *progress_outp = sg_get_unaligned_be16(bp + 6); return true; } else return false; default: return false; } } char * sg_get_pdt_str(int pdt, int buff_len, char * buff) { if ((pdt < 0) || (pdt > 31)) scnpr(buff, buff_len, "bad pdt"); else scnpr(buff, buff_len, "%s", sg_lib_pdt_strs[pdt]); return buff; } int sg_lib_pdt_decay(int pdt) { if ((pdt < 0) || (pdt > 31)) return 0; return sg_lib_pdt_decay_arr[pdt]; } char * sg_get_trans_proto_str(int tpi, int buff_len, char * buff) { if ((tpi < 0) || (tpi > 15)) scnpr(buff, buff_len, "bad tpi"); else scnpr(buff, buff_len, "%s", sg_lib_transport_proto_strs[tpi]); return buff; } #define TRANSPORT_ID_MIN_LEN 24 char * sg_decode_transportid_str(const char * lip, unsigned char * bp, int bplen, bool only_one, int blen, char * b) { int proto_id, num, k, n, normal_len, tpid_format; uint64_t ull; int bump; if ((NULL == b) || (blen < 1)) return b; else if (1 == blen) { b[0] = '\0'; return b; } if (NULL == lip) lip = ""; bump = TRANSPORT_ID_MIN_LEN; /* should be overwritten in all loop paths */ for (k = 0, n = 0; bplen > 0; ++k, bp += bump, bplen -= bump) { if ((k > 0) && only_one) break; if ((bplen < 24) || (0 != (bplen % 4))) n += scnpr(b + n, blen - n, "%sTransport Id short or not " "multiple of 4 [length=%d]:\n", lip, blen); else n += scnpr(b + n, blen - n, "%sTransport Id of initiator:\n", lip); tpid_format = ((bp[0] >> 6) & 0x3); proto_id = (bp[0] & 0xf); normal_len = (bplen > TRANSPORT_ID_MIN_LEN) ? TRANSPORT_ID_MIN_LEN : bplen; switch (proto_id) { case TPROTO_FCP: /* Fibre channel */ n += scnpr(b + n, blen - n, "%s FCP-2 World Wide Name:\n", lip); if (0 != tpid_format) n += scnpr(b + n, blen - n, "%s [Unexpected TPID format: " "%d]\n", lip, tpid_format); n += hex2str(bp + 8, 8, lip, 1, blen - n, b + n); bump = TRANSPORT_ID_MIN_LEN; break; case TPROTO_SPI: /* Scsi Parallel Interface, obsolete */ n += scnpr(b + n, blen - n, "%s Parallel SCSI initiator SCSI " "address: 0x%x\n", lip, sg_get_unaligned_be16(bp + 2)); if (0 != tpid_format) n += scnpr(b + n, blen - n, "%s [Unexpected TPID format: " "%d]\n", lip, tpid_format); n += scnpr(b + n, blen - n, "%s relative port number (of " "corresponding target): 0x%x\n", lip, sg_get_unaligned_be16(bp + 6)); bump = TRANSPORT_ID_MIN_LEN; break; case TPROTO_SSA: n += scnpr(b + n, blen - n, "%s SSA (transport id not " "defined):\n", lip); n += scnpr(b + n, blen - n, "%s TPID format: %d\n", lip, tpid_format); n += hex2str(bp, normal_len, lip, 1, blen - n, b + n); bump = TRANSPORT_ID_MIN_LEN; break; case TPROTO_1394: /* IEEE 1394 */ n += scnpr(b + n, blen - n, "%s IEEE 1394 EUI-64 name:\n", lip); if (0 != tpid_format) n += scnpr(b + n, blen - n, "%s [Unexpected TPID format: " "%d]\n", lip, tpid_format); n += hex2str(&bp[8], 8, lip, 1, blen - n, b + n); bump = TRANSPORT_ID_MIN_LEN; break; case TPROTO_SRP: /* SCSI over RDMA */ n += scnpr(b + n, blen - n, "%s RDMA initiator port " "identifier:\n", lip); if (0 != tpid_format) n += scnpr(b + n, blen - n, "%s [Unexpected TPID format: " "%d]\n", lip, tpid_format); n += hex2str(bp + 8, 16, lip, 1, blen - n, b + n); bump = TRANSPORT_ID_MIN_LEN; break; case TPROTO_ISCSI: n += scnpr(b + n, blen - n, "%s iSCSI ", lip); num = sg_get_unaligned_be16(bp + 2); if (0 == tpid_format) n += scnpr(b + n, blen - n, "name: %.*s\n", num, &bp[4]); else if (1 == tpid_format) n += scnpr(b + n, blen - n, "world wide unique port id: " "%.*s\n", num, &bp[4]); else { n += scnpr(b + n, blen - n, " [Unexpected TPID format: " "%d]\n", tpid_format); n += hex2str(bp, num + 4, lip, 0, blen - n, b + n); } bump = (((num + 4) < TRANSPORT_ID_MIN_LEN) ? TRANSPORT_ID_MIN_LEN : num + 4); break; case TPROTO_SAS: ull = sg_get_unaligned_be64(bp + 4); n += scnpr(b + n, blen - n, "%s SAS address: 0x%" PRIx64 "\n", lip, ull); if (0 != tpid_format) n += scnpr(b + n, blen - n, "%s [Unexpected TPID format: " "%d]\n", lip, tpid_format); bump = TRANSPORT_ID_MIN_LEN; break; case TPROTO_ADT: /* no TransportID defined by T10 yet */ n += scnpr(b + n, blen - n, "%s ADT:\n", lip); n += scnpr(b + n, blen - n, "%s TPID format: %d\n", lip, tpid_format); n += hex2str(bp, normal_len, lip, 1, blen - n, b + n); bump = TRANSPORT_ID_MIN_LEN; break; case TPROTO_ATA: /* no TransportID defined by T10 yet */ n += scnpr(b + n, blen - n, "%s ATAPI:\n", lip); n += scnpr(b + n, blen - n, "%s TPID format: %d\n", lip, tpid_format); n += hex2str(bp, normal_len, lip, 1, blen - n, b + n); bump = TRANSPORT_ID_MIN_LEN; break; case TPROTO_UAS: /* no TransportID defined by T10 yet */ n += scnpr(b + n, blen - n, "%s UAS:\n", lip); n += scnpr(b + n, blen - n, "%s TPID format: %d\n", lip, tpid_format); n += hex2str(bp, normal_len, lip, 1, blen - n, b + n); bump = TRANSPORT_ID_MIN_LEN; break; case TPROTO_SOP: n += scnpr(b + n, blen - n, "%s SOP ", lip); num = sg_get_unaligned_be16(bp + 2); if (0 == tpid_format) n += scnpr(b + n, blen - n, "Routing ID: 0x%x\n", num); else { n += scnpr(b + n, blen - n, " [Unexpected TPID format: " "%d]\n", tpid_format); n += hex2str(bp, normal_len, lip, 1, blen - n, b + n); } bump = TRANSPORT_ID_MIN_LEN; break; case TPROTO_PCIE: /* no TransportID defined by T10 yet */ n += scnpr(b + n, blen - n, "%s PCIE:\n", lip); n += scnpr(b + n, blen - n, "%s TPID format: %d\n", lip, tpid_format); n += hex2str(bp, normal_len, lip, 1, blen - n, b + n); bump = TRANSPORT_ID_MIN_LEN; break; case TPROTO_NONE: /* no TransportID defined by T10 */ n += scnpr(b + n, blen - n, "%s No specified protocol\n", lip); /* n += hex2str(bp, ((bplen > 24) ? 24 : bplen), * lip, 0, blen - n, b + n); */ bump = TRANSPORT_ID_MIN_LEN; break; default: n += scnpr(b + n, blen - n, "%s unknown protocol id=0x%x " "TPID format=%d\n", lip, proto_id, tpid_format); n += hex2str(bp, normal_len, lip, 1, blen - n, b + n); bump = TRANSPORT_ID_MIN_LEN; break; } } return b; } static const char * desig_code_set_str_arr[] = { "Reserved [0x0]", "Binary", "ASCII", "UTF-8", "Reserved [0x4]", "Reserved [0x5]", "Reserved [0x6]", "Reserved [0x7]", "Reserved [0x8]", "Reserved [0x9]", "Reserved [0xa]", "Reserved [0xb]", "Reserved [0xc]", "Reserved [0xd]", "Reserved [0xe]", "Reserved [0xf]", }; const char * sg_get_desig_code_set_str(int val) { if ((val >= 0) && (val < 16)) return desig_code_set_str_arr[val]; else return NULL; } static const char * desig_assoc_str_arr[] = { "Addressed logical unit", "Target port", /* that received request; unless SCSI ports VPD */ "Target device that contains addressed lu", "Reserved [0x3]", }; const char * sg_get_desig_assoc_str(int val) { if ((val >= 0) && (val < 4)) return desig_assoc_str_arr[val]; else return NULL; } static const char * desig_type_str_arr[] = { "vendor specific [0x0]", "T10 vendor identification", "EUI-64 based", "NAA", "Relative target port", "Target port group", /* spc4r09: _primary_ target port group */ "Logical unit group", "MD5 logical unit identifier", "SCSI name string", "Protocol specific port identifier", /* spc4r36 */ "UUID identifier", /* spc5r08 */ "Reserved [0xb]", "Reserved [0xc]", "Reserved [0xd]", "Reserved [0xe]", "Reserved [0xf]", }; const char * sg_get_desig_type_str(int val) { if ((val >= 0) && (val < 16)) return desig_type_str_arr[val]; else return NULL; } int sg_get_designation_descriptor_str(const char * lip, const unsigned char * ddp, int dd_len, bool print_assoc, bool do_long, int blen, char * b) { int m, p_id, piv, c_set, assoc, desig_type, ci_off, c_id, d_id, naa; int vsi, k, n, dlen; const unsigned char * ip; uint64_t vsei; uint64_t id_ext; char e[64]; const char * cp; n = 0; if (NULL == lip) lip = ""; if (dd_len < 4) { n += scnpr(b + n, blen - n, "%sdesignator desc too short: got " "length of %d want 4 or more\n", lip, dd_len); return n; } dlen = ddp[3]; if (dlen > (dd_len - 4)) { n += scnpr(b + n, blen - n, "%sdesignator too long: says it is %d " "bytes, but given %d bytes\n", lip, dlen, dd_len - 4); return n; } ip = ddp + 4; p_id = ((ddp[0] >> 4) & 0xf); c_set = (ddp[0] & 0xf); piv = ((ddp[1] & 0x80) ? 1 : 0); assoc = ((ddp[1] >> 4) & 0x3); desig_type = (ddp[1] & 0xf); if (print_assoc && ((cp = sg_get_desig_assoc_str(assoc)))) n += scnpr(b + n, blen - n, "%s %s:\n", lip, cp); n += scnpr(b + n, blen - n, "%s designator type: ", lip); cp = sg_get_desig_type_str(desig_type); if (cp) n += scnpr(b + n, blen - n, "%s", cp); n += scnpr(b + n, blen - n, ", code set: "); cp = sg_get_desig_code_set_str(c_set); if (cp) n += scnpr(b + n, blen - n, "%s", cp); n += scnpr(b + n, blen - n, "\n"); if (piv && ((1 == assoc) || (2 == assoc))) n += scnpr(b + n, blen - n, "%s transport: %s\n", lip, sg_get_trans_proto_str(p_id, sizeof(e), e)); /* printf(" associated with the %s\n", sdparm_assoc_arr[assoc]); */ switch (desig_type) { case 0: /* vendor specific */ k = 0; if ((1 == c_set) || (2 == c_set)) { /* ASCII or UTF-8 */ for (k = 0; (k < dlen) && my_isprint(ip[k]); ++k) ; if (k >= dlen) k = 1; } if (k) n += scnpr(b + n, blen - n, "%s vendor specific: %.*s\n", lip, dlen, ip); else { n += scnpr(b + n, blen - n, "%s vendor specific:\n", lip); n += hex2str(ip, dlen, lip, 0, blen - n, b + n); } break; case 1: /* T10 vendor identification */ n += scnpr(b + n, blen - n, "%s vendor id: %.8s\n", lip, ip); if (dlen > 8) { if ((2 == c_set) || (3 == c_set)) { /* ASCII or UTF-8 */ n += scnpr(b + n, blen - n, "%s vendor specific: " "%.*s\n", lip, dlen - 8, ip + 8); } else { n += scnpr(b + n, blen - n, "%s vendor specific: 0x", lip); for (m = 8; m < dlen; ++m) n += scnpr(b + n, blen - n, "%02x", (unsigned int)ip[m]); n += scnpr(b + n, blen - n, "\n"); } } break; case 2: /* EUI-64 based */ if (! do_long) { if ((8 != dlen) && (12 != dlen) && (16 != dlen)) { n += scnpr(b + n, blen - n, "%s << expect 8, 12 and 16 " "byte EUI, got %d >>\n", lip, dlen); n += hex2str(ip, dlen, lip, 1, blen - n, b + n); break; } n += scnpr(b + n, blen - n, "%s 0x", lip); for (m = 0; m < dlen; ++m) n += scnpr(b + n, blen - n, "%02x", (unsigned int)ip[m]); n += scnpr(b + n, blen - n, "\n"); break; } n += scnpr(b + n, blen - n, "%s EUI-64 based %d byte " "identifier\n", lip, dlen); if (1 != c_set) { n += scnpr(b + n, blen - n, "%s << expected binary code_set " "(1) >>\n", lip); n += hex2str(ip, dlen, lip, 1, blen - n, b + n); break; } ci_off = 0; if (16 == dlen) { ci_off = 8; id_ext = sg_get_unaligned_be64(ip); n += scnpr(b + n, blen - n, "%s Identifier extension: 0x%" PRIx64 "\n", lip, id_ext); } else if ((8 != dlen) && (12 != dlen)) { n += scnpr(b + n, blen - n, "%s << can only decode 8, 12 " "and 16 byte ids >>\n", lip); n += hex2str(ip, dlen, lip, 1, blen - n, b + n); break; } c_id = sg_get_unaligned_be24(ip + ci_off); n += scnpr(b + n, blen - n, "%s IEEE Company_id: 0x%x\n", lip, c_id); vsei = 0; for (m = 0; m < 5; ++m) { if (m > 0) vsei <<= 8; vsei |= ip[ci_off + 3 + m]; } n += scnpr(b + n, blen - n, "%s Vendor Specific Extension " "Identifier: 0x%" PRIx64 "\n", lip, vsei); if (12 == dlen) { d_id = sg_get_unaligned_be32(ip + 8); n += scnpr(b + n, blen - n, "%s Directory ID: 0x%x\n", lip, d_id); } break; case 3: /* NAA */ if (1 != c_set) { n += scnpr(b + n, blen - n, "%s << unexpected code set %d " "for NAA >>\n", lip, c_set); n += hex2str(ip, dlen, lip, 1, blen - n, b + n); break; } naa = (ip[0] >> 4) & 0xff; switch (naa) { case 2: /* NAA 2: IEEE Extended */ if (8 != dlen) { n += scnpr(b + n, blen - n, "%s << unexpected NAA 2 " "identifier length: 0x%x >>\n", lip, dlen); n += hex2str(ip, dlen, lip, 1, blen - n, b + n); break; } d_id = (((ip[0] & 0xf) << 8) | ip[1]); c_id = sg_get_unaligned_be24(ip + 2); vsi = sg_get_unaligned_be24(ip + 5); if (do_long) { n += scnpr(b + n, blen - n, "%s NAA 2, vendor specific " "identifier A: 0x%x\n", lip, d_id); n += scnpr(b + n, blen - n, "%s IEEE Company_id: 0x%x\n", lip, c_id); n += scnpr(b + n, blen - n, "%s vendor specific " "identifier B: 0x%x\n", lip, vsi); n += scnpr(b + n, blen - n, "%s [0x", lip); for (m = 0; m < 8; ++m) n += scnpr(b + n, blen - n, "%02x", (unsigned int)ip[m]); n += scnpr(b + n, blen - n, "]\n"); } n += scnpr(b + n, blen - n, "%s 0x", lip); for (m = 0; m < 8; ++m) n += scnpr(b + n, blen - n, "%02x", (unsigned int)ip[m]); n += scnpr(b + n, blen - n, "\n"); break; case 3: /* NAA 3: Locally assigned */ if (8 != dlen) { n += scnpr(b + n, blen - n, "%s << unexpected NAA 3 " "identifier length: 0x%x >>\n", lip, dlen); n += hex2str(ip, dlen, lip, 1, blen - n, b + n); break; } if (do_long) n += scnpr(b + n, blen - n, "%s NAA 3, Locally " "assigned:\n", lip); n += scnpr(b + n, blen - n, "%s 0x", lip); for (m = 0; m < 8; ++m) n += scnpr(b + n, blen - n, "%02x", (unsigned int)ip[m]); n += scnpr(b + n, blen - n, "\n"); break; case 5: /* NAA 5: IEEE Registered */ if (8 != dlen) { n += scnpr(b + n, blen - n, "%s << unexpected NAA 5 " "identifier length: 0x%x >>\n", lip, dlen); n += hex2str(ip, dlen, lip, 1, blen - n, b + n); break; } c_id = (((ip[0] & 0xf) << 20) | (ip[1] << 12) | (ip[2] << 4) | ((ip[3] & 0xf0) >> 4)); vsei = ip[3] & 0xf; for (m = 1; m < 5; ++m) { vsei <<= 8; vsei |= ip[3 + m]; } if (do_long) { n += scnpr(b + n, blen - n, "%s NAA 5, IEEE " "Company_id: 0x%x\n", lip, c_id); n += scnpr(b + n, blen - n, "%s Vendor Specific " "Identifier: 0x%" PRIx64 "\n", lip, vsei); n += scnpr(b + n, blen - n, "%s [0x", lip); for (m = 0; m < 8; ++m) n += scnpr(b + n, blen - n, "%02x", (unsigned int)ip[m]); n += scnpr(b + n, blen - n, "]\n"); } else { n += scnpr(b + n, blen - n, "%s 0x", lip); for (m = 0; m < 8; ++m) n += scnpr(b + n, blen - n, "%02x", (unsigned int)ip[m]); n += scnpr(b + n, blen - n, "\n"); } break; case 6: /* NAA 6: IEEE Registered extended */ if (16 != dlen) { n += scnpr(b + n, blen - n, "%s << unexpected NAA 6 " "identifier length: 0x%x >>\n", lip, dlen); n += hex2str(ip, dlen, lip, 1, blen - n, b + n); break; } c_id = (((ip[0] & 0xf) << 20) | (ip[1] << 12) | (ip[2] << 4) | ((ip[3] & 0xf0) >> 4)); vsei = ip[3] & 0xf; for (m = 1; m < 5; ++m) { vsei <<= 8; vsei |= ip[3 + m]; } if (do_long) { n += scnpr(b + n, blen - n, "%s NAA 6, IEEE " "Company_id: 0x%x\n", lip, c_id); n += scnpr(b + n, blen - n, "%s Vendor Specific " "Identifier: 0x%" PRIx64 "\n", lip, vsei); vsei = sg_get_unaligned_be64(ip + 8); n += scnpr(b + n, blen - n, "%s Vendor Specific " "Identifier Extension: 0x%" PRIx64 "\n", lip, vsei); n += scnpr(b + n, blen - n, "%s [0x", lip); for (m = 0; m < 16; ++m) n += scnpr(b + n, blen - n, "%02x", (unsigned int)ip[m]); n += scnpr(b + n, blen - n, "]\n"); } else { n += scnpr(b + n, blen - n, "%s 0x", lip); for (m = 0; m < 16; ++m) n += scnpr(b + n, blen - n, "%02x", (unsigned int)ip[m]); n += scnpr(b + n, blen - n, "\n"); } break; default: n += scnpr(b + n, blen - n, "%s << unexpected NAA [0x%x] " ">>\n", lip, naa); n += hex2str(ip, dlen, lip, 1, blen - n, b + n); break; } break; case 4: /* Relative target port */ if ((1 != c_set) || (1 != assoc) || (4 != dlen)) { n += scnpr(b + n, blen - n, "%s << expected binary " "code_set, target port association, length 4 >>\n", lip); n += hex2str(ip, dlen, "", 1, blen - n, b + n); break; } d_id = sg_get_unaligned_be16(ip + 2); n += scnpr(b + n, blen - n, "%s Relative target port: 0x%x\n", lip, d_id); break; case 5: /* (primary) Target port group */ if ((1 != c_set) || (1 != assoc) || (4 != dlen)) { n += scnpr(b + n, blen - n, "%s << expected binary " "code_set, target port association, length 4 >>\n", lip); n += hex2str(ip, dlen, lip, 1, blen - n, b + n); break; } d_id = sg_get_unaligned_be16(ip + 2); n += scnpr(b + n, blen - n, "%s Target port group: 0x%x\n", lip, d_id); break; case 6: /* Logical unit group */ if ((1 != c_set) || (0 != assoc) || (4 != dlen)) { n += scnpr(b + n, blen - n, "%s << expected binary " "code_set, logical unit association, length 4 >>\n", lip); n += hex2str(ip, dlen, lip, 1, blen - n, b + n); break; } d_id = sg_get_unaligned_be16(ip + 2); n += scnpr(b + n, blen - n, "%s Logical unit group: 0x%x\n", lip, d_id); break; case 7: /* MD5 logical unit identifier */ if ((1 != c_set) || (0 != assoc)) { n += scnpr(b + n, blen - n, "%s << expected binary " "code_set, logical unit association >>\n", lip); n += hex2str(ip, dlen, "", 1, blen - n, b + n); break; } n += scnpr(b + n, blen - n, "%s MD5 logical unit identifier:\n", lip); n += hex2str(ip, dlen, lip, 1, blen - n, b + n); break; case 8: /* SCSI name string */ if (3 != c_set) { /* accept ASCII as subset of UTF-8 */ if (2 == c_set) { if (do_long) n += scnpr(b + n, blen - n, "%s << expected UTF-8, " "use ASCII >>\n", lip); } else { n += scnpr(b + n, blen - n, "%s << expected UTF-8 " "code_set >>\n", lip); n += hex2str(ip, dlen, lip, 0, blen - n, b + n); break; } } n += scnpr(b + n, blen - n, "%s SCSI name string:\n", lip); /* does %s print out UTF-8 ok?? * Seems to depend on the locale. Looks ok here with my * locale setting: en_AU.UTF-8 */ n += scnpr(b + n, blen - n, "%s %.*s\n", lip, dlen, (const char *)ip); break; case 9: /* Protocol specific port identifier */ /* added in spc4r36, PIV must be set, proto_id indicates */ /* whether UAS (USB) or SOP (PCIe) or ... */ if (! piv) n += scnpr(b + n, blen - n, " %s >>>> Protocol specific " "port identifier expects protocol\n" "%s identifier to be valid and it is not\n", lip, lip); if (TPROTO_UAS == p_id) { n += scnpr(b + n, blen - n, "%s USB device address: 0x%x\n", lip, 0x7f & ip[0]); n += scnpr(b + n, blen - n, "%s USB interface number: " "0x%x\n", lip, ip[2]); } else if (TPROTO_SOP == p_id) { n += scnpr(b + n, blen - n, "%s PCIe routing ID, bus " "number: 0x%x\n", lip, ip[0]); n += scnpr(b + n, blen - n, "%s function number: 0x%x\n", lip, ip[1]); n += scnpr(b + n, blen - n, "%s [or device number: " "0x%x, function number: 0x%x]\n", lip, (0x1f & (ip[1] >> 3)), 0x7 & ip[1]); } else n += scnpr(b + n, blen - n, "%s >>>> unexpected protocol " "indentifier: %s\n%s with Protocol specific " "port identifier\n", lip, sg_get_trans_proto_str(p_id, sizeof(e), e), lip); break; case 0xa: /* UUID identifier */ if (1 != c_set) { n += scnpr(b + n, blen - n, "%s << expected binary " "code_set >>\n", lip); n += hex2str(ip, dlen, lip, 0, blen - n, b + n); break; } if ((1 != ((ip[0] >> 4) & 0xf)) || (18 != dlen)) { n += scnpr(b + n, blen - n, "%s << expected locally " "assigned UUID, 16 bytes long >>\n", lip); n += hex2str(ip, dlen, lip, 0, blen - n, b + n); break; } n += scnpr(b + n, blen - n, "%s Locally assigned UUID: ", lip); for (m = 0; m < 16; ++m) { if ((4 == m) || (6 == m) || (8 == m) || (10 == m)) n += scnpr(b + n, blen - n, "-"); n += scnpr(b + n, blen - n, "%02x", (unsigned int)ip[2 + m]); } n += scnpr(b + n, blen - n, "\n"); if (do_long) { n += scnpr(b + n, blen - n, "%s [0x", lip); for (m = 0; m < 16; ++m) n += scnpr(b + n, blen - n, "%02x", (unsigned int)ip[2 + m]); n += scnpr(b + n, blen - n, "]\n"); } break; default: /* reserved */ n += scnpr(b + n, blen - n, "%s reserved designator=0x%x\n", lip, desig_type); n += hex2str(ip, dlen, lip, 1, blen - n, b + n); break; } return n; } static int decode_sks(const char * lip, const unsigned char * descp, int add_d_len, int sense_key, bool * processedp, int blen, char * b) { int progress, pr, rem, n; n = 0; if (NULL == lip) lip = ""; switch (sense_key) { case SPC_SK_ILLEGAL_REQUEST: if (add_d_len < 6) { n += scnpr(b + n, blen - n, "Field pointer: "); goto too_short; } /* abbreviate to fit on one line */ n += scnpr(b + n, blen - n, "Field pointer:\n"); n += scnpr(b + n, blen - n, "%s Error in %s: byte %d", lip, (descp[4] & 0x40) ? "Command" : "Data parameters", sg_get_unaligned_be16(descp + 5)); if (descp[4] & 0x08) { n += scnpr(b + n, blen - n, " bit %d\n", descp[4] & 0x07); } else n += scnpr(b + n, blen - n, "\n"); break; case SPC_SK_HARDWARE_ERROR: case SPC_SK_MEDIUM_ERROR: case SPC_SK_RECOVERED_ERROR: n += scnpr(b + n, blen - n, "Actual retry count: "); if (add_d_len < 6) goto too_short; n += scnpr(b + n, blen - n,"%u\n", sg_get_unaligned_be16(descp + 5)); break; case SPC_SK_NO_SENSE: case SPC_SK_NOT_READY: n += scnpr(b + n, blen - n, "Progress indication: "); if (add_d_len < 6) goto too_short; progress = sg_get_unaligned_be16(descp + 5); pr = (progress * 100) / 65536; rem = ((progress * 100) % 65536) / 656; n += scnpr(b + n, blen - n, "%d.%02d%%\n", pr, rem); break; case SPC_SK_COPY_ABORTED: n += scnpr(b + n, blen - n, "Segment pointer:\n"); if (add_d_len < 6) goto too_short; n += scnpr(b + n, blen - n, "%s Relative to start of %s, byte " "%d", lip, (descp[4] & 0x20) ? "segment descriptor" : "parameter list", sg_get_unaligned_be16(descp + 5)); if (descp[4] & 0x08) n += scnpr(b + n, blen - n, " bit %d\n", descp[4] & 0x07); else n += scnpr(b + n, blen - n, "\n"); break; case SPC_SK_UNIT_ATTENTION: n += scnpr(b + n, blen - n, "Unit attention condition queue:\n"); n += scnpr(b + n, blen - n, "%s overflow flag is %d\n", lip, !!(descp[4] & 0x1)); break; default: n += scnpr(b + n, blen - n, "Sense_key: 0x%x unexpected\n", sense_key); *processedp = false; break; } return n; too_short: n += scnpr(b + n, blen - n, "%s\n", " >> descriptor too short"); *processedp = false; return n; } #define TPGS_STATE_OPTIMIZED 0x0 #define TPGS_STATE_NONOPTIMIZED 0x1 #define TPGS_STATE_STANDBY 0x2 #define TPGS_STATE_UNAVAILABLE 0x3 #define TPGS_STATE_OFFLINE 0xe #define TPGS_STATE_TRANSITIONING 0xf static int decode_tpgs_state(int st, char * b, int blen) { switch (st) { case TPGS_STATE_OPTIMIZED: return scnpr(b, blen, "active/optimized"); case TPGS_STATE_NONOPTIMIZED: return scnpr(b, blen, "active/non optimized"); case TPGS_STATE_STANDBY: return scnpr(b, blen, "standby"); case TPGS_STATE_UNAVAILABLE: return scnpr(b, blen, "unavailable"); case TPGS_STATE_OFFLINE: return scnpr(b, blen, "offline"); case TPGS_STATE_TRANSITIONING: return scnpr(b, blen, "transitioning between states"); default: return scnpr(b, blen, "unknown: 0x%x", st); } } static int uds_referral_descriptor_str(char * b, int blen, const unsigned char * dp, int alen, const char * lip) { int n = 0; int dlen = alen - 2; int k, j, g, f, tpgd; const unsigned char * tp; uint64_t ull; char c[40]; if (NULL == lip) lip = ""; n += scnpr(b + n, blen - n, "%s Not all referrals: %d\n", lip, !!(dp[2] & 0x1)); dp += 4; for (k = 0, f = 1; (k + 4) < dlen; k += g, dp += g, ++f) { tpgd = dp[3]; g = (tpgd * 4) + 20; n += scnpr(b + n, blen - n, "%s Descriptor %d\n", lip, f); if ((k + g) > dlen) { n += scnpr(b + n, blen - n, "%s truncated descriptor, " "stop\n", lip); return n; } ull = sg_get_unaligned_be64(dp + 4); n += scnpr(b + n, blen - n, "%s first uds LBA: 0x%" PRIx64 "\n", lip, ull); ull = sg_get_unaligned_be64(dp + 12); n += scnpr(b + n, blen - n, "%s last uds LBA: 0x%" PRIx64 "\n", lip, ull); for (j = 0; j < tpgd; ++j) { tp = dp + 20 + (j * 4); decode_tpgs_state(tp[0] & 0xf, c, sizeof(c)); n += scnpr(b + n, blen - n, "%s tpg: %d state: %s\n", lip, sg_get_unaligned_be16(tp + 2), c); } } return n; } static const char * dd_usage_reason_str_arr[] = { "Unknown", "resend this and further commands to:", "resend this command to:", "new subsiduary lu added to this administrative lu:", "administrative lu associated with a preferred binding:", }; /* Decode descriptor format sense descriptors (assumes sense buffer is * in descriptor format) */ int sg_get_sense_descriptors_str(const char * lip, const unsigned char * sbp, int sb_len, int blen, char * b) { int add_sb_len, add_d_len, desc_len, k, j, sense_key; int n, progress, pr, rem; bool processed; const unsigned char * descp; const char * dtsp = " >> descriptor too short"; const char * eccp = "Extended copy command"; const char * ddp = "destination device"; char z[64]; if ((NULL == b) || (blen <= 0)) return 0; b[0] = '\0'; if (lip) scnpr(z, sizeof(z), "%.60s ", lip); else scnpr(z, sizeof(z), " "); if ((sb_len < 8) || (0 == (add_sb_len = sbp[7]))) return 0; add_sb_len = (add_sb_len < (sb_len - 8)) ? add_sb_len : (sb_len - 8); sense_key = (sbp[1] & 0xf); for (descp = (sbp + 8), k = 0, n = 0; (k < add_sb_len) && (n < blen); k += desc_len, descp += desc_len) { add_d_len = (k < (add_sb_len - 1)) ? descp[1] : -1; if ((k + add_d_len + 2) > add_sb_len) add_d_len = add_sb_len - k - 2; desc_len = add_d_len + 2; n += scnpr(b + n, blen - n, "%s Descriptor type: ", lip); processed = true; switch (descp[0]) { case 0: n += scnpr(b + n, blen - n, "Information: "); if ((add_d_len >= 10) && (0x80 & descp[2])) { n += scnpr(b + n, blen - n, "0x"); for (j = 0; j < 8; ++j) n += scnpr(b + n, blen - n, "%02x", descp[4 + j]); n += scnpr(b + n, blen - n, "\n"); } else { n += scnpr(b + n, blen - n, "%s\n", dtsp); processed = false; } break; case 1: n += scnpr(b + n, blen - n, "Command specific: "); if (add_d_len >= 10) { n += scnpr(b + n, blen - n, "0x"); for (j = 0; j < 8; ++j) n += scnpr(b + n, blen - n, "%02x", descp[4 + j]); n += scnpr(b + n, blen - n, "\n"); } else { n += scnpr(b + n, blen - n, "%s\n", dtsp); processed = false; } break; case 2: /* Sense Key Specific */ n += scnpr(b + n, blen - n, "Sense key specific: "); n += decode_sks(lip, descp, add_d_len, sense_key, &processed, blen - n, b + n); break; case 3: n += scnpr(b + n, blen - n, "Field replaceable unit code: "); if (add_d_len >= 2) n += scnpr(b + n, blen - n, "0x%x\n", descp[3]); else { n += scnpr(b + n, blen - n, "%s\n", dtsp); processed = false; } break; case 4: n += scnpr(b + n, blen - n, "Stream commands: "); if (add_d_len >= 2) { if (descp[3] & 0x80) n += scnpr(b + n, blen - n, "FILEMARK"); if (descp[3] & 0x40) n += scnpr(b + n, blen - n, "End Of Medium (EOM)"); if (descp[3] & 0x20) n += scnpr(b + n, blen - n, "Incorrect Length Indicator " "(ILI)"); n += scnpr(b + n, blen - n, "\n"); } else { n += scnpr(b + n, blen - n, "%s\n", dtsp); processed = false; } break; case 5: n += scnpr(b + n, blen - n, "Block commands: "); if (add_d_len >= 2) n += scnpr(b + n, blen - n, "Incorrect Length Indicator " "(ILI) %s\n", (descp[3] & 0x20) ? "set" : "clear"); else { n += scnpr(b + n, blen - n, "%s\n", dtsp); processed = false; } break; case 6: n += scnpr(b + n, blen - n, "OSD object identification\n"); processed = false; break; case 7: n += scnpr(b + n, blen - n, "OSD response integrity check " "value\n"); processed = false; break; case 8: n += scnpr(b + n, blen - n, "OSD attribute identification\n"); processed = false; break; case 9: /* this is defined in SAT (SAT-2) */ n += scnpr(b + n, blen - n, "ATA Status Return: "); if (add_d_len >= 12) { int extend, count; extend = descp[2] & 1; count = descp[5] + (extend ? (descp[4] << 8) : 0); n += scnpr(b + n, blen - n, "extend=%d error=0x%x \n%s" " count=0x%x ", extend, descp[3], lip, count); if (extend) n += scnpr(b + n, blen - n, "lba=0x%02x%02x%02x%02x%02x%02x ", descp[10], descp[8], descp[6], descp[11], descp[9], descp[7]); else n += scnpr(b + n, blen - n, "lba=0x%02x%02x%02x ", descp[11], descp[9], descp[7]); n += scnpr(b + n, blen - n, "device=0x%x status=0x%x\n", descp[12], descp[13]); } else { n += scnpr(b + n, blen - n, "%s\n", dtsp); processed = false; } break; case 0xa: /* Added in SPC-4 rev 17, became 'Another ...' in rev 34 */ n += scnpr(b + n, blen - n, "Another progress indication: "); if (add_d_len < 6) { n += scnpr(b + n, blen - n, "%s\n", dtsp); processed = false; break; } progress = sg_get_unaligned_be16(descp + 6); pr = (progress * 100) / 65536; rem = ((progress * 100) % 65536) / 656; n += scnpr(b + n, blen - n, "%d.02%d%%\n", pr, rem); n += scnpr(b + n, blen - n, "%s [sense_key=0x%x " "asc,ascq=0x%x,0x%x]\n", lip, descp[2], descp[3], descp[4]); break; case 0xb: /* Added in SPC-4 rev 23, defined in SBC-3 rev 22 */ n += scnpr(b + n, blen - n, "User data segment referral: "); if (add_d_len < 2) { n += scnpr(b + n, blen - n, "%s\n", dtsp); processed = false; break; } n += scnpr(b + n, blen - n, "\n"); n += uds_referral_descriptor_str(b + n, blen - n, descp, add_d_len, lip); break; case 0xc: /* Added in SPC-4 rev 28 */ n += scnpr(b + n, blen - n, "Forwarded sense data\n"); if (add_d_len < 2) { n += scnpr(b + n, blen - n, "%s\n", dtsp); processed = false; break; } n += scnpr(b + n, blen - n, "%s FSDT: %s\n", lip, (descp[2] & 0x80) ? "set" : "clear"); j = descp[2] & 0xf; n += scnpr(b + n, blen - n, "%s Sense data source: ", lip); switch (j) { case 0: n += scnpr(b + n, blen - n, "%s source device\n", eccp); break; case 1: case 2: case 3: case 4: case 5: case 6: case 7: n += scnpr(b + n, blen - n, "%s %s %d\n", eccp, ddp, j - 1); break; default: n += scnpr(b + n, blen - n, "unknown [%d]\n", j); } { char c[480]; sg_get_scsi_status_str(descp[3], sizeof(c) - 1, c); c[sizeof(c) - 1] = '\0'; n += scnpr(b + n, blen - n, "%s Forwarded status: %s\n", lip, c); if (add_d_len > 2) { /* recursing; hope not to get carried away */ n += scnpr(b + n, blen - n, "%s vvvvvvvvvvvvvvvv\n", lip); sg_get_sense_str(lip, descp + 4, add_d_len - 2, false, sizeof(c), c); n += scnpr(b + n, blen - n, "%s", c); n += scnpr(b + n, blen - n, "%s ^^^^^^^^^^^^^^^^\n", lip); } } break; case 0xd: /* Added in SBC-3 rev 36d */ /* this descriptor combines descriptors 0, 1, 2 and 3 */ n += scnpr(b + n, blen - n, "Direct-access block device\n"); if (add_d_len < 28) { n += scnpr(b + n, blen - n, "%s\n", dtsp); processed = false; break; } if (0x20 & descp[2]) n += scnpr(b + n, blen - n, "%s ILI (incorrect length " "indication) set\n", lip); if (0x80 & descp[4]) { n += scnpr(b + n, blen - n, "%s Sense key specific: ", lip); n += decode_sks(lip, descp, add_d_len, sense_key, &processed, blen - n, b + n); } n += scnpr(b + n, blen - n, "%s Field replaceable unit code: " "0x%x\n", lip, descp[7]); if (0x80 & descp[2]) { n += scnpr(b + n, blen - n, "%s Information: 0x", lip); for (j = 0; j < 8; ++j) n += scnpr(b + n, blen - n, "%02x", descp[8 + j]); n += scnpr(b + n, blen - n, "\n"); } n += scnpr(b + n, blen - n, "%s Command specific: 0x", lip); for (j = 0; j < 8; ++j) n += scnpr(b + n, blen - n, "%02x", descp[16 + j]); n += scnpr(b + n, blen - n, "\n"); break; case 0xe: /* Added in SPC-5 rev 6 (for Bind/Unbind) */ n += scnpr(b + n, blen - n, "Device designation\n"); j = (int)(sizeof(dd_usage_reason_str_arr) / sizeof(dd_usage_reason_str_arr[0])); if (descp[3] < j) n += scnpr(b + n, blen - n, "%s Usage reason: %s\n", lip, dd_usage_reason_str_arr[descp[3]]); else n += scnpr(b + n, blen - n, "%s Usage reason: " "reserved[%d]\n", lip, descp[3]); n += sg_get_designation_descriptor_str(z, descp + 4, descp[1] - 2, true, false, blen - n, b + n); break; case 0xf: /* Added in SPC-5 rev 10 (for Write buffer) */ n += scnpr(b + n, blen - n, "Microcode activation "); if (add_d_len < 6) { n += scnpr(b + n, blen - n, "%s\n", dtsp); processed = false; break; } progress = sg_get_unaligned_be16(descp + 6); n += scnpr(b + n, blen - n, "time: "); if (0 == progress) n += scnpr(b + n, blen - n, "unknown\n"); else n += scnpr(b + n, blen - n, "%d seconds\n", progress); break; default: if (descp[0] >= 0x80) n += scnpr(b + n, blen - n, "Vendor specific [0x%x]\n", descp[0]); else n += scnpr(b + n, blen - n, "Unknown [0x%x]\n", descp[0]); processed = false; break; } if (! processed) { if (add_d_len > 0) { n += scnpr(b + n, blen - n, "%s ", lip); for (j = 0; j < add_d_len; ++j) { if ((j > 0) && (0 == (j % 24))) n += scnpr(b + n, blen - n, "\n%s ", lip); n += scnpr(b + n, blen - n, "%02x ", descp[j + 2]); } n += scnpr(b + n, blen - n, "\n"); } } if (add_d_len < 0) n += scnpr(b + n, blen - n, "%s short descriptor\n", lip); } return n; } /* Decode SAT ATA PASS-THROUGH fixed format sense. Shows "+" after 'count' * and/or 'lba' values to indicate that not all data in those fields is shown. * That extra field information may be available in the ATA pass-through * results log page parameter with the corresponding 'log_index'. */ static int sg_get_sense_sat_pt_fixed_str(const char * lip, const unsigned char * sp, int slen, int blen, char * b) { int n = 0; bool extend, count_upper_nz, lba_upper_nz; if ((blen < 1) || (slen < 12)) return n; if (NULL == lip) lip = ""; if (SPC_SK_RECOVERED_ERROR != (0xf & sp[2])) n += scnpr(b + n, blen - n, "%s >> expected Sense key: Recovered " "Error ??\n", lip); /* Fixed sense command-specific information field starts at sp + 8 */ extend = !!(0x80 & sp[8]); count_upper_nz = !!(0x40 & sp[8]); lba_upper_nz = !!(0x20 & sp[8]); /* Fixed sense information field starts at sp + 3 */ n += scnpr(b + n, blen - n, "%s error=0x%x, status=0x%x, device=0x%x, " "count(7:0)=0x%x%c\n", lip, sp[3], sp[4], sp[5], sp[6], (count_upper_nz ? '+' : ' ')); n += scnpr(b + n, blen - n, "%s extend=%d, log_index=0x%x, " "lba_high,mid,low(7:0)=0x%x,0x%x,0x%x%c\n", lip, (int)extend, (0xf & sp[8]), sp[9], sp[10], sp[11], (lba_upper_nz ? '+' : ' ')); return n; } /* Fetch sense information */ int sg_get_sense_str(const char * lip, const unsigned char * sbp, int sb_len, bool raw_sinfo, int cblen, char * cbp) { bool descriptor_format = false; bool sdat_ovfl = false; bool valid; int len, progress, n, r, pr, rem, blen; unsigned int info; uint8_t resp_code; const char * ebp = NULL; char ebuff[64]; char b[256]; struct sg_scsi_sense_hdr ssh; if ((NULL == cbp) || (cblen <= 0)) return 0; else if (1 == cblen) { cbp[0] = '\0'; return 0; } blen = sizeof(b); n = 0; if (NULL == lip) lip = ""; if ((NULL == sbp) || (sb_len < 1)) { n += scnpr(cbp, cblen, "%s >>> sense buffer empty\n", lip); return n; } resp_code = 0x7f & sbp[0]; valid = !!(sbp[0] & 0x80); len = sb_len; if (sg_scsi_normalize_sense(sbp, sb_len, &ssh)) { switch (ssh.response_code) { case 0x70: /* fixed, current */ ebp = "Fixed format, current"; len = (sb_len > 7) ? (sbp[7] + 8) : sb_len; len = (len > sb_len) ? sb_len : len; sdat_ovfl = (len > 2) ? !!(sbp[2] & 0x10) : false; break; case 0x71: /* fixed, deferred */ /* error related to a previous command */ ebp = "Fixed format, <<>>"; len = (sb_len > 7) ? (sbp[7] + 8) : sb_len; len = (len > sb_len) ? sb_len : len; sdat_ovfl = (len > 2) ? !!(sbp[2] & 0x10) : false; break; case 0x72: /* descriptor, current */ descriptor_format = true; ebp = "Descriptor format, current"; sdat_ovfl = (sb_len > 4) ? !!(sbp[4] & 0x80) : false; break; case 0x73: /* descriptor, deferred */ descriptor_format = true; ebp = "Descriptor format, <<>>"; sdat_ovfl = (sb_len > 4) ? !!(sbp[4] & 0x80) : false; break; case 0x0: ebp = "Response code: 0x0 (?)"; break; default: scnpr(ebuff, sizeof(ebuff), "Unknown response code: 0x%x", ssh.response_code); ebp = ebuff; break; } n += scnpr(cbp + n, cblen - n, "%s%s; Sense key: %s\n", lip, ebp, sg_lib_sense_key_desc[ssh.sense_key]); if (sdat_ovfl) n += scnpr(cbp + n, cblen - n, "%s<<>>\n", lip); if (descriptor_format) { n += scnpr(cbp + n, cblen - n, "%s%s\n", lip, sg_get_asc_ascq_str(ssh.asc, ssh.ascq, blen, b)); n += sg_get_sense_descriptors_str(lip, sbp, len, cblen - n, cbp + n); } else if ((len > 12) && (0 == ssh.asc) && (ASCQ_ATA_PT_INFO_AVAILABLE == ssh.ascq)) { /* SAT ATA PASS-THROUGH fixed format */ n += scnpr(cbp + n, cblen - n, "%s%s\n", lip, sg_get_asc_ascq_str(ssh.asc, ssh.ascq, blen, b)); n += sg_get_sense_sat_pt_fixed_str(lip, sbp, len, cblen - n, cbp + n); } else if (len > 2) { /* fixed format */ if (len > 12) n += scnpr(cbp + n, cblen - n, "%s%s\n", lip, sg_get_asc_ascq_str(ssh.asc, ssh.ascq, blen, b)); r = 0; if (strlen(lip) > 0) r += scnpr(b + r, blen - r, "%s", lip); if (len > 6) { info = sg_get_unaligned_be32(sbp + 3); if (valid) r += scnpr(b + r, blen - r, " Info fld=0x%x [%u] ", info, info); else if (info > 0) r += scnpr(b + r, blen - r, " Valid=0, Info fld=0x%x " "[%u] ", info, info); } else info = 0; if (sbp[2] & 0xe0) { if (sbp[2] & 0x80) r += scnpr(b + r, blen - r, " FMK"); /* current command has read a filemark */ if (sbp[2] & 0x40) r += scnpr(b + r, blen - r, " EOM"); /* end-of-medium condition exists */ if (sbp[2] & 0x20) r += scnpr(b + r, blen - r, " ILI"); /* incorrect block length requested */ r += scnpr(b + r, blen - r, "\n"); } else if (valid || (info > 0)) r += scnpr(b + r, blen - r, "\n"); if ((len >= 14) && sbp[14]) r += scnpr(b + r, blen - r, "%s Field replaceable unit " "code: %d\n", lip, sbp[14]); if ((len >= 18) && (sbp[15] & 0x80)) { /* sense key specific decoding */ switch (ssh.sense_key) { case SPC_SK_ILLEGAL_REQUEST: r += scnpr(b + r, blen - r, "%s Sense Key Specific: " "Error in %s: byte %d", lip, ((sbp[15] & 0x40) ? "Command" : "Data parameters"), sg_get_unaligned_be16(sbp + 16)); if (sbp[15] & 0x08) r += scnpr(b + r, blen - r, " bit %d\n", sbp[15] & 0x07); else r += scnpr(b + r, blen - r, "\n"); break; case SPC_SK_NO_SENSE: case SPC_SK_NOT_READY: progress = sg_get_unaligned_be16(sbp + 16); pr = (progress * 100) / 65536; rem = ((progress * 100) % 65536) / 656; r += scnpr(b + r, blen - r, "%s Progress indication: " "%d.%02d%%\n", lip, pr, rem); break; case SPC_SK_HARDWARE_ERROR: case SPC_SK_MEDIUM_ERROR: case SPC_SK_RECOVERED_ERROR: r += scnpr(b + r, blen - r, "%s Actual retry count: " "0x%02x%02x\n", lip, sbp[16], sbp[17]); break; case SPC_SK_COPY_ABORTED: r += scnpr(b + r, blen - r, "%s Segment pointer: ", lip); r += scnpr(b + r, blen - r, "Relative to start of %s, " "byte %d", ((sbp[15] & 0x20) ? "segment descriptor" : "parameter list"), sg_get_unaligned_be16(sbp + 16)); if (sbp[15] & 0x08) r += scnpr(b + r, blen - r, " bit %d\n", sbp[15] & 0x07); else r += scnpr(b + r, blen - r, "\n"); break; case SPC_SK_UNIT_ATTENTION: r += scnpr(b + r, blen - r, "%s Unit attention " "condition queue: ", lip); r += scnpr(b + r, blen - r, "overflow flag is %d\n", !!(sbp[15] & 0x1)); break; default: r += scnpr(b + r, blen - r, "%s Sense_key: 0x%x " "unexpected\n", lip, ssh.sense_key); break; } } if (r > 0) n += scnpr(cbp + n, cblen - n, "%s", b); } else n += scnpr(cbp + n, cblen - n, "%s fixed descriptor length " "too short, len=%d\n", lip, len); } else { /* unable to normalise sense buffer, something irregular */ if (sb_len < 4) { /* Too short */ n += scnpr(cbp + n, cblen - n, "%ssense buffer too short (4 " "byte minimum)\n", lip); goto check_raw; } if (0x7f == resp_code) { /* Vendor specific */ n += scnpr(cbp + n, cblen - n, "%sVendor specific sense buffer, " "in hex:\n", lip); n += hex2str(sbp, sb_len, lip, -1, cblen - n, cbp + n); return n; /* no need to check raw, just output in hex */ } /* non-extended SCSI-1 sense data ?? */ r = 0; if (strlen(lip) > 0) r += scnpr(b + r, blen - r, "%s", lip); r += scnpr(b + r, blen - r, "Probably uninitialized data.\n%s Try " "to view as SCSI-1 non-extended sense:\n", lip); r += scnpr(b + r, blen - r, " AdValid=%d Error class=%d Error " "code=%d\n", valid, ((sbp[0] >> 4) & 0x7), (sbp[0] & 0xf)); if (valid) scnpr(b + r, blen - r, "%s lba=0x%x\n", lip, sg_get_unaligned_be24(sbp + 1) & 0x1fffff); n += scnpr(cbp + n, cblen - n, "%s\n", b); len = sb_len; if (len > 32) len = 32; /* trim in case there is a lot of rubbish */ } check_raw: if (raw_sinfo) { char z[64]; n += scnpr(cbp + n, cblen - n, "%s Raw sense data (in hex):\n", lip); if (n >= (cblen - 1)) return n; scnpr(z, sizeof(z), "%.50s ", lip); n += hex2str(sbp, len, z, -1, cblen - n, cbp + n); } return n; } /* Print sense information */ void sg_print_sense(const char * leadin, const unsigned char * sbp, int sb_len, bool raw_sinfo) { uint32_t pg_sz = sg_get_page_size(); char *cp; uint8_t *free_cp; cp = (char *)sg_memalign(pg_sz, pg_sz, &free_cp, 0); if (NULL == cp) return; sg_get_sense_str(leadin, sbp, sb_len, raw_sinfo, pg_sz, cp); pr2ws("%s", cp); free(free_cp); } /* Following examines exit_status and outputs a clear error message to * warnings_strm (usually stderr) if one is known and returns true. * Otherwise it doesn't print anything and returns false. Note that * if exit_status==0 then returns true but prints nothing and if * exit_status<0 ("some error occurred") false is returned. If leadin is * non-NULL then it is printed before the error message. */ bool sg_if_can2stderr(const char * leadin, int exit_status) { const char * s = leadin ? leadin : ""; if (exit_status < 0) return false; else if (0 == exit_status) return true; switch (exit_status) { case SG_LIB_CAT_NOT_READY: /* 2 */ pr2ws("%sDevice not ready\n", s); return true; case SG_LIB_CAT_MEDIUM_HARD: /* 3 */ pr2ws("%sMedium or hardware error\n", s); /* 3 sense keys: Medium, */ return true; /* hardware error or 'Blank check' for tapes */ case SG_LIB_CAT_UNIT_ATTENTION: /* 6 */ pr2ws("%sDevice reported 'Unit attention'\n", s); return true; case SG_LIB_CAT_DATA_PROTECT: /* 7 */ pr2ws("%sDevice reported 'Data protect', read-only?\n", s); return true; case SG_LIB_CAT_COPY_ABORTED: /* 10 */ pr2ws("%sCopy aborted\n", s); return true; case SG_LIB_CAT_ABORTED_COMMAND: /* 11 */ pr2ws("%sCommand aborted\n", s); return true; case SG_LIB_CAT_MISCOMPARE: /* 14 */ pr2ws("%sMiscompare\n", s); return true; case SG_LIB_CAT_RES_CONFLICT: /* 24 */ pr2ws("%sReservation conflict\n", s); return true; case SG_LIB_CAT_BUSY: /* 26 */ pr2ws("%sDevice is busy, try again\n", s); return true; case SG_LIB_CAT_TASK_ABORTED: /* 29 */ pr2ws("%sTask aborted\n", s); return true; case SG_LIB_CAT_TIMEOUT: /* 33 */ pr2ws("%sTime out\n", s); return true; case SG_LIB_CAT_PROTECTION: /* 40 */ pr2ws("%sProtection error\n", s); return true; case SG_LIB_NVME_STATUS: /* 48 */ pr2ws("%sNVMe error (non-zero status)\n", s); return true; case SG_LIB_OS_BASE_ERR + EACCES: /* 50 + */ pr2ws("%sPermission denied\n", s); return true; case SG_LIB_OS_BASE_ERR + ENOMEM: pr2ws("%sUtility unable to allocate memory\n", s); return true; case SG_LIB_OS_BASE_ERR + ENOTTY: pr2ws("%sInappropriate I/O control operation\n", s); return true; case SG_LIB_OS_BASE_ERR + EPERM: pr2ws("%sNot permitted\n", s); return true; case SG_LIB_OS_BASE_ERR + EINTR: pr2ws("%sInterrupted system call\n", s); return true; case SG_LIB_OS_BASE_ERR + EIO: pr2ws("%sInput/output error\n", s); return true; case SG_LIB_OS_BASE_ERR + ENODEV: pr2ws("%sNo such device\n", s); return true; case SG_LIB_OS_BASE_ERR + ENOENT: pr2ws("%sNo such file or directory\n", s); return true; default: return false; } return false; } /* If os_err_num is within bounds then the returned value is 'os_err_num + * SG_LIB_OS_BASE_ERR' otherwise -1 is returned. If os_err_num is 0 then 0 * is returned. */ int sg_convert_errno(int os_err_num) { if (os_err_num <= 0) { if (os_err_num < -1) return -1; return os_err_num; } if (os_err_num < (SG_LIB_CAT_MALFORMED - SG_LIB_OS_BASE_ERR)) return SG_LIB_OS_BASE_ERR + os_err_num; return -1; } /* See description in sg_lib.h header file */ bool sg_scsi_normalize_sense(const unsigned char * sbp, int sb_len, struct sg_scsi_sense_hdr * sshp) { uint8_t resp_code; if (sshp) memset(sshp, 0, sizeof(struct sg_scsi_sense_hdr)); if ((NULL == sbp) || (sb_len < 1)) return false; resp_code = 0x7f & sbp[0]; if ((resp_code < 0x70) || (resp_code > 0x73)) return false; if (sshp) { sshp->response_code = resp_code; if (sshp->response_code >= 0x72) { /* descriptor format */ if (sb_len > 1) sshp->sense_key = (0xf & sbp[1]); if (sb_len > 2) sshp->asc = sbp[2]; if (sb_len > 3) sshp->ascq = sbp[3]; if (sb_len > 7) sshp->additional_length = sbp[7]; } else { /* fixed format */ if (sb_len > 2) sshp->sense_key = (0xf & sbp[2]); if (sb_len > 7) { sb_len = (sb_len < (sbp[7] + 8)) ? sb_len : (sbp[7] + 8); if (sb_len > 12) sshp->asc = sbp[12]; if (sb_len > 13) sshp->ascq = sbp[13]; } } } return true; } /* Returns a SG_LIB_CAT_* value. If cannot decode sense buffer (sbp) or a * less common sense key then return SG_LIB_CAT_SENSE .*/ int sg_err_category_sense(const unsigned char * sbp, int sb_len) { struct sg_scsi_sense_hdr ssh; if ((sbp && (sb_len > 2)) && (sg_scsi_normalize_sense(sbp, sb_len, &ssh))) { switch (ssh.sense_key) { /* 0 to 0x1f */ case SPC_SK_NO_SENSE: return SG_LIB_CAT_NO_SENSE; case SPC_SK_RECOVERED_ERROR: return SG_LIB_CAT_RECOVERED; case SPC_SK_NOT_READY: return SG_LIB_CAT_NOT_READY; case SPC_SK_MEDIUM_ERROR: case SPC_SK_HARDWARE_ERROR: case SPC_SK_BLANK_CHECK: return SG_LIB_CAT_MEDIUM_HARD; case SPC_SK_UNIT_ATTENTION: return SG_LIB_CAT_UNIT_ATTENTION; /* used to return SG_LIB_CAT_MEDIA_CHANGED when ssh.asc==0x28 */ case SPC_SK_ILLEGAL_REQUEST: if ((0x20 == ssh.asc) && (0x0 == ssh.ascq)) return SG_LIB_CAT_INVALID_OP; else return SG_LIB_CAT_ILLEGAL_REQ; break; case SPC_SK_ABORTED_COMMAND: if (0x10 == ssh.asc) return SG_LIB_CAT_PROTECTION; else return SG_LIB_CAT_ABORTED_COMMAND; case SPC_SK_MISCOMPARE: return SG_LIB_CAT_MISCOMPARE; case SPC_SK_DATA_PROTECT: return SG_LIB_CAT_DATA_PROTECT; case SPC_SK_COPY_ABORTED: return SG_LIB_CAT_COPY_ABORTED; case SPC_SK_COMPLETED: case SPC_SK_VOLUME_OVERFLOW: return SG_LIB_CAT_SENSE; default: ; /* reserved and vendor specific sense keys fall through */ } } return SG_LIB_CAT_SENSE; } /* Beware: gives wrong answer for variable length command (opcode=0x7f) */ int sg_get_command_size(unsigned char opcode) { switch ((opcode >> 5) & 0x7) { case 0: return 6; case 1: case 2: case 6: case 7: return 10; case 3: case 5: return 12; break; case 4: return 16; default: return 10; } } void sg_get_command_name(const unsigned char * cmdp, int peri_type, int buff_len, char * buff) { int service_action; if ((NULL == buff) || (buff_len < 1)) return; else if (1 == buff_len) { buff[0] = '\0'; return; } if (NULL == cmdp) { scnpr(buff, buff_len, "%s", " command pointer"); return; } service_action = (SG_VARIABLE_LENGTH_CMD == cmdp[0]) ? sg_get_unaligned_be16(cmdp + 8) : (cmdp[1] & 0x1f); sg_get_opcode_sa_name(cmdp[0], service_action, peri_type, buff_len, buff); } struct op_code2sa_t { int op_code; int pdt_match; /* -1->all; 0->disk,ZBC,RCB, 1->tape+adc+smc */ struct sg_lib_value_name_t * arr; const char * prefix; }; static struct op_code2sa_t op_code2sa_arr[] = { {SG_VARIABLE_LENGTH_CMD, -1, sg_lib_variable_length_arr, NULL}, {SG_MAINTENANCE_IN, -1, sg_lib_maint_in_arr, NULL}, {SG_MAINTENANCE_OUT, -1, sg_lib_maint_out_arr, NULL}, {SG_SERVICE_ACTION_IN_12, -1, sg_lib_serv_in12_arr, NULL}, {SG_SERVICE_ACTION_OUT_12, -1, sg_lib_serv_out12_arr, NULL}, {SG_SERVICE_ACTION_IN_16, -1, sg_lib_serv_in16_arr, NULL}, {SG_SERVICE_ACTION_OUT_16, -1, sg_lib_serv_out16_arr, NULL}, {SG_SERVICE_ACTION_BIDI, -1, sg_lib_serv_bidi_arr, NULL}, {SG_PERSISTENT_RESERVE_IN, -1, sg_lib_pr_in_arr, "Persistent reserve in"}, {SG_PERSISTENT_RESERVE_OUT, -1, sg_lib_pr_out_arr, "Persistent reserve out"}, {SG_3PARTY_COPY_OUT, -1, sg_lib_xcopy_sa_arr, NULL}, {SG_3PARTY_COPY_IN, -1, sg_lib_rec_copy_sa_arr, NULL}, {SG_READ_BUFFER, -1, sg_lib_read_buff_arr, "Read buffer(10)"}, {SG_READ_BUFFER_16, -1, sg_lib_read_buff_arr, "Read buffer(16)"}, {SG_READ_ATTRIBUTE, -1, sg_lib_read_attr_arr, "Read attribute"}, {SG_READ_POSITION, 1, sg_lib_read_pos_arr, "Read position"}, {SG_SANITIZE, 0, sg_lib_sanitize_sa_arr, "Sanitize"}, {SG_WRITE_BUFFER, -1, sg_lib_write_buff_arr, "Write buffer"}, {SG_ZONING_IN, 0, sg_lib_zoning_in_arr, NULL}, {SG_ZONING_OUT, 0, sg_lib_zoning_out_arr, NULL}, {0xffff, -1, NULL, NULL}, }; void sg_get_opcode_sa_name(unsigned char cmd_byte0, int service_action, int peri_type, int buff_len, char * buff) { int d_pdt; const struct sg_lib_value_name_t * vnp; const struct op_code2sa_t * osp; char b[80]; if ((NULL == buff) || (buff_len < 1)) return; else if (1 == buff_len) { buff[0] = '\0'; return; } if (peri_type < 0) peri_type = 0; d_pdt = sg_lib_pdt_decay(peri_type); for (osp = op_code2sa_arr; osp->arr; ++osp) { if ((int)cmd_byte0 == osp->op_code) { if ((osp->pdt_match < 0) || (d_pdt == osp->pdt_match)) { vnp = get_value_name(osp->arr, service_action, peri_type); if (vnp) { if (osp->prefix) scnpr(buff, buff_len, "%s, %s", osp->prefix, vnp->name); else scnpr(buff, buff_len, "%s", vnp->name); } else { sg_get_opcode_name(cmd_byte0, peri_type, sizeof(b), b); scnpr(buff, buff_len, "%s service action=0x%x", b, service_action); } } else sg_get_opcode_name(cmd_byte0, peri_type, buff_len, buff); return; } } sg_get_opcode_name(cmd_byte0, peri_type, buff_len, buff); } void sg_get_opcode_name(unsigned char cmd_byte0, int peri_type, int buff_len, char * buff) { const struct sg_lib_value_name_t * vnp; int grp; if ((NULL == buff) || (buff_len < 1)) return; else if (1 == buff_len) { buff[0] = '\0'; return; } if (SG_VARIABLE_LENGTH_CMD == cmd_byte0) { scnpr(buff, buff_len, "%s", "Variable length"); return; } grp = (cmd_byte0 >> 5) & 0x7; switch (grp) { case 0: case 1: case 2: case 4: case 5: vnp = get_value_name(sg_lib_normal_opcodes, cmd_byte0, peri_type); if (vnp) scnpr(buff, buff_len, "%s", vnp->name); else scnpr(buff, buff_len, "Opcode=0x%x", (int)cmd_byte0); break; case 3: scnpr(buff, buff_len, "Reserved [0x%x]", (int)cmd_byte0); break; case 6: case 7: scnpr(buff, buff_len, "Vendor specific [0x%x]", (int)cmd_byte0); break; default: scnpr(buff, buff_len, "Opcode=0x%x", (int)cmd_byte0); break; } } /* Iterates to next designation descriptor in the device identification * VPD page. The 'initial_desig_desc' should point to start of first * descriptor with 'page_len' being the number of valid bytes in that * and following descriptors. To start, 'off' should point to a negative * value, thereafter it should point to the value yielded by the previous * call. If 0 returned then 'initial_desig_desc + *off' should be a valid * descriptor; returns -1 if normal end condition and -2 for an abnormal * termination. Matches association, designator_type and/or code_set when * any of those values are greater than or equal to zero. */ int sg_vpd_dev_id_iter(const unsigned char * initial_desig_desc, int page_len, int * off, int m_assoc, int m_desig_type, int m_code_set) { bool fltr = ((m_assoc >= 0) || (m_desig_type >= 0) || (m_code_set >= 0)); int k = *off; const unsigned char * bp = initial_desig_desc; while ((k + 3) < page_len) { k = (k < 0) ? 0 : (k + bp[k + 3] + 4); if ((k + 4) > page_len) break; if (fltr) { if (m_code_set >= 0) { if ((bp[k] & 0xf) != m_code_set) continue; } if (m_assoc >= 0) { if (((bp[k + 1] >> 4) & 0x3) != m_assoc) continue; } if (m_desig_type >= 0) { if ((bp[k + 1] & 0xf) != m_desig_type) continue; } } *off = k; return 0; } return (k == page_len) ? -1 : -2; } static const char * const bad_sense_cat = "Bad sense category"; /* Yield string associated with sense category. Returns 'buff' (or pointer * to "Bad sense category" if 'buff' is NULL). If sense_cat unknown then * yield "Sense category: " string. */ const char * sg_get_category_sense_str(int sense_cat, int buff_len, char * buff, int verbose) { int n; if (NULL == buff) return bad_sense_cat; if (buff_len <= 0) return buff; switch (sense_cat) { case SG_LIB_CAT_CLEAN: /* 0 */ scnpr(buff, buff_len, "No errors"); break; case SG_LIB_SYNTAX_ERROR: /* 1 */ scnpr(buff, buff_len, "Syntax error"); break; case SG_LIB_CAT_NOT_READY: /* 2 */ n = scnpr(buff, buff_len, "Not ready"); if (verbose && (n < (buff_len - 1))) scnpr(buff + n, buff_len - n, " sense key"); break; case SG_LIB_CAT_MEDIUM_HARD: /* 3 */ n = scnpr(buff, buff_len, "Medium or hardware error"); if (verbose && (n < (buff_len - 1))) scnpr(buff + n, buff_len - n, " sense key (plus blank check)"); break; case SG_LIB_CAT_ILLEGAL_REQ: /* 5 */ n = scnpr(buff, buff_len, "Illegal request"); if (verbose && (n < (buff_len - 1))) scnpr(buff + n, buff_len - n, " sense key, apart from Invalid " "opcode"); break; case SG_LIB_CAT_UNIT_ATTENTION: /* 6 */ n = scnpr(buff, buff_len, "Unit attention"); if (verbose && (n < (buff_len - 1))) scnpr(buff + n, buff_len - n, " sense key"); break; case SG_LIB_CAT_DATA_PROTECT: /* 7 */ n = scnpr(buff, buff_len, "Data protect"); if (verbose && (n < (buff_len - 1))) scnpr(buff + n, buff_len - n, " sense key, write protected " "media?"); break; case SG_LIB_CAT_INVALID_OP: /* 9 */ n = scnpr(buff, buff_len, "Illegal request, invalid opcode"); if (verbose && (n < (buff_len - 1))) scnpr(buff + n, buff_len - n, " sense key"); break; case SG_LIB_CAT_COPY_ABORTED: /* 10 */ n = scnpr(buff, buff_len, "Copy aborted"); if (verbose && (n < (buff_len - 1))) scnpr(buff + n, buff_len - n, " sense key"); break; case SG_LIB_CAT_ABORTED_COMMAND: /* 11 */ n = scnpr(buff, buff_len, "Aborted command"); if (verbose && (n < (buff_len - 1))) scnpr(buff + n, buff_len - n, " sense key, other than " "protection related (asc=0x10)"); break; case SG_LIB_CAT_MISCOMPARE: /* 14 */ n = scnpr(buff, buff_len, "Miscompare"); if (verbose && (n < (buff_len - 1))) scnpr(buff + n, buff_len - n, " sense key"); break; case SG_LIB_FILE_ERROR: /* 15 */ scnpr(buff, buff_len, "File error"); break; case SG_LIB_CAT_ILLEGAL_REQ_WITH_INFO: /* 17 */ scnpr(buff, buff_len, "Illegal request with info"); break; case SG_LIB_CAT_MEDIUM_HARD_WITH_INFO: /* 18 */ scnpr(buff, buff_len, "Medium or hardware error with info"); break; case SG_LIB_CAT_NO_SENSE: /* 20 */ n = scnpr(buff, buff_len, "No sense key"); if (verbose && (n < (buff_len - 1))) scnpr(buff + n, buff_len - n, " probably additional sense " "information"); break; case SG_LIB_CAT_RECOVERED: /* 21 */ n = scnpr(buff, buff_len, "Recovered error"); if (verbose && (n < (buff_len - 1))) scnpr(buff + n, buff_len - n, " sense key"); break; case SG_LIB_CAT_RES_CONFLICT: /* 24 */ n = scnpr(buff, buff_len, "Reservation conflict"); if (verbose && (n < (buff_len - 1))) scnpr(buff + n, buff_len - n, " SCSI status"); break; case SG_LIB_CAT_CONDITION_MET: /* 25 */ n = scnpr(buff, buff_len, "Condition met"); if (verbose && (n < (buff_len - 1))) scnpr(buff + n, buff_len - n, " SCSI status"); break; case SG_LIB_CAT_BUSY: /* 26 */ n = scnpr(buff, buff_len, "Busy"); if (verbose && (n < (buff_len - 1))) scnpr(buff + n, buff_len - n, " SCSI status"); break; case SG_LIB_CAT_TS_FULL: /* 27 */ n = scnpr(buff, buff_len, "Task set full"); if (verbose && (n < (buff_len - 1))) scnpr(buff + n, buff_len - n, " SCSI status"); break; case SG_LIB_CAT_ACA_ACTIVE: /* 28 */ n = scnpr(buff, buff_len, "ACA active"); if (verbose && (n < (buff_len - 1))) scnpr(buff + n, buff_len - n, " SCSI status"); break; case SG_LIB_CAT_TASK_ABORTED: /* 29 */ n = scnpr(buff, buff_len, "Task aborted"); if (verbose && (n < (buff_len - 1))) scnpr(buff + n, buff_len - n, " SCSI status"); break; case SG_LIB_CAT_TIMEOUT: /* 33 */ scnpr(buff, buff_len, "SCSI command timeout"); break; case SG_LIB_CAT_PROTECTION: /* 40 */ n = scnpr(buff, buff_len, "Aborted command, protection"); if (verbose && (n < (buff_len - 1))) scnpr(buff + n, buff_len - n, " information (PI) problem"); break; case SG_LIB_CAT_PROTECTION_WITH_INFO: /* 41 */ n = scnpr(buff, buff_len, "Aborted command with info, protection"); if (verbose && (n < (buff_len - 1))) scnpr(buff + n, buff_len - n, " information (PI) problem"); break; case SG_LIB_CAT_MALFORMED: /* 97 */ n = scnpr(buff, buff_len, "Malformed response"); if (verbose && (n < (buff_len - 1))) scnpr(buff + n, buff_len - n, " to SCSI command"); break; case SG_LIB_CAT_SENSE: /* 98 */ n = scnpr(buff, buff_len, "Some other sense data problem"); if (verbose && (n < (buff_len - 1))) scnpr(buff + n, buff_len - n, ", try '-v' option for more " "information"); break; case SG_LIB_CAT_OTHER: /* 99 */ n = scnpr(buff, buff_len, "Some other error/warning has occurred"); if ((0 == verbose) && (n < (buff_len - 1))) scnpr(buff + n, buff_len - n, ", possible transport of driver " "issue"); break; default: if ((sense_cat > SG_LIB_OS_BASE_ERR) && (sense_cat < (SG_LIB_OS_BASE_ERR + 47))) { int k = sense_cat - SG_LIB_OS_BASE_ERR; n = scnpr(buff, buff_len, "OS error: %s [%d]", safe_strerror(k), k); } else { n = scnpr(buff, buff_len, "Sense category: %d", sense_cat); if ((0 == verbose) && (n < (buff_len - 1))) scnpr(buff + n, buff_len - n, ", try '-v' option for more " "information"); } break; } return buff; } static const char * sg_sfs_spc_reserved = "SPC Reserved"; static const char * sg_sfs_sbc_reserved = "SBC Reserved"; static const char * sg_sfs_ssc_reserved = "SSC Reserved"; static const char * sg_sfs_zbc_reserved = "ZBC Reserved"; static const char * sg_sfs_reserved = "Reserved"; /* Yield SCSI Feature Set (sfs) string. When 'peri_type' is < -1 (or > 31) * returns pointer to string (same as 'buff') associated with 'sfs_code'. * When 'peri_type' is between -1 (for SPC) and 31 (inclusive) then a match * on both 'sfs_code' and 'peri_type' is required. If 'foundp' is not NULL * then where it points is set to true if a match is found else it is set to * false. If 'buff' is not NULL then in the case of a match a descriptive * string is written to 'buff' while if there is not a not then a string * ending in "Reserved" is written (and may be prefixed with SPC, SBC, SSC * or ZBC). Returns 'buff' (i.e. a pointer value) even if it is NULL. * Example: * char b[64]; * ... * printf("%s\n", sg_get_sfs_str(sfs_code, -2, sizeof(b), b, NULL, 0)); */ const char * sg_get_sfs_str(uint16_t sfs_code, int peri_type, int buff_len, char * buff, bool * foundp, int verbose) { const struct sg_lib_value_name_t * vnp = NULL; int n = 0; int my_pdt; if ((NULL == buff) || (buff_len < 1)) { if (foundp) *foundp = false; return NULL; } else if (1 == buff_len) { buff[0] = '\0'; if (foundp) *foundp = false; return NULL; } my_pdt = ((peri_type < -1) || (peri_type > 0x1f)) ? -2 : peri_type; vnp = get_value_name(sg_lib_scsi_feature_sets, sfs_code, my_pdt); if (vnp && (-2 != my_pdt)) { if (peri_type != vnp->peri_dev_type) vnp = NULL; /* shouldn't really happen */ } if (foundp) *foundp = vnp ? true : false; if (sfs_code < 0x100) { /* SPC Feature Sets */ if (vnp) { if (verbose) n += scnpr(buff, buff_len, "SPC %s", vnp->name); else n += scnpr(buff, buff_len, "%s", vnp->name); } else n += scnpr(buff, buff_len, "%s", sg_sfs_spc_reserved); } else if (sfs_code < 0x200) { /* SBC Feature Sets */ if (vnp) { if (verbose) n += scnpr(buff, buff_len, "SBC %s", vnp->name); else n += scnpr(buff, buff_len, "%s", vnp->name); } else n += scnpr(buff, buff_len, "%s", sg_sfs_sbc_reserved); } else if (sfs_code < 0x300) { /* SSC Feature Sets */ if (vnp) { if (verbose) n += scnpr(buff, buff_len, "SSC %s", vnp->name); else n += scnpr(buff, buff_len, "%s", vnp->name); } else n += scnpr(buff, buff_len, "%s", sg_sfs_ssc_reserved); } else if (sfs_code < 0x400) { /* ZBC Feature Sets */ if (vnp) { if (verbose) n += scnpr(buff, buff_len, "ZBC %s", vnp->name); else n += scnpr(buff, buff_len, "%s", vnp->name); } else n += scnpr(buff, buff_len, "%s", sg_sfs_zbc_reserved); } else { /* Other SCSI Feature Sets */ if (vnp) { if (verbose) n += scnpr(buff, buff_len, "[unrecognized PDT] %s", vnp->name); else n += scnpr(buff, buff_len, "%s", vnp->name); } else n += scnpr(buff, buff_len, "%s", sg_sfs_reserved); } if (verbose > 4) pr2serr("%s: length of returned string (n) %d\n", __func__, n); return buff; } /* This is a heuristic that takes into account the command bytes and length * to decide whether the presented unstructured sequence of bytes could be * a SCSI command. If so it returns true otherwise false. Vendor specific * SCSI commands (i.e. opcodes from 0xc0 to 0xff), if presented, are assumed * to follow SCSI conventions (i.e. length of 6, 10, 12 or 16 bytes). The * only SCSI commands considered above 16 bytes of length are the Variable * Length Commands (opcode 0x7f) and the XCDB wrapped commands (opcode 0x7e). * Both have an inbuilt length field which can be cross checked with clen. * No NVMe commands (64 bytes long plus some extra added by some OSes) have * opcodes 0x7e or 0x7f yet. ATA is register based but SATA has FIS * structures that are sent across the wire. The FIS register structure is * used to move a command from a SATA host to device, but the ATA 'command' * is not the first byte. So it is harder to say what will happen if a * FIS structure is presented as a SCSI command, hopfully there is a low * probability this function will yield true in that case. */ bool sg_is_scsi_cdb(const uint8_t * cdbp, int clen) { int ilen, sa; uint8_t opcode; uint8_t top3bits; if (clen < 6) return false; opcode = cdbp[0]; top3bits = opcode >> 5; if (0x3 == top3bits) { if ((clen < 12) || (clen % 4)) return false; /* must be modulo 4 and 12 or more bytes */ switch (opcode) { case 0x7e: /* Extended cdb (XCDB) */ ilen = 4 + sg_get_unaligned_be16(cdbp + 2); return (ilen == clen); case 0x7f: /* Variable Length cdb */ ilen = 8 + cdbp[7]; sa = sg_get_unaligned_be16(cdbp + 8); /* service action (sa) 0x0 is reserved */ return ((ilen == clen) && sa); default: return false; } } else if (clen <= 16) { switch (clen) { case 6: if (top3bits > 0x5) /* vendor */ return true; return (0x0 == top3bits); /* 6 byte cdb */ case 10: if (top3bits > 0x5) /* vendor */ return true; return ((0x1 == top3bits) || (0x2 == top3bits)); /* 10 byte cdb */ case 16: if (top3bits > 0x5) /* vendor */ return true; return (0x4 == top3bits); /* 16 byte cdb */ case 12: if (top3bits > 0x5) /* vendor */ return true; return (0x5 == top3bits); /* 12 byte cdb */ default: return false; } } /* NVMe probably falls out here, clen > 16 and (opcode < 0x60 or * opcode > 0x7f). */ return false; } /* Yield string associated with NVMe command status value in sct_sc. It * expects to decode DW3 bits 27:17 from the completion queue. Bits 27:25 * are the Status Code Type (SCT) and bits 24:17 are the Status Code (SC). * Bit 17 in DW3 should be bit 0 in sct_sc. If no status string is found * a string of the form "Reserved [0x]" is generated. * Returns 'buff'. Does nothing if buff_len<=0 or if buff is NULL.*/ char * sg_get_nvme_cmd_status_str(uint16_t sct_sc, int b_len, char * b) { int k; uint16_t s = 0x3ff & sct_sc; const struct sg_lib_value_name_t * vp = sg_lib_nvme_cmd_status_arr; if ((b_len <= 0) || (NULL == b)) return b; else if (1 == b_len) { b[0] = '\0'; return b; } for (k = 0; (vp->name && (k < 1000)); ++k, ++vp) { if (s == (uint16_t)vp->value) { strncpy(b, vp->name, b_len); b[b_len - 1] = '\0'; return b; } } if (k >= 1000) pr2ws("%s: where is sentinel for sg_lib_nvme_cmd_status_arr ??\n", __func__); snprintf(b, b_len, "Reserved [0x%x]", sct_sc); return b; } /* Attempts to map NVMe status value ((SCT << 8) | SC) to SCSI status, * sense_key, asc and ascq tuple. If successful returns true and writes to * non-NULL pointer arguments; otherwise returns false. */ bool sg_nvme_status2scsi(uint16_t sct_sc, uint8_t * status_p, uint8_t * sk_p, uint8_t * asc_p, uint8_t * ascq_p) { int k, ind; uint16_t s = 0x3ff & sct_sc; struct sg_lib_value_name_t * vp = sg_lib_nvme_cmd_status_arr; struct sg_lib_4tuple_u8 * mp = sg_lib_scsi_status_sense_arr; for (k = 0; (vp->name && (k < 1000)); ++k, ++vp) { if (s == (uint16_t)vp->value) break; } if (k >= 1000) { pr2ws("%s: where is sentinel for sg_lib_nvme_cmd_status_arr ??\n", __func__); return false; } if (NULL == vp->name) return false; ind = vp->peri_dev_type; for (k = 0; (0xff != mp->t2) && k < 1000; ++k, ++mp) ; /* count entries for valid index range */ if (k >= 1000) { pr2ws("%s: where is sentinel for sg_lib_scsi_status_sense_arr ??\n", __func__); return false; } else if (ind >= k) return false; mp = sg_lib_scsi_status_sense_arr + ind; if (status_p) *status_p = mp->t1; if (sk_p) *sk_p = mp->t2; if (asc_p) *asc_p = mp->t3; if (ascq_p) *ascq_p = mp->t4; return true; } /* safe_strerror() contributed by Clayton Weaver * Allows for situation in which strerror() is given a wild value (or the * C library is incomplete) and returns NULL. Still not thread safe. */ static char safe_errbuf[64] = {'u', 'n', 'k', 'n', 'o', 'w', 'n', ' ', 'e', 'r', 'r', 'n', 'o', ':', ' ', 0}; char * safe_strerror(int errnum) { size_t len; char * errstr; if (errnum < 0) errnum = -errnum; errstr = strerror(errnum); if (NULL == errstr) { len = strlen(safe_errbuf); scnpr(safe_errbuf + len, sizeof(safe_errbuf) - len, "%i", errnum); return safe_errbuf; } return errstr; } static void trimTrailingSpaces(char * b) { int k; for (k = ((int)strlen(b) - 1); k >= 0; --k) { if (' ' != b[k]) break; } if ('\0' != b[k + 1]) b[k + 1] = '\0'; } /* Note the ASCII-hex output goes to stdout. [Most other output from functions * in this file go to sg_warnings_strm (default stderr).] * 'no_ascii' allows for 3 output types: * > 0 each line has address then up to 16 ASCII-hex bytes * = 0 in addition, the bytes are listed in ASCII to the right * < 0 only the ASCII-hex bytes are listed (i.e. without address) */ static void dStrHexFp(const char* str, int len, int no_ascii, FILE * fp) { const char * p = str; const char * formatstr; unsigned char c; char buff[82]; int a = 0; int bpstart = 5; const int cpstart = 60; int cpos = cpstart; int bpos = bpstart; int i, k, blen; if (len <= 0) return; blen = (int)sizeof(buff); if (0 == no_ascii) /* address at left and ASCII at right */ formatstr = "%.76s\n"; else /* previously when > 0 str was "%.58s\n" */ formatstr = "%s\n"; /* when < 0 str was: "%.48s\n" */ memset(buff, ' ', 80); buff[80] = '\0'; if (no_ascii < 0) { bpstart = 0; bpos = bpstart; for (k = 0; k < len; k++) { c = *p++; if (bpos == (bpstart + (8 * 3))) bpos++; scnpr(&buff[bpos], blen - bpos, "%.2x", (int)(unsigned char)c); buff[bpos + 2] = ' '; if ((k > 0) && (0 == ((k + 1) % 16))) { trimTrailingSpaces(buff); fprintf(fp, formatstr, buff); bpos = bpstart; memset(buff, ' ', 80); } else bpos += 3; } if (bpos > bpstart) { buff[bpos + 2] = '\0'; trimTrailingSpaces(buff); fprintf(fp, "%s\n", buff); } return; } /* no_ascii>=0, start each line with address (offset) */ k = scnpr(buff + 1, blen - 1, "%.2x", a); buff[k + 1] = ' '; for (i = 0; i < len; i++) { c = *p++; bpos += 3; if (bpos == (bpstart + (9 * 3))) bpos++; scnpr(&buff[bpos], blen - bpos, "%.2x", (int)(unsigned char)c); buff[bpos + 2] = ' '; if (no_ascii) buff[cpos++] = ' '; else { if (! my_isprint(c)) c = '.'; buff[cpos++] = c; } if (cpos > (cpstart + 15)) { if (no_ascii) trimTrailingSpaces(buff); fprintf(fp, formatstr, buff); bpos = bpstart; cpos = cpstart; a += 16; memset(buff, ' ', 80); k = scnpr(buff + 1, blen - 1, "%.2x", a); buff[k + 1] = ' '; } } if (cpos > cpstart) { buff[cpos] = '\0'; if (no_ascii) trimTrailingSpaces(buff); fprintf(fp, "%s\n", buff); } } void dStrHex(const char* str, int len, int no_ascii) { dStrHexFp(str, len, no_ascii, stdout); } void dStrHexErr(const char* str, int len, int no_ascii) { dStrHexFp(str, len, no_ascii, (sg_warnings_strm ? sg_warnings_strm : stderr)); } #define DSHS_LINE_BLEN 160 #define DSHS_BPL 16 /* Read 'len' bytes from 'str' and output as ASCII-Hex bytes (space * separated) to 'b' not to exceed 'b_len' characters. Each line * starts with 'leadin' (NULL for no leadin) and there are 16 bytes * per line with an extra space between the 8th and 9th bytes. 'format' * is 0 for repeat in printable ASCII ('.' for non printable) to * right of each line; 1 don't (so just output ASCII hex). Returns * number of bytes written to 'b' excluding the trailing '\0'. */ int dStrHexStr(const char * str, int len, const char * leadin, int format, int b_len, char * b) { unsigned char c; int bpstart, bpos, k, n, prior_ascii_len; bool want_ascii; char buff[DSHS_LINE_BLEN + 2]; char a[DSHS_BPL + 1]; const char * p = str; if (len <= 0) { if (b_len > 0) b[0] = '\0'; return 0; } if (b_len <= 0) return 0; want_ascii = !format; if (want_ascii) { memset(a, ' ', DSHS_BPL); a[DSHS_BPL] = '\0'; } if (leadin) { bpstart = strlen(leadin); /* Cap leadin at (DSHS_LINE_BLEN - 70) characters */ if (bpstart > (DSHS_LINE_BLEN - 70)) bpstart = DSHS_LINE_BLEN - 70; } else bpstart = 0; bpos = bpstart; prior_ascii_len = bpstart + (DSHS_BPL * 3) + 1; n = 0; memset(buff, ' ', DSHS_LINE_BLEN); buff[DSHS_LINE_BLEN] = '\0'; if (bpstart > 0) memcpy(buff, leadin, bpstart); for (k = 0; k < len; k++) { c = *p++; if (bpos == (bpstart + ((DSHS_BPL / 2) * 3))) bpos++; /* for extra space in middle of each line's hex */ scnpr(buff + bpos, (int)sizeof(buff) - bpos, "%.2x", (int)(unsigned char)c); buff[bpos + 2] = ' '; if (want_ascii) a[k % DSHS_BPL] = my_isprint(c) ? c : '.'; if ((k > 0) && (0 == ((k + 1) % DSHS_BPL))) { trimTrailingSpaces(buff); if (want_ascii) { n += scnpr(b + n, b_len - n, "%-*s %s\n", prior_ascii_len, buff, a); memset(a, ' ', DSHS_BPL); } else n += scnpr(b + n, b_len - n, "%s\n", buff); if (n >= (b_len - 1)) return n; memset(buff, ' ', DSHS_LINE_BLEN); bpos = bpstart; if (bpstart > 0) memcpy(buff, leadin, bpstart); } else bpos += 3; } if (bpos > bpstart) { trimTrailingSpaces(buff); if (want_ascii) n += scnpr(b + n, b_len - n, "%-*s %s\n", prior_ascii_len, buff, a); else n += scnpr(b + n, b_len - n, "%s\n", buff); } return n; } void hex2stdout(const uint8_t * b_str, int len, int no_ascii) { dStrHex((const char *)b_str, len, no_ascii); } void hex2stderr(const uint8_t * b_str, int len, int no_ascii) { dStrHexErr((const char *)b_str, len, no_ascii); } int hex2str(const uint8_t * b_str, int len, const char * leadin, int format, int b_len, char * b) { return dStrHexStr((const char *)b_str, len, leadin, format, b_len, b); } /* Returns true when executed on big endian machine; else returns false. * Useful for displaying ATA identify words (which need swapping on a * big endian machine). */ bool sg_is_big_endian() { union u_t { uint16_t s; unsigned char c[sizeof(uint16_t)]; } u; u.s = 0x0102; return (u.c[0] == 0x01); /* The lowest address contains the most significant byte */ } bool sg_all_zeros(const uint8_t * bp, int b_len) { if ((NULL == bp) || (b_len <= 0)) return false; for (--b_len; b_len >= 0; --b_len) { if (0x0 != bp[b_len]) return false; } return true; } bool sg_all_ffs(const uint8_t * bp, int b_len) { if ((NULL == bp) || (b_len <= 0)) return false; for (--b_len; b_len >= 0; --b_len) { if (0xff != bp[b_len]) return false; } return true; } static uint16_t swapb_uint16(uint16_t u) { uint16_t r; r = (u >> 8) & 0xff; r |= ((u & 0xff) << 8); return r; } /* Note the ASCII-hex output goes to stdout. [Most other output from functions * in this file go to sg_warnings_strm (default stderr).] * 'no_ascii' allows for 3 output types: * > 0 each line has address then up to 8 ASCII-hex 16 bit words * = 0 in addition, the ASCI bytes pairs are listed to the right * = -1 only the ASCII-hex words are listed (i.e. without address) * = -2 only the ASCII-hex words, formatted for "hdparm --Istdin" * < -2 same as -1 * If 'swapb' is true then bytes in each word swapped. Needs to be set * for ATA IDENTIFY DEVICE response on big-endian machines. */ void dWordHex(const uint16_t* words, int num, int no_ascii, bool swapb) { const uint16_t * p = words; uint16_t c; char buff[82]; unsigned char upp, low; int a = 0; const int bpstart = 3; const int cpstart = 52; int cpos = cpstart; int bpos = bpstart; int i, k, blen; if (num <= 0) return; blen = (int)sizeof(buff); memset(buff, ' ', 80); buff[80] = '\0'; if (no_ascii < 0) { for (k = 0; k < num; k++) { c = *p++; if (swapb) c = swapb_uint16(c); bpos += 5; scnpr(buff + bpos, blen - bpos, "%.4x", (unsigned int)c); buff[bpos + 4] = ' '; if ((k > 0) && (0 == ((k + 1) % 8))) { if (-2 == no_ascii) printf("%.39s\n", buff +8); else printf("%.47s\n", buff); bpos = bpstart; memset(buff, ' ', 80); } } if (bpos > bpstart) { if (-2 == no_ascii) printf("%.39s\n", buff +8); else printf("%.47s\n", buff); } return; } /* no_ascii>=0, start each line with address (offset) */ k = scnpr(buff + 1, blen - 1, "%.2x", a); buff[k + 1] = ' '; for (i = 0; i < num; i++) { c = *p++; if (swapb) c = swapb_uint16(c); bpos += 5; scnpr(buff + bpos, blen - bpos, "%.4x", (unsigned int)c); buff[bpos + 4] = ' '; if (no_ascii) { buff[cpos++] = ' '; buff[cpos++] = ' '; buff[cpos++] = ' '; } else { upp = (c >> 8) & 0xff; low = c & 0xff; if (! my_isprint(upp)) upp = '.'; buff[cpos++] = upp; if (! my_isprint(low)) low = '.'; buff[cpos++] = low; buff[cpos++] = ' '; } if (cpos > (cpstart + 23)) { printf("%.76s\n", buff); bpos = bpstart; cpos = cpstart; a += 8; memset(buff, ' ', 80); k = scnpr(buff + 1, blen - 1, "%.2x", a); buff[k + 1] = ' '; } } if (cpos > cpstart) printf("%.76s\n", buff); } /* If the number in 'buf' can be decoded or the multiplier is unknown * then -1 is returned. Accepts a hex prefix (0x or 0X) or a decimal * multiplier suffix (as per GNU's dd (since 2002: SI and IEC 60027-2)). * Main (SI) multipliers supported: K, M, G. Ignore leading spaces and * tabs; accept comma, hyphen, space, tab and hash as terminator. */ int sg_get_num(const char * buf) { int res, num, n, len; unsigned int unum; char * cp; const char * b; char c = 'c'; char c2 = '\0'; /* keep static checker happy */ char c3 = '\0'; /* keep static checker happy */ char lb[16]; if ((NULL == buf) || ('\0' == buf[0])) return -1; len = strlen(buf); n = strspn(buf, " \t"); if (n > 0) { if (n == len) return -1; buf += n; len -= n; } /* following hack to keep C++ happy */ cp = strpbrk((char *)buf, " \t,#-"); if (cp) { len = cp - buf; n = (int)sizeof(lb) - 1; len = (len < n) ? len : n; memcpy(lb, buf, len); lb[len] = '\0'; b = lb; } else b = buf; if (('0' == b[0]) && (('x' == b[1]) || ('X' == b[1]))) { res = sscanf(b + 2, "%x", &unum); num = unum; } else if ('H' == toupper((int)b[len - 1])) { res = sscanf(b, "%x", &unum); num = unum; } else res = sscanf(b, "%d%c%c%c", &num, &c, &c2, &c3); if (res < 1) return -1LL; else if (1 == res) return num; else { if (res > 2) c2 = toupper((int)c2); if (res > 3) c3 = toupper((int)c3); switch (toupper((int)c)) { case 'C': return num; case 'W': return num * 2; case 'B': return num * 512; case 'K': if (2 == res) return num * 1024; if (('B' == c2) || ('D' == c2)) return num * 1000; if (('I' == c2) && (4 == res) && ('B' == c3)) return num * 1024; return -1; case 'M': if (2 == res) return num * 1048576; if (('B' == c2) || ('D' == c2)) return num * 1000000; if (('I' == c2) && (4 == res) && ('B' == c3)) return num * 1048576; return -1; case 'G': if (2 == res) return num * 1073741824; if (('B' == c2) || ('D' == c2)) return num * 1000000000; if (('I' == c2) && (4 == res) && ('B' == c3)) return num * 1073741824; return -1; case 'X': cp = (char *)strchr(b, 'x'); if (NULL == cp) cp = (char *)strchr(b, 'X'); if (cp) { n = sg_get_num(cp + 1); if (-1 != n) return num * n; } return -1; default: pr2ws("unrecognized multiplier\n"); return -1; } } } /* If the number in 'buf' can not be decoded then -1 is returned. Accepts a * hex prefix (0x or 0X) or a 'h' (or 'H') suffix; otherwise decimal is * assumed. Does not accept multipliers. Accept a comma (","), hyphen ("-"), * a whitespace or newline as terminator. */ int sg_get_num_nomult(const char * buf) { int res, len, num; unsigned int unum; char * commap; if ((NULL == buf) || ('\0' == buf[0])) return -1; len = strlen(buf); commap = (char *)strchr(buf + 1, ','); if (('0' == buf[0]) && (('x' == buf[1]) || ('X' == buf[1]))) { res = sscanf(buf + 2, "%x", &unum); num = unum; } else if (commap && ('H' == toupper((int)*(commap - 1)))) { res = sscanf(buf, "%x", &unum); num = unum; } else if ((NULL == commap) && ('H' == toupper((int)buf[len - 1]))) { res = sscanf(buf, "%x", &unum); num = unum; } else res = sscanf(buf, "%d", &num); if (1 == res) return num; else return -1; } /* If the number in 'buf' can be decoded or the multiplier is unknown * then -1LL is returned. Accepts a hex prefix (0x or 0X) or a decimal * multiplier suffix (as per GNU's dd (since 2002: SI and IEC 60027-2)). * Main (SI) multipliers supported: K, M, G, T, P. Ignore leading spaces * and tabs; accept comma, hyphen, space, tab and hash as terminator. */ int64_t sg_get_llnum(const char * buf) { int res, len, n; int64_t num, ll; uint64_t unum; char * cp; const char * b; char c = 'c'; char c2 = '\0'; /* keep static checker happy */ char c3 = '\0'; /* keep static checker happy */ char lb[32]; if ((NULL == buf) || ('\0' == buf[0])) return -1LL; len = strlen(buf); n = strspn(buf, " \t"); if (n > 0) { if (n == len) return -1LL; buf += n; len -= n; } /* following hack to keep C++ happy */ cp = strpbrk((char *)buf, " \t,#-"); if (cp) { len = cp - buf; n = (int)sizeof(lb) - 1; len = (len < n) ? len : n; memcpy(lb, buf, len); lb[len] = '\0'; b = lb; } else b = buf; if (('0' == b[0]) && (('x' == b[1]) || ('X' == b[1]))) { res = sscanf(b + 2, "%" SCNx64 , &unum); num = unum; } else if ('H' == toupper((int)b[len - 1])) { res = sscanf(b, "%" SCNx64 , &unum); num = unum; } else res = sscanf(b, "%" SCNd64 "%c%c%c", &num, &c, &c2, &c3); if (res < 1) return -1LL; else if (1 == res) return num; else { if (res > 2) c2 = toupper((int)c2); if (res > 3) c3 = toupper((int)c3); switch (toupper((int)c)) { case 'C': return num; case 'W': return num * 2; case 'B': return num * 512; case 'K': if (2 == res) return num * 1024; if (('B' == c2) || ('D' == c2)) return num * 1000; if (('I' == c2) && (4 == res) && ('B' == c3)) return num * 1024; return -1LL; case 'M': if (2 == res) return num * 1048576; if (('B' == c2) || ('D' == c2)) return num * 1000000; if (('I' == c2) && (4 == res) && ('B' == c3)) return num * 1048576; return -1LL; case 'G': if (2 == res) return num * 1073741824; if (('B' == c2) || ('D' == c2)) return num * 1000000000; if (('I' == c2) && (4 == res) && ('B' == c3)) return num * 1073741824; return -1LL; case 'T': if (2 == res) return num * 1099511627776LL; if (('B' == c2) || ('D' == c2)) return num * 1000000000000LL; if (('I' == c2) && (4 == res) && ('B' == c3)) return num * 1099511627776LL; return -1LL; case 'P': if (2 == res) return num * 1099511627776LL * 1024; if (('B' == c2) || ('D' == c2)) return num * 1000000000000LL * 1000; if (('I' == c2) && (4 == res) && ('B' == c3)) return num * 1099511627776LL * 1024; return -1LL; case 'X': cp = (char *)strchr(b, 'x'); if (NULL == cp) cp = (char *)strchr(b, 'X'); if (cp) { ll = sg_get_llnum(cp + 1); if (-1LL != ll) return num * ll; } return -1LL; default: pr2ws("unrecognized multiplier\n"); return -1LL; } } } /* If the number in 'buf' can not be decoded then -1 is returned. Accepts a * hex prefix (0x or 0X) or a 'h' (or 'H') suffix; otherwise decimal is * assumed. Does not accept multipliers. Accept a comma (","), hyphen ("-"), * a whitespace or newline as terminator. Only decimal numbers can represent * negative numbers and '-1' must be treated separately. */ int64_t sg_get_llnum_nomult(const char * buf) { int res, len; int64_t num; uint64_t unum; if ((NULL == buf) || ('\0' == buf[0])) return -1; len = strlen(buf); if (('0' == buf[0]) && (('x' == buf[1]) || ('X' == buf[1]))) { res = sscanf(buf + 2, "%" SCNx64 "", &unum); num = unum; } else if ('H' == toupper(buf[len - 1])) { res = sscanf(buf, "%" SCNx64 "", &unum); num = unum; } else res = sscanf(buf, "%" SCNd64 "", &num); return (1 == res) ? num : -1; } /* Extract character sequence from ATA words as in the model string * in a IDENTIFY DEVICE response. Returns number of characters * written to 'ochars' before 0 character is found or 'num' words * are processed. */ int sg_ata_get_chars(const uint16_t * word_arr, int start_word, int num_words, bool is_big_endian, char * ochars) { int k; uint16_t s; char a, b; char * op = ochars; for (k = start_word; k < (start_word + num_words); ++k) { s = word_arr[k]; if (is_big_endian) { a = s & 0xff; b = (s >> 8) & 0xff; } else { a = (s >> 8) & 0xff; b = s & 0xff; } if (a == 0) break; *op++ = a; if (b == 0) break; *op++ = b; } return op - ochars; } int pr2serr(const char * fmt, ...) { va_list args; int n; va_start(args, fmt); n = vfprintf(stderr, fmt, args); va_end(args); return n; } #ifdef SG_LIB_FREEBSD #include #elif defined(SG_LIB_WIN32) #include static bool got_page_size = false; static uint32_t win_page_size; #endif uint32_t sg_get_page_size(void) { #if defined(HAVE_SYSCONF) && defined(_SC_PAGESIZE) return sysconf(_SC_PAGESIZE); /* POSIX.1 (was getpagesize()) */ #elif defined(SG_LIB_WIN32) if (! got_page_size) { SYSTEM_INFO si; GetSystemInfo(&si); win_page_size = si.dwPageSize; got_page_size = true; } return win_page_size; #elif defined(SG_LIB_FREEBSD) return PAGE_SIZE; #else return 4096; /* give up, pick likely figure */ #endif } /* Returns pointer to heap (or NULL) that is aligned to a align_to byte * boundary. Sends back *buff_to_free pointer in third argument that may be * different from the return value. If it is different then the *buff_to_free * pointer should be freed (rather than the returned value) when the heap is * no longer needed. If align_to is 0 then aligns to OS's page size. Sets all * returned heap to zeros. If num_bytes is 0 then set to page size. */ uint8_t * sg_memalign(uint32_t num_bytes, uint32_t align_to, uint8_t ** buff_to_free, bool vb) { size_t psz; uint8_t * res; if (buff_to_free) /* make sure buff_to_free is NULL if alloc fails */ *buff_to_free = NULL; psz = (align_to > 0) ? align_to : sg_get_page_size(); if (0 == num_bytes) num_bytes = psz; /* ugly to handle otherwise */ #ifdef HAVE_POSIX_MEMALIGN { int err; void * wp = NULL; err = posix_memalign(&wp, psz, num_bytes); if (err || (NULL == wp)) { pr2ws("%s: posix_memalign: error [%d], out of memory?\n", __func__, err); return NULL; } memset(wp, 0, num_bytes); if (buff_to_free) *buff_to_free = (uint8_t *)wp; res = (uint8_t *)wp; if (vb) { pr2ws("%s: posix_ma, len=%d, ", __func__, num_bytes); if (buff_to_free) pr2ws("wrkBuffp=%p, ", (void *)res); pr2ws("psz=%u, rp=%p\n", (unsigned int)psz, (void *)res); } return res; } #else { void * wrkBuff; sg_uintptr_t align_1 = psz - 1; wrkBuff = (uint8_t *)calloc(num_bytes + psz, 1); if (NULL == wrkBuff) { if (buff_to_free) *buff_to_free = NULL; return NULL; } else if (buff_to_free) *buff_to_free = (uint8_t *)wrkBuff; res = (uint8_t *)(void *) (((sg_uintptr_t)wrkBuff + align_1) & (~align_1)); if (vb) { pr2ws("%s: hack, len=%d, ", __func__, num_bytes); if (buff_to_free) pr2ws("buff_to_free=%p, ", wrkBuff); pr2ws("align_1=%lu, rp=%p\n", (unsigned long)align_1, (void *)res); } return res; } #endif } const char * sg_lib_version() { return sg_lib_version_str; } #ifdef SG_LIB_MINGW /* Non Unix OSes distinguish between text and binary files. Set text mode on fd. Does nothing in Unix. Returns negative number on failure. */ #include #include int sg_set_text_mode(int fd) { return setmode(fd, O_TEXT); } /* Set binary mode on fd. Does nothing in Unix. Returns negative number on failure. */ int sg_set_binary_mode(int fd) { return setmode(fd, O_BINARY); } #else /* For Unix the following functions are dummies. */ int sg_set_text_mode(int fd) { return fd; /* fd should be >= 0 */ } int sg_set_binary_mode(int fd) { return fd; } #endif f2fs-tools-1.11.0/tools/sg_write_buffer/sg_lib_data.c000066400000000000000000002300211332120552100224700ustar00rootroot00000000000000/* * Copyright (c) 2007-2018 Douglas Gilbert. * All rights reserved. * Use of this source code is governed by a BSD-style * license that can be found in the BSD_LICENSE file. */ #include #ifdef HAVE_CONFIG_H #include "config.h" #else #define SG_SCSI_STRINGS 1 #endif #include "sg_lib.h" #include "sg_lib_data.h" const char * sg_lib_version_str = "2.38 20180122";/* spc5r17, sbc4r15 */ /* indexed by pdt; those that map to own index do not decay */ int sg_lib_pdt_decay_arr[32] = { PDT_DISK, PDT_TAPE, PDT_TAPE /* printer */, PDT_PROCESSOR, PDT_DISK /* WO */, PDT_MMC, PDT_SCANNER, PDT_DISK /* optical */, PDT_MCHANGER, PDT_COMMS, 0xa, 0xb, PDT_SAC, PDT_SES, PDT_DISK /* rbc */, PDT_OCRW, PDT_BCC, PDT_OSD, PDT_TAPE /* adc */, PDT_SMD, PDT_DISK /* zbc */, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, PDT_WLUN, PDT_UNKNOWN }; #ifdef SG_SCSI_STRINGS struct sg_lib_value_name_t sg_lib_normal_opcodes[] = { {0, 0, "Test Unit Ready"}, {0x1, 0, "Rezero Unit"}, {0x1, PDT_TAPE, "Rewind"}, {0x3, 0, "Request Sense"}, {0x4, 0, "Format Unit"}, {0x4, PDT_TAPE, "Format medium"}, {0x4, PDT_PRINTER, "Format"}, {0x5, 0, "Read Block Limits"}, {0x7, 0, "Reassign Blocks"}, {0x7, PDT_MCHANGER, "Initialize element status"}, {0x8, 0, "Read(6)"}, /* obsolete in sbc3r30 */ {0x8, PDT_PROCESSOR, "Receive"}, {0xa, 0, "Write(6)"}, /* obsolete in sbc3r30 */ {0xa, PDT_PRINTER, "Print"}, {0xa, PDT_PROCESSOR, "Send"}, {0xb, 0, "Seek(6)"}, {0xb, PDT_TAPE, "Set capacity"}, {0xb, PDT_PRINTER, "Slew and print"}, {0xf, 0, "Read reverse(6)"}, {0x10, 0, "Write filemarks(6)"}, {0x10, PDT_PRINTER, "Synchronize buffer"}, {0x11, 0, "Space(6)"}, {0x12, 0, "Inquiry"}, {0x13, 0, "Verify(6)"}, /* SSC */ {0x14, 0, "Recover buffered data"}, {0x15, 0, "Mode select(6)"}, /* SBC-3 r31 recommends Mode select(10) */ {0x16, 0, "Reserve(6)"}, /* obsolete in SPC-4 r11 */ {0x16, PDT_MCHANGER, "Reserve element(6)"}, {0x17, 0, "Release(6)"}, /* obsolete in SPC-4 r11 */ {0x17, PDT_MCHANGER, "Release element(6)"}, {0x18, 0, "Copy"}, /* obsolete in SPC-4 r11 */ {0x19, 0, "Erase(6)"}, {0x1a, 0, "Mode sense(6)"}, /* SBC-3 r31 recommends Mode sense(10) */ {0x1b, 0, "Start stop unit"}, {0x1b, PDT_TAPE, "Load unload"}, {0x1b, PDT_ADC, "Load unload"}, {0x1b, PDT_PRINTER, "Stop print"}, {0x1c, 0, "Receive diagnostic results"}, {0x1d, 0, "Send diagnostic"}, {0x1e, 0, "Prevent allow medium removal"}, {0x23, 0, "Read Format capacities"}, {0x24, 0, "Set window"}, {0x25, 0, "Read capacity(10)"}, /* SBC-3 r31 recommends Read capacity(16) */ {0x25, PDT_OCRW, "Read card capacity"}, {0x28, 0, "Read(10)"}, /* SBC-3 r31 recommends Read(16) */ {0x29, 0, "Read generation"}, {0x2a, 0, "Write(10)"}, /* SBC-3 r31 recommends Write(16) */ {0x2b, 0, "Seek(10)"}, {0x2b, PDT_TAPE, "Locate(10)"}, {0x2b, PDT_MCHANGER, "Position to element"}, {0x2c, 0, "Erase(10)"}, {0x2d, 0, "Read updated block"}, {0x2e, 0, "Write and verify(10)"}, /* SBC-3 r31 recommends Write and verify(16) */ {0x2f, 0, "Verify(10)"}, /* SBC-3 r31 recommends Verify(16) */ {0x30, 0, "Search data high(10)"}, {0x31, 0, "Search data equal(10)"}, {0x32, 0, "Search data low(10)"}, {0x33, 0, "Set limits(10)"}, {0x34, 0, "Pre-fetch(10)"}, /* SBC-3 r31 recommends Pre-fetch(16) */ {0x34, PDT_TAPE, "Read position"}, {0x35, 0, "Synchronize cache(10)"}, /* SBC-3 r31 recommends Synchronize cache(16) */ {0x36, 0, "Lock unlock cache(10)"}, {0x37, 0, "Read defect data(10)"}, /* SBC-3 r31 recommends Read defect data(12) */ {0x37, PDT_MCHANGER, "Initialize element status with range"}, {0x38, 0, "Medium scan"}, {0x39, 0, "Compare"}, /* obsolete in SPC-4 r11 */ {0x3a, 0, "Copy and verify"}, /* obsolete in SPC-4 r11 */ {0x3b, 0, "Write buffer"}, {0x3c, 0, "Read buffer(10)"}, {0x3d, 0, "Update block"}, {0x3e, 0, "Read long(10)"}, /* obsolete in SBC-4 r7 */ {0x3f, 0, "Write long(10)"}, /* SBC-3 r31 recommends Write long(16) */ {0x40, 0, "Change definition"}, /* obsolete in SPC-4 r11 */ {0x41, 0, "Write same(10)"}, /* SBC-3 r31 recommends Write same(16) */ {0x42, 0, "Unmap"}, /* added SPC-4 rev 18 */ {0x42, PDT_MMC, "Read sub-channel"}, {0x43, PDT_MMC, "Read TOC/PMA/ATIP"}, {0x44, 0, "Report density support"}, {0x45, PDT_MMC, "Play audio(10)"}, {0x46, PDT_MMC, "Get configuration"}, {0x47, PDT_MMC, "Play audio msf"}, {0x48, 0, "Sanitize"}, {0x4a, PDT_MMC, "Get event status notification"}, {0x4b, PDT_MMC, "Pause/resume"}, {0x4c, 0, "Log select"}, {0x4d, 0, "Log sense"}, {0x4e, 0, "Stop play/scan"}, {0x50, 0, "Xdwrite(10)"}, /* obsolete in SBC-3 r31 */ {0x51, 0, "Xpwrite(10)"}, /* obsolete in SBC-4 r15 */ {0x51, PDT_MMC, "Read disk information"}, {0x52, 0, "Xdread(10)"}, /* obsolete in SBC-3 r31 */ {0x52, PDT_MMC, "Read track information"}, {0x53, 0, "Xdwriteread(10)"}, /* obsolete in SBC-4 r15 */ {0x54, 0, "Send OPC information"}, {0x55, 0, "Mode select(10)"}, {0x56, 0, "Reserve(10)"}, /* obsolete in SPC-4 r11 */ {0x56, PDT_MCHANGER, "Reserve element(10)"}, {0x57, 0, "Release(10)"}, /* obsolete in SPC-4 r11 */ {0x57, PDT_MCHANGER, "Release element(10)"}, {0x58, 0, "Repair track"}, {0x5a, 0, "Mode sense(10)"}, {0x5b, 0, "Close track/session"}, {0x5c, 0, "Read buffer capacity"}, {0x5d, 0, "Send cue sheet"}, {0x5e, 0, "Persistent reserve in"}, {0x5f, 0, "Persistent reserve out"}, {0x7e, 0, "Extended cdb (XCBD)"}, /* added in SPC-4 r12 */ {0x80, 0, "Xdwrite extended(16)"}, /* obsolete in SBC-4 r15 */ {0x80, PDT_TAPE, "Write filemarks(16)"}, {0x81, 0, "Rebuild(16)"}, {0x81, PDT_TAPE, "Read reverse(16)"}, {0x82, 0, "Regenerate(16)"}, {0x83, 0, "Third party copy out"}, /* Extended copy, before spc4r34 */ /* Following was "Receive copy results", before spc4r34 */ {0x84, 0, "Third party copy in"}, {0x85, 0, "ATA pass-through(16)"}, /* was 0x98 in spc3 rev21c */ {0x86, 0, "Access control in"}, {0x87, 0, "Access control out"}, {0x88, 0, "Read(16)"}, {0x89, 0, "Compare and write"}, {0x8a, 0, "Write(16)"}, {0x8b, 0, "Orwrite(16)"}, {0x8c, 0, "Read attribute"}, {0x8d, 0, "Write attribute"}, {0x8e, 0, "Write and verify(16)"}, {0x8f, 0, "Verify(16)"}, {0x90, 0, "Pre-fetch(16)"}, {0x91, 0, "Synchronize cache(16)"}, {0x91, PDT_TAPE, "Space(16)"}, {0x92, 0, "Lock unlock cache(16)"}, {0x92, PDT_TAPE, "Locate(16)"}, {0x93, 0, "Write same(16)"}, {0x93, PDT_TAPE, "Erase(16)"}, {0x94, PDT_ZBC, "ZBC out"}, /* new sbc4r04, has service actions */ {0x95, PDT_ZBC, "ZBC in"}, /* new sbc4r04, has service actions */ {0x9a, 0, "Write stream(16)"}, /* added sbc4r07 */ {0x9b, 0, "Read buffer(16)"}, /* added spc5r02 */ {0x9c, 0, "Write atomic(16)"}, {0x9d, 0, "Service action bidirectional"}, /* added spc4r35 */ {0x9e, 0, "Service action in(16)"}, {0x9f, 0, "Service action out(16)"}, {0xa0, 0, "Report luns"}, {0xa1, 0, "ATA pass-through(12)"}, {0xa1, PDT_MMC, "Blank"}, {0xa2, 0, "Security protocol in"}, {0xa3, 0, "Maintenance in"}, {0xa3, PDT_MMC, "Send key"}, {0xa4, 0, "Maintenance out"}, {0xa4, PDT_MMC, "Report key"}, {0xa5, 0, "Move medium"}, {0xa5, PDT_MMC, "Play audio(12)"}, {0xa6, 0, "Exchange medium"}, {0xa6, PDT_MMC, "Load/unload medium"}, {0xa7, 0, "Move medium attached"}, {0xa7, PDT_MMC, "Set read ahead"}, {0xa8, 0, "Read(12)"}, /* SBC-3 r31 recommends Read(16) */ {0xa9, 0, "Service action out(12)"}, {0xaa, 0, "Write(12)"}, /* SBC-3 r31 recommends Write(16) */ {0xab, 0, "Service action in(12)"}, {0xac, 0, "erase(12)"}, {0xac, PDT_MMC, "Get performance"}, {0xad, PDT_MMC, "Read DVD/BD structure"}, {0xae, 0, "Write and verify(12)"}, /* SBC-3 r31 recommends Write and verify(16) */ {0xaf, 0, "Verify(12)"}, /* SBC-3 r31 recommends Verify(16) */ {0xb0, 0, "Search data high(12)"}, {0xb1, 0, "Search data equal(12)"}, {0xb1, PDT_MCHANGER, "Open/close import/export element"}, {0xb2, 0, "Search data low(12)"}, {0xb3, 0, "Set limits(12)"}, {0xb4, 0, "Read element status attached"}, {0xb5, 0, "Security protocol out"}, {0xb5, PDT_MCHANGER, "Request volume element address"}, {0xb6, 0, "Send volume tag"}, {0xb6, PDT_MMC, "Set streaming"}, {0xb7, 0, "Read defect data(12)"}, {0xb8, 0, "Read element status"}, {0xb9, 0, "Read CD msf"}, {0xba, 0, "Redundancy group in"}, {0xba, PDT_MMC, "Scan"}, {0xbb, 0, "Redundancy group out"}, {0xbb, PDT_MMC, "Set CD speed"}, {0xbc, 0, "Spare in"}, {0xbd, 0, "Spare out"}, {0xbd, PDT_MMC, "Mechanism status"}, {0xbe, 0, "Volume set in"}, {0xbe, PDT_MMC, "Read CD"}, {0xbf, 0, "Volume set out"}, {0xbf, PDT_MMC, "Send DVD/BD structure"}, {0xffff, 0, NULL}, }; /* Read buffer(10) [0x3c] and Read buffer(16) [0x9b] service actions (sa), * need prefix */ struct sg_lib_value_name_t sg_lib_read_buff_arr[] = { {0x0, 0, "combined header and data [or multiple modes]"}, {0x2, 0, "data"}, {0x3, 0, "descriptor"}, {0xa, 0, "read data from echo buffer"}, {0xb, 0, "echo buffer descriptor"}, {0x1a, 0, "enable expander comms protocol and echo buffer"}, {0x1c, 0, "error history"}, {0xffff, 0, NULL}, }; /* Write buffer [0x3b] service actions, need prefix */ struct sg_lib_value_name_t sg_lib_write_buff_arr[] = { {0x0, 0, "combined header and data [or multiple modes]"}, {0x2, 0, "data"}, {0x4, 0, "download microcode and activate"}, {0x5, 0, "download microcode, save, and activate"}, {0x6, 0, "download microcode with offsets and activate"}, {0x7, 0, "download microcode with offsets, save, and activate"}, {0xa, 0, "write data to echo buffer"}, {0xd, 0, "download microcode with offsets, select activation events, " "save and defer activate"}, {0xe, 0, "download microcode with offsets, save and defer activate"}, {0xf, 0, "activate deferred microcode"}, {0x1a, 0, "enable expander comms protocol and echo buffer"}, {0x1b, 0, "disable expander comms protocol"}, {0x1c, 0, "download application client error history"}, {0xffff, 0, NULL}, }; /* Read position (SSC) [0x34] service actions, need prefix */ struct sg_lib_value_name_t sg_lib_read_pos_arr[] = { {0x0, PDT_TAPE, "short form - block id"}, {0x1, PDT_TAPE, "short form - vendor specific"}, {0x6, PDT_TAPE, "long form"}, {0x8, PDT_TAPE, "extended form"}, {0xffff, 0, NULL}, }; /* Maintenance in [0xa3] service actions */ struct sg_lib_value_name_t sg_lib_maint_in_arr[] = { {0x0, PDT_SAC, "Report assigned/unassigned p_extent"}, {0x1, PDT_SAC, "Report component device"}, {0x2, PDT_SAC, "Report component device attachments"}, {0x3, PDT_SAC, "Report peripheral device"}, {0x4, PDT_SAC, "Report peripheral device associations"}, {0x5, 0, "Report identifying information"}, /* was "Report device identifier" prior to spc4r07 */ {0x6, PDT_SAC, "Report states"}, {0x7, PDT_SAC, "Report device identification"}, {0x8, PDT_SAC, "Report unconfigured capacity"}, {0x9, PDT_SAC, "Report supported configuration method"}, {0xa, 0, "Report target port groups"}, {0xb, 0, "Report aliases"}, {0xc, 0, "Report supported operation codes"}, {0xd, 0, "Report supported task management functions"}, {0xe, 0, "Report priority"}, {0xf, 0, "Report timestamp"}, {0x10, 0, "Management protocol in"}, {0x1d, PDT_DISK, "Report provisioning initialization pattern"}, /* added in sbc4r07, shares sa 0x1d with ssc5r01 (tape) */ {0x1d, PDT_TAPE, "Receive recommended access order"}, {0x1e, PDT_TAPE, "Read dynamic runtime attribute"}, {0x1e, PDT_ADC, "Report automation device attributes"}, {0x1f, 0, "Maintenance in vendor specific"}, {0xffff, 0, NULL}, }; /* Maintenance out [0xa4] service actions */ struct sg_lib_value_name_t sg_lib_maint_out_arr[] = { {0x0, PDT_SAC, "Add peripheral device / component device"}, {0x1, PDT_SAC, "Attach to component device"}, {0x2, PDT_SAC, "Exchange p_extent"}, {0x3, PDT_SAC, "Exchange peripheral device / component device"}, {0x4, PDT_SAC, "Instruct component device"}, {0x5, PDT_SAC, "Remove peripheral device / component device"}, {0x6, 0, "Set identifying information"}, /* was "Set device identifier" prior to spc4r07 */ {0x7, PDT_SAC, "Break peripheral device / component device"}, {0xa, 0, "Set target port groups"}, {0xb, 0, "Change aliases"}, {0xc, 0, "Remove I_T nexus"}, {0xe, 0, "Set priority"}, {0xf, 0, "Set timestamp"}, {0x10, 0, "Management protocol out"}, {0x1d, PDT_TAPE, "Generate recommended access order"}, {0x1e, PDT_TAPE, "write dynamic runtime attribute"}, {0x1e, PDT_ADC, "Set automation device attributes"}, {0x1f, 0, "Maintenance out vendor specific"}, {0xffff, 0, NULL}, }; /* Sanitize [0x48] service actions, need prefix */ struct sg_lib_value_name_t sg_lib_sanitize_sa_arr[] = { {0x1, 0, "overwrite"}, {0x2, 0, "block erase"}, {0x3, 0, "cryptographic erase"}, {0x1f, 0, "exit failure mode"}, {0xffff, 0, NULL}, }; /* Service action in(12) [0xab] service actions */ struct sg_lib_value_name_t sg_lib_serv_in12_arr[] = { {0x1, 0, "Read media serial number"}, {0xffff, 0, NULL}, }; /* Service action out(12) [0xa9] service actions */ struct sg_lib_value_name_t sg_lib_serv_out12_arr[] = { {0xff, 0, "Impossible command name"}, {0xffff, 0, NULL}, }; /* Service action in(16) [0x9e] service actions */ struct sg_lib_value_name_t sg_lib_serv_in16_arr[] = { {0xf, 0, "Receive binding report"}, /* added spc5r11 */ {0x10, 0, "Read capacity(16)"}, {0x11, 0, "Read long(16)"}, /* obsolete in SBC-4 r7 */ {0x12, 0, "Get LBA status(16)"}, /* 32 byte variant added in sbc4r14 */ {0x13, 0, "Report referrals"}, {0x14, 0, "Stream control"}, {0x15, 0, "Background control"}, {0x16, 0, "Get stream status"}, {0x17, 0, "Get physical element status"}, /* added sbc4r13 */ {0x18, 0, "Remove element and truncate"}, /* added sbc4r13 */ {0xffff, 0, NULL}, }; /* Service action out(16) [0x9f] service actions */ struct sg_lib_value_name_t sg_lib_serv_out16_arr[] = { {0x0b, 0, "Test bind"}, /* added spc5r13 */ {0x0c, 0, "Prepare bind report"}, /* added spc5r11 */ {0x0d, 0, "Set affiliation"}, {0x0e, 0, "Bind"}, {0x0f, 0, "Unbind"}, {0x11, 0, "Write long(16)"}, {0x12, 0, "Write scattered(16)"}, /* added sbc4r11 */ {0x14, PDT_ZBC, "Reset write pointer"}, {0x1f, PDT_ADC, "Notify data transfer device(16)"}, {0xffff, 0, NULL}, }; /* Service action bidirectional [0x9d] service actions */ struct sg_lib_value_name_t sg_lib_serv_bidi_arr[] = { {0xffff, 0, NULL}, }; /* Persistent reserve in [0x5e] service actions, need prefix */ struct sg_lib_value_name_t sg_lib_pr_in_arr[] = { {0x0, 0, "read keys"}, {0x1, 0, "read reservation"}, {0x2, 0, "report capabilities"}, {0x3, 0, "read full status"}, {0xffff, 0, NULL}, }; /* Persistent reserve out [0x5f] service actions, need prefix */ struct sg_lib_value_name_t sg_lib_pr_out_arr[] = { {0x0, 0, "register"}, {0x1, 0, "reserve"}, {0x2, 0, "release"}, {0x3, 0, "clear"}, {0x4, 0, "preempt"}, {0x5, 0, "preempt and abort"}, {0x6, 0, "register and ignore existing key"}, {0x7, 0, "register and move"}, {0x8, 0, "replace lost reservation"}, {0xffff, 0, NULL}, }; /* Third party copy in [0x83] service actions * Opcode 'Receive copy results' was renamed 'Third party copy in' in spc4r34 * LID1 is an abbreviation of List Identifier length of 1 byte */ struct sg_lib_value_name_t sg_lib_xcopy_sa_arr[] = { {0x0, 0, "Extended copy(LID1)"}, {0x1, 0, "Extended copy(LID4)"}, {0x10, 0, "Populate token"}, {0x11, 0, "Write using token"}, {0x1c, 0, "Copy operation abort"}, {0xffff, 0, NULL}, }; /* Third party copy out [0x84] service actions * Opcode 'Extended copy' was renamed 'Third party copy out' in spc4r34 * LID4 is an abbreviation of List Identifier length of 4 bytes */ struct sg_lib_value_name_t sg_lib_rec_copy_sa_arr[] = { {0x0, 0, "Receive copy status(LID1)"}, {0x1, 0, "Receive copy data(LID1)"}, {0x3, 0, "Receive copy operating parameters"}, {0x4, 0, "Receive copy failure details(LID1)"}, {0x5, 0, "Receive copy status(LID4)"}, {0x6, 0, "Receive copy data(LID4)"}, {0x7, 0, "Receive ROD token information"}, {0x8, 0, "Report all ROD tokens"}, {0xffff, 0, NULL}, }; /* Variable length cdb [0x7f] service actions (more than 16 bytes long) */ struct sg_lib_value_name_t sg_lib_variable_length_arr[] = { {0x1, 0, "Rebuild(32)"}, {0x2, 0, "Regenerate(32)"}, {0x3, 0, "Xdread(32)"}, /* obsolete in SBC-3 r31 */ {0x4, 0, "Xdwrite(32)"}, /* obsolete in SBC-3 r31 */ {0x5, 0, "Xdwrite extended(32)"}, /* obsolete in SBC-4 r15 */ {0x6, 0, "Xpwrite(32)"}, /* obsolete in SBC-4 r15 */ {0x7, 0, "Xdwriteread(32)"}, /* obsolete in SBC-4 r15 */ {0x8, 0, "Xdwrite extended(64)"}, /* obsolete in SBC-4 r15 */ {0x9, 0, "Read(32)"}, {0xa, 0, "Verify(32)"}, {0xb, 0, "Write(32)"}, {0xc, 0, "Write and verify(32)"}, {0xd, 0, "Write same(32)"}, {0xe, 0, "Orwrite(32)"}, /* added sbc3r25 */ {0xf, 0, "Atomic write(32)"}, /* added sbc4r02 */ {0x10, 0, "Write stream(32)"}, /* added sbc4r07 */ {0x11, 0, "Write scattered(32)"}, /* added sbc4r11 */ {0x12, 0, "Get LBA status(32)"}, /* added sbc4r14 */ {0x1800, 0, "Receive credential"}, {0x1ff0, 0, "ATA pass-through(32)"},/* added sat4r05 */ {0x8801, 0, "Format OSD (osd)"}, {0x8802, 0, "Create (osd)"}, {0x8803, 0, "List (osd)"}, {0x8805, 0, "Read (osd)"}, {0x8806, 0, "Write (osd)"}, {0x8807, 0, "Append (osd)"}, {0x8808, 0, "Flush (osd)"}, {0x880a, 0, "Remove (osd)"}, {0x880b, 0, "Create partition (osd)"}, {0x880c, 0, "Remove partition (osd)"}, {0x880e, 0, "Get attributes (osd)"}, {0x880f, 0, "Set attributes (osd)"}, {0x8812, 0, "Create and write (osd)"}, {0x8815, 0, "Create collection (osd)"}, {0x8816, 0, "Remove collection (osd)"}, {0x8817, 0, "List collection (osd)"}, {0x8818, 0, "Set key (osd)"}, {0x8819, 0, "Set master key (osd)"}, {0x881a, 0, "Flush collection (osd)"}, {0x881b, 0, "Flush partition (osd)"}, {0x881c, 0, "Flush OSD (osd)"}, {0x8880, 0, "Object structure check (osd-2)"}, {0x8881, 0, "Format OSD (osd-2)"}, {0x8882, 0, "Create (osd-2)"}, {0x8883, 0, "List (osd-2)"}, {0x8884, 0, "Punch (osd-2)"}, {0x8885, 0, "Read (osd-2)"}, {0x8886, 0, "Write (osd-2)"}, {0x8887, 0, "Append (osd-2)"}, {0x8888, 0, "Flush (osd-2)"}, {0x8889, 0, "Clear (osd-2)"}, {0x888a, 0, "Remove (osd-2)"}, {0x888b, 0, "Create partition (osd-2)"}, {0x888c, 0, "Remove partition (osd-2)"}, {0x888e, 0, "Get attributes (osd-2)"}, {0x888f, 0, "Set attributes (osd-2)"}, {0x8892, 0, "Create and write (osd-2)"}, {0x8895, 0, "Create collection (osd-2)"}, {0x8896, 0, "Remove collection (osd-2)"}, {0x8897, 0, "List collection (osd-2)"}, {0x8898, 0, "Set key (osd-2)"}, {0x8899, 0, "Set master key (osd-2)"}, {0x889a, 0, "Flush collection (osd-2)"}, {0x889b, 0, "Flush partition (osd-2)"}, {0x889c, 0, "Flush OSD (osd-2)"}, {0x88a0, 0, "Query (osd-2)"}, {0x88a1, 0, "Remove member objects (osd-2)"}, {0x88a2, 0, "Get member attributes (osd-2)"}, {0x88a3, 0, "Set member attributes (osd-2)"}, {0x88b1, 0, "Read map (osd-2)"}, {0x8f7c, 0, "Perform SCSI command (osd-2)"}, {0x8f7d, 0, "Perform task management function (osd-2)"}, {0x8f7e, 0, "Perform SCSI command (osd)"}, {0x8f7f, 0, "Perform task management function (osd)"}, {0xffff, 0, NULL}, }; /* Zoning out [0x94] service actions */ struct sg_lib_value_name_t sg_lib_zoning_out_arr[] = { {0x1, PDT_ZBC, "Close zone"}, {0x2, PDT_ZBC, "Finish zone"}, {0x3, PDT_ZBC, "Open zone"}, {0x4, PDT_ZBC, "Reset write pointer"}, {0xffff, 0, NULL}, }; /* Zoning in [0x95] service actions */ struct sg_lib_value_name_t sg_lib_zoning_in_arr[] = { {0x0, PDT_ZBC, "Report zones"}, {0xffff, 0, NULL}, }; /* Read attribute [0x8c] service actions */ struct sg_lib_value_name_t sg_lib_read_attr_arr[] = { {0x0, 0, "attribute values"}, {0x1, 0, "attribute list"}, {0x2, 0, "logical volume list"}, {0x3, 0, "partition list"}, {0x5, 0, "supported attributes"}, {0xffff, 0, NULL}, }; #else /* SG_SCSI_STRINGS */ struct sg_lib_value_name_t sg_lib_normal_opcodes[] = { {0xffff, 0, NULL}, }; struct sg_lib_value_name_t sg_lib_read_buff_arr[] = { /* opcode 0x3c */ {0xffff, 0, NULL}, }; struct sg_lib_value_name_t sg_lib_write_buff_arr[] = { /* opcode 0x3b */ {0xffff, 0, NULL}, }; struct sg_lib_value_name_t sg_lib_read_pos_arr[] = { /* opcode 0x34 */ {0xffff, 0, NULL}, }; struct sg_lib_value_name_t sg_lib_maint_in_arr[] = { /* opcode 0xa3 */ {0xffff, 0, NULL}, }; struct sg_lib_value_name_t sg_lib_maint_out_arr[] = { /* opcode 0xa4 */ {0xffff, 0, NULL}, }; struct sg_lib_value_name_t sg_lib_sanitize_sa_arr[] = { /* opcode 0x94 */ {0xffff, 0, NULL}, }; struct sg_lib_value_name_t sg_lib_serv_in12_arr[] = { /* opcode 0xab */ {0xffff, 0, NULL}, }; struct sg_lib_value_name_t sg_lib_serv_out12_arr[] = { /* opcode 0xa9 */ {0xffff, 0, NULL}, }; struct sg_lib_value_name_t sg_lib_serv_in16_arr[] = { /* opcode 0x9e */ {0xffff, 0, NULL}, }; struct sg_lib_value_name_t sg_lib_serv_out16_arr[] = { /* opcode 0x9f */ {0xffff, 0, NULL}, }; struct sg_lib_value_name_t sg_lib_serv_bidi_arr[] = { /* opcode 0x9d */ {0xffff, 0, NULL}, }; struct sg_lib_value_name_t sg_lib_pr_in_arr[] = { /* opcode 0x5e */ {0xffff, 0, NULL}, }; struct sg_lib_value_name_t sg_lib_pr_out_arr[] = { /* opcode 0x5f */ {0xffff, 0, NULL}, }; struct sg_lib_value_name_t sg_lib_xcopy_sa_arr[] = { /* opcode 0x83 */ {0xffff, 0, NULL}, }; struct sg_lib_value_name_t sg_lib_rec_copy_sa_arr[] = { /* opcode 0x84 */ {0xffff, 0, NULL}, }; struct sg_lib_value_name_t sg_lib_variable_length_arr[] = { {0xffff, 0, NULL}, }; struct sg_lib_value_name_t sg_lib_zoning_out_arr[] = { {0xffff, 0, NULL}, }; struct sg_lib_value_name_t sg_lib_zoning_in_arr[] = { {0xffff, 0, NULL}, }; struct sg_lib_value_name_t sg_lib_read_attr_arr[] = { {0xffff, 0, NULL}, }; #endif /* SG_SCSI_STRINGS */ /* A conveniently formatted list of SCSI ASC/ASCQ codes and their * corresponding text can be found at: www.t10.org/lists/asc-num.txt * The following should match asc-num.txt dated 20150423 */ #ifdef SG_SCSI_STRINGS struct sg_lib_asc_ascq_range_t sg_lib_asc_ascq_range[] = { {0x40,0x01,0x7f,"Ram failure [0x%x]"}, {0x40,0x80,0xff,"Diagnostic failure on component [0x%x]"}, {0x41,0x01,0xff,"Data path failure [0x%x]"}, {0x42,0x01,0xff,"Power-on or self-test failure [0x%x]"}, {0x4d,0x00,0xff,"Tagged overlapped commands [0x%x]"}, {0x70,0x00,0xff,"Decompression exception short algorithm id of 0x%x"}, {0, 0, 0, NULL} }; struct sg_lib_asc_ascq_t sg_lib_asc_ascq[] = { {0x00,0x00,"No additional sense information"}, {0x00,0x01,"Filemark detected"}, {0x00,0x02,"End-of-partition/medium detected"}, {0x00,0x03,"Setmark detected"}, {0x00,0x04,"Beginning-of-partition/medium detected"}, {0x00,0x05,"End-of-data detected"}, {0x00,0x06,"I/O process terminated"}, {0x00,0x07,"Programmable early warning detected"}, {0x00,0x11,"Audio play operation in progress"}, {0x00,0x12,"Audio play operation paused"}, {0x00,0x13,"Audio play operation successfully completed"}, {0x00,0x14,"Audio play operation stopped due to error"}, {0x00,0x15,"No current audio status to return"}, {0x00,0x16,"operation in progress"}, {0x00,0x17,"Cleaning requested"}, {0x00,0x18,"Erase operation in progress"}, {0x00,0x19,"Locate operation in progress"}, {0x00,0x1a,"Rewind operation in progress"}, {0x00,0x1b,"Set capacity operation in progress"}, {0x00,0x1c,"Verify operation in progress"}, {0x00,0x1d,"ATA pass through information available"}, {0x00,0x1e,"Conflicting SA creation request"}, {0x00,0x1f,"Logical unit transitioning to another power condition"}, {0x00,0x20,"Extended copy information available"}, {0x00,0x21,"Atomic command aborted due to ACA"}, {0x00,0x22,"Deferred microcode is pending"}, {0x01,0x00,"No index/sector signal"}, {0x02,0x00,"No seek complete"}, {0x03,0x00,"Peripheral device write fault"}, {0x03,0x01,"No write current"}, {0x03,0x02,"Excessive write errors"}, {0x04,0x00,"Logical unit not ready, cause not reportable"}, {0x04,0x01,"Logical unit is in process of becoming ready"}, {0x04,0x02,"Logical unit not ready, " "initializing command required"}, {0x04,0x03,"Logical unit not ready, " "manual intervention required"}, {0x04,0x04,"Logical unit not ready, format in progress"}, {0x04,0x05,"Logical unit not ready, rebuild in progress"}, {0x04,0x06,"Logical unit not ready, recalculation in progress"}, {0x04,0x07,"Logical unit not ready, operation in progress"}, {0x04,0x08,"Logical unit not ready, long write in progress"}, {0x04,0x09,"Logical unit not ready, self-test in progress"}, {0x04,0x0a,"Logical unit " "not accessible, asymmetric access state transition"}, {0x04,0x0b,"Logical unit " "not accessible, target port in standby state"}, {0x04,0x0c,"Logical unit " "not accessible, target port in unavailable state"}, {0x04,0x0d,"Logical unit not ready, structure check required"}, {0x04,0x0e,"Logical unit not ready, security session in progress"}, {0x04,0x10,"Logical unit not ready, " "auxiliary memory not accessible"}, {0x04,0x11,"Logical unit not ready, " "notify (enable spinup) required"}, {0x04,0x12,"Logical unit not ready, offline"}, {0x04,0x13,"Logical unit not ready, SA creation in progress"}, {0x04,0x14,"Logical unit not ready, space allocation in progress"}, {0x04,0x15,"Logical unit not ready, robotics disabled"}, {0x04,0x16,"Logical unit not ready, configuration required"}, {0x04,0x17,"Logical unit not ready, calibration required"}, {0x04,0x18,"Logical unit not ready, a door is open"}, {0x04,0x19,"Logical unit not ready, operating in sequential mode"}, {0x04,0x1a,"Logical unit not ready, start stop unit command in progress"}, {0x04,0x1b,"Logical unit not ready, sanitize in progress"}, {0x04,0x1c,"Logical unit not ready, additional power use not yet " "granted"}, {0x04,0x1d,"Logical unit not ready, configuration in progress"}, {0x04,0x1e,"Logical unit not ready, microcode activation required"}, {0x04,0x1f,"Logical unit not ready, microcode download required"}, {0x04,0x20,"Logical unit not ready, logical unit reset required"}, {0x04,0x21,"Logical unit not ready, hard reset required"}, {0x04,0x22,"Logical unit not ready, power cycle required"}, {0x04,0x23,"Logical unit not ready, affiliation required"}, {0x05,0x00,"Logical unit does not respond to selection"}, {0x06,0x00,"No reference position found"}, {0x07,0x00,"Multiple peripheral devices selected"}, {0x08,0x00,"Logical unit communication failure"}, {0x08,0x01,"Logical unit communication time-out"}, {0x08,0x02,"Logical unit communication parity error"}, {0x08,0x03,"Logical unit communication CRC error (Ultra-DMA/32)"}, {0x08,0x04,"Unreachable copy target"}, {0x09,0x00,"Track following error"}, {0x09,0x01,"Tracking servo failure"}, {0x09,0x02,"Focus servo failure"}, {0x09,0x03,"Spindle servo failure"}, {0x09,0x04,"Head select fault"}, {0x09,0x05,"Vibration induced tracking error"}, {0x0A,0x00,"Error log overflow"}, {0x0B,0x00,"Warning"}, {0x0B,0x01,"Warning - specified temperature exceeded"}, {0x0B,0x02,"Warning - enclosure degraded"}, {0x0B,0x03,"Warning - background self-test failed"}, {0x0B,0x04,"Warning - background pre-scan detected medium error"}, {0x0B,0x05,"Warning - background medium scan detected medium error"}, {0x0B,0x06,"Warning - non-volatile cache now volatile"}, {0x0B,0x07,"Warning - degraded power to non-volatile cache"}, {0x0B,0x08,"Warning - power loss expected"}, {0x0B,0x09,"Warning - device statistics notification active"}, {0x0B,0x0A,"Warning - high critical temperature limit exceeded"}, {0x0B,0x0B,"Warning - low critical temperature limit exceeded"}, {0x0B,0x0C,"Warning - high operating temperature limit exceeded"}, {0x0B,0x0D,"Warning - low operating temperature limit exceeded"}, {0x0B,0x0E,"Warning - high critical humidity limit exceeded"}, {0x0B,0x0F,"Warning - low critical humidity limit exceeded"}, {0x0B,0x10,"Warning - high operating humidity limit exceeded"}, {0x0B,0x11,"Warning - low operating humidity limit exceeded"}, {0x0B,0x12,"Warning - microcode security at risk"}, {0x0B,0x13,"Warning - microcode digital signature validation failure"}, {0x0C,0x00,"Write error"}, {0x0C,0x01,"Write error - recovered with auto reallocation"}, {0x0C,0x02,"Write error - auto reallocation failed"}, {0x0C,0x03,"Write error - recommend reassignment"}, {0x0C,0x04,"Compression check miscompare error"}, {0x0C,0x05,"Data expansion occurred during compression"}, {0x0C,0x06,"Block not compressible"}, {0x0C,0x07,"Write error - recovery needed"}, {0x0C,0x08,"Write error - recovery failed"}, {0x0C,0x09,"Write error - loss of streaming"}, {0x0C,0x0A,"Write error - padding blocks added"}, {0x0C,0x0B,"Auxiliary memory write error"}, {0x0C,0x0C,"Write error - unexpected unsolicited data"}, {0x0C,0x0D,"Write error - not enough unsolicited data"}, {0x0C,0x0E,"Multiple write errors"}, {0x0C,0x0F,"Defects in error window"}, {0x0C,0x10,"Incomplete multiple atomic write operations"}, {0x0C,0x11,"Write error - recovery scan needed"}, {0x0C,0x12,"Write error - insufficient zone resources"}, {0x0D,0x00,"Error detected by third party temporary initiator"}, {0x0D,0x01,"Third party device failure"}, {0x0D,0x02,"Copy target device not reachable"}, {0x0D,0x03,"Incorrect copy target device type"}, {0x0D,0x04,"Copy target device data underrun"}, {0x0D,0x05,"Copy target device data overrun"}, {0x0E,0x00,"Invalid information unit"}, {0x0E,0x01,"Information unit too short"}, {0x0E,0x02,"Information unit too long"}, {0x0E,0x03,"Invalid field in command information unit"}, {0x10,0x00,"Id CRC or ECC error"}, {0x10,0x01,"Logical block guard check failed"}, {0x10,0x02,"Logical block application tag check failed"}, {0x10,0x03,"Logical block reference tag check failed"}, {0x10,0x04,"Logical block protection error on recover buffered data"}, {0x10,0x05,"Logical block protection method error"}, {0x11,0x00,"Unrecovered read error"}, {0x11,0x01,"Read retries exhausted"}, {0x11,0x02,"Error too long to correct"}, {0x11,0x03,"Multiple read errors"}, {0x11,0x04,"Unrecovered read error - auto reallocate failed"}, {0x11,0x05,"L-EC uncorrectable error"}, {0x11,0x06,"CIRC unrecovered error"}, {0x11,0x07,"Data re-synchronization error"}, {0x11,0x08,"Incomplete block read"}, {0x11,0x09,"No gap found"}, {0x11,0x0A,"Miscorrected error"}, {0x11,0x0B,"Unrecovered read error - recommend reassignment"}, {0x11,0x0C,"Unrecovered read error - recommend rewrite the data"}, {0x11,0x0D,"De-compression CRC error"}, {0x11,0x0E,"Cannot decompress using declared algorithm"}, {0x11,0x0F,"Error reading UPC/EAN number"}, {0x11,0x10,"Error reading ISRC number"}, {0x11,0x11,"Read error - loss of streaming"}, {0x11,0x12,"Auxiliary memory read error"}, {0x11,0x13,"Read error - failed retransmission request"}, {0x11,0x14,"Read error - LBA marked bad by application client"}, {0x11,0x15,"Write after sanitize required"}, {0x12,0x00,"Address mark not found for id field"}, {0x13,0x00,"Address mark not found for data field"}, {0x14,0x00,"Recorded entity not found"}, {0x14,0x01,"Record not found"}, {0x14,0x02,"Filemark or setmark not found"}, {0x14,0x03,"End-of-data not found"}, {0x14,0x04,"Block sequence error"}, {0x14,0x05,"Record not found - recommend reassignment"}, {0x14,0x06,"Record not found - data auto-reallocated"}, {0x14,0x07,"Locate operation failure"}, {0x15,0x00,"Random positioning error"}, {0x15,0x01,"Mechanical positioning error"}, {0x15,0x02,"Positioning error detected by read of medium"}, {0x16,0x00,"Data synchronization mark error"}, {0x16,0x01,"Data sync error - data rewritten"}, {0x16,0x02,"Data sync error - recommend rewrite"}, {0x16,0x03,"Data sync error - data auto-reallocated"}, {0x16,0x04,"Data sync error - recommend reassignment"}, {0x17,0x00,"Recovered data with no error correction applied"}, {0x17,0x01,"Recovered data with retries"}, {0x17,0x02,"Recovered data with positive head offset"}, {0x17,0x03,"Recovered data with negative head offset"}, {0x17,0x04,"Recovered data with retries and/or circ applied"}, {0x17,0x05,"Recovered data using previous sector id"}, {0x17,0x06,"Recovered data without ECC - data auto-reallocated"}, {0x17,0x07,"Recovered data without ECC - recommend reassignment"}, {0x17,0x08,"Recovered data without ECC - recommend rewrite"}, {0x17,0x09,"Recovered data without ECC - data rewritten"}, {0x18,0x00,"Recovered data with error correction applied"}, {0x18,0x01,"Recovered data with error corr. & retries applied"}, {0x18,0x02,"Recovered data - data auto-reallocated"}, {0x18,0x03,"Recovered data with CIRC"}, {0x18,0x04,"Recovered data with L-EC"}, {0x18,0x05,"Recovered data - recommend reassignment"}, {0x18,0x06,"Recovered data - recommend rewrite"}, {0x18,0x07,"Recovered data with ECC - data rewritten"}, {0x18,0x08,"Recovered data with linking"}, {0x19,0x00,"Defect list error"}, {0x19,0x01,"Defect list not available"}, {0x19,0x02,"Defect list error in primary list"}, {0x19,0x03,"Defect list error in grown list"}, {0x1A,0x00,"Parameter list length error"}, {0x1B,0x00,"Synchronous data transfer error"}, {0x1C,0x00,"Defect list not found"}, {0x1C,0x01,"Primary defect list not found"}, {0x1C,0x02,"Grown defect list not found"}, {0x1D,0x00,"Miscompare during verify operation"}, {0x1D,0x01,"Miscompare verify of unmapped lba"}, {0x1E,0x00,"Recovered id with ECC correction"}, {0x1F,0x00,"Partial defect list transfer"}, {0x20,0x00,"Invalid command operation code"}, {0x20,0x01,"Access denied - initiator pending-enrolled"}, {0x20,0x02,"Access denied - no access rights"}, {0x20,0x03,"Access denied - invalid mgmt id key"}, {0x20,0x04,"Illegal command while in write capable state"}, {0x20,0x05,"Write type operation while in read capable state (obs)"}, {0x20,0x06,"Illegal command while in explicit address mode"}, {0x20,0x07,"Illegal command while in implicit address mode"}, {0x20,0x08,"Access denied - enrollment conflict"}, {0x20,0x09,"Access denied - invalid LU identifier"}, {0x20,0x0A,"Access denied - invalid proxy token"}, {0x20,0x0B,"Access denied - ACL LUN conflict"}, {0x20,0x0C,"Illegal command when not in append-only mode"}, {0x20,0x0D,"Not an administrative logical unit"}, {0x20,0x0E,"Not a subsidiary logical unit"}, {0x20,0x0F,"Not a conglomerate logical unit"}, {0x21,0x00,"Logical block address out of range"}, {0x21,0x01,"Invalid element address"}, {0x21,0x02,"Invalid address for write"}, {0x21,0x03,"Invalid write crossing layer jump"}, {0x21,0x04,"Unaligned write command"}, {0x21,0x05,"Write boundary violation"}, {0x21,0x06,"Attempt to read invalid data"}, {0x21,0x07,"Read boundary violation"}, {0x21,0x08,"Misaligned write command"}, {0x22,0x00,"Illegal function (use 20 00, 24 00, or 26 00)"}, {0x23,0x00,"Invalid token operation, cause not reportable"}, {0x23,0x01,"Invalid token operation, unsupported token type"}, {0x23,0x02,"Invalid token operation, remote token usage not supported"}, {0x23,0x03,"invalid token operation, remote rod token creation not " "supported"}, {0x23,0x04,"Invalid token operation, token unknown"}, {0x23,0x05,"Invalid token operation, token corrupt"}, {0x23,0x06,"Invalid token operation, token revoked"}, {0x23,0x07,"Invalid token operation, token expired"}, {0x23,0x08,"Invalid token operation, token cancelled"}, {0x23,0x09,"Invalid token operation, token deleted"}, {0x23,0x0a,"Invalid token operation, invalid token length"}, {0x24,0x00,"Invalid field in cdb"}, {0x24,0x01,"CDB decryption error"}, {0x24,0x02,"Invalid cdb field while in explicit block model (obs)"}, {0x24,0x03,"Invalid cdb field while in implicit block model (obs)"}, {0x24,0x04,"Security audit value frozen"}, {0x24,0x05,"Security working key frozen"}, {0x24,0x06,"Nonce not unique"}, {0x24,0x07,"Nonce timestamp out of range"}, {0x24,0x08,"Invalid xcdb"}, {0x24,0x09,"Invalid fast format"}, {0x25,0x00,"Logical unit not supported"}, {0x26,0x00,"Invalid field in parameter list"}, {0x26,0x01,"Parameter not supported"}, {0x26,0x02,"Parameter value invalid"}, {0x26,0x03,"Threshold parameters not supported"}, {0x26,0x04,"Invalid release of persistent reservation"}, {0x26,0x05,"Data decryption error"}, {0x26,0x06,"Too many target descriptors"}, {0x26,0x07,"Unsupported target descriptor type code"}, {0x26,0x08,"Too many segment descriptors"}, {0x26,0x09,"Unsupported segment descriptor type code"}, {0x26,0x0A,"Unexpected inexact segment"}, {0x26,0x0B,"Inline data length exceeded"}, {0x26,0x0C,"Invalid operation for copy source or destination"}, {0x26,0x0D,"Copy segment granularity violation"}, {0x26,0x0E,"Invalid parameter while port is enabled"}, {0x26,0x0F,"Invalid data-out buffer integrity check value"}, {0x26,0x10,"Data decryption key fail limit reached"}, {0x26,0x11,"Incomplete key-associated data set"}, {0x26,0x12,"Vendor specific key reference not found"}, {0x26,0x13,"Application tag mode page is invalid"}, {0x26,0x14,"Tape stream mirroring prevented"}, {0x26,0x15,"Copy source or copy destination not authorized"}, {0x27,0x00,"Write protected"}, {0x27,0x01,"Hardware write protected"}, {0x27,0x02,"Logical unit software write protected"}, {0x27,0x03,"Associated write protect"}, {0x27,0x04,"Persistent write protect"}, {0x27,0x05,"Permanent write protect"}, {0x27,0x06,"Conditional write protect"}, {0x27,0x07,"Space allocation failed write protect"}, {0x27,0x08,"Zone is read only"}, {0x28,0x00,"Not ready to ready change, medium may have changed"}, {0x28,0x01,"Import or export element accessed"}, {0x28,0x02,"Format-layer may have changed"}, {0x28,0x03,"Import/export element accessed, medium changed"}, {0x29,0x00,"Power on, reset, or bus device reset occurred"}, {0x29,0x01,"Power on occurred"}, {0x29,0x02,"SCSI bus reset occurred"}, {0x29,0x03,"Bus device reset function occurred"}, {0x29,0x04,"Device internal reset"}, {0x29,0x05,"Transceiver mode changed to single-ended"}, {0x29,0x06,"Transceiver mode changed to lvd"}, {0x29,0x07,"I_T nexus loss occurred"}, {0x2A,0x00,"Parameters changed"}, {0x2A,0x01,"Mode parameters changed"}, {0x2A,0x02,"Log parameters changed"}, {0x2A,0x03,"Reservations preempted"}, {0x2A,0x04,"Reservations released"}, {0x2A,0x05,"Registrations preempted"}, {0x2A,0x06,"Asymmetric access state changed"}, {0x2A,0x07,"Implicit asymmetric access state transition failed"}, {0x2A,0x08,"Priority changed"}, {0x2A,0x09,"Capacity data has changed"}, {0x2A,0x0c, "Error recovery attributes have changed"}, {0x2A,0x0d, "Data encryption capabilities changed"}, {0x2A,0x10,"Timestamp changed"}, {0x2A,0x11,"Data encryption parameters changed by another i_t nexus"}, {0x2A,0x12,"Data encryption parameters changed by vendor specific event"}, {0x2A,0x13,"Data encryption key instance counter has changed"}, {0x2A,0x0a,"Error history i_t nexus cleared"}, {0x2A,0x0b,"Error history snapshot released"}, {0x2A,0x14,"SA creation capabilities data has changed"}, {0x2A,0x15,"Medium removal prevention preempted"}, {0x2A,0x16,"Zone reset write pointer recommended"}, {0x2B,0x00,"Copy cannot execute since host cannot disconnect"}, {0x2C,0x00,"Command sequence error"}, {0x2C,0x01,"Too many windows specified"}, {0x2C,0x02,"Invalid combination of windows specified"}, {0x2C,0x03,"Current program area is not empty"}, {0x2C,0x04,"Current program area is empty"}, {0x2C,0x05,"Illegal power condition request"}, {0x2C,0x06,"Persistent prevent conflict"}, {0x2C,0x07,"Previous busy status"}, {0x2C,0x08,"Previous task set full status"}, {0x2C,0x09,"Previous reservation conflict status"}, {0x2C,0x0A,"Partition or collection contains user objects"}, {0x2C,0x0B,"Not reserved"}, {0x2C,0x0C,"ORWRITE generation does not match"}, {0x2C,0x0D,"Reset write pointer not allowed"}, {0x2C,0x0E,"Zone is offline"}, {0x2C,0x0F,"Stream not open"}, {0x2C,0x10,"Unwritten data in zone"}, {0x2C,0x11,"Descriptor format sense data required"}, {0x2D,0x00,"Overwrite error on update in place"}, {0x2E,0x00,"Insufficient time for operation"}, {0x2E,0x01,"Command timeout before processing"}, {0x2E,0x02,"Command timeout during processing"}, {0x2E,0x03,"Command timeout during processing due to error recovery"}, {0x2F,0x00,"Commands cleared by another initiator"}, {0x2F,0x01,"Commands cleared by power loss notification"}, {0x2F,0x02,"Commands cleared by device server"}, {0x2F,0x03,"Some commands cleared by queuing layer event"}, {0x30,0x00,"Incompatible medium installed"}, {0x30,0x01,"Cannot read medium - unknown format"}, {0x30,0x02,"Cannot read medium - incompatible format"}, {0x30,0x03,"Cleaning cartridge installed"}, {0x30,0x04,"Cannot write medium - unknown format"}, {0x30,0x05,"Cannot write medium - incompatible format"}, {0x30,0x06,"Cannot format medium - incompatible medium"}, {0x30,0x07,"Cleaning failure"}, {0x30,0x08,"Cannot write - application code mismatch"}, {0x30,0x09,"Current session not fixated for append"}, {0x30,0x0A,"Cleaning request rejected"}, {0x30,0x0B,"Cleaning tape expired"}, {0x30,0x0C,"WORM medium - overwrite attempted"}, {0x30,0x0D,"WORM medium - integrity check"}, {0x30,0x10,"Medium not formatted"}, {0x30,0x11,"Incompatible volume type"}, {0x30,0x12,"Incompatible volume qualifier"}, {0x30,0x13,"Cleaning volume expired"}, {0x31,0x00,"Medium format corrupted"}, {0x31,0x01,"Format command failed"}, {0x31,0x02,"Zoned formatting failed due to spare linking"}, {0x31,0x03,"Sanitize command failed"}, {0x32,0x00,"No defect spare location available"}, {0x32,0x01,"Defect list update failure"}, {0x33,0x00,"Tape length error"}, {0x34,0x00,"Enclosure failure"}, {0x35,0x00,"Enclosure services failure"}, {0x35,0x01,"Unsupported enclosure function"}, {0x35,0x02,"Enclosure services unavailable"}, {0x35,0x03,"Enclosure services transfer failure"}, {0x35,0x04,"Enclosure services transfer refused"}, {0x35,0x05,"Enclosure services checksum error"}, {0x36,0x00,"Ribbon, ink, or toner failure"}, {0x37,0x00,"Rounded parameter"}, {0x38,0x00,"Event status notification"}, {0x38,0x02,"Esn - power management class event"}, {0x38,0x04,"Esn - media class event"}, {0x38,0x06,"Esn - device busy class event"}, {0x38,0x07,"Thin provisioning soft threshold reached"}, {0x39,0x00,"Saving parameters not supported"}, {0x3A,0x00,"Medium not present"}, {0x3A,0x01,"Medium not present - tray closed"}, {0x3A,0x02,"Medium not present - tray open"}, {0x3A,0x03,"Medium not present - loadable"}, {0x3A,0x04,"Medium not present - medium auxiliary memory accessible"}, {0x3B,0x00,"Sequential positioning error"}, {0x3B,0x01,"Tape position error at beginning-of-medium"}, {0x3B,0x02,"Tape position error at end-of-medium"}, {0x3B,0x03,"Tape or electronic vertical forms unit not ready"}, {0x3B,0x04,"Slew failure"}, {0x3B,0x05,"Paper jam"}, {0x3B,0x06,"Failed to sense top-of-form"}, {0x3B,0x07,"Failed to sense bottom-of-form"}, {0x3B,0x08,"Reposition error"}, {0x3B,0x09,"Read past end of medium"}, {0x3B,0x0A,"Read past beginning of medium"}, {0x3B,0x0B,"Position past end of medium"}, {0x3B,0x0C,"Position past beginning of medium"}, {0x3B,0x0D,"Medium destination element full"}, {0x3B,0x0E,"Medium source element empty"}, {0x3B,0x0F,"End of medium reached"}, {0x3B,0x11,"Medium magazine not accessible"}, {0x3B,0x12,"Medium magazine removed"}, {0x3B,0x13,"Medium magazine inserted"}, {0x3B,0x14,"Medium magazine locked"}, {0x3B,0x15,"Medium magazine unlocked"}, {0x3B,0x16,"Mechanical positioning or changer error"}, {0x3B,0x17,"Read past end of user object"}, {0x3B,0x18,"Element disabled"}, {0x3B,0x19,"Element enabled"}, {0x3B,0x1a,"Data transfer device removed"}, {0x3B,0x1b,"Data transfer device inserted"}, {0x3B,0x1c,"Too many logical objects on partition to support operation"}, {0x3D,0x00,"Invalid bits in identify message"}, {0x3E,0x00,"Logical unit has not self-configured yet"}, {0x3E,0x01,"Logical unit failure"}, {0x3E,0x02,"Timeout on logical unit"}, {0x3E,0x03,"Logical unit failed self-test"}, {0x3E,0x04,"Logical unit unable to update self-test log"}, {0x3F,0x00,"Target operating conditions have changed"}, {0x3F,0x01,"Microcode has been changed"}, {0x3F,0x02,"Changed operating definition"}, {0x3F,0x03,"Inquiry data has changed"}, {0x3F,0x04,"Component device attached"}, {0x3F,0x05,"Device identifier changed"}, {0x3F,0x06,"Redundancy group created or modified"}, {0x3F,0x07,"Redundancy group deleted"}, {0x3F,0x08,"Spare created or modified"}, {0x3F,0x09,"Spare deleted"}, {0x3F,0x0A,"Volume set created or modified"}, {0x3F,0x0B,"Volume set deleted"}, {0x3F,0x0C,"Volume set deassigned"}, {0x3F,0x0D,"Volume set reassigned"}, {0x3F,0x0E,"Reported luns data has changed"}, {0x3F,0x0F,"Echo buffer overwritten"}, {0x3F,0x10,"Medium loadable"}, {0x3F,0x11,"Medium auxiliary memory accessible"}, {0x3F,0x12,"iSCSI IP address added"}, {0x3F,0x13,"iSCSI IP address removed"}, {0x3F,0x14,"iSCSI IP address changed"}, {0x3F,0x15,"Inspect referrals sense descriptors"}, {0x3F,0x16,"Microcode has been changed without reset"}, {0x3F,0x17,"Zone transition to full"}, {0x3F,0x18,"Bind completed"}, {0x3F,0x19,"Bind redirected"}, {0x3F,0x1A,"Subsidiary binding changed"}, /* * ASC 0x40, 0x41 and 0x42 overridden by "additional2" array entries * for ascq > 1. Preferred error message for this group is * "Diagnostic failure on component nn (80h-ffh)". */ {0x40,0x00,"Ram failure (should use 40 nn)"}, {0x41,0x00,"Data path failure (should use 40 nn)"}, {0x42,0x00,"Power-on or self-test failure (should use 40 nn)"}, {0x43,0x00,"Message error"}, {0x44,0x00,"Internal target failure"}, {0x44,0x01,"Persistent reservation information lost"}, {0x44,0x71,"ATA device failed Set Features"}, {0x45,0x00,"Select or reselect failure"}, {0x46,0x00,"Unsuccessful soft reset"}, {0x47,0x00,"SCSI parity error"}, {0x47,0x01,"Data phase CRC error detected"}, {0x47,0x02,"SCSI parity error detected during st data phase"}, {0x47,0x03,"Information unit iuCRC error detected"}, {0x47,0x04,"Asynchronous information protection error detected"}, {0x47,0x05,"Protocol service CRC error"}, {0x47,0x06,"Phy test function in progress"}, {0x47,0x7F,"Some commands cleared by iSCSI protocol event"}, {0x48,0x00,"Initiator detected error message received"}, {0x49,0x00,"Invalid message error"}, {0x4A,0x00,"Command phase error"}, {0x4B,0x00,"Data phase error"}, {0x4B,0x01,"Invalid target port transfer tag received"}, {0x4B,0x02,"Too much write data"}, {0x4B,0x03,"Ack/nak timeout"}, {0x4B,0x04,"Nak received"}, {0x4B,0x05,"Data offset error"}, {0x4B,0x06,"Initiator response timeout"}, {0x4B,0x07,"Connection lost"}, {0x4B,0x08,"Data-in buffer overflow - data buffer size"}, {0x4B,0x09,"Data-in buffer overflow - data buffer descriptor area"}, {0x4B,0x0A,"Data-in buffer error"}, {0x4B,0x0B,"Data-out buffer overflow - data buffer size"}, {0x4B,0x0C,"Data-out buffer overflow - data buffer descriptor area"}, {0x4B,0x0D,"Data-out buffer error"}, {0x4B,0x0E,"PCIe fabric error"}, {0x4B,0x0f,"PCIe completion timeout"}, {0x4B,0x10,"PCIe completer abort"}, {0x4B,0x11,"PCIe poisoned tlp received"}, {0x4B,0x12,"PCIe ecrc check failed"}, {0x4B,0x13,"PCIe unsupported request"}, {0x4B,0x14,"PCIe acs violation"}, {0x4B,0x15,"PCIe tlp prefix blocked"}, {0x4C,0x00,"Logical unit failed self-configuration"}, /* * ASC 0x4D overridden by an "additional2" array entry * so there is no need to have them here. */ /* {0x4D,0x00,"Tagged overlapped commands (nn = queue tag)"}, */ {0x4E,0x00,"Overlapped commands attempted"}, {0x50,0x00,"Write append error"}, {0x50,0x01,"Write append position error"}, {0x50,0x02,"Position error related to timing"}, {0x51,0x00,"Erase failure"}, {0x51,0x01,"Erase failure - incomplete erase operation detected"}, {0x52,0x00,"Cartridge fault"}, {0x53,0x00,"Media load or eject failed"}, {0x53,0x01,"Unload tape failure"}, {0x53,0x02,"Medium removal prevented"}, {0x53,0x03,"Medium removal prevented by data transfer element"}, {0x53,0x04,"Medium thread or unthread failure"}, {0x53,0x05,"Volume identifier invalid"}, {0x53,0x06,"Volume identifier missing"}, {0x53,0x07,"Duplicate volume identifier"}, {0x53,0x08,"Element status unknown"}, {0x53,0x09,"Data transfer device error - load failed"}, {0x53,0x0A,"Data transfer device error - unload failed"}, {0x53,0x0B,"Data transfer device error - unload missing"}, {0x53,0x0C,"Data transfer device error - eject failed"}, {0x53,0x0D,"Data transfer device error - library communication failed"}, {0x54,0x00,"SCSI to host system interface failure"}, {0x55,0x00,"System resource failure"}, {0x55,0x01,"System buffer full"}, {0x55,0x02,"Insufficient reservation resources"}, {0x55,0x03,"Insufficient resources"}, {0x55,0x04,"Insufficient registration resources"}, {0x55,0x05,"Insufficient access control resources"}, {0x55,0x06,"Auxiliary memory out of space"}, {0x55,0x07,"Quota error"}, {0x55,0x08,"Maximum number of supplemental decryption keys exceeded"}, {0x55,0x09,"Medium auxiliary memory not accessible"}, {0x55,0x0a,"Data currently unavailable"}, {0x55,0x0b,"Insufficient power for operation"}, {0x55,0x0c,"Insufficient resources to create rod"}, {0x55,0x0d,"Insufficient resources to create rod token"}, {0x55,0x0e,"Insufficient zone resources"}, {0x55,0x0f,"Insufficient zone resources to complete write"}, {0x55,0x10,"Maximum number of streams open"}, {0x55,0x11,"Insufficient resources to bind"}, {0x57,0x00,"Unable to recover table-of-contents"}, {0x58,0x00,"Generation does not exist"}, {0x59,0x00,"Updated block read"}, {0x5A,0x00,"Operator request or state change input"}, {0x5A,0x01,"Operator medium removal request"}, {0x5A,0x02,"Operator selected write protect"}, {0x5A,0x03,"Operator selected write permit"}, {0x5B,0x00,"Log exception"}, {0x5B,0x01,"Threshold condition met"}, {0x5B,0x02,"Log counter at maximum"}, {0x5B,0x03,"Log list codes exhausted"}, {0x5C,0x00,"Rpl status change"}, {0x5C,0x01,"Spindles synchronized"}, {0x5C,0x02,"Spindles not synchronized"}, {0x5D,0x00,"Failure prediction threshold exceeded"}, {0x5D,0x01,"Media failure prediction threshold exceeded"}, {0x5D,0x02,"Logical unit failure prediction threshold exceeded"}, {0x5D,0x03,"spare area exhaustion prediction threshold exceeded"}, {0x5D,0x10,"Hardware impending failure general hard drive failure"}, {0x5D,0x11,"Hardware impending failure drive error rate too high" }, {0x5D,0x12,"Hardware impending failure data error rate too high" }, {0x5D,0x13,"Hardware impending failure seek error rate too high" }, {0x5D,0x14,"Hardware impending failure too many block reassigns"}, {0x5D,0x15,"Hardware impending failure access times too high" }, {0x5D,0x16,"Hardware impending failure start unit times too high" }, {0x5D,0x17,"Hardware impending failure channel parametrics"}, {0x5D,0x18,"Hardware impending failure controller detected"}, {0x5D,0x19,"Hardware impending failure throughput performance"}, {0x5D,0x1A,"Hardware impending failure seek time performance"}, {0x5D,0x1B,"Hardware impending failure spin-up retry count"}, {0x5D,0x1C,"Hardware impending failure drive calibration retry count"}, {0x5D,0x1D,"Hardware impending failure power loss protection circuit"}, {0x5D,0x20,"Controller impending failure general hard drive failure"}, {0x5D,0x21,"Controller impending failure drive error rate too high" }, {0x5D,0x22,"Controller impending failure data error rate too high" }, {0x5D,0x23,"Controller impending failure seek error rate too high" }, {0x5D,0x24,"Controller impending failure too many block reassigns"}, {0x5D,0x25,"Controller impending failure access times too high" }, {0x5D,0x26,"Controller impending failure start unit times too high" }, {0x5D,0x27,"Controller impending failure channel parametrics"}, {0x5D,0x28,"Controller impending failure controller detected"}, {0x5D,0x29,"Controller impending failure throughput performance"}, {0x5D,0x2A,"Controller impending failure seek time performance"}, {0x5D,0x2B,"Controller impending failure spin-up retry count"}, {0x5D,0x2C,"Controller impending failure drive calibration retry count"}, {0x5D,0x30,"Data channel impending failure general hard drive failure"}, {0x5D,0x31,"Data channel impending failure drive error rate too high" }, {0x5D,0x32,"Data channel impending failure data error rate too high" }, {0x5D,0x33,"Data channel impending failure seek error rate too high" }, {0x5D,0x34,"Data channel impending failure too many block reassigns"}, {0x5D,0x35,"Data channel impending failure access times too high" }, {0x5D,0x36,"Data channel impending failure start unit times too high" }, {0x5D,0x37,"Data channel impending failure channel parametrics"}, {0x5D,0x38,"Data channel impending failure controller detected"}, {0x5D,0x39,"Data channel impending failure throughput performance"}, {0x5D,0x3A,"Data channel impending failure seek time performance"}, {0x5D,0x3B,"Data channel impending failure spin-up retry count"}, {0x5D,0x3C,"Data channel impending failure drive calibration retry count"}, {0x5D,0x40,"Servo impending failure general hard drive failure"}, {0x5D,0x41,"Servo impending failure drive error rate too high" }, {0x5D,0x42,"Servo impending failure data error rate too high" }, {0x5D,0x43,"Servo impending failure seek error rate too high" }, {0x5D,0x44,"Servo impending failure too many block reassigns"}, {0x5D,0x45,"Servo impending failure access times too high" }, {0x5D,0x46,"Servo impending failure start unit times too high" }, {0x5D,0x47,"Servo impending failure channel parametrics"}, {0x5D,0x48,"Servo impending failure controller detected"}, {0x5D,0x49,"Servo impending failure throughput performance"}, {0x5D,0x4A,"Servo impending failure seek time performance"}, {0x5D,0x4B,"Servo impending failure spin-up retry count"}, {0x5D,0x4C,"Servo impending failure drive calibration retry count"}, {0x5D,0x50,"Spindle impending failure general hard drive failure"}, {0x5D,0x51,"Spindle impending failure drive error rate too high" }, {0x5D,0x52,"Spindle impending failure data error rate too high" }, {0x5D,0x53,"Spindle impending failure seek error rate too high" }, {0x5D,0x54,"Spindle impending failure too many block reassigns"}, {0x5D,0x55,"Spindle impending failure access times too high" }, {0x5D,0x56,"Spindle impending failure start unit times too high" }, {0x5D,0x57,"Spindle impending failure channel parametrics"}, {0x5D,0x58,"Spindle impending failure controller detected"}, {0x5D,0x59,"Spindle impending failure throughput performance"}, {0x5D,0x5A,"Spindle impending failure seek time performance"}, {0x5D,0x5B,"Spindle impending failure spin-up retry count"}, {0x5D,0x5C,"Spindle impending failure drive calibration retry count"}, {0x5D,0x60,"Firmware impending failure general hard drive failure"}, {0x5D,0x61,"Firmware impending failure drive error rate too high" }, {0x5D,0x62,"Firmware impending failure data error rate too high" }, {0x5D,0x63,"Firmware impending failure seek error rate too high" }, {0x5D,0x64,"Firmware impending failure too many block reassigns"}, {0x5D,0x65,"Firmware impending failure access times too high" }, {0x5D,0x66,"Firmware impending failure start unit times too high" }, {0x5D,0x67,"Firmware impending failure channel parametrics"}, {0x5D,0x68,"Firmware impending failure controller detected"}, {0x5D,0x69,"Firmware impending failure throughput performance"}, {0x5D,0x6A,"Firmware impending failure seek time performance"}, {0x5D,0x6B,"Firmware impending failure spin-up retry count"}, {0x5D,0x6C,"Firmware impending failure drive calibration retry count"}, {0x5D,0x73,"Media impending failure endurance limit met"}, {0x5D,0xFF,"Failure prediction threshold exceeded (false)"}, {0x5E,0x00,"Low power condition on"}, {0x5E,0x01,"Idle condition activated by timer"}, {0x5E,0x02,"Standby condition activated by timer"}, {0x5E,0x03,"Idle condition activated by command"}, {0x5E,0x04,"Standby condition activated by command"}, {0x5E,0x05,"Idle_b condition activated by timer"}, {0x5E,0x06,"Idle_b condition activated by command"}, {0x5E,0x07,"Idle_c condition activated by timer"}, {0x5E,0x08,"Idle_c condition activated by command"}, {0x5E,0x09,"Standby_y condition activated by timer"}, {0x5E,0x0a,"Standby_y condition activated by command"}, {0x5E,0x41,"Power state change to active"}, {0x5E,0x42,"Power state change to idle"}, {0x5E,0x43,"Power state change to standby"}, {0x5E,0x45,"Power state change to sleep"}, {0x5E,0x47,"Power state change to device control"}, {0x60,0x00,"Lamp failure"}, {0x61,0x00,"Video acquisition error"}, {0x61,0x01,"Unable to acquire video"}, {0x61,0x02,"Out of focus"}, {0x62,0x00,"Scan head positioning error"}, {0x63,0x00,"End of user area encountered on this track"}, {0x63,0x01,"Packet does not fit in available space"}, {0x64,0x00,"Illegal mode for this track"}, {0x64,0x01,"Invalid packet size"}, {0x65,0x00,"Voltage fault"}, {0x66,0x00,"Automatic document feeder cover up"}, {0x66,0x01,"Automatic document feeder lift up"}, {0x66,0x02,"Document jam in automatic document feeder"}, {0x66,0x03,"Document miss feed automatic in document feeder"}, {0x67,0x00,"Configuration failure"}, {0x67,0x01,"Configuration of incapable logical units failed"}, {0x67,0x02,"Add logical unit failed"}, {0x67,0x03,"Modification of logical unit failed"}, {0x67,0x04,"Exchange of logical unit failed"}, {0x67,0x05,"Remove of logical unit failed"}, {0x67,0x06,"Attachment of logical unit failed"}, {0x67,0x07,"Creation of logical unit failed"}, {0x67,0x08,"Assign failure occurred"}, {0x67,0x09,"Multiply assigned logical unit"}, {0x67,0x0A,"Set target port groups command failed"}, {0x67,0x0B,"ATA device feature not enabled"}, {0x67,0x0C,"Command rejected"}, {0x67,0x0D,"Explicit bind not allowed"}, {0x68,0x00,"Logical unit not configured"}, {0x68,0x01,"Subsidiary logical unit not configured"}, {0x69,0x00,"Data loss on logical unit"}, {0x69,0x01,"Multiple logical unit failures"}, {0x69,0x02,"Parity/data mismatch"}, {0x6A,0x00,"Informational, refer to log"}, {0x6B,0x00,"State change has occurred"}, {0x6B,0x01,"Redundancy level got better"}, {0x6B,0x02,"Redundancy level got worse"}, {0x6C,0x00,"Rebuild failure occurred"}, {0x6D,0x00,"Recalculate failure occurred"}, {0x6E,0x00,"Command to logical unit failed"}, {0x6F,0x00,"Copy protection key exchange failure - authentication " "failure"}, {0x6F,0x01,"Copy protection key exchange failure - key not present"}, {0x6F,0x02,"Copy protection key exchange failure - key not established"}, {0x6F,0x03,"Read of scrambled sector without authentication"}, {0x6F,0x04,"Media region code is mismatched to logical unit region"}, {0x6F,0x05,"Drive region must be permanent/region reset count error"}, {0x6F,0x06,"Insufficient block count for binding nonce recording"}, {0x6F,0x07,"Conflict in binding nonce recording"}, {0x6F,0x08,"Insufficient permission"}, {0x6F,0x09,"Invalid drive-host pairing server"}, {0x6F,0x0A,"Drive-host pairing suspended"}, /* * ASC 0x70 overridden by an "additional2" array entry * so there is no need to have them here. */ /* {0x70,0x00,"Decompression exception short algorithm id of nn"}, */ {0x71,0x00,"Decompression exception long algorithm id"}, {0x72,0x00,"Session fixation error"}, {0x72,0x01,"Session fixation error writing lead-in"}, {0x72,0x02,"Session fixation error writing lead-out"}, {0x72,0x03,"Session fixation error - incomplete track in session"}, {0x72,0x04,"Empty or partially written reserved track"}, {0x72,0x05,"No more track reservations allowed"}, {0x72,0x06,"RMZ extension is not allowed"}, {0x72,0x07,"No more test zone extensions are allowed"}, {0x73,0x00,"CD control error"}, {0x73,0x01,"Power calibration area almost full"}, {0x73,0x02,"Power calibration area is full"}, {0x73,0x03,"Power calibration area error"}, {0x73,0x04,"Program memory area update failure"}, {0x73,0x05,"Program memory area is full"}, {0x73,0x06,"RMA/PMA is almost full"}, {0x73,0x10,"Current power calibration area almost full"}, {0x73,0x11,"Current power calibration area is full"}, {0x73,0x17,"RDZ is full"}, {0x74,0x00,"Security error"}, {0x74,0x01,"Unable to decrypt data"}, {0x74,0x02,"Unencrypted data encountered while decrypting"}, {0x74,0x03,"Incorrect data encryption key"}, {0x74,0x04,"Cryptographic integrity validation failed"}, {0x74,0x05,"Error decrypting data"}, {0x74,0x06,"Unknown signature verification key"}, {0x74,0x07,"Encryption parameters not useable"}, {0x74,0x08,"Digital signature validation failure"}, {0x74,0x09,"Encryption mode mismatch on read"}, {0x74,0x0a,"Encrypted block not raw read enabled"}, {0x74,0x0b,"Incorrect Encryption parameters"}, {0x74,0x0c,"Unable to decrypt parameter list"}, {0x74,0x0d,"Encryption algorithm disabled"}, {0x74,0x10,"SA creation parameter value invalid"}, {0x74,0x11,"SA creation parameter value rejected"}, {0x74,0x12,"Invalid SA usage"}, {0x74,0x21,"Data encryption configuration prevented"}, {0x74,0x30,"SA creation parameter not supported"}, {0x74,0x40,"Authentication failed"}, {0x74,0x61,"External data encryption key manager access error"}, {0x74,0x62,"External data encryption key manager error"}, {0x74,0x63,"External data encryption key not found"}, {0x74,0x64,"External data encryption request not authorized"}, {0x74,0x6e,"External data encryption control timeout"}, {0x74,0x6f,"External data encryption control error"}, {0x74,0x71,"Logical unit access not authorized"}, {0x74,0x79,"Security conflict in translated device"}, {0, 0, NULL} }; #else /* SG_SCSI_STRINGS */ struct sg_lib_asc_ascq_range_t sg_lib_asc_ascq_range[] = { {0, 0, 0, NULL} }; struct sg_lib_asc_ascq_t sg_lib_asc_ascq[] = { {0, 0, NULL} }; #endif /* SG_SCSI_STRINGS */ const char * sg_lib_sense_key_desc[] = { "No Sense", /* Filemark, ILI and/or EOM; progress indication (during FORMAT); power condition sensing (REQUEST SENSE) */ "Recovered Error", /* The last command completed successfully but used error correction */ "Not Ready", /* The addressed target is not ready */ "Medium Error", /* Data error detected on the medium */ "Hardware Error", /* Controller or device failure */ "Illegal Request", "Unit Attention", /* Removable medium was changed, or the target has been reset */ "Data Protect", /* Access to the data is blocked */ "Blank Check", /* Reached unexpected written or unwritten region of the medium */ "Vendor specific(9)", /* Vendor specific */ "Copy Aborted", /* COPY or COMPARE was aborted */ "Aborted Command", /* The target aborted the command */ "Equal", /* SEARCH DATA found data equal (obsolete) */ "Volume Overflow", /* Medium full with data to be written */ "Miscompare", /* Source data and data on the medium do not agree */ "Completed" /* may occur for successful cmd (spc4r23) */ }; const char * sg_lib_pdt_strs[32] = { /* should have 2**5 elements */ /* 0 */ "disk", "tape", "printer", /* obsolete, spc5r01 */ "processor", /* often SAF-TE device, copy manager */ "write once optical disk", /* obsolete, spc5r01 */ /* 5 */ "cd/dvd", "scanner", /* obsolete */ "optical memory device", "medium changer", "communications", /* obsolete */ /* 0xa */ "graphics [0xa]", /* obsolete */ "graphics [0xb]", /* obsolete */ "storage array controller", "enclosure services device", "simplified direct access device", "optical card reader/writer device", /* 0x10 */ "bridge controller commands", "object based storage", "automation/driver interface", "security manager device", /* obsolete, spc5r01 */ "zoned block commands", "0x15", "0x16", "0x17", "0x18", "0x19", "0x1a", "0x1b", "0x1c", "0x1d", "well known logical unit", "no physical device on this lu", }; const char * sg_lib_transport_proto_strs[] = { "Fibre Channel Protocol for SCSI (FCP-4)", "SCSI Parallel Interface (SPI-5)", /* obsolete in spc5r01 */ "Serial Storage Architecture SCSI-3 Protocol (SSA-S3P)", "Serial Bus Protocol for IEEE 1394 (SBP-3)", "SCSI RDMA Protocol (SRP)", "Internet SCSI (iSCSI)", "Serial Attached SCSI Protocol (SPL-4)", "Automation/Drive Interface Transport (ADT-2)", "AT Attachment Interface (ACS-2)", /* 0x8 */ "USB Attached SCSI (UAS-2)", "SCSI over PCI Express (SOP)", "PCIe", /* added in spc5r02 */ "Oxc", "Oxd", "Oxe", "No specific protocol" }; /* SCSI Feature Sets array. code->value, pdt->peri_dev_type (-1 for SPC) */ struct sg_lib_value_name_t sg_lib_scsi_feature_sets[] = { {SCSI_FS_SPC_DISCOVERY_2016, -1, "Discovery 2016"}, {SCSI_FS_SBC_BASE_2010, PDT_DISK, "SBC Base 2010"}, {SCSI_FS_SBC_BASE_2016, PDT_DISK, "SBC Base 2016"}, {SCSI_FS_SBC_BASIC_PROV_2016, PDT_DISK, "Basic provisioning 2016"}, {SCSI_FS_SBC_DRIVE_MAINT_2016, PDT_DISK, "Drive maintenance 2016"}, {0x0, 0, NULL}, /* 0x0 is reserved sfs; trailing sentinel */ }; #if (SG_SCSI_STRINGS && HAVE_NVME && (! IGNORE_NVME)) /* .value is completion queue's DW3 as follows: ((DW3 >> 17) & 0x3ff) * .peri_dev_type is an index for the sg_lib_scsi_status_sense_arr[] * .name is taken from NVMe 1.3a document, section 4.6.1.2.1 with less * capitalization. * NVMe term bits 31:17 of DW3 in the completion field as the "Status * Field" (SF). Bit 31 is "Do not retry" (DNR) and bit 30 is "More" (M). * Bits 29:28 are reserved, bit 27:25 are the "Status Code Type" (SCT) * and bits 24:17 are the Status Code (SC). This table is in ascending * order of its .value field so a binary search could be done on it. */ struct sg_lib_value_name_t sg_lib_nvme_cmd_status_arr[] = { /* Generic command status values, Status Code Type (SCT): 0h * Lowest 8 bits are the Status Code (SC), in this case: * 00h - 7Fh: Applicable to Admin Command Set, or across multiple * command sets * 80h - BFh: I/O Command Set Specific status codes * c0h - FFh: I/O Vendor Specific status codes */ {0x0, 0, "Successful completion"}, {0x1, 1, "Invalid command opcode"}, {0x2, 2, "Invalid field in command"}, {0x3, 2, "Command id conflict"}, {0x4, 3, "Data transfer error"}, {0x5, 4, "Command aborted due to power loss notication"}, {0x6, 5, "Internal error"}, {0x7, 6, "Command abort requested"}, {0x8, 6, "Command aborted due to SQ deletion"}, {0x9, 6, "Command aborted due to failed fused command"}, {0xa, 6, "Command aborted due to missing fused command"}, {0xb, 7, "Invalid namespace or format"}, {0xc, 5, "Command sequence error"}, {0xd, 5, "Invalid SGL segment descriptor"}, {0xe, 5, "Invalid number of SGL descriptors"}, {0xf, 5, "Data SGL length invalid"}, {0x10, 5, "Matadata SGL length invalid"}, {0x11, 5, "SGL descriptor type invalid"}, {0x12, 5, "Invalid use of controller memory buffer"}, {0x13, 5, "PRP offset invalid"}, {0x14, 2, "Atomic write unit exceeded"}, {0x15, 8, "Operation denied"}, {0x16, 5, "SGL offset invalid"}, {0x17, 5, "Reserved [0x17]"}, {0x18, 5, "Host identifier inconsistent format"}, {0x19, 5, "Keep alive timeout expired"}, {0x1a, 5, "Keep alive timeout invalid"}, {0x1b, 6, "Command aborted due to Preempt and Abort"}, {0x1c, 10, "Sanitize failed"}, {0x1d, 11, "Sanitize in progress"}, {0x1e, 5, "SGL data block granularity invalid"}, {0x1f, 5, "Command not supported for queue in CMB"}, /* Generic command status values, NVM (I/O) Command Set */ {0x80, 12, "LBA out of range"}, {0x81, 3, "Capacity exceeded"}, {0x82, 13, "Namespace not ready"}, {0x83, 14, "Reservation conflict"}, {0x84, 15, "Format in progress"}, /* 0xc0 - 0xff: vendor specific */ /* Command specific status values, Status Code Type (SCT): 1h */ {0x100, 5, "Completion queue invalid"}, {0x101, 5, "Invalid queue identifier"}, {0x102, 5, "Invalid queue size"}, {0x103, 5, "Abort command limit exceeded"}, {0x104, 5, "Reserved [0x104]"}, {0x105, 5, "Asynchronous event request limit exceeded"}, {0x106, 5, "Invalid firmware slot"}, {0x107, 5, "Invalid firmware image"}, {0x108, 5, "Invalid interrupt vector"}, {0x109, 5, "Invalid log page"}, {0x10a,16, "Invalid format"}, {0x10b, 5, "Firmware activation requires conventional reset"}, {0x10c, 5, "Invalid queue deletion"}, {0x10d, 5, "Feature identifier not saveable"}, {0x10e, 5, "Feature not changeable"}, {0x10f, 5, "Feature not namespace specific"}, {0x110, 5, "Firmware activation requires NVM subsystem reset"}, {0x111, 5, "Firmware activation requires reset"}, {0x112, 5, "Firmware activation requires maximum time violation"}, {0x113, 5, "Firmware activation prohibited"}, {0x114, 5, "Overlapping range"}, {0x115, 5, "Namespace insufficient capacity"}, {0x116, 5, "Namespace identifier unavailable"}, {0x117, 5, "Reserved [0x107]"}, {0x118, 5, "Namespace already attached"}, {0x119, 5, "Namespace is private"}, {0x11a, 5, "Namespace not attached"}, {0x11b, 3, "Thin provisioning not supported"}, {0x11c, 3, "Controller list invalid"}, {0x11d,17, "Device self-test in progress"}, {0x11e,18, "Boot partition write prohibited"}, {0x11f, 5, "Invalid controller identifier"}, {0x120, 5, "Invalid secondary controller state"}, {0x121, 5, "Invalid number of controller resorces"}, {0x122, 5, "Invalid resorce identifier"}, /* Command specific status values, Status Code Type (SCT): 1h * for NVM (I/O) Command Set */ {0x180, 2, "Conflicting attributes"}, {0x181,19, "Invalid protection information"}, {0x182,18, "Attempted write to read only range"}, /* 0x1c0 - 0x1ff: vendor specific */ /* Media and Data Integrity error values, Status Code Type (SCT): 2h */ {0x280,20, "Write fault"}, {0x281,21, "Unrecovered read error"}, {0x282,22, "End-to-end guard check error"}, {0x283,23, "End-to-end application tag check error"}, {0x284,24, "End-to-end reference tag check error"}, {0x285,25, "Compare failure"}, {0x286, 8, "Access denied"}, {0x287,26, "Deallocated or unwritten logical block"}, /* 0x2c0 - 0x2ff: vendor specific */ /* Leave this Sentinel value at end of this array */ {0x3ff, 0, NULL}, }; /* The sg_lib_nvme_cmd_status_arr[n].peri_dev_type field is an index * to this array. It allows an NVMe status (error) value to be mapped * to this SCSI tuple: status, sense_key, additional sense code (asc) and * asc qualifier (ascq). For brevity SAM_STAT_CHECK_CONDITION is written * as 0x2. */ struct sg_lib_4tuple_u8 sg_lib_scsi_status_sense_arr[] = { {SAM_STAT_GOOD, SPC_SK_NO_SENSE, 0, 0}, /* it's all good */ /* 0 */ {SAM_STAT_CHECK_CONDITION, SPC_SK_ILLEGAL_REQUEST, 0x20, 0x0},/* opcode */ {0x2, SPC_SK_ILLEGAL_REQUEST, 0x24, 0x0}, /* field in cdb */ {0x2, SPC_SK_MEDIUM_ERROR, 0x0, 0x0}, {SAM_STAT_TASK_ABORTED, SPC_SK_ABORTED_COMMAND, 0xb, 0x8}, {0x2, SPC_SK_HARDWARE_ERROR, 0x44, 0x0}, /* internal error */ /* 5 */ {SAM_STAT_TASK_ABORTED, SPC_SK_ABORTED_COMMAND, 0x0, 0x0}, {0x2, SPC_SK_ILLEGAL_REQUEST, 0x20, 0x9}, /* invalid LU */ {0x2, SPC_SK_ILLEGAL_REQUEST, 0x20, 0x2}, /* access denied */ {0x2, SPC_SK_ILLEGAL_REQUEST, 0x2c, 0x0}, /* cmd sequence error */ {0x2, SPC_SK_MEDIUM_ERROR, 0x31, 0x3}, /* sanitize failed */ /* 10 */ {0x2, SPC_SK_NOT_READY, 0x4, 0x1b}, /* sanitize in progress */ {0x2, SPC_SK_ILLEGAL_REQUEST, 0x21, 0x0}, /* LBA out of range */ {0x2, SPC_SK_NOT_READY, 0x4, 0x0}, /* not reportable; 0x1: becoming */ {SAM_STAT_RESERVATION_CONFLICT, 0x0, 0x0, 0x0}, {0x2, SPC_SK_NOT_READY, 0x4, 0x4}, /* format in progress */ /* 15 */ {0x2, SPC_SK_ILLEGAL_REQUEST, 0x31, 0x1}, /* format failed */ {0x2, SPC_SK_NOT_READY, 0x4, 0x9}, /* self-test in progress */ {0x2, SPC_SK_DATA_PROTECT, 0x27, 0x0}, /* write prohibited */ {0x2, SPC_SK_ILLEGAL_REQUEST, 0x10, 0x5}, /* protection info */ {0x2, SPC_SK_MEDIUM_ERROR, 0x3, 0x0}, /* periph dev w fault */ /* 20 */ {0x2, SPC_SK_MEDIUM_ERROR, 0x11, 0x0}, /* unrecoc rd */ {0x2, SPC_SK_MEDIUM_ERROR, 0x10, 0x1}, /* PI guard */ {0x2, SPC_SK_MEDIUM_ERROR, 0x10, 0x2}, /* PI app tag */ {0x2, SPC_SK_MEDIUM_ERROR, 0x10, 0x2}, /* PI app tag */ {0x2, SPC_SK_MISCOMPARE, 0x1d, 0x0}, /* during verify */ /* 25 */ {0x2, SPC_SK_MEDIUM_ERROR, 0x21, 0x6}, /* read invalid data */ /* Leave this Sentinel value at end of this array */ {0xff, 0xff, 0xff, 0xff}, }; #else /* (SG_SCSI_STRINGS && HAVE_NVME && (! IGNORE_NVME)) */ struct sg_lib_value_name_t sg_lib_nvme_cmd_status_arr[] = { /* Leave this Sentinel value at end of this array */ {0x3ff, 0, NULL}, }; struct sg_lib_4tuple_u8 sg_lib_scsi_status_sense_arr[] = { /* Leave this Sentinel value at end of this array */ {0xff, 0xff, 0xff, 0xff}, }; #endif /* (SG_SCSI_STRINGS && HAVE_NVME && (! IGNORE_NVME)) */ f2fs-tools-1.11.0/tools/sg_write_buffer/sg_pt_common.c000066400000000000000000000112531332120552100227300ustar00rootroot00000000000000/* * Copyright (c) 2009-2018 Douglas Gilbert. * All rights reserved. * Use of this source code is governed by a BSD-style * license that can be found in the BSD_LICENSE file. */ #include #include #include #include #include #include #define __STDC_FORMAT_MACROS 1 #include #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "sg_lib.h" #include "sg_pt.h" #include "sg_pt_nvme.h" static const char * scsi_pt_version_str = "3.03 20180115"; static const char * nvme_scsi_vendor_str = "NVMe "; const char * scsi_pt_version() { return scsi_pt_version_str; } /* Given the NVMe Identify controller response and optionally the NVMe * Identify namespace response (NULL otherwise), generate the SCSI VPD * page 0x83 (device identification) descriptor(s) in dop. Return the * number of bytes written which will not exceed max_do_len. Probably use * Peripheral Device Type (pdt) of 0 (disk) for don't know. Transport * protocol (tproto) should be -1 if not known, else SCSI value. * N.B. Does not write total VPD page length into dop[2:3] . */ int sg_make_vpd_devid_for_nvme(const uint8_t * nvme_id_ctl_p, const uint8_t * nvme_id_ns_p, int pdt, int tproto, uint8_t * dop, int max_do_len) { bool have_nguid, have_eui64; int k, n; char b[4]; if ((NULL == nvme_id_ctl_p) || (NULL == dop) || (max_do_len < 56)) return 0; memset(dop, 0, max_do_len); dop[0] = 0x1f & pdt; /* (PQ=0)<<5 | (PDT=pdt); 0 or 0xd (SES) */ dop[1] = 0x83; /* Device Identification VPD page number */ /* Build a T10 Vendor ID based designator (desig_id=1) for controller */ if (tproto >= 0) { dop[4] = ((0xf & tproto) << 4) | 0x2; dop[5] = 0xa1; /* PIV=1, ASSOC=2 (target device), desig_id=1 */ } else { dop[4] = 0x2; /* Prococol id=0, code_set=2 (ASCII) */ dop[5] = 0x21; /* PIV=0, ASSOC=2 (target device), desig_id=1 */ } memcpy(dop + 8, nvme_scsi_vendor_str, 8); /* N.B. this is "NVMe " */ memcpy(dop + 16, nvme_id_ctl_p + 24, 40); /* MN */ for (k = 40; k > 0; --k) { if (' ' == dop[15 + k]) dop[15 + k] = '_'; /* convert trailing spaces */ else break; } if (40 == k) --k; n = 16 + 1 + k; if (max_do_len < (n + 20)) return 0; memcpy(dop + n, nvme_id_ctl_p + 4, 20); /* SN */ for (k = 20; k > 0; --k) { /* trim trailing spaces */ if (' ' == dop[n + k - 1]) dop[n + k - 1] = '\0'; else break; } n += k; if (0 != (n % 4)) n = ((n / 4) + 1) * 4; /* round up to next modulo 4 */ dop[7] = n - 8; if (NULL == nvme_id_ns_p) return n; /* Look for NGUID (16 byte identifier) or EUI64 (8 byte) fields in * NVME Identify for namespace. If found form a EUI and a SCSI string * descriptor for non-zero NGUID or EUI64 (prefer NGUID if both). */ have_nguid = ! sg_all_zeros(nvme_id_ns_p + 104, 16); have_eui64 = ! sg_all_zeros(nvme_id_ns_p + 120, 8); if ((! have_nguid) && (! have_eui64)) return n; if (have_nguid) { if (max_do_len < (n + 20)) return n; dop[n + 0] = 0x1; /* Prococol id=0, code_set=1 (binary) */ dop[n + 1] = 0x02; /* PIV=0, ASSOC=0 (lu), desig_id=2 (eui) */ dop[n + 3] = 16; memcpy(dop + n + 4, nvme_id_ns_p + 104, 16); n += 20; if (max_do_len < (n + 40)) return n; dop[n + 0] = 0x3; /* Prococol id=0, code_set=3 (utf8) */ dop[n + 1] = 0x08; /* PIV=0, ASSOC=0 (lu), desig_id=8 (scsi string) */ dop[n + 3] = 36; memcpy(dop + n + 4, "eui.", 4); for (k = 0; k < 16; ++k) { snprintf(b, sizeof(b), "%02X", nvme_id_ns_p[104 + k]); memcpy(dop + n + 8 + (2 * k), b, 2); } return n + 40; } else { /* have_eui64 is true, 8 byte identifier */ if (max_do_len < (n + 12)) return n; dop[n + 0] = 0x1; /* Prococol id=0, code_set=1 (binary) */ dop[n + 1] = 0x02; /* PIV=0, ASSOC=0 (lu), desig_id=2 (eui) */ dop[n + 3] = 8; memcpy(dop + n + 4, nvme_id_ns_p + 120, 8); n += 12; if (max_do_len < (n + 24)) return n; dop[n + 0] = 0x3; /* Prococol id=0, code_set=3 (utf8) */ dop[n + 1] = 0x08; /* PIV=0, ASSOC=0 (lu), desig_id=8 (scsi string) */ dop[n + 3] = 20; memcpy(dop + n + 4, "eui.", 4); for (k = 0; k < 8; ++k) { snprintf(b, sizeof(b), "%02X", nvme_id_ns_p[120 + k]); memcpy(dop + n + 8 + (2 * k), b, 2); } return n + 24; } } f2fs-tools-1.11.0/tools/sg_write_buffer/sg_pt_linux.c000066400000000000000000000700771332120552100226100ustar00rootroot00000000000000/* * Copyright (c) 2005-2018 Douglas Gilbert. * All rights reserved. * Use of this source code is governed by a BSD-style * license that can be found in the BSD_LICENSE file. */ /* sg_pt_linux version 1.37 20180126 */ #include #include #include #include #include #include #include #include #include #include #include #include /* to define 'major' */ #ifndef major #include #endif #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include "sg_pt.h" #include "sg_lib.h" #include "sg_linux_inc.h" #include "sg_pt_linux.h" #ifdef major #define SG_DEV_MAJOR major #else #ifdef HAVE_LINUX_KDEV_T_H #include #endif #define SG_DEV_MAJOR MAJOR /* MAJOR() macro faulty if > 255 minors */ #endif #ifndef BLOCK_EXT_MAJOR #define BLOCK_EXT_MAJOR 259 #endif #define DEF_TIMEOUT 60000 /* 60,000 millisecs (60 seconds) */ static const char * linux_host_bytes[] = { "DID_OK", "DID_NO_CONNECT", "DID_BUS_BUSY", "DID_TIME_OUT", "DID_BAD_TARGET", "DID_ABORT", "DID_PARITY", "DID_ERROR", "DID_RESET", "DID_BAD_INTR", "DID_PASSTHROUGH", "DID_SOFT_ERROR", "DID_IMM_RETRY", "DID_REQUEUE" /* 0xd */, "DID_TRANSPORT_DISRUPTED", "DID_TRANSPORT_FAILFAST", "DID_TARGET_FAILURE" /* 0x10 */, "DID_NEXUS_FAILURE (reservation conflict)", "DID_ALLOC_FAILURE", "DID_MEDIUM_ERROR", }; #define LINUX_HOST_BYTES_SZ \ (int)(sizeof(linux_host_bytes) / sizeof(linux_host_bytes[0])) static const char * linux_driver_bytes[] = { "DRIVER_OK", "DRIVER_BUSY", "DRIVER_SOFT", "DRIVER_MEDIA", "DRIVER_ERROR", "DRIVER_INVALID", "DRIVER_TIMEOUT", "DRIVER_HARD", "DRIVER_SENSE" }; #define LINUX_DRIVER_BYTES_SZ \ (int)(sizeof(linux_driver_bytes) / sizeof(linux_driver_bytes[0])) #if 0 static const char * linux_driver_suggests[] = { "SUGGEST_OK", "SUGGEST_RETRY", "SUGGEST_ABORT", "SUGGEST_REMAP", "SUGGEST_DIE", "UNKNOWN","UNKNOWN","UNKNOWN", "SUGGEST_SENSE" }; #define LINUX_DRIVER_SUGGESTS_SZ \ (int)(sizeof(linux_driver_suggests) / sizeof(linux_driver_suggests[0])) #endif /* * These defines are for constants that should be visible in the * /usr/include/scsi directory (brought in by sg_linux_inc.h). * Redefined and aliased here to decouple this code from * sg_io_linux.h N.B. the SUGGEST_* constants are no longer used. */ #ifndef DRIVER_MASK #define DRIVER_MASK 0x0f #endif #ifndef SUGGEST_MASK #define SUGGEST_MASK 0xf0 #endif #ifndef DRIVER_SENSE #define DRIVER_SENSE 0x08 #endif #define SG_LIB_DRIVER_MASK DRIVER_MASK #define SG_LIB_SUGGEST_MASK SUGGEST_MASK #define SG_LIB_DRIVER_SENSE DRIVER_SENSE bool sg_bsg_nvme_char_major_checked = false; int sg_bsg_major = 0; volatile int sg_nvme_char_major = 0; long sg_lin_page_size = 4096; /* default, overridden with correct value */ #if defined(__GNUC__) || defined(__clang__) static int pr2ws(const char * fmt, ...) __attribute__ ((format (printf, 1, 2))); #else static int pr2ws(const char * fmt, ...); #endif static int pr2ws(const char * fmt, ...) { va_list args; int n; va_start(args, fmt); n = vfprintf(sg_warnings_strm ? sg_warnings_strm : stderr, fmt, args); va_end(args); return n; } /* This function only needs to be called once (unless a NVMe controller * can be hot-plugged into system in which case it should be called * (again) after that event). */ void sg_find_bsg_nvme_char_major(int verbose) { bool got_one = false; int n; const char * proc_devices = "/proc/devices"; char * cp; FILE *fp; char a[128]; char b[128]; sg_lin_page_size = sysconf(_SC_PAGESIZE); if (NULL == (fp = fopen(proc_devices, "r"))) { if (verbose) pr2ws("fopen %s failed: %s\n", proc_devices, strerror(errno)); return; } while ((cp = fgets(b, sizeof(b), fp))) { if ((1 == sscanf(b, "%126s", a)) && (0 == memcmp(a, "Character", 9))) break; } while (cp && (cp = fgets(b, sizeof(b), fp))) { if (2 == sscanf(b, "%d %126s", &n, a)) { if (0 == strcmp("bsg", a)) { sg_bsg_major = n; if (got_one) break; got_one = true; } else if (0 == strcmp("nvme", a)) { sg_nvme_char_major = n; if (got_one) break; got_one = true; } } else break; } if (verbose > 3) { if (cp) { if (sg_bsg_major > 0) pr2ws("found sg_bsg_major=%d\n", sg_bsg_major); if (sg_nvme_char_major > 0) pr2ws("found sg_nvme_char_major=%d\n", sg_nvme_char_major); } else pr2ws("found no bsg not nvme char device in %s\n", proc_devices); } fclose(fp); } /* Assumes that sg_find_bsg_nvme_char_major() has already been called. Returns * true if dev_fd is a scsi generic pass-through device. If yields * *is_nvme_p = true with *nsid_p = 0 then dev_fd is a NVMe char device. * If yields *nsid_p > 0 then dev_fd is a NVMe block device. */ static bool check_file_type(int dev_fd, struct stat * dev_statp, bool * is_bsg_p, bool * is_nvme_p, uint32_t * nsid_p, int * os_err_p, int verbose) { bool is_nvme = false; bool is_sg = false; bool is_bsg = false; bool is_block = false; int os_err = 0; int major_num; uint32_t nsid = 0; /* invalid NSID */ if (dev_fd >= 0) { if (fstat(dev_fd, dev_statp) < 0) { os_err = errno; if (verbose) pr2ws("%s: fstat() failed: %s (errno=%d)\n", __func__, safe_strerror(os_err), os_err); goto skip_out; } major_num = (int)SG_DEV_MAJOR(dev_statp->st_rdev); if (S_ISCHR(dev_statp->st_mode)) { if (SCSI_GENERIC_MAJOR == major_num) is_sg = true; else if (sg_bsg_major == major_num) is_bsg = true; else if (sg_nvme_char_major == major_num) is_nvme = true; } else if (S_ISBLK(dev_statp->st_mode)) { is_block = true; if (BLOCK_EXT_MAJOR == major_num) { is_nvme = true; nsid = ioctl(dev_fd, NVME_IOCTL_ID, NULL); if (SG_NVME_BROADCAST_NSID == nsid) { /* means ioctl error */ os_err = errno; if (verbose) pr2ws("%s: ioctl(NVME_IOCTL_ID) failed: %s " "(errno=%d)\n", __func__, safe_strerror(os_err), os_err); } else os_err = 0; } } } else { os_err = EBADF; if (verbose) pr2ws("%s: invalid file descriptor (%d)\n", __func__, dev_fd); } skip_out: if (verbose > 3) { pr2ws("%s: file descriptor is ", __func__); if (is_sg) pr2ws("sg device\n"); else if (is_bsg) pr2ws("bsg device\n"); else if (is_nvme && (0 == nsid)) pr2ws("NVMe char device\n"); else if (is_nvme) pr2ws("NVMe block device, nsid=%lld\n", ((uint32_t)-1 == nsid) ? -1LL : (long long)nsid); else if (is_block) pr2ws("block device\n"); else pr2ws("undetermined device, could be regular file\n"); } if (is_bsg_p) *is_bsg_p = is_bsg; if (is_nvme_p) *is_nvme_p = is_nvme; if (nsid_p) *nsid_p = nsid; if (os_err_p) *os_err_p = os_err; return is_sg; } /* Assumes dev_fd is an "open" file handle associated with device_name. If * the implementation (possibly for one OS) cannot determine from dev_fd if * a SCSI or NVMe pass-through is referenced, then it might guess based on * device_name. Returns 1 if SCSI generic pass-though device, returns 2 if * secondary SCSI pass-through device (in Linux a bsg device); returns 3 is * char NVMe device (i.e. no NSID); returns 4 if block NVMe device (includes * NSID), or 0 if something else (e.g. ATA block device) or dev_fd < 0. * If error, returns negated errno (operating system) value. */ int check_pt_file_handle(int dev_fd, const char * device_name, int verbose) { if (verbose > 4) pr2ws("%s: dev_fd=%d, device_name: %s\n", __func__, dev_fd, device_name); /* Linux doesn't need device_name to determine which pass-through */ if (! sg_bsg_nvme_char_major_checked) { sg_bsg_nvme_char_major_checked = true; sg_find_bsg_nvme_char_major(verbose); } if (dev_fd >= 0) { bool is_sg, is_bsg, is_nvme; int err; uint32_t nsid; struct stat a_stat; is_sg = check_file_type(dev_fd, &a_stat, &is_bsg, &is_nvme, &nsid, &err, verbose); if (err) return -err; else if (is_sg) return 1; else if (is_bsg) return 2; else if (is_nvme && (0 == nsid)) return 3; else if (is_nvme) return 4; else return 0; } else return 0; } /* * We make a runtime decision whether to use the sg v3 interface or the sg * v4 interface (currently exclusively used by the bsg driver). If all the * following are true we use sg v4 which is only currently supported on bsg * device nodes: * a) there is a bsg entry in the /proc/devices file * b) the device node given to scsi_pt_open() is a char device * c) the char major number of the device node given to scsi_pt_open() * matches the char major number of the bsg entry in /proc/devices * Otherwise the sg v3 interface is used. * * Note that in either case we prepare the data in a sg v4 structure. If * the runtime tests indicate that the v3 interface is needed then * do_scsi_pt_v3() transfers the input data into a v3 structure and * then the output data is transferred back into a sg v4 structure. * That implementation detail could change in the future. * * [20120806] Only use MAJOR() macro in kdev_t.h if that header file is * available and major() macro [N.B. lower case] is not available. */ #ifdef major #define SG_DEV_MAJOR major #else #ifdef HAVE_LINUX_KDEV_T_H #include #endif #define SG_DEV_MAJOR MAJOR /* MAJOR() macro faulty if > 255 minors */ #endif /* Returns >= 0 if successful. If error in Unix returns negated errno. */ int scsi_pt_open_device(const char * device_name, bool read_only, int verbose) { int oflags = O_NONBLOCK; oflags |= (read_only ? O_RDONLY : O_RDWR); return scsi_pt_open_flags(device_name, oflags, verbose); } /* Similar to scsi_pt_open_device() but takes Unix style open flags OR-ed */ /* together. The 'flags' argument is advisory and may be ignored. */ /* Returns >= 0 if successful, otherwise returns negated errno. */ int scsi_pt_open_flags(const char * device_name, int flags, int verbose) { int fd; if (! sg_bsg_nvme_char_major_checked) { sg_bsg_nvme_char_major_checked = true; sg_find_bsg_nvme_char_major(verbose); } if (verbose > 1) { pr2ws("open %s with flags=0x%x\n", device_name, flags); } fd = open(device_name, flags); if (fd < 0) { fd = -errno; if (verbose > 1) pr2ws("%s: open(%s, 0x%x) failed: %s\n", __func__, device_name, flags, safe_strerror(-fd)); } return fd; } /* Returns 0 if successful. If error in Unix returns negated errno. */ int scsi_pt_close_device(int device_fd) { int res; res = close(device_fd); if (res < 0) res = -errno; return res; } /* Caller should additionally call get_scsi_pt_os_err() after this call */ struct sg_pt_base * construct_scsi_pt_obj_with_fd(int dev_fd, int verbose) { int err; struct sg_pt_linux_scsi * ptp; /* The following 2 lines are temporary. It is to avoid a NULL pointer * crash when an old utility is used with a newer library built after * the sg_warnings_strm cleanup */ if (NULL == sg_warnings_strm) sg_warnings_strm = stderr; ptp = (struct sg_pt_linux_scsi *) calloc(1, sizeof(struct sg_pt_linux_scsi)); if (ptp) { err = set_pt_file_handle((struct sg_pt_base *)ptp, dev_fd, verbose); if ((0 == err) && (! ptp->is_nvme)) { ptp->io_hdr.guard = 'Q'; #ifdef BSG_PROTOCOL_SCSI ptp->io_hdr.protocol = BSG_PROTOCOL_SCSI; #endif #ifdef BSG_SUB_PROTOCOL_SCSI_CMD ptp->io_hdr.subprotocol = BSG_SUB_PROTOCOL_SCSI_CMD; #endif } } else if (verbose) pr2ws("%s: calloc() failed, out of memory?\n", __func__); return (struct sg_pt_base *)ptp; } struct sg_pt_base * construct_scsi_pt_obj() { return construct_scsi_pt_obj_with_fd(-1 /* dev_fd */, 0 /* verbose */); } void destruct_scsi_pt_obj(struct sg_pt_base * vp) { struct sg_pt_linux_scsi * ptp = &vp->impl; if (ptp->free_nvme_id_ctlp) { free(ptp->free_nvme_id_ctlp); ptp->free_nvme_id_ctlp = NULL; ptp->nvme_id_ctlp = NULL; } if (ptp) free(ptp); } /* Remembers previous device file descriptor */ void clear_scsi_pt_obj(struct sg_pt_base * vp) { bool is_sg, is_bsg, is_nvme; int fd; uint32_t nvme_nsid; struct sg_pt_linux_scsi * ptp = &vp->impl; if (ptp) { fd = ptp->dev_fd; is_sg = ptp->is_sg; is_bsg = ptp->is_bsg; is_nvme = ptp->is_nvme; nvme_nsid = ptp->nvme_nsid; memset(ptp, 0, sizeof(struct sg_pt_linux_scsi)); ptp->io_hdr.guard = 'Q'; #ifdef BSG_PROTOCOL_SCSI ptp->io_hdr.protocol = BSG_PROTOCOL_SCSI; #endif #ifdef BSG_SUB_PROTOCOL_SCSI_CMD ptp->io_hdr.subprotocol = BSG_SUB_PROTOCOL_SCSI_CMD; #endif ptp->dev_fd = fd; ptp->is_sg = is_sg; ptp->is_bsg = is_bsg; ptp->is_nvme = is_nvme; ptp->nvme_direct = false; ptp->nvme_nsid = nvme_nsid; } } /* Forget any previous dev_fd and install the one given. May attempt to * find file type (e.g. if pass-though) from OS so there could be an error. * Returns 0 for success or the same value as get_scsi_pt_os_err() * will return. dev_fd should be >= 0 for a valid file handle or -1 . */ int set_pt_file_handle(struct sg_pt_base * vp, int dev_fd, int verbose) { struct sg_pt_linux_scsi * ptp = &vp->impl; struct stat a_stat; if (! sg_bsg_nvme_char_major_checked) { sg_bsg_nvme_char_major_checked = true; sg_find_bsg_nvme_char_major(verbose); } ptp->dev_fd = dev_fd; if (dev_fd >= 0) ptp->is_sg = check_file_type(dev_fd, &a_stat, &ptp->is_bsg, &ptp->is_nvme, &ptp->nvme_nsid, &ptp->os_err, verbose); else { ptp->is_sg = false; ptp->is_bsg = false; ptp->is_nvme = false; ptp->nvme_direct = false; ptp->nvme_nsid = 0; ptp->os_err = 0; } return ptp->os_err; } /* Valid file handles (which is the return value) are >= 0 . Returns -1 * if there is no valid file handle. */ int get_pt_file_handle(const struct sg_pt_base * vp) { const struct sg_pt_linux_scsi * ptp = &vp->impl; return ptp->dev_fd; } void set_scsi_pt_cdb(struct sg_pt_base * vp, const unsigned char * cdb, int cdb_len) { struct sg_pt_linux_scsi * ptp = &vp->impl; if (ptp->io_hdr.request) ++ptp->in_err; ptp->io_hdr.request = (__u64)(sg_uintptr_t)cdb; ptp->io_hdr.request_len = cdb_len; } void set_scsi_pt_sense(struct sg_pt_base * vp, unsigned char * sense, int max_sense_len) { struct sg_pt_linux_scsi * ptp = &vp->impl; if (ptp->io_hdr.response) ++ptp->in_err; memset(sense, 0, max_sense_len); ptp->io_hdr.response = (__u64)(sg_uintptr_t)sense; ptp->io_hdr.max_response_len = max_sense_len; } /* Setup for data transfer from device */ void set_scsi_pt_data_in(struct sg_pt_base * vp, unsigned char * dxferp, int dxfer_ilen) { struct sg_pt_linux_scsi * ptp = &vp->impl; if (ptp->io_hdr.din_xferp) ++ptp->in_err; if (dxfer_ilen > 0) { ptp->io_hdr.din_xferp = (__u64)(sg_uintptr_t)dxferp; ptp->io_hdr.din_xfer_len = dxfer_ilen; } } /* Setup for data transfer toward device */ void set_scsi_pt_data_out(struct sg_pt_base * vp, const unsigned char * dxferp, int dxfer_olen) { struct sg_pt_linux_scsi * ptp = &vp->impl; if (ptp->io_hdr.dout_xferp) ++ptp->in_err; if (dxfer_olen > 0) { ptp->io_hdr.dout_xferp = (__u64)(sg_uintptr_t)dxferp; ptp->io_hdr.dout_xfer_len = dxfer_olen; } } void set_pt_metadata_xfer(struct sg_pt_base * vp, unsigned char * dxferp, uint32_t dxfer_len, bool out_true) { struct sg_pt_linux_scsi * ptp = &vp->impl; if (dxfer_len > 0) { ptp->mdxferp = dxferp; ptp->mdxfer_len = dxfer_len; ptp->mdxfer_out = out_true; } } void set_scsi_pt_packet_id(struct sg_pt_base * vp, int pack_id) { struct sg_pt_linux_scsi * ptp = &vp->impl; ptp->io_hdr.spare_in = pack_id; } void set_scsi_pt_tag(struct sg_pt_base * vp, uint64_t tag) { struct sg_pt_linux_scsi * ptp = &vp->impl; ptp->io_hdr.request_tag = tag; } /* Note that task management function codes are transport specific */ void set_scsi_pt_task_management(struct sg_pt_base * vp, int tmf_code) { struct sg_pt_linux_scsi * ptp = &vp->impl; ptp->io_hdr.subprotocol = 1; /* SCSI task management function */ ptp->tmf_request[0] = (unsigned char)tmf_code; /* assume it fits */ ptp->io_hdr.request = (__u64)(sg_uintptr_t)(&(ptp->tmf_request[0])); ptp->io_hdr.request_len = 1; } void set_scsi_pt_task_attr(struct sg_pt_base * vp, int attribute, int priority) { struct sg_pt_linux_scsi * ptp = &vp->impl; ptp->io_hdr.request_attr = attribute; ptp->io_hdr.request_priority = priority; } #ifndef BSG_FLAG_Q_AT_TAIL #define BSG_FLAG_Q_AT_TAIL 0x10 #endif #ifndef BSG_FLAG_Q_AT_HEAD #define BSG_FLAG_Q_AT_HEAD 0x20 #endif /* Need this later if translated to v3 interface */ #ifndef SG_FLAG_Q_AT_TAIL #define SG_FLAG_Q_AT_TAIL 0x10 #endif #ifndef SG_FLAG_Q_AT_HEAD #define SG_FLAG_Q_AT_HEAD 0x20 #endif void set_scsi_pt_flags(struct sg_pt_base * vp, int flags) { struct sg_pt_linux_scsi * ptp = &vp->impl; /* default action of bsg driver (sg v4) is QUEUE_AT_HEAD */ /* default action of block layer SG_IO ioctl is QUEUE_AT_TAIL */ if (SCSI_PT_FLAGS_QUEUE_AT_HEAD & flags) { /* favour AT_HEAD */ ptp->io_hdr.flags |= BSG_FLAG_Q_AT_HEAD; ptp->io_hdr.flags &= ~BSG_FLAG_Q_AT_TAIL; } else if (SCSI_PT_FLAGS_QUEUE_AT_TAIL & flags) { ptp->io_hdr.flags |= BSG_FLAG_Q_AT_TAIL; ptp->io_hdr.flags &= ~BSG_FLAG_Q_AT_HEAD; } } /* N.B. Returns din_resid and ignores dout_resid */ int get_scsi_pt_resid(const struct sg_pt_base * vp) { const struct sg_pt_linux_scsi * ptp = &vp->impl; if (NULL == ptp) return 0; return ptp->nvme_direct ? 0 : ptp->io_hdr.din_resid; } int get_scsi_pt_status_response(const struct sg_pt_base * vp) { const struct sg_pt_linux_scsi * ptp = &vp->impl; if (NULL == ptp) return 0; return (int)(ptp->nvme_direct ? ptp->nvme_status : ptp->io_hdr.device_status); } uint32_t get_pt_result(const struct sg_pt_base * vp) { const struct sg_pt_linux_scsi * ptp = &vp->impl; if (NULL == ptp) return 0; return ptp->nvme_direct ? ptp->nvme_result : ptp->io_hdr.device_status; } int get_scsi_pt_sense_len(const struct sg_pt_base * vp) { const struct sg_pt_linux_scsi * ptp = &vp->impl; return ptp->io_hdr.response_len; } int get_scsi_pt_duration_ms(const struct sg_pt_base * vp) { const struct sg_pt_linux_scsi * ptp = &vp->impl; return ptp->io_hdr.duration; } int get_scsi_pt_transport_err(const struct sg_pt_base * vp) { const struct sg_pt_linux_scsi * ptp = &vp->impl; return ptp->io_hdr.transport_status; } void set_scsi_pt_transport_err(struct sg_pt_base * vp, int err) { struct sg_pt_linux_scsi * ptp = &vp->impl; ptp->io_hdr.transport_status = err; } /* Returns b which will contain a null char terminated string (if * max_b_len > 0). Combined driver and transport (called "host" in Linux * kernel) statuses */ char * get_scsi_pt_transport_err_str(const struct sg_pt_base * vp, int max_b_len, char * b) { const struct sg_pt_linux_scsi * ptp = &vp->impl; int ds = ptp->io_hdr.driver_status; int hs = ptp->io_hdr.transport_status; int n, m; char * cp = b; int driv; const char * driv_cp = "invalid"; if (max_b_len < 1) return b; m = max_b_len; n = 0; if (hs) { if ((hs < 0) || (hs >= LINUX_HOST_BYTES_SZ)) n = snprintf(cp, m, "Host_status=0x%02x is invalid\n", hs); else n = snprintf(cp, m, "Host_status=0x%02x [%s]\n", hs, linux_host_bytes[hs]); } m -= n; if (m < 1) { b[max_b_len - 1] = '\0'; return b; } cp += n; driv = ds & SG_LIB_DRIVER_MASK; if (driv < LINUX_DRIVER_BYTES_SZ) driv_cp = linux_driver_bytes[driv]; #if 0 sugg = (ds & SG_LIB_SUGGEST_MASK) >> 4; if (sugg < LINUX_DRIVER_SUGGESTS_SZ) sugg_cp = linux_driver_suggests[sugg]; #endif n = snprintf(cp, m, "Driver_status=0x%02x [%s]\n", ds, driv_cp); m -= n; if (m < 1) b[max_b_len - 1] = '\0'; return b; } int get_scsi_pt_result_category(const struct sg_pt_base * vp) { const struct sg_pt_linux_scsi * ptp = &vp->impl; int dr_st = ptp->io_hdr.driver_status & SG_LIB_DRIVER_MASK; int scsi_st = ptp->io_hdr.device_status & 0x7e; if (ptp->os_err) return SCSI_PT_RESULT_OS_ERR; else if (ptp->io_hdr.transport_status) return SCSI_PT_RESULT_TRANSPORT_ERR; else if (dr_st && (SG_LIB_DRIVER_SENSE != dr_st)) return SCSI_PT_RESULT_TRANSPORT_ERR; else if ((SG_LIB_DRIVER_SENSE == dr_st) || (SAM_STAT_CHECK_CONDITION == scsi_st) || (SAM_STAT_COMMAND_TERMINATED == scsi_st)) return SCSI_PT_RESULT_SENSE; else if (scsi_st) return SCSI_PT_RESULT_STATUS; else return SCSI_PT_RESULT_GOOD; } int get_scsi_pt_os_err(const struct sg_pt_base * vp) { const struct sg_pt_linux_scsi * ptp = &vp->impl; return ptp->os_err; } char * get_scsi_pt_os_err_str(const struct sg_pt_base * vp, int max_b_len, char * b) { const struct sg_pt_linux_scsi * ptp = &vp->impl; const char * cp; cp = safe_strerror(ptp->os_err); strncpy(b, cp, max_b_len); if ((int)strlen(cp) >= max_b_len) b[max_b_len - 1] = '\0'; return b; } bool pt_device_is_nvme(const struct sg_pt_base * vp) { const struct sg_pt_linux_scsi * ptp = &vp->impl; return ptp->is_nvme; } /* If a NVMe block device (which includes the NSID) handle is associated * with 'vp', then its NSID is returned (values range from 0x1 to * 0xffffffe). Otherwise 0 is returned. */ uint32_t get_pt_nvme_nsid(const struct sg_pt_base * vp) { const struct sg_pt_linux_scsi * ptp = &vp->impl; return ptp->nvme_nsid; } /* Executes SCSI command using sg v3 interface */ static int do_scsi_pt_v3(struct sg_pt_linux_scsi * ptp, int fd, int time_secs, int verbose) { struct sg_io_hdr v3_hdr; memset(&v3_hdr, 0, sizeof(v3_hdr)); /* convert v4 to v3 header */ v3_hdr.interface_id = 'S'; v3_hdr.dxfer_direction = SG_DXFER_NONE; v3_hdr.cmdp = (unsigned char *)(long)ptp->io_hdr.request; v3_hdr.cmd_len = (unsigned char)ptp->io_hdr.request_len; if (ptp->io_hdr.din_xfer_len > 0) { if (ptp->io_hdr.dout_xfer_len > 0) { if (verbose) pr2ws("sgv3 doesn't support bidi\n"); return SCSI_PT_DO_BAD_PARAMS; } v3_hdr.dxferp = (void *)(long)ptp->io_hdr.din_xferp; v3_hdr.dxfer_len = (unsigned int)ptp->io_hdr.din_xfer_len; v3_hdr.dxfer_direction = SG_DXFER_FROM_DEV; } else if (ptp->io_hdr.dout_xfer_len > 0) { v3_hdr.dxferp = (void *)(long)ptp->io_hdr.dout_xferp; v3_hdr.dxfer_len = (unsigned int)ptp->io_hdr.dout_xfer_len; v3_hdr.dxfer_direction = SG_DXFER_TO_DEV; } if (ptp->io_hdr.response && (ptp->io_hdr.max_response_len > 0)) { v3_hdr.sbp = (unsigned char *)(long)ptp->io_hdr.response; v3_hdr.mx_sb_len = (unsigned char)ptp->io_hdr.max_response_len; } v3_hdr.pack_id = (int)ptp->io_hdr.spare_in; if (BSG_FLAG_Q_AT_HEAD & ptp->io_hdr.flags) v3_hdr.flags |= SG_FLAG_Q_AT_HEAD; /* favour AT_HEAD */ else if (BSG_FLAG_Q_AT_TAIL & ptp->io_hdr.flags) v3_hdr.flags |= SG_FLAG_Q_AT_TAIL; if (NULL == v3_hdr.cmdp) { if (verbose) pr2ws("No SCSI command (cdb) given\n"); return SCSI_PT_DO_BAD_PARAMS; } /* io_hdr.timeout is in milliseconds, if greater than zero */ v3_hdr.timeout = ((time_secs > 0) ? (time_secs * 1000) : DEF_TIMEOUT); /* Finally do the v3 SG_IO ioctl */ if (ioctl(fd, SG_IO, &v3_hdr) < 0) { ptp->os_err = errno; if (verbose > 1) pr2ws("ioctl(SG_IO v3) failed: %s (errno=%d)\n", safe_strerror(ptp->os_err), ptp->os_err); return -ptp->os_err; } ptp->io_hdr.device_status = (__u32)v3_hdr.status; ptp->io_hdr.driver_status = (__u32)v3_hdr.driver_status; ptp->io_hdr.transport_status = (__u32)v3_hdr.host_status; ptp->io_hdr.response_len = (__u32)v3_hdr.sb_len_wr; ptp->io_hdr.duration = (__u32)v3_hdr.duration; ptp->io_hdr.din_resid = (__s32)v3_hdr.resid; /* v3_hdr.info not passed back since no mapping defined (yet) */ return 0; } /* Executes SCSI command (or at least forwards it to lower layers). * Returns 0 for success, negative numbers are negated 'errno' values from * OS system calls. Positive return values are errors from this package. */ int do_scsi_pt(struct sg_pt_base * vp, int fd, int time_secs, int verbose) { int err; struct sg_pt_linux_scsi * ptp = &vp->impl; bool have_checked_for_type = (ptp->dev_fd >= 0); if (! sg_bsg_nvme_char_major_checked) { sg_bsg_nvme_char_major_checked = true; sg_find_bsg_nvme_char_major(verbose); } if (ptp->in_err) { if (verbose) pr2ws("Replicated or unused set_scsi_pt... functions\n"); return SCSI_PT_DO_BAD_PARAMS; } if (fd >= 0) { if ((ptp->dev_fd >= 0) && (fd != ptp->dev_fd)) { if (verbose) pr2ws("%s: file descriptor given to create() and here " "differ\n", __func__); return SCSI_PT_DO_BAD_PARAMS; } ptp->dev_fd = fd; } else if (ptp->dev_fd < 0) { if (verbose) pr2ws("%s: invalid file descriptors\n", __func__); return SCSI_PT_DO_BAD_PARAMS; } else fd = ptp->dev_fd; if (! have_checked_for_type) { err = set_pt_file_handle(vp, ptp->dev_fd, verbose); if (err) return -ptp->os_err; } if (ptp->os_err) return -ptp->os_err; if (ptp->is_nvme) return sg_do_nvme_pt(vp, -1, time_secs, verbose); else if (sg_bsg_major <= 0) return do_scsi_pt_v3(ptp, fd, time_secs, verbose); else if (ptp->is_bsg) ; /* drop through to sg v4 implementation */ else return do_scsi_pt_v3(ptp, fd, time_secs, verbose); if (! ptp->io_hdr.request) { if (verbose) pr2ws("No SCSI command (cdb) given (v4)\n"); return SCSI_PT_DO_BAD_PARAMS; } /* io_hdr.timeout is in milliseconds */ ptp->io_hdr.timeout = ((time_secs > 0) ? (time_secs * 1000) : DEF_TIMEOUT); #if 0 /* sense buffer already zeroed */ if (ptp->io_hdr.response && (ptp->io_hdr.max_response_len > 0)) { void * p; p = (void *)(long)ptp->io_hdr.response; memset(p, 0, ptp->io_hdr.max_response_len); } #endif if (ioctl(fd, SG_IO, &ptp->io_hdr) < 0) { ptp->os_err = errno; if (verbose > 1) pr2ws("ioctl(SG_IO v4) failed: %s (errno=%d)\n", safe_strerror(ptp->os_err), ptp->os_err); return -ptp->os_err; } return 0; } f2fs-tools-1.11.0/tools/sg_write_buffer/sg_pt_linux_nvme.c000066400000000000000000001203631332120552100236270ustar00rootroot00000000000000/* * Copyright (c) 2017-2018 Douglas Gilbert. * All rights reserved. * Use of this source code is governed by a BSD-style * license that can be found in the BSD_LICENSE file. * * The code to use the NVMe Management Interface (MI) SES pass-through * was provided by WDC in November 2017. */ /* * Copyright 2017, Western Digital Corporation * * Written by Berck Nash * * Use of this source code is governed by a BSD-style * license that can be found in the BSD_LICENSE file. * * Based on the NVM-Express command line utility, which bore the following * notice: * * Copyright (c) 2014-2015, Intel Corporation. * * Written by Keith Busch * * 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. */ /* sg_pt_linux_nvme version 1.04 20180115 */ #include #include #include #include #include #include #include #include #include #define __STDC_FORMAT_MACROS 1 #include #include #include #include /* to define 'major' */ #ifndef major #include #endif #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include "sg_pt.h" #include "sg_lib.h" #include "sg_linux_inc.h" #include "sg_pt_linux.h" #include "sg_unaligned.h" #define SCSI_INQUIRY_OPC 0x12 #define SCSI_REPORT_LUNS_OPC 0xa0 #define SCSI_TEST_UNIT_READY_OPC 0x0 #define SCSI_REQUEST_SENSE_OPC 0x3 #define SCSI_SEND_DIAGNOSTIC_OPC 0x1d #define SCSI_RECEIVE_DIAGNOSTIC_OPC 0x1c #define SCSI_MAINT_IN_OPC 0xa3 #define SCSI_REP_SUP_OPCS_OPC 0xc #define SCSI_REP_SUP_TMFS_OPC 0xd /* Additional Sense Code (ASC) */ #define NO_ADDITIONAL_SENSE 0x0 #define LOGICAL_UNIT_NOT_READY 0x4 #define LOGICAL_UNIT_COMMUNICATION_FAILURE 0x8 #define UNRECOVERED_READ_ERR 0x11 #define PARAMETER_LIST_LENGTH_ERR 0x1a #define INVALID_OPCODE 0x20 #define LBA_OUT_OF_RANGE 0x21 #define INVALID_FIELD_IN_CDB 0x24 #define INVALID_FIELD_IN_PARAM_LIST 0x26 #define UA_RESET_ASC 0x29 #define UA_CHANGED_ASC 0x2a #define TARGET_CHANGED_ASC 0x3f #define LUNS_CHANGED_ASCQ 0x0e #define INSUFF_RES_ASC 0x55 #define INSUFF_RES_ASCQ 0x3 #define LOW_POWER_COND_ON_ASC 0x5e /* ASCQ=0 */ #define POWER_ON_RESET_ASCQ 0x0 #define BUS_RESET_ASCQ 0x2 /* scsi bus reset occurred */ #define MODE_CHANGED_ASCQ 0x1 /* mode parameters changed */ #define CAPACITY_CHANGED_ASCQ 0x9 #define SAVING_PARAMS_UNSUP 0x39 #define TRANSPORT_PROBLEM 0x4b #define THRESHOLD_EXCEEDED 0x5d #define LOW_POWER_COND_ON 0x5e #define MISCOMPARE_VERIFY_ASC 0x1d #define MICROCODE_CHANGED_ASCQ 0x1 /* with TARGET_CHANGED_ASC */ #define MICROCODE_CHANGED_WO_RESET_ASCQ 0x16 static inline bool is_aligned(const void * pointer, size_t byte_count) { return ((sg_uintptr_t)pointer % byte_count) == 0; } #if defined(__GNUC__) || defined(__clang__) static int pr2ws(const char * fmt, ...) __attribute__ ((format (printf, 1, 2))); #else static int pr2ws(const char * fmt, ...); #endif static int pr2ws(const char * fmt, ...) { va_list args; int n; va_start(args, fmt); n = vfprintf(sg_warnings_strm ? sg_warnings_strm : stderr, fmt, args); va_end(args); return n; } #if (HAVE_NVME && (! IGNORE_NVME)) /* This trims given NVMe block device name in Linux (e.g. /dev/nvme0n1p5) * to the name of its associated char device (e.g. /dev/nvme0). If this * occurs true is returned and the char device name is placed in 'b' (as * long as b_len is sufficient). Otherwise false is returned. */ bool sg_get_nvme_char_devname(const char * nvme_block_devname, uint32_t b_len, char * b) { uint32_t n, tlen; const char * cp; char buff[8]; if ((NULL == b) || (b_len < 5)) return false; /* degenerate cases */ cp = strstr(nvme_block_devname, "nvme"); if (NULL == cp) return false; /* expected to find "nvme" in given name */ if (1 != sscanf(cp, "nvme%u", &n)) return false; /* didn't find valid "nvme" */ snprintf(buff, sizeof(buff), "%u", n); tlen = (cp - nvme_block_devname) + 4 + strlen(buff); if ((tlen + 1) > b_len) return false; /* b isn't long enough to fit output */ memcpy(b, nvme_block_devname, tlen); b[tlen] = '\0'; return true; } static void build_sense_buffer(bool desc, uint8_t *buf, uint8_t skey, uint8_t asc, uint8_t ascq) { if (desc) { buf[0] = 0x72; /* descriptor, current */ buf[1] = skey; buf[2] = asc; buf[3] = ascq; buf[7] = 0; } else { buf[0] = 0x70; /* fixed, current */ buf[2] = skey; buf[7] = 0xa; /* Assumes length is 18 bytes */ buf[12] = asc; buf[13] = ascq; } } /* Set in_bit to -1 to indicate no bit position of invalid field */ static void mk_sense_asc_ascq(struct sg_pt_linux_scsi * ptp, int sk, int asc, int ascq, int vb) { bool dsense = ptp->scsi_dsense; int n; uint8_t * sbp = (uint8_t *)ptp->io_hdr.response; ptp->io_hdr.device_status = SAM_STAT_CHECK_CONDITION; n = ptp->io_hdr.max_response_len; if ((n < 8) || ((! dsense) && (n < 14))) { if (vb) pr2ws("%s: max_response_len=%d too short, want 14 or more\n", __func__, n); return; } else ptp->io_hdr.response_len = dsense ? 8 : ((n < 18) ? n : 18); memset(sbp, 0, n); build_sense_buffer(dsense, sbp, sk, asc, ascq); if (vb > 3) pr2ws("%s: [sense_key,asc,ascq]: [0x%x,0x%x,0x%x]\n", __func__, sk, asc, ascq); } static void mk_sense_from_nvme_status(struct sg_pt_linux_scsi * ptp, int vb) { bool ok; bool dsense = ptp->scsi_dsense; int n; uint8_t sstatus, sk, asc, ascq; uint8_t * sbp = (uint8_t *)ptp->io_hdr.response; ok = sg_nvme_status2scsi(ptp->nvme_status, &sstatus, &sk, &asc, &ascq); if (! ok) { /* can't find a mapping to a SCSI error, so ... */ sstatus = SAM_STAT_CHECK_CONDITION; sk = SPC_SK_ILLEGAL_REQUEST; asc = 0xb; ascq = 0x0; /* asc: "WARNING" purposely vague */ } ptp->io_hdr.device_status = sstatus; n = ptp->io_hdr.max_response_len; if ((n < 8) || ((! dsense) && (n < 14))) { pr2ws("%s: sense_len=%d too short, want 14 or more\n", __func__, n); return; } else ptp->io_hdr.response_len = (dsense ? 8 : ((n < 18) ? n : 18)); memset(sbp, 0, n); build_sense_buffer(dsense, sbp, sk, asc, ascq); if (vb > 3) pr2ws("%s: [status, sense_key,asc,ascq]: [0x%x, 0x%x,0x%x,0x%x]\n", __func__, sstatus, sk, asc, ascq); } /* Set in_bit to -1 to indicate no bit position of invalid field */ static void mk_sense_invalid_fld(struct sg_pt_linux_scsi * ptp, bool in_cdb, int in_byte, int in_bit, int vb) { bool dsense = ptp->scsi_dsense; int sl, asc, n; uint8_t * sbp = (uint8_t *)ptp->io_hdr.response; uint8_t sks[4]; ptp->io_hdr.device_status = SAM_STAT_CHECK_CONDITION; asc = in_cdb ? INVALID_FIELD_IN_CDB : INVALID_FIELD_IN_PARAM_LIST; n = ptp->io_hdr.max_response_len; if ((n < 8) || ((! dsense) && (n < 14))) { if (vb) pr2ws("%s: max_response_len=%d too short, want 14 or more\n", __func__, n); return; } else ptp->io_hdr.response_len = dsense ? 8 : ((n < 18) ? n : 18); memset(sbp, 0, n); build_sense_buffer(dsense, sbp, SPC_SK_ILLEGAL_REQUEST, asc, 0); memset(sks, 0, sizeof(sks)); sks[0] = 0x80; if (in_cdb) sks[0] |= 0x40; if (in_bit >= 0) { sks[0] |= 0x8; sks[0] |= (0x7 & in_bit); } sg_put_unaligned_be16(in_byte, sks + 1); if (dsense) { sl = sbp[7] + 8; sbp[7] = sl; sbp[sl] = 0x2; sbp[sl + 1] = 0x6; memcpy(sbp + sl + 4, sks, 3); } else memcpy(sbp + 15, sks, 3); if (vb > 3) pr2ws("%s: [sense_key,asc,ascq]: [0x5,0x%x,0x0] %c byte=%d, bit=%d\n", __func__, asc, in_cdb ? 'C' : 'D', in_byte, in_bit); } /* Returns 0 for success. Returns SG_LIB_NVME_STATUS if there is non-zero * NVMe status (from the completion queue) with the value placed in * ptp->nvme_status. If Unix error from ioctl then return negated value * (equivalent -errno from basic Unix system functions like open()). * CDW0 from the completion queue is placed in ptp->nvme_result in the * absence of a Unix error. If time_secs is negative it is treated as * a timeout in milliseconds (of abs(time_secs) ). */ static int do_nvme_admin_cmd(struct sg_pt_linux_scsi * ptp, struct sg_nvme_passthru_cmd *cmdp, void * dp, bool is_read, int time_secs, int vb) { const uint32_t cmd_len = sizeof(struct sg_nvme_passthru_cmd); int res; uint32_t n; uint16_t sct_sc; const uint8_t * up = ((const uint8_t *)cmdp) + SG_NVME_PT_OPCODE; cmdp->timeout_ms = (time_secs < 0) ? (-time_secs) : (1000 * time_secs); ptp->os_err = 0; if (vb > 2) { pr2ws("NVMe command:\n"); hex2stderr((const uint8_t *)cmdp, cmd_len, 1); if ((vb > 3) && (! is_read) && dp) { uint32_t len = sg_get_unaligned_le32(up + SG_NVME_PT_DATA_LEN); if (len > 0) { n = len; if ((len < 512) || (vb > 5)) pr2ws("\nData-out buffer (%u bytes):\n", n); else { pr2ws("\nData-out buffer (first 512 of %u bytes):\n", n); n = 512; } hex2stderr((const uint8_t *)dp, n, 0); } } } res = ioctl(ptp->dev_fd, NVME_IOCTL_ADMIN_CMD, cmdp); if (res < 0) { /* OS error (errno negated) */ ptp->os_err = -res; if (vb > 1) { pr2ws("%s: ioctl opcode=0x%x failed: %s " "(errno=%d)\n", __func__, *up, strerror(-res), -res); } return res; } /* Now res contains NVMe completion queue CDW3 31:17 (15 bits) */ ptp->nvme_result = cmdp->result; if (ptp->nvme_direct && ptp->io_hdr.response && (ptp->io_hdr.max_response_len > 3)) { /* build 16 byte "sense" buffer */ uint8_t * sbp = (uint8_t *)ptp->io_hdr.response; uint16_t st = (uint16_t)res; n = ptp->io_hdr.max_response_len; n = (n < 16) ? n : 16; memset(sbp, 0 , n); ptp->io_hdr.response_len = n; sg_put_unaligned_le32(cmdp->result, sbp + SG_NVME_PT_CQ_RESULT); if (n > 15) /* LSBit will be 0 (Phase bit) after (st << 1) */ sg_put_unaligned_le16(st << 1, sbp + SG_NVME_PT_CQ_STATUS_P); } /* clear upper bits (DNR and More) leaving ((SCT << 8) | SC) */ sct_sc = 0x3ff & res; ptp->nvme_status = sct_sc; if (sct_sc) { /* when non-zero, treat as command error */ if (vb > 1) { char b[80]; pr2ws("%s: ioctl opcode=0x%x failed: NVMe status: %s [0x%x]\n", __func__, *up, sg_get_nvme_cmd_status_str(sct_sc, sizeof(b), b), sct_sc); } return SG_LIB_NVME_STATUS; /* == SCSI_PT_DO_NVME_STATUS */ } if ((vb > 3) && is_read && dp) { uint32_t len = sg_get_unaligned_le32(up + SG_NVME_PT_DATA_LEN); if (len > 0) { n = len; if ((len < 1024) || (vb > 5)) pr2ws("\nData-in buffer (%u bytes):\n", n); else { pr2ws("\nData-in buffer (first 1024 of %u bytes):\n", n); n = 1024; } hex2stderr((const uint8_t *)dp, n, 0); } } return 0; } /* Returns 0 on success; otherwise a positive value is returned */ static int sntl_cache_identity(struct sg_pt_linux_scsi * ptp, int time_secs, int vb) { struct sg_nvme_passthru_cmd cmd; uint32_t pg_sz = sg_get_page_size(); uint8_t * up; up = sg_memalign(pg_sz, pg_sz, &ptp->free_nvme_id_ctlp, vb > 3); ptp->nvme_id_ctlp = up; if (NULL == up) { pr2ws("%s: sg_memalign() failed to get memory\n", __func__); return -ENOMEM; } memset(&cmd, 0, sizeof(cmd)); cmd.opcode = 0x6; /* Identify */ cmd.cdw10 = 0x1; /* CNS=0x1 Identify controller */ cmd.addr = (uint64_t)(sg_uintptr_t)ptp->nvme_id_ctlp; cmd.data_len = pg_sz; return do_nvme_admin_cmd(ptp, &cmd, up, true, time_secs, vb); } static const char * nvme_scsi_vendor_str = "NVMe "; static const uint16_t inq_resp_len = 36; static int sntl_inq(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp, int time_secs, int vb) { bool evpd; bool cp_id_ctl = false; int res; uint16_t n, alloc_len, pg_cd; uint32_t pg_sz = sg_get_page_size(); uint8_t * nvme_id_ns = NULL; uint8_t * free_nvme_id_ns = NULL; uint8_t inq_dout[256]; if (vb > 3) pr2ws("%s: time_secs=%d\n", __func__, time_secs); if (0x2 & cdbp[1]) { /* Reject CmdDt=1 */ mk_sense_invalid_fld(ptp, true, 1, 1, vb); return 0; } if (NULL == ptp->nvme_id_ctlp) { res = sntl_cache_identity(ptp, time_secs, vb); if (SG_LIB_NVME_STATUS == res) { mk_sense_from_nvme_status(ptp, vb); return 0; } else if (res) /* should be negative errno */ return res; } memset(inq_dout, 0, sizeof(inq_dout)); alloc_len = sg_get_unaligned_be16(cdbp + 3); evpd = !!(0x1 & cdbp[1]); pg_cd = cdbp[2]; if (evpd) { /* VPD page responses */ switch (pg_cd) { case 0: /* inq_dout[0] = (PQ=0)<<5 | (PDT=0); prefer pdt=0xd --> SES */ inq_dout[1] = pg_cd; n = 8; sg_put_unaligned_be16(n - 4, inq_dout + 2); inq_dout[4] = 0x0; inq_dout[5] = 0x80; inq_dout[6] = 0x83; inq_dout[n - 1] = 0xde; /* last VPD number */ break; case 0x80: /* inq_dout[0] = (PQ=0)<<5 | (PDT=0); prefer pdt=0xd --> SES */ inq_dout[1] = pg_cd; sg_put_unaligned_be16(20, inq_dout + 2); memcpy(inq_dout + 4, ptp->nvme_id_ctlp + 4, 20); /* SN */ n = 24; break; case 0x83: if ((ptp->nvme_nsid > 0) && (ptp->nvme_nsid < SG_NVME_BROADCAST_NSID)) { nvme_id_ns = sg_memalign(pg_sz, pg_sz, &free_nvme_id_ns, vb > 3); if (nvme_id_ns) { struct sg_nvme_passthru_cmd cmd; memset(&cmd, 0, sizeof(cmd)); cmd.opcode = 0x6; /* Identify */ cmd.nsid = ptp->nvme_nsid; cmd.cdw10 = 0x0; /* CNS=0x0 Identify namespace */ cmd.addr = (uint64_t)(sg_uintptr_t)nvme_id_ns; cmd.data_len = pg_sz; res = do_nvme_admin_cmd(ptp, &cmd, nvme_id_ns, true, time_secs, vb > 3); if (res) { free(free_nvme_id_ns); free_nvme_id_ns = NULL; nvme_id_ns = NULL; } } } n = sg_make_vpd_devid_for_nvme(ptp->nvme_id_ctlp, nvme_id_ns, 0 /* pdt */, -1 /*tproto */, inq_dout, sizeof(inq_dout)); if (n > 3) sg_put_unaligned_be16(n - 4, inq_dout + 2); if (free_nvme_id_ns) { free(free_nvme_id_ns); free_nvme_id_ns = NULL; nvme_id_ns = NULL; } break; case 0xde: inq_dout[1] = pg_cd; sg_put_unaligned_be16((16 + 4096) - 4, inq_dout + 2); n = 16 + 4096; cp_id_ctl = true; break; default: /* Point to page_code field in cdb */ mk_sense_invalid_fld(ptp, true, 2, 7, vb); return 0; } if (alloc_len > 0) { n = (alloc_len < n) ? alloc_len : n; n = (n < ptp->io_hdr.din_xfer_len) ? n : ptp->io_hdr.din_xfer_len; ptp->io_hdr.din_resid = ptp->io_hdr.din_xfer_len - n; if (n > 0) { if (cp_id_ctl) { memcpy((uint8_t *)ptp->io_hdr.din_xferp, inq_dout, (n < 16 ? n : 16)); if (n > 16) memcpy((uint8_t *)ptp->io_hdr.din_xferp + 16, ptp->nvme_id_ctlp, n - 16); } else memcpy((uint8_t *)ptp->io_hdr.din_xferp, inq_dout, n); } } } else { /* Standard INQUIRY response */ /* inq_dout[0] = (PQ=0)<<5 | (PDT=0); pdt=0 --> SBC; 0xd --> SES */ inq_dout[2] = 6; /* version: SPC-4 */ inq_dout[3] = 2; /* NORMACA=0, HISUP=0, response data format: 2 */ inq_dout[4] = 31; /* so response length is (or could be) 36 bytes */ inq_dout[6] = 0x40; /* ENCSERV=1 */ inq_dout[7] = 0x2; /* CMDQUE=1 */ memcpy(inq_dout + 8, nvme_scsi_vendor_str, 8); /* NVMe not Intel */ memcpy(inq_dout + 16, ptp->nvme_id_ctlp + 24, 16); /* Prod <-- MN */ memcpy(inq_dout + 32, ptp->nvme_id_ctlp + 64, 4); /* Rev <-- FR */ if (alloc_len > 0) { n = (alloc_len < inq_resp_len) ? alloc_len : inq_resp_len; n = (n < ptp->io_hdr.din_xfer_len) ? n : ptp->io_hdr.din_xfer_len; ptp->io_hdr.din_resid = ptp->io_hdr.din_xfer_len - n; if (n > 0) memcpy((uint8_t *)ptp->io_hdr.din_xferp, inq_dout, n); } } return 0; } static int sntl_rluns(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp, int time_secs, int vb) { int res; uint16_t sel_report; uint32_t alloc_len, k, n, num, max_nsid; uint8_t * rl_doutp; uint8_t * up; if (vb > 3) pr2ws("%s: time_secs=%d\n", __func__, time_secs); sel_report = cdbp[2]; alloc_len = sg_get_unaligned_be32(cdbp + 6); if (NULL == ptp->nvme_id_ctlp) { res = sntl_cache_identity(ptp, time_secs, vb); if (SG_LIB_NVME_STATUS == res) { mk_sense_from_nvme_status(ptp, vb); return 0; } else if (res) return res; } max_nsid = sg_get_unaligned_le32(ptp->nvme_id_ctlp + 516); switch (sel_report) { case 0: case 2: num = max_nsid; break; case 1: case 0x10: case 0x12: num = 0; break; case 0x11: num = (1 == ptp->nvme_nsid) ? max_nsid : 0; break; default: if (vb > 1) pr2ws("%s: bad select_report value: 0x%x\n", __func__, sel_report); mk_sense_invalid_fld(ptp, true, 2, 7, vb); return 0; } rl_doutp = (uint8_t *)calloc(num + 1, 8); if (NULL == rl_doutp) { pr2ws("%s: calloc() failed to get memory\n", __func__); return -ENOMEM; } for (k = 0, up = rl_doutp + 8; k < num; ++k, up += 8) sg_put_unaligned_be16(k, up); n = num * 8; sg_put_unaligned_be32(n, rl_doutp); n+= 8; if (alloc_len > 0) { n = (alloc_len < n) ? alloc_len : n; n = (n < ptp->io_hdr.din_xfer_len) ? n : ptp->io_hdr.din_xfer_len; ptp->io_hdr.din_resid = ptp->io_hdr.din_xfer_len - n; if (n > 0) memcpy((uint8_t *)ptp->io_hdr.din_xferp, rl_doutp, n); } res = 0; free(rl_doutp); return res; } static int sntl_tur(struct sg_pt_linux_scsi * ptp, int time_secs, int vb) { int res; uint32_t pow_state; struct sg_nvme_passthru_cmd cmd; if (vb > 4) pr2ws("%s: time_secs=%d\n", __func__, time_secs); if (NULL == ptp->nvme_id_ctlp) { res = sntl_cache_identity(ptp, time_secs, vb); if (SG_LIB_NVME_STATUS == res) { mk_sense_from_nvme_status(ptp, vb); return 0; } else if (res) return res; } memset(&cmd, 0, sizeof(cmd)); cmd.opcode = 0xa; /* Get feature */ cmd.nsid = SG_NVME_BROADCAST_NSID; cmd.cdw10 = 0x2; /* SEL=0 (current), Feature=2 Power Management */ cmd.timeout_ms = (time_secs < 0) ? 0 : (1000 * time_secs); res = do_nvme_admin_cmd(ptp, &cmd, NULL, false, time_secs, vb); if (0 != res) { if (SG_LIB_NVME_STATUS == res) { mk_sense_from_nvme_status(ptp, vb); return 0; } else return res; } else { ptp->os_err = 0; ptp->nvme_status = 0; } pow_state = (0x1f & ptp->nvme_result); if (vb > 3) pr2ws("%s: pow_state=%u\n", __func__, pow_state); #if 0 /* pow_state bounces around too much on laptop */ if (pow_state) mk_sense_asc_ascq(ptp, SPC_SK_NOT_READY, LOW_POWER_COND_ON_ASC, 0, vb); #endif return 0; } static int sntl_req_sense(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp, int time_secs, int vb) { bool desc; int res; uint32_t pow_state, alloc_len, n; struct sg_nvme_passthru_cmd cmd; uint8_t rs_dout[64]; if (vb > 3) pr2ws("%s: time_secs=%d\n", __func__, time_secs); if (NULL == ptp->nvme_id_ctlp) { res = sntl_cache_identity(ptp, time_secs, vb); if (SG_LIB_NVME_STATUS == res) { mk_sense_from_nvme_status(ptp, vb); return 0; } else if (res) return res; } desc = !!(0x1 & cdbp[1]); alloc_len = cdbp[4]; memset(&cmd, 0, sizeof(cmd)); cmd.opcode = 0xa; /* Get feature */ cmd.nsid = SG_NVME_BROADCAST_NSID; cmd.cdw10 = 0x2; /* SEL=0 (current), Feature=2 Power Management */ cmd.timeout_ms = (time_secs < 0) ? 0 : (1000 * time_secs); res = do_nvme_admin_cmd(ptp, &cmd, NULL, false, time_secs, vb); if (0 != res) { if (SG_LIB_NVME_STATUS == res) { mk_sense_from_nvme_status(ptp, vb); return 0; } else return res; } else { ptp->os_err = 0; ptp->nvme_status = 0; } ptp->io_hdr.response_len = 0; pow_state = (0x1f & ptp->nvme_result); if (vb > 3) pr2ws("%s: pow_state=%u\n", __func__, pow_state); memset(rs_dout, 0, sizeof(rs_dout)); if (pow_state) build_sense_buffer(desc, rs_dout, SPC_SK_NO_SENSE, LOW_POWER_COND_ON_ASC, 0); else build_sense_buffer(desc, rs_dout, SPC_SK_NO_SENSE, NO_ADDITIONAL_SENSE, 0); n = desc ? 8 : 18; n = (n < alloc_len) ? n : alloc_len; n = (n < ptp->io_hdr.din_xfer_len) ? n : ptp->io_hdr.din_xfer_len; ptp->io_hdr.din_resid = ptp->io_hdr.din_xfer_len - n; if (n > 0) memcpy((uint8_t *)ptp->io_hdr.din_xferp, rs_dout, n); return 0; } /* This is not really a SNTL. For SCSI SEND DIAGNOSTIC(PF=1) NVMe-MI * has a special command (SES Send) to tunnel through pages to an * enclosure. The NVMe enclosure is meant to understand the SES * (SCSI Enclosure Services) use of diagnostics pages that are * related to SES. */ static int sntl_senddiag(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp, int time_secs, int vb) { bool pf, self_test; int res; uint8_t st_cd, dpg_cd; uint32_t alloc_len, n, dout_len, dpg_len, nvme_dst; uint32_t pg_sz = sg_get_page_size(); uint8_t * dop; struct sg_nvme_passthru_cmd cmd; uint8_t * cmd_up = (uint8_t *)&cmd; st_cd = 0x7 & (cdbp[1] >> 5); self_test = !! (0x4 & cdbp[1]); pf = !! (0x10 & cdbp[1]); if (vb > 3) pr2ws("%s: pf=%d, self_test=%d (st_code=%d)\n", __func__, (int)pf, (int)self_test, (int)st_cd); if (self_test || st_cd) { memset(cmd_up, 0, sizeof(cmd)); cmd_up[SG_NVME_PT_OPCODE] = 0x14; /* Device self-test */ /* just this namespace (if there is one) and controller */ sg_put_unaligned_le32(ptp->nvme_nsid, cmd_up + SG_NVME_PT_NSID); switch (st_cd) { case 0: /* Here if self_test is set, do short self-test */ case 1: /* Background short */ case 5: /* Foreground short */ nvme_dst = 1; break; case 2: /* Background extended */ case 6: /* Foreground extended */ nvme_dst = 2; break; case 4: /* Abort self-test */ nvme_dst = 0xf; break; default: pr2ws("%s: bad self-test code [0x%x]\n", __func__, st_cd); mk_sense_invalid_fld(ptp, true, 1, 7, vb); return 0; } sg_put_unaligned_le32(nvme_dst, cmd_up + SG_NVME_PT_CDW10); res = do_nvme_admin_cmd(ptp, &cmd, NULL, false, time_secs, vb); if (0 != res) { if (SG_LIB_NVME_STATUS == res) { mk_sense_from_nvme_status(ptp, vb); return 0; } else return res; } } alloc_len = sg_get_unaligned_be16(cdbp + 3); /* parameter list length */ dout_len = ptp->io_hdr.dout_xfer_len; if (pf) { if (0 == alloc_len) { mk_sense_invalid_fld(ptp, true, 3, 7, vb); if (vb) pr2ws("%s: PF bit set bit param_list_len=0\n", __func__); return 0; } } else { /* PF bit clear */ if (alloc_len) { mk_sense_invalid_fld(ptp, true, 3, 7, vb); if (vb) pr2ws("%s: param_list_len>0 but PF clear\n", __func__); return 0; } else return 0; /* nothing to do */ if (dout_len > 0) { if (vb) pr2ws("%s: dout given but PF clear\n", __func__); return SCSI_PT_DO_BAD_PARAMS; } } if (dout_len < 4) { if (vb) pr2ws("%s: dout length (%u bytes) too short\n", __func__, dout_len); return SCSI_PT_DO_BAD_PARAMS; } n = dout_len; n = (n < alloc_len) ? n : alloc_len; dop = (uint8_t *)ptp->io_hdr.dout_xferp; if (! is_aligned(dop, pg_sz)) { /* caller best use sg_memalign(,pg_sz) */ if (vb) pr2ws("%s: dout [0x%" PRIx64 "] not page aligned\n", __func__, (uint64_t)ptp->io_hdr.dout_xferp); return SCSI_PT_DO_BAD_PARAMS; } dpg_cd = dop[0]; dpg_len = sg_get_unaligned_be16(dop + 2) + 4; /* should we allow for more than one D_PG is dout ?? */ n = (n < dpg_len) ? n : dpg_len; /* not yet ... */ if (vb) pr2ws("%s: passing through d_pg=0x%x, len=%u to NVME_MI SES send\n", __func__, dpg_cd, dpg_len); memset(&cmd, 0, sizeof(cmd)); cmd.opcode = 0x1d; /* MI send; hmmm same opcode as SEND DIAG */ cmd.addr = (uint64_t)(sg_uintptr_t)dop; cmd.data_len = 0x1000; /* NVMe 4k page size. Maybe determine this? */ /* dout_len > 0x1000, is this a problem?? */ cmd.cdw10 = 0x0804; /* NVMe Message Header */ cmd.cdw11 = 0x9; /* nvme_mi_ses_send; (0x8 -> mi_ses_recv) */ cmd.cdw13 = n; res = do_nvme_admin_cmd(ptp, &cmd, dop, false, time_secs, vb); if (0 != res) { if (SG_LIB_NVME_STATUS == res) { mk_sense_from_nvme_status(ptp, vb); return 0; } } return res; } /* This is not really a SNTL. For SCSI RECEIVE DIAGNOSTIC RESULTS(PCV=1) * NVMe-MI has a special command (SES Receive) to read pages through a * tunnel from an enclosure. The NVMe enclosure is meant to understand the * SES (SCSI Enclosure Services) use of diagnostics pages that are * related to SES. */ static int sntl_recvdiag(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp, int time_secs, int vb) { bool pcv; int res; uint8_t dpg_cd; uint32_t alloc_len, n, din_len; uint32_t pg_sz = sg_get_page_size(); uint8_t * dip; struct sg_nvme_passthru_cmd cmd; pcv = !! (0x1 & cdbp[1]); dpg_cd = cdbp[2]; alloc_len = sg_get_unaligned_be16(cdbp + 3); /* parameter list length */ if (vb > 3) pr2ws("%s: dpg_cd=0x%x, pcv=%d, alloc_len=0x%x\n", __func__, dpg_cd, (int)pcv, alloc_len); din_len = ptp->io_hdr.din_xfer_len; n = din_len; n = (n < alloc_len) ? n : alloc_len; dip = (uint8_t *)ptp->io_hdr.din_xferp; if (! is_aligned(dip, pg_sz)) { /* caller best use sg_memalign(,pg_sz) */ if (vb) pr2ws("%s: din [0x%" PRIx64 "] not page aligned\n", __func__, (uint64_t)ptp->io_hdr.din_xferp); return SCSI_PT_DO_BAD_PARAMS; } if (vb) pr2ws("%s: expecting d_pg=0x%x from NVME_MI SES receive\n", __func__, dpg_cd); memset(&cmd, 0, sizeof(cmd)); cmd.opcode = 0x1e; /* MI receive */ cmd.addr = (uint64_t)(sg_uintptr_t)dip; cmd.data_len = 0x1000; /* NVMe 4k page size. Maybe determine this? */ /* din_len > 0x1000, is this a problem?? */ cmd.cdw10 = 0x0804; /* NVMe Message Header */ cmd.cdw11 = 0x8; /* nvme_mi_ses_receive */ cmd.cdw12 = dpg_cd; cmd.cdw13 = n; res = do_nvme_admin_cmd(ptp, &cmd, dip, true, time_secs, vb); if (0 != res) { if (SG_LIB_NVME_STATUS == res) { mk_sense_from_nvme_status(ptp, vb); return 0; } else return res; } ptp->io_hdr.din_resid = din_len - n; return res; } #define F_SA_LOW 0x80 /* cdb byte 1, bits 4 to 0 */ #define F_SA_HIGH 0x100 /* as used by variable length cdbs */ #define FF_SA (F_SA_HIGH | F_SA_LOW) #define F_INV_OP 0x200 static struct opcode_info_t { uint8_t opcode; uint16_t sa; /* service action, 0 for none */ uint32_t flags; /* OR-ed set of F_* flags */ uint8_t len_mask[16]; /* len=len_mask[0], then mask for cdb[1]... */ /* ignore cdb bytes after position 15 */ } opcode_info_arr[] = { {0x0, 0, 0, {6, /* TEST UNIT READY */ 0, 0, 0, 0, 0xc7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }, {0x3, 0, 0, {6, /* REQUEST SENSE */ 0xe1, 0, 0, 0xff, 0xc7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }, {0x12, 0, 0, {6, /* INQUIRY */ 0xe3, 0xff, 0xff, 0xff, 0xc7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }, {0x1c, 0, 0, {6, /* RECEIVE DIAGNOSTIC RESULTS */ 0x1, 0xff, 0xff, 0xff, 0xc7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }, {0x1d, 0, 0, {6, /* SEND DIAGNOSTIC */ 0xf7, 0x0, 0xff, 0xff, 0xc7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }, {0xa0, 0, 0, {12, /* REPORT LUNS */ 0xe3, 0xff, 0, 0, 0, 0xff, 0xff, 0xff, 0xff, 0, 0xc7, 0, 0, 0, 0} }, {0xa3, 0xc, F_SA_LOW, {12, /* REPORT SUPPORTED OPERATION CODES */ 0xc, 0x87, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0, 0xc7, 0, 0, 0, 0} }, {0xa3, 0xd, F_SA_LOW, {12, /* REPORT SUPPORTED TASK MAN. FUNCTIONS */ 0xd, 0x80, 0, 0, 0, 0xff, 0xff, 0xff, 0xff, 0, 0xc7, 0, 0, 0, 0} }, {0xff, 0xffff, 0xffff, {0, /* Sentinel, keep as last element */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }, }; static int sntl_rep_opcodes(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp, int time_secs, int vb) { bool rctd; uint8_t reporting_opts, req_opcode, supp; uint16_t req_sa, u; uint32_t alloc_len, offset, a_len; uint32_t pg_sz = sg_get_page_size(); int k, len, count, bump; const struct opcode_info_t *oip; uint8_t *arr; uint8_t *free_arr; if (vb > 3) pr2ws("%s: time_secs=%d\n", __func__, time_secs); rctd = !!(cdbp[2] & 0x80); /* report command timeout desc. */ reporting_opts = cdbp[2] & 0x7; req_opcode = cdbp[3]; req_sa = sg_get_unaligned_be16(cdbp + 4); alloc_len = sg_get_unaligned_be32(cdbp + 6); if (alloc_len < 4 || alloc_len > 0xffff) { mk_sense_invalid_fld(ptp, true, 6, -1, vb); return 0; } a_len = pg_sz - 72; arr = sg_memalign(pg_sz, pg_sz, &free_arr, vb > 3); if (NULL == arr) { pr2ws("%s: calloc() failed to get memory\n", __func__); return -ENOMEM; } switch (reporting_opts) { case 0: /* all commands */ count = 0; bump = rctd ? 20 : 8; for (offset = 4, oip = opcode_info_arr; (oip->flags != 0xffff) && (offset < a_len); ++oip) { if (F_INV_OP & oip->flags) continue; ++count; arr[offset] = oip->opcode; sg_put_unaligned_be16(oip->sa, arr + offset + 2); if (rctd) arr[offset + 5] |= 0x2; if (FF_SA & oip->flags) arr[offset + 5] |= 0x1; sg_put_unaligned_be16(oip->len_mask[0], arr + offset + 6); if (rctd) sg_put_unaligned_be16(0xa, arr + offset + 8); offset += bump; } sg_put_unaligned_be32(count * bump, arr + 0); break; case 1: /* one command: opcode only */ case 2: /* one command: opcode plus service action */ case 3: /* one command: if sa==0 then opcode only else opcode+sa */ for (oip = opcode_info_arr; oip->flags != 0xffff; ++oip) { if ((req_opcode == oip->opcode) && (req_sa == oip->sa)) break; } if ((0xffff == oip->flags) || (F_INV_OP & oip->flags)) { supp = 1; offset = 4; } else { if (1 == reporting_opts) { if (FF_SA & oip->flags) { mk_sense_invalid_fld(ptp, true, 2, 2, vb); free(free_arr); return 0; } req_sa = 0; } else if ((2 == reporting_opts) && 0 == (FF_SA & oip->flags)) { mk_sense_invalid_fld(ptp, true, 4, -1, vb); free(free_arr); return 0; } if ((0 == (FF_SA & oip->flags)) && (req_opcode == oip->opcode)) supp = 3; else if (0 == (FF_SA & oip->flags)) supp = 1; else if (req_sa != oip->sa) supp = 1; else supp = 3; if (3 == supp) { u = oip->len_mask[0]; sg_put_unaligned_be16(u, arr + 2); arr[4] = oip->opcode; for (k = 1; k < u; ++k) arr[4 + k] = (k < 16) ? oip->len_mask[k] : 0xff; offset = 4 + u; } else offset = 4; } arr[1] = (rctd ? 0x80 : 0) | supp; if (rctd) { sg_put_unaligned_be16(0xa, arr + offset); offset += 12; } break; default: mk_sense_invalid_fld(ptp, true, 2, 2, vb); free(free_arr); return 0; } offset = (offset < a_len) ? offset : a_len; len = (offset < alloc_len) ? offset : alloc_len; ptp->io_hdr.din_resid = ptp->io_hdr.din_xfer_len - len; if (len > 0) memcpy((uint8_t *)ptp->io_hdr.din_xferp, arr, len); free(free_arr); return 0; } static int sntl_rep_tmfs(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp, int time_secs, int vb) { bool repd; uint32_t alloc_len, len; uint8_t arr[16]; if (vb > 3) pr2ws("%s: time_secs=%d\n", __func__, time_secs); memset(arr, 0, sizeof(arr)); repd = !!(cdbp[2] & 0x80); alloc_len = sg_get_unaligned_be32(cdbp + 6); if (alloc_len < 4) { mk_sense_invalid_fld(ptp, true, 6, -1, vb); return 0; } arr[0] = 0xc8; /* ATS | ATSS | LURS */ arr[1] = 0x1; /* ITNRS */ if (repd) { arr[3] = 0xc; len = 16; } else len = 4; len = (len < alloc_len) ? len : alloc_len; ptp->io_hdr.din_resid = ptp->io_hdr.din_xfer_len - len; if (len > 0) memcpy((uint8_t *)ptp->io_hdr.din_xferp, arr, len); return 0; } /* Executes NVMe Admin command (or at least forwards it to lower layers). * Returns 0 for success, negative numbers are negated 'errno' values from * OS system calls. Positive return values are errors from this package. * When time_secs is 0 the Linux NVMe Admin command default of 60 seconds * is used. */ int sg_do_nvme_pt(struct sg_pt_base * vp, int fd, int time_secs, int vb) { bool scsi_cdb; bool is_read = false; int n, len; uint16_t sa; struct sg_pt_linux_scsi * ptp = &vp->impl; struct sg_nvme_passthru_cmd cmd; const uint8_t * cdbp; void * dp = NULL; if (! ptp->io_hdr.request) { if (vb) pr2ws("No NVMe command given (set_scsi_pt_cdb())\n"); return SCSI_PT_DO_BAD_PARAMS; } if (fd >= 0) { if ((ptp->dev_fd >= 0) && (fd != ptp->dev_fd)) { if (vb) pr2ws("%s: file descriptor given to create() and here " "differ\n", __func__); return SCSI_PT_DO_BAD_PARAMS; } ptp->dev_fd = fd; } else if (ptp->dev_fd < 0) { if (vb) pr2ws("%s: invalid file descriptors\n", __func__); return SCSI_PT_DO_BAD_PARAMS; } n = ptp->io_hdr.request_len; cdbp = (const uint8_t *)ptp->io_hdr.request; if (vb > 3) pr2ws("%s: opcode=0x%x, fd=%d, time_secs=%d\n", __func__, cdbp[0], fd, time_secs); scsi_cdb = sg_is_scsi_cdb(cdbp, n); /* direct NVMe command (i.e. 64 bytes long) or SNTL */ ptp->nvme_direct = ! scsi_cdb; if (scsi_cdb) { switch (cdbp[0]) { case SCSI_INQUIRY_OPC: return sntl_inq(ptp, cdbp, time_secs, vb); case SCSI_REPORT_LUNS_OPC: return sntl_rluns(ptp, cdbp, time_secs, vb); case SCSI_TEST_UNIT_READY_OPC: return sntl_tur(ptp, time_secs, vb); case SCSI_REQUEST_SENSE_OPC: return sntl_req_sense(ptp, cdbp, time_secs, vb); case SCSI_SEND_DIAGNOSTIC_OPC: return sntl_senddiag(ptp, cdbp, time_secs, vb); case SCSI_RECEIVE_DIAGNOSTIC_OPC: return sntl_recvdiag(ptp, cdbp, time_secs, vb); case SCSI_MAINT_IN_OPC: sa = 0x1f & cdbp[1]; /* service action */ if (SCSI_REP_SUP_OPCS_OPC == sa) return sntl_rep_opcodes(ptp, cdbp, time_secs, vb); else if (SCSI_REP_SUP_TMFS_OPC == sa) return sntl_rep_tmfs(ptp, cdbp, time_secs, vb); /* fall through */ default: if (vb > 2) { char b[64]; sg_get_command_name(cdbp, -1, sizeof(b), b); pr2ws("%s: no translation to NVMe for SCSI %s command\n", __func__, b); } mk_sense_asc_ascq(ptp, SPC_SK_ILLEGAL_REQUEST, INVALID_OPCODE, 0, vb); return 0; } } len = (int)sizeof(cmd); n = (n < len) ? n : len; if (n < 64) { if (vb) pr2ws("%s: command length of %d bytes is too short\n", __func__, n); return SCSI_PT_DO_BAD_PARAMS; } memcpy(&cmd, (const uint8_t *)ptp->io_hdr.request, n); if (n < len) /* zero out rest of 'cmd' */ memset((unsigned char *)&cmd + n, 0, len - n); if (ptp->io_hdr.din_xfer_len > 0) { cmd.data_len = ptp->io_hdr.din_xfer_len; dp = (void *)ptp->io_hdr.din_xferp; cmd.addr = (uint64_t)(sg_uintptr_t)ptp->io_hdr.din_xferp; is_read = true; } else if (ptp->io_hdr.dout_xfer_len > 0) { cmd.data_len = ptp->io_hdr.dout_xfer_len; dp = (void *)ptp->io_hdr.dout_xferp; cmd.addr = (uint64_t)(sg_uintptr_t)ptp->io_hdr.dout_xferp; is_read = false; } return do_nvme_admin_cmd(ptp, &cmd, dp, is_read, time_secs, vb); } #else /* (HAVE_NVME && (! IGNORE_NVME)) */ int sg_do_nvme_pt(struct sg_pt_base * vp, int fd, int time_secs, int vb) { if (vb) pr2ws("%s: not supported\n", __func__); if (vp) { ; } /* suppress warning */ if (fd) { ; } /* suppress warning */ if (time_secs) { ; } /* suppress warning */ return -ENOTTY; /* inappropriate ioctl error */ } #endif /* (HAVE_NVME && (! IGNORE_NVME)) */ f2fs-tools-1.11.0/tools/sg_write_buffer/sg_write_buffer.c000066400000000000000000000427741332120552100234340ustar00rootroot00000000000000/* * Copyright (c) 2006-2018 Luben Tuikov and Douglas Gilbert. * All rights reserved. * Use of this source code is governed by a BSD-style * license that can be found in the BSD_LICENSE file. */ #include #include #include #include #include #include #include #include #include #define __STDC_FORMAT_MACROS 1 #include #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "sg_lib.h" #include "sg_cmds_basic.h" #include "sg_cmds_extra.h" #include "sg_unaligned.h" #include "sg_pr2serr.h" #ifdef SG_LIB_WIN32 #ifdef SG_LIB_WIN32_DIRECT #include "sg_pt.h" /* needed for scsi_pt_win32_direct() */ #endif #endif /* * This utility issues the SCSI WRITE BUFFER command to the given device. */ static const char * version_str = "1.24 20180111"; /* spc5r18 */ #define ME "sg_write_buffer: " #define DEF_XFER_LEN (8 * 1024 * 1024) #define EBUFF_SZ 256 #define WRITE_BUFFER_CMD 0x3b #define WRITE_BUFFER_CMDLEN 10 #define SENSE_BUFF_LEN 64 /* Arbitrary, could be larger */ #define DEF_PT_TIMEOUT 300 /* 300 seconds, 5 minutes */ static struct option long_options[] = { {"bpw", required_argument, 0, 'b'}, {"dry-run", no_argument, 0, 'd'}, {"dry_run", no_argument, 0, 'd'}, {"help", no_argument, 0, 'h'}, {"id", required_argument, 0, 'i'}, {"in", required_argument, 0, 'I'}, {"length", required_argument, 0, 'l'}, {"mode", required_argument, 0, 'm'}, {"offset", required_argument, 0, 'o'}, {"read-stdin", no_argument, 0, 'r'}, {"read_stdin", no_argument, 0, 'r'}, {"raw", no_argument, 0, 'r'}, {"skip", required_argument, 0, 's'}, {"specific", required_argument, 0, 'S'}, {"timeout", required_argument, 0, 't' }, {"verbose", no_argument, 0, 'v'}, {"version", no_argument, 0, 'V'}, {0, 0, 0, 0}, }; static void usage() { pr2serr("Usage: " "sg_write_buffer [--bpw=CS] [--dry-run] [--help] [--id=ID] " "[--in=FILE]\n" " [--length=LEN] [--mode=MO] " "[--offset=OFF]\n" " [--read-stdin] [--skip=SKIP] " "[--specific=MS]\n" " [--timeout=TO] [--verbose] [--version] " "DEVICE\n" " where:\n" " --bpw=CS|-b CS CS is chunk size: bytes per write " "buffer\n" " command (def: 0 -> as many as " "possible)\n" " --dry-run|-d skip WRITE BUFFER commands, do " "everything else\n" " --help|-h print out usage message then exit\n" " --id=ID|-i ID buffer identifier (0 (default) to " "255)\n" " --in=FILE|-I FILE read from FILE ('-I -' read " "from stdin)\n" " --length=LEN|-l LEN length in bytes to write; may be " "deduced from\n" " FILE\n" " --mode=MO|-m MO write buffer mode, MO is number or " "acronym\n" " (def: 0 -> 'combined header and " "data' (obs))\n" " --offset=OFF|-o OFF buffer offset (unit: bytes, def: 0)\n" " --read-stdin|-r read from stdin (same as '-I -')\n" " --skip=SKIP|-s SKIP bytes in file FILE to skip before " "reading\n" " --specific=MS|-S MS mode specific value; 3 bit field " "(0 to 7)\n" " --timeout=TO|-t TO command timeout in seconds (def: " "300)\n" " --verbose|-v increase verbosity\n" " --version|-V print version string and exit\n\n" "Performs one or more SCSI WRITE BUFFER commands. Use '-m xxx' " "to list\navailable modes. A chunk size of 4 KB ('--bpw=4k') " "seems to work well.\nExample: sg_write_buffer -b 4k -I xxx.lod " "-m 7 /dev/sg3\n" ); } #define MODE_HEADER_DATA 0 #define MODE_VENDOR 1 #define MODE_DATA 2 #define MODE_DNLD_MC 4 #define MODE_DNLD_MC_SAVE 5 #define MODE_DNLD_MC_OFFS 6 #define MODE_DNLD_MC_OFFS_SAVE 7 #define MODE_ECHO_BUFFER 0x0A #define MODE_DNLD_MC_EV_OFFS_DEFER 0x0D #define MODE_DNLD_MC_OFFS_DEFER 0x0E #define MODE_ACTIVATE_MC 0x0F #define MODE_EN_EX_ECHO 0x1A #define MODE_DIS_EX 0x1B #define MODE_DNLD_ERR_HISTORY 0x1C struct mode_s { const char *mode_string; int mode; const char *comment; }; static struct mode_s mode_arr[] = { {"hd", MODE_HEADER_DATA, "combined header and data " "(obsolete)"}, {"vendor", MODE_VENDOR, "vendor specific"}, {"data", MODE_DATA, "data"}, {"dmc", MODE_DNLD_MC, "download microcode and activate"}, {"dmc_save", MODE_DNLD_MC_SAVE, "download microcode, save and " "activate"}, {"dmc_offs", MODE_DNLD_MC_OFFS, "download microcode with offsets " "and activate"}, {"dmc_offs_save", MODE_DNLD_MC_OFFS_SAVE, "download microcode with " "offsets, save and\n\t\t\t\tactivate"}, {"echo", MODE_ECHO_BUFFER, "write data to echo buffer"}, {"dmc_offs_ev_defer", MODE_DNLD_MC_EV_OFFS_DEFER, "download " "microcode with offsets, select\n\t\t\t\tactivation event, " "save and defer activation"}, {"dmc_offs_defer", MODE_DNLD_MC_OFFS_DEFER, "download microcode " "with offsets, save and\n\t\t\t\tdefer activation"}, {"activate_mc", MODE_ACTIVATE_MC, "activate deferred microcode"}, {"en_ex", MODE_EN_EX_ECHO, "enable expander communications " "protocol and\n\t\t\t\techo buffer (obsolete)"}, {"dis_ex", MODE_DIS_EX, "disable expander communications " "protocol\n\t\t\t\t(obsolete)"}, {"deh", MODE_DNLD_ERR_HISTORY, "download application client " "error history "}, {NULL, 0, NULL}, }; static void print_modes(void) { const struct mode_s * mp; pr2serr("The modes parameter argument can be numeric (hex or decimal)\n" "or symbolic:\n"); for (mp = mode_arr; mp->mode_string; ++mp) { pr2serr(" %2d (0x%02x) %-18s%s\n", mp->mode, mp->mode, mp->mode_string, mp->comment); } pr2serr("\nAdditionally '--bpw=,act' does a activate deferred " "microcode after\nsuccessful dmc_offs_defer and " "dmc_offs_ev_defer mode downloads.\n"); } int main(int argc, char * argv[]) { bool bpw_then_activate = false; bool dry_run = false; bool got_stdin = false; bool wb_len_given = false; int sg_fd, infd, res, c, len, k, n; int bpw = 0; int do_help = 0; int ret = 0; int verbose = 0; int wb_id = 0; int wb_len = 0; int wb_mode = 0; int wb_offset = 0; int wb_skip = 0; int wb_timeout = DEF_PT_TIMEOUT; int wb_mspec = 0; const char * device_name = NULL; const char * file_name = NULL; unsigned char * dop = NULL; char * cp; const struct mode_s * mp; char ebuff[EBUFF_SZ]; while (1) { int option_index = 0; c = getopt_long(argc, argv, "b:dhi:I:l:m:o:rs:S:t:vV", long_options, &option_index); if (c == -1) break; switch (c) { case 'b': bpw = sg_get_num(optarg); if (bpw < 0) { pr2serr("argument to '--bpw' should be in a positive " "number\n"); return SG_LIB_SYNTAX_ERROR; } if ((cp = strchr(optarg, ','))) { if (0 == strncmp("act", cp + 1, 3)) bpw_then_activate = true; } break; case 'd': dry_run = true; break; case 'h': case '?': ++do_help; break; case 'i': wb_id = sg_get_num(optarg); if ((wb_id < 0) || (wb_id > 255)) { pr2serr("argument to '--id' should be in the range 0 to " "255\n"); return SG_LIB_SYNTAX_ERROR; } break; case 'I': file_name = optarg; break; case 'l': wb_len = sg_get_num(optarg); if (wb_len < 0) { pr2serr("bad argument to '--length'\n"); return SG_LIB_SYNTAX_ERROR; } wb_len_given = true; break; case 'm': if (isdigit(*optarg)) { wb_mode = sg_get_num(optarg); if ((wb_mode < 0) || (wb_mode > 31)) { pr2serr("argument to '--mode' should be in the range 0 " "to 31\n"); return SG_LIB_SYNTAX_ERROR; } } else { len = strlen(optarg); for (mp = mode_arr; mp->mode_string; ++mp) { if (0 == strncmp(mp->mode_string, optarg, len)) { wb_mode = mp->mode; break; } } if (! mp->mode_string) { print_modes(); return SG_LIB_SYNTAX_ERROR; } } break; case 'o': wb_offset = sg_get_num(optarg); if (wb_offset < 0) { pr2serr("bad argument to '--offset'\n"); return SG_LIB_SYNTAX_ERROR; } break; case 'r': /* --read-stdin and --raw (previous name) */ file_name = "-"; break; case 's': wb_skip = sg_get_num(optarg); if (wb_skip < 0) { pr2serr("bad argument to '--skip'\n"); return SG_LIB_SYNTAX_ERROR; } break; case 'S': wb_mspec = sg_get_num(optarg); if ((wb_mspec < 0) || (wb_mspec > 7)) { pr2serr("expected argument to '--specific' to be 0 to 7\n"); return SG_LIB_SYNTAX_ERROR; } break; case 't': wb_timeout = sg_get_num(optarg); if (wb_timeout < 0) { pr2serr("Invalid argument to '--timeout'\n"); return SG_LIB_SYNTAX_ERROR; } break; case 'v': ++verbose; break; case 'V': pr2serr(ME "version: %s\n", version_str); return 0; default: pr2serr("unrecognised option code 0x%x ??\n", c); usage(); return SG_LIB_SYNTAX_ERROR; } } if (do_help) { if (do_help > 1) { usage(); pr2serr("\n"); print_modes(); } else usage(); return 0; } if (optind < argc) { if (NULL == device_name) { device_name = argv[optind]; ++optind; } if (optind < argc) { for (; optind < argc; ++optind) pr2serr("Unexpected extra argument: %s\n", argv[optind]); usage(); return SG_LIB_SYNTAX_ERROR; } } if (NULL == device_name) { pr2serr("missing device name!\n"); usage(); return SG_LIB_SYNTAX_ERROR; } if ((wb_len > 0) && (bpw > wb_len)) { pr2serr("trim chunk size (CS) to be the same as LEN\n"); bpw = wb_len; } #ifdef SG_LIB_WIN32 #ifdef SG_LIB_WIN32_DIRECT if (verbose > 4) pr2serr("Initial win32 SPT interface state: %s\n", scsi_pt_win32_spt_state() ? "direct" : "indirect"); scsi_pt_win32_direct(SG_LIB_WIN32_DIRECT /* SPT pt interface */); #endif #endif sg_fd = sg_cmds_open_device(device_name, false /* rw */, verbose); if (sg_fd < 0) { pr2serr(ME "open error: %s: %s\n", device_name, safe_strerror(-sg_fd)); return SG_LIB_FILE_ERROR; } if (file_name || (wb_len > 0)) { if (0 == wb_len) wb_len = DEF_XFER_LEN; if (NULL == (dop = (unsigned char *)malloc(wb_len))) { pr2serr(ME "out of memory\n"); ret = SG_LIB_SYNTAX_ERROR; goto err_out; } memset(dop, 0xff, wb_len); if (file_name) { got_stdin = (0 == strcmp(file_name, "-")); if (got_stdin) { if (wb_skip > 0) { pr2serr("Can't skip on stdin\n"); ret = SG_LIB_FILE_ERROR; goto err_out; } infd = STDIN_FILENO; } else { if ((infd = open(file_name, O_RDONLY)) < 0) { snprintf(ebuff, EBUFF_SZ, ME "could not open %s for reading", file_name); perror(ebuff); ret = SG_LIB_FILE_ERROR; goto err_out; } else if (sg_set_binary_mode(infd) < 0) perror("sg_set_binary_mode"); if (wb_skip > 0) { if (lseek(infd, wb_skip, SEEK_SET) < 0) { snprintf(ebuff, EBUFF_SZ, ME "couldn't skip to " "required position on %s", file_name); perror(ebuff); close(infd); ret = SG_LIB_FILE_ERROR; goto err_out; } } } res = read(infd, dop, wb_len); if (res < 0) { snprintf(ebuff, EBUFF_SZ, ME "couldn't read from %s", file_name); perror(ebuff); if (! got_stdin) close(infd); ret = SG_LIB_FILE_ERROR; goto err_out; } if (res < wb_len) { if (wb_len_given) { pr2serr("tried to read %d bytes from %s, got %d bytes\n", wb_len, file_name, res); pr2serr("pad with 0xff bytes and continue\n"); } else { if (verbose) { pr2serr("tried to read %d bytes from %s, got %d " "bytes\n", wb_len, file_name, res); pr2serr("will write %d bytes", res); if ((bpw > 0) && (bpw < wb_len)) pr2serr(", %d bytes per WRITE BUFFER command\n", bpw); else pr2serr("\n"); } wb_len = res; } } if (! got_stdin) close(infd); } } res = 0; if (bpw > 0) { for (k = 0; k < wb_len; k += n) { n = wb_len - k; if (n > bpw) n = bpw; if (verbose) pr2serr("sending write buffer, mode=0x%x, mspec=%d, id=%d, " " offset=%d, len=%d\n", wb_mode, wb_mspec, wb_id, wb_offset + k, n); if (dry_run) { if (verbose) pr2serr("skipping WRITE BUFFER command due to " "--dry-run\n"); res = 0; } else res = sg_ll_write_buffer_v2(sg_fd, wb_mode, wb_mspec, wb_id, wb_offset + k, dop + k, n, wb_timeout, true, verbose); if (res) break; } if (bpw_then_activate) { if (verbose) pr2serr("sending Activate deferred microcode [0xf]\n"); if (dry_run) { if (verbose) pr2serr("skipping WRITE BUFFER(ACTIVATE) command due to " "--dry-run\n"); res = 0; } else res = sg_ll_write_buffer_v2(sg_fd, MODE_ACTIVATE_MC, 0 /* buffer_id */, 0 /* buffer_offset */, 0, NULL, 0, wb_timeout, true, verbose); } } else { if (verbose) pr2serr("sending single write buffer, mode=0x%x, mpsec=%d, " "id=%d, offset=%d, len=%d\n", wb_mode, wb_mspec, wb_id, wb_offset, wb_len); if (dry_run) { if (verbose) pr2serr("skipping WRITE BUFFER(all in one) command due to " "--dry-run\n"); res = 0; } else res = sg_ll_write_buffer_v2(sg_fd, wb_mode, wb_mspec, wb_id, wb_offset, dop, wb_len, wb_timeout, true, verbose); } if (0 != res) { char b[80]; ret = res; sg_get_category_sense_str(res, sizeof(b), b, verbose); pr2serr("Write buffer failed: %s\n", b); } err_out: if (dop) free(dop); res = sg_cmds_close_device(sg_fd); if (res < 0) { pr2serr("close error: %s\n", safe_strerror(-res)); if (0 == ret) return SG_LIB_FILE_ERROR; } return (ret >= 0) ? ret : SG_LIB_CAT_OTHER; } f2fs-tools-1.11.0/tools/sha512.c000066400000000000000000000243631332120552100161010ustar00rootroot00000000000000/* * 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 */