pax_global_header00006660000000000000000000000064127137126270014522gustar00rootroot0000000000000052 comment=8da7b8df3e35c9775caeff4a1ff8d1930dfff012 libiscsi-1.17.0/000077500000000000000000000000001271371262700134115ustar00rootroot00000000000000libiscsi-1.17.0/.gitignore000066400000000000000000000013231271371262700154000ustar00rootroot00000000000000/autom4te.cache /depcomp /aclocal.m4 /missing /m4/ /config.guess /config.sub /install-sh /configure /config.h.in /Makefile.in /Makefile .deps .libs /stamp-h1 *.o *.lo *.la *.so bin /config.h /config.log /config.status .dirstamp /libtool /ltmain.sh libiscsi-*.tar.gz TAGS /ar-lib /compile /doc/Makefile.in /doc/Makefile /examples/Makefile.in /examples/Makefile /examples/iscsi-dd /examples/iscsiclient /examples/ld_iscsi.so /lib/Makefile.in /lib/Makefile /test-tool/Makefile.in /test-tool/Makefile /test-tool/iscsi-test-cu /tests/Makefile.in /tests/Makefile /utils/Makefile.in /utils/Makefile /utils/iscsi-inq /utils/iscsi-ls /utils/iscsi-perf /utils/iscsi-readcapacity16 /utils/iscsi-swp /libiscsi.pc /.cproject /.project libiscsi-1.17.0/COPYING000066400000000000000000000032361271371262700144500ustar00rootroot00000000000000Libiscsi components fall under two separate licences The lib and include directories =============================== The iscsi client library itself, i.e. the lib and include directories, is licenced under 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. The library also contains one file : lib/md5.c that is under Public Domain. A copy of LGPL 2.1 is included in the file LICENCE-LGPL-2.1.txt but can also be downloaded from http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html This is the licence that applies to the libiscsi library and its use. src/ld_iscsi.c ============== This LD_PRELOAD utility is licenced under 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. The src, examples and test-tool directories EXCEPT src/ld_iscsi.c ============================================================= The utility and example applications using this library, i.e. the src and the examples directories, are licenced under 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. These are executable applications that link with the libiscsi library. A copy of GPL 2 is included in the file LICENCE-GPL-2.txt but can also be downloaded from http://www.gnu.org/licenses/old-licenses/gpl-2.0.html This licence applies only to the source code for the provided applications that link to and use libiscsi. To avoid any confusion, every source file also contain a licence boilerplate. libiscsi-1.17.0/INSTALL000066400000000000000000000003331271371262700144410ustar00rootroot00000000000000This installs a new shared library under /usr/lib[64] and executables under /usr/bin From source: ============ ./autogen.sh ./configure make sudo make install Building RPM: ============= ./packaging/RPM/makerpms.sh libiscsi-1.17.0/LICENCE-GPL-2.txt000066400000000000000000000432541271371262700160030ustar00rootroot00000000000000 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 Lesser 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 Lesser General Public License instead of this License. libiscsi-1.17.0/LICENCE-LGPL-2.1.txt000066400000000000000000000636421271371262700162610ustar00rootroot00000000000000 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! libiscsi-1.17.0/Makefile.am000066400000000000000000000016721271371262700154530ustar00rootroot00000000000000# Generic definitions SUBDIRS = lib doc utils test-tool tests examples ACLOCAL_AMFLAGS =-I m4 AUTOMAKE_OPTIONS = foreign subdir-objects AM_CPPFLAGS=-I. -I$(srcdir)/include "-D_U_=__attribute__((unused))" \ "-D_R_(A,B)=__attribute__((format(printf,A,B)))" AM_CFLAGS=$(WARN_CFLAGS) LDADD = lib/libiscsi.la EXTRA_DIST = autogen.sh COPYING LICENCE-GPL-2.txt LICENCE-LGPL-2.1.txt \ packaging/RPM/libiscsi.spec.in packaging/RPM/makerpms.sh \ aros/aros_compat.c aros/aros_compat.h aros/iscsi-ls.c \ aros/Makefile.AROS test-tool/iscsi-multipath.h \ win32/vsbuild.bat win32/win32_compat.c win32/win32_compat.h \ patches/mtx-iscsi.diff patches/README patches/sg3_utils-1.32.patch pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = libiscsi.pc iscsi_includedir = $(includedir)/iscsi dist_iscsi_include_HEADERS = include/iscsi.h include/scsi-lowlevel.h dist_noinst_HEADERS = include/iscsi-private.h include/md5.h include/slist.h libiscsi-1.17.0/README000066400000000000000000000234051271371262700142750ustar00rootroot00000000000000Libiscsi is a client-side library to implement the iSCSI protocol that can be used to access the resources of an iSCSI target. The library is fully asynchronous with regards to iSCSI commands and SCSI tasks, but a synchronous layer is also provided for ease of use for simpler applications. The src directory contains a handful of useful iSCSI utilities such as logging in to and enumerating all targets on a portal and all devices of a target. The examples directory contains example implementation of how to access both the synchronous and asynchronous APIs of libiscsi. Libiscsi is a work in progress. It aims to become a fully asynchronous library for iSCSI functionality, including all features required to establish and maintain an iSCSI session, as well as a low-level SCSI library to create SCSI CDBs and parse/unmarshall data-in structures. Installation ============ ./autogen.sh ./configure make sudo make install Build RPM ========= To build RPMs run the following script from the libiscsi root directory ./packaging/RPM/makerpms.sh iSCSI URL Format ================ iSCSI devices are specified by a URL format of the following form : iscsi://[[%]@][:]//[?[&]*] Arguments: Username and password for bidirectional CHAP authentication: target_user= target_password= Example: iscsi://server/iqn.ronnie.test/1 CHAP Authentication =================== CHAP authentication can be specified two ways. Either via the URL itself or through environment variables. Note that when setting it via the URL, be careful so that username/password will not be visible in logfiles or the process list. URL --- CHAP authentication via URL is specified by providing %@ in the server part of the URL: Example: iscsi://ronnie%password@server/iqn.ronnie.test/1 Environment variables --------------------- Setting the CHAP authentication via environment variables: LIBISCSI_CHAP_USERNAME=ronnie LIBISCSI_CHAP_PASSWORD=password Example: LIBISCSI_CHAP_PASSWORD=password iscsi-inq iscsi://ronnie@10.1.1.27/iqn.ronnie.test/1 Bidirectional CHAP Authentication ================================= Bidirectional CHAP is when you not only authenticate the initiator to the target but also authenticate the target back to the initiator. This is only available if you also first specify normal authentication as per the previous section. Bidirectional CHAP can be set either via URL arguments or via environment variables. If specifying it via URL arguments, be careful so that you do not leak the username/password via logfiles or the process list or similar. URL --- URL arguments contain the '&' character so make sure to escape them properly if you pass them in via a commandline. Example: iscsi://127.0.0.1/iqn.ronnie.test/1?target_user=target\&target_password=target Environment variables --------------------- Setting the CHAP authentication via environment variables: LIBISCSI_CHAP_TARGET_USERNAME=target LIBISCSI_CHAP_TARGET_PASSWORD=password IPv6 support ============ Libiscsi supports IPv6, either as names resolving into IPv6 addresses or when IPv6 addresses are explicitely set in the URL. When specifying IPv6 addresses in the URL, they have to be specified in [...] bracket form. Example: iscsi://[fec0:2727::3]:3260/iqn.ronnie.test/1 Header Digest ============= Libiscsi supports HeaderDigest. By default, libiscsi will offer None,CRC32C and let the target pick whether Header digest is to be used or not. This can be overridden by an application by calling iscsi_set_header_digest() if the application wants to force a specific setting. Patches ======= The patches subdirectory contains patches to make some external packages iSCSI-aware and make them use libiscsi. Currently we have SG3-UTILS and MTX. Patches for other packages would be welcome. ISCSI-TEST-CU ============= iscsi-test-cu is a CUnit based test tool for scsi and iscsi. iscsi-test-cu depends on the CUnit library and will only build if libcunit can be found during configure. The configure script will check if a suitable libcunit is available and only build the test tool if it can find libcunit. This test is done toward the end of the configure phase and should result in a line similar to : checking whether libcunit is available... yes Test family/suite/test ---------------------- Tests are divided up in families, suites and tests and are specified as --test=.. A is a logical collection of test suites to cover a broad set of functionality. Example families are 'SCSI' for performing all tests for SCSI commands and 'iSCSI' that contain tests for the iSCSI layer. To run all tests in the SCSI family you can just specify --test=SCSI or --test=SCSI.*.* The next layer of tests are the suites. Within a family there are a collection of suites that perform test to cover a specific area of functionality. For example, to run all SCSI tests that cover the Read10 opcode you would specify it as --test=SCSI.Read10 or --test=SCSI.Read10.* Finally at the lowest level you have the individual tests. These tests perform specific topic in a suite. For example, we have tests for the Read10 opcode that verifies that the target implements the DPO/FUA flags properly. To run those tests you would specify --test=SCSI.Read10.DpoFua Test discovery -------------- To discover which tests exist you can use the command iscsi-test-cu --list Examples -------- Run the DpoFua test for Read10 iscsi-test-cu --test=SCSI.Read10.DpoFua iscsi://127.0.0.1/iqn.example.test/1 Run all Read10 tests iscsi-test-cu --test=SCSI.Read10 iscsi://127.0.0.1/iqn.example.test/1 Run all SCSI tests for all opcodes iscsi-test-cu --test=SCSI iscsi://127.0.0.1/iqn.example.test/1 Initiator names used by the test suite -------------------------------------- Most tests only require a single login to the target, but some tests, for example the it nexus loss tests may need to login two separate sessions. By default the initiator names use for the logins will be "iqn.2007-10.com.github:sahlberg:libiscsi:iscsi-test"; for the primary connection for all tests, and "iqn.2007-10.com.github:sahlberg:libiscsi:iscsi-test-2" for the secondary connection for the test that needs two sessions. These names can be controlled by using the arguments --initiator-name and --initiator-name-2 The tests are all self-documented to describe what they test and how they test. Use -V with a test to print the documentation for a test. XLM/JUNIT ========= iscsi-test-cu can produce machine-readable test results for consumption by your CI server. Use the --xml option with any test suite(s), and a file called CUnitAutomated-Results.xml will be written to your current working directory. These results can be converted to JUnit format using this script: http://git.cyrusimap.org/cyrus-imapd/plain/cunit/cunit-to-junit.pl See also: CUnit-List.xsl -------------- https://code.google.com/p/warc-tools/source/browse/trunk/utest/outputs/CUnit-List.xsl CUnit-Run.xsl ------------- https://code.google.com/p/mdflib/source/browse/trunk/cunit/CUnit-Run.xsl Linux SG_IO devices ------------------- When used on Linux, the test tool also supports talking directly to local SG_IO devices. Accessing SG_IO devices often require that you are root: sudo iscsi-test-cu --test LINUX.Read10.Simple /dev/sg1 Unit Tests ---------- The tests directory contains test scripts and programs to verify the functionality of libiscsi itself. These tests require that you have STGT version 1.0.58 or later installed to use as a taget to test against. To run the tests: cd tests make test LD_PRELOAD FUN ============== There is a small LD_PRELOAD hack in the src directory that intercepts a handful of system calls and converts iSCSI URLs to look and behave as if they are normal read-only files. This allows using standard UNIX tools to become iSCSI-aware with no modifications. For example: The stat command: this shows the size of the iSCSI LUN as if it was a normal file: $ LD_PRELOAD=ld_iscsi.so stat iscsi://127.0.0.1:3262/iqn.ronnie.test/2 File: `iscsi://127.0.0.1:3262/iqn.ronnie.test/2' Size: 3431540736 Blocks: 0 IO Block: 0 regular file Device: 0h/0d Inode: 0 Links: 0 Access: (0444/-r--r--r--) Uid: ( 0/ root) Gid: ( 0/ root) Access: 1970-01-01 10:00:00.000000000 +1000 Modify: 1970-01-01 10:00:00.000000000 +1000 Change: 1970-01-01 10:00:00.000000000 +1000 The cat command, which allows you to read/dump a iSCSI LUN to a file : $ LD_PRELOAD=ld_iscsi.so cat iscsi://127.0.0.1:3262/iqn.ronnie.test/2 >copy_of_iscsi_lun Or using dd even : $ LD_PRELOAD=ld_iscsi.so dd if=iscsi://127.0.0.1:3262/iqn.ronnie.test/2 of=copy_of_LUN bs=10M count=1 The LD_PRELOAD hack is incomplete and needs more functions to be intercepted before becoming fully functional. Patches welcome! For now, it is sufficiently complete for trivial commands like stat and cat. You probably need to implement at least lseek, pread, pwrite before it becomes really useful. SUPPORTED PLATFORMS =================== libiscsi is pure POSIX and should with some tweaks run on any host that provides a POSIX-like environment. Libiscsi has been tested on: * Linux (32 and 64 bit) * Cygwin * FreeBSD * Windows (Win7-VisualStudio10) * OpenSolaris * Solaris 11 : Use "gmake" to build. * OS X RELEASE TARBALLS ================ Release tarballs are available at https://sites.google.com/site/libiscsitarballs/libiscsitarballs/ CONTRIBUTING ============ If you want to contribute, please do. For sending me patches you can either do that by sending a pull request to my github repo or you can send them in an email directly to me at ronniesahlberg@gmail.com MAILINGLIST =========== A libiscsi mailing list is available at http://groups.google.com/group/libiscsi Announcements of new versions of libiscsi will be posted to this list. libiscsi-1.17.0/TODO000066400000000000000000000011101271371262700140720ustar00rootroot00000000000000Some features that should be added * More efficient api for read/write commands where we read/write straight from the socket into the buffer the application specified instead of as now we pass the data to a callback and then copy it. * More scsi marshalling and unmarshalling functions in scsi-lowlevel * Autoconnect for session faiulures. When the tcp session fail, try several times to reconnect and relogin. If successful re-issue any commands that were in flight. * Redirects * Integrate with other relevant utilities such as dvdrecord, ... * Data Digest libiscsi-1.17.0/aros/000077500000000000000000000000001271371262700143555ustar00rootroot00000000000000libiscsi-1.17.0/aros/Makefile.AROS000077500000000000000000000010441271371262700165620ustar00rootroot00000000000000AR=ar CC=gcc CFLAGS=-g -O0 -DAROS=1 -D_U_=" " -DHAVE_SYS_TYPES_H -DHAVE_SOCKADDR_LEN -I. -Iinclude -Iaros OBJS=lib/connect.o lib/crc32c.o lib/discovery.o lib/init.o lib/iscsi-command.o lib/logging.o lib/login.o lib/md5.o lib/nop.o lib/pdu.o lib/scsi-lowlevel.o lib/socket.o lib/sync.o lib/task_mgmt.o aros/aros_compat.o all: lib/libiscsi.a lib/libiscsi.a: $(OBJS) $(AR) cru $@ $(OBJS) .c.o: echo $(CC) $(CFLAGS) -c -o $@ $< $(CC) $(CFLAGS) -c -o $@ $< aros/iscsi-ls: aros/iscsi-ls.c lib/libiscsi.a $(CC) $(CFLAGS) -o $@ $< lib/libiscsi.a libiscsi-1.17.0/aros/aros_compat.c000066400000000000000000000102761271371262700170360ustar00rootroot00000000000000/* Copyright (C) 2013 by Ronnie Sahlberg This program 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 program is distributed in the hope that 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 program; if not, see . */ #include #include #include #include #include #include #include #include "aros_compat.h" #include #include #undef poll int aros_getnameinfo(const struct sockaddr *sa, socklen_t salen, char *host, size_t hostlen, char *serv, size_t servlen, int flags) { struct sockaddr_in *sin = (struct sockaddr_in *)sa; if (host) { snprintf(host, hostlen, Inet_NtoA(sin->sin_addr.s_addr)); } return 0; } int aros_getaddrinfo(const char *node, const char*service, const struct addrinfo *hints, struct addrinfo **res) { struct sockaddr_in *sin; sin = malloc(sizeof(struct sockaddr_in)); sin->sin_len = sizeof(struct sockaddr_in); sin->sin_family=AF_INET; /* Some error checking would be nice */ sin->sin_addr.s_addr = inet_addr(node); sin->sin_port=0; if (service) { sin->sin_port=htons(atoi(service)); } *res = malloc(sizeof(struct addrinfo)); (*res)->ai_family = AF_INET; (*res)->ai_addrlen = sizeof(struct sockaddr_in); (*res)->ai_addr = (struct sockaddr *)sin; return 0; } void aros_freeaddrinfo(struct addrinfo *res) { free(res->ai_addr); free(res); } int aros_inet_pton(int af, char *src, void *dst) { struct sockaddr_in sin; sin.sin_addr.s_addr = inet_addr(src); memcpy(dst, &sin.sin_addr.s_addr, sizeof(sin.sin_addr.s_addr)); return 1; } struct Library * SocketBase = NULL; extern int errno; int h_errno = 0; void aros_init_socket(void) { if (!(SocketBase = OpenLibrary("bsdsocket.library", 4))) { printf("NoTCP/IP Stack available"); exit(10); } if (SocketBaseTags(SBTM_SETVAL(SBTC_ERRNOPTR(sizeof(errno))), (IPTR)&errno, SBTM_SETVAL(SBTC_HERRNOLONGPTR), (IPTR)&h_errno, TAG_DONE)) { printf("Failed to set ERRNO"); exit(10); } } int aros_poll(struct pollfd *fds, unsigned int nfds, int timo) { struct timeval timeout, *toptr; fd_set ifds, ofds, efds, *ip, *op; unsigned int i, maxfd = 0; int rc; // Set up the file-descriptor sets in ifds, ofds and efds. FD_ZERO(&ifds); FD_ZERO(&ofds); FD_ZERO(&efds); for (i = 0, op = ip = 0; i < nfds; ++i) { fds[i].revents = 0; if(fds[i].events & (POLLIN|POLLPRI)) { ip = &ifds; FD_SET(fds[i].fd, ip); } if(fds[i].events & POLLOUT) { op = &ofds; FD_SET(fds[i].fd, op); } FD_SET(fds[i].fd, &efds); if (fds[i].fd > maxfd) { maxfd = fds[i].fd; } } // Set up the timeval structure for the timeout parameter if(timo < 0) { toptr = 0; } else { toptr = &timeout; timeout.tv_sec = timo / 1000; timeout.tv_usec = (timo - timeout.tv_sec * 1000) * 1000; } rc = WaitSelect(maxfd + 1, ip, op, &efds, toptr, NULL); if(rc <= 0) return rc; if(rc > 0) { for (i = 0; i < nfds; ++i) { int fd = fds[i].fd; if(fds[i].events & (POLLIN|POLLPRI) && FD_ISSET(fd, &ifds)) fds[i].revents |= POLLIN; if(fds[i].events & POLLOUT && FD_ISSET(fd, &ofds)) fds[i].revents |= POLLOUT; if(FD_ISSET(fd, &efds)) // Some error was detected ... should be some way to know. fds[i].revents |= POLLHUP; } } return rc; } ssize_t aros_readv(int fd, const struct iovec *iov, int iovcnt) { return read(fd, iov[0].iov_base, iov[0].iov_len); } ssize_t aros_writev(int fd, const struct iovec *iov, int iovcnt) { return write(fd, iov[0].iov_base, iov[0].iov_len); } libiscsi-1.17.0/aros/aros_compat.h000066400000000000000000000022061271371262700170350ustar00rootroot00000000000000#ifndef AROS_COMPAT_H #define AROS_COMPAT_H #include #include #include #include #include #include #include #include #define statvfs statfs #define ioctl IoctlSocket #define close CloseSocket #define writev aros_writev #define readv aros_readv #define inet_pton aros_inet_pton #define freeaddrinfo aros_freeaddrinfo #define getnameinfo aros_getnameinfo #define getaddrinfo aros_getaddrinfo #define SOL_TCP IPPROTO_TCP extern struct Library * SocketBase; void aros_init_socket(void); #define POLLIN 0x0001 /* There is data to read */ #define POLLPRI 0x0002 /* There is urgent data to read */ #define POLLOUT 0x0004 /* Writing now will not block */ #define POLLERR 0x0008 /* Error condition */ #define POLLHUP 0x0010 /* Hung up */ #define POLLNVAL 0x0020 /* Invalid request: fd not open */ struct pollfd { int fd; /* file descriptor */ short events; /* requested events */ short revents; /* returned events */ }; #define poll(x, y, z) aros_poll(x, y, z) #endif libiscsi-1.17.0/aros/iscsi-ls.c000066400000000000000000000246231271371262700162560ustar00rootroot00000000000000/* Copyright (C) 2010 by Ronnie Sahlberg 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, see . */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #ifdef HAVE_POLL_H #include #endif #ifdef HAVE_UNISTD_H #include #endif #ifdef AROS #include "aros/aros_compat.h" #endif #include #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #ifndef discard_const #define discard_const(ptr) ((void *)((intptr_t)(ptr))) #endif int showluns; const char *initiator = "iqn.2007-10.com.github:sahlberg:libiscsi:iscsi-ls"; struct client_state { int finished; int status; int lun; int type; const char *username; const char *password; }; void event_loop(struct iscsi_context *iscsi, struct client_state *state) { struct pollfd pfd; while (state->finished == 0) { pfd.fd = iscsi_get_fd(iscsi); pfd.events = iscsi_which_events(iscsi); if (poll(&pfd, 1, -1) < 0) { fprintf(stderr, "Poll failed"); exit(10); } if (iscsi_service(iscsi, pfd.revents) < 0) { fprintf(stderr, "iscsi_service failed with : %s\n", iscsi_get_error(iscsi)); exit(10); } } } void show_lun(struct iscsi_context *iscsi, int lun) { struct scsi_task *task; struct scsi_inquiry_standard *inq; int type, no_media; long long size = 0; int size_pf = 0; static const char sf[] = {' ', 'k', 'M', 'G', 'T' }; /* check we can talk to the lun */ tur_try_again: if ((task = iscsi_testunitready_sync(iscsi, lun)) == NULL) { fprintf(stderr, "testunitready failed\n"); exit(10); } if (task->status == SCSI_STATUS_CHECK_CONDITION) { if (task->sense.key == SCSI_SENSE_UNIT_ATTENTION && task->sense.ascq == SCSI_SENSE_ASCQ_BUS_RESET) { scsi_free_scsi_task(task); goto tur_try_again; } } no_media = 0; if (task->status == SCSI_STATUS_CHECK_CONDITION && task->sense.key == SCSI_SENSE_NOT_READY && task->sense.ascq == SCSI_SENSE_ASCQ_MEDIUM_NOT_PRESENT) { /* not an error, just a cdrom without a disk most likely */ no_media = 1; } else if (task->status != SCSI_STATUS_GOOD) { fprintf(stderr, "TESTUNITREADY failed with %s\n", iscsi_get_error(iscsi)); exit(10); } scsi_free_scsi_task(task); /* check what type of lun we have */ task = iscsi_inquiry_sync(iscsi, lun, 0, 0, 64); if (task == NULL || task->status != SCSI_STATUS_GOOD) { fprintf(stderr, "failed to send inquiry command : %s\n", iscsi_get_error(iscsi)); exit(10); } inq = scsi_datain_unmarshall(task); if (inq == NULL) { fprintf(stderr, "failed to unmarshall inquiry datain blob\n"); exit(10); } type = inq->device_type; scsi_free_scsi_task(task); if (type == SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_DIRECT_ACCESS) { struct scsi_readcapacity10 *rc10; task = iscsi_readcapacity10_sync(iscsi, lun, 0, 0); if (task == NULL || task->status != SCSI_STATUS_GOOD) { fprintf(stderr, "failed to send readcapacity command\n"); exit(10); } rc10 = scsi_datain_unmarshall(task); if (rc10 == NULL) { fprintf(stderr, "failed to unmarshall readcapacity10 data\n"); exit(10); } size = rc10->block_size; size *= rc10->lba; for (size_pf=0; size_pf<4 && size > 1024; size_pf++) { size /= 1024; } scsi_free_scsi_task(task); } printf("Lun:%-4d Type:%s", lun, scsi_devtype_to_str(type)); if (type == SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_DIRECT_ACCESS) { printf(" (Size:%lld%c)", size, sf[size_pf]); } if (no_media) { printf(" (No media loaded)"); } printf("\n"); } void list_luns(struct client_state *clnt, const char *target, const char *portal) { struct iscsi_context *iscsi; struct scsi_task *task; struct scsi_reportluns_list *list; int full_report_size; int i; iscsi = iscsi_create_context(initiator); if (iscsi == NULL) { printf("Failed to create context\n"); exit(10); } iscsi_set_initiator_username_pwd(iscsi, clnt->username, clnt->password); if (iscsi_set_targetname(iscsi, target)) { fprintf(stderr, "Failed to set target name\n"); exit(10); } iscsi_set_session_type(iscsi, ISCSI_SESSION_NORMAL); iscsi_set_header_digest(iscsi, ISCSI_HEADER_DIGEST_NONE_CRC32C); if (iscsi_full_connect_sync(iscsi, portal, -1) != 0) { printf("iscsi_connect failed. %s\n", iscsi_get_error(iscsi)); exit(10); } /* get initial reportluns data, all targets can report 16 bytes but some * fail if we ask for too much. */ if ((task = iscsi_reportluns_sync(iscsi, 0, 16)) == NULL) { fprintf(stderr, "reportluns failed : %s\n", iscsi_get_error(iscsi)); exit(10); } full_report_size = scsi_datain_getfullsize(task); if (full_report_size > task->datain.size) { scsi_free_scsi_task(task); /* we need more data for the full list */ if ((task = iscsi_reportluns_sync(iscsi, 0, full_report_size)) == NULL) { fprintf(stderr, "reportluns failed : %s\n", iscsi_get_error(iscsi)); exit(10); } } list = scsi_datain_unmarshall(task); if (list == NULL) { fprintf(stderr, "failed to unmarshall reportluns datain blob\n"); exit(10); } for (i=0; i < (int)list->num; i++) { show_lun(iscsi, list->luns[i]); } scsi_free_scsi_task(task); iscsi_destroy_context(iscsi); } void discoverylogout_cb(struct iscsi_context *iscsi, int status, void *command_data _U_, void *private_data) { struct client_state *state = (struct client_state *)private_data; if (status != 0) { fprintf(stderr, "Failed to logout from target. : %s\n", iscsi_get_error(iscsi)); exit(10); } if (iscsi_disconnect(iscsi) != 0) { fprintf(stderr, "Failed to disconnect old socket\n"); exit(10); } state->finished = 1; } void discovery_cb(struct iscsi_context *iscsi, int status, void *command_data, void *private_data) { struct iscsi_discovery_address *addr; if (status != 0) { fprintf(stderr, "Failed to do discovery on target. : %s\n", iscsi_get_error(iscsi)); exit(10); } for(addr=command_data; addr; addr=addr->next) { printf("Target:%s Portal:%s\n", addr->target_name, addr->target_address); if (showluns != 0) { list_luns(private_data, addr->target_name, addr->target_address); } } if (iscsi_logout_async(iscsi, discoverylogout_cb, private_data) != 0) { fprintf(stderr, "iscsi_logout_async failed : %s\n", iscsi_get_error(iscsi)); exit(10); } } void discoverylogin_cb(struct iscsi_context *iscsi, int status, void *command_data _U_, void *private_data) { if (status != 0) { fprintf(stderr, "Login failed. %s\n", iscsi_get_error(iscsi)); exit(10); } if (iscsi_discovery_async(iscsi, discovery_cb, private_data) != 0) { fprintf(stderr, "failed to send discovery command : %s\n", iscsi_get_error(iscsi)); exit(10); } } void discoveryconnect_cb(struct iscsi_context *iscsi, int status, void *command_data _U_, void *private_data) { if (status != 0) { fprintf(stderr, "discoveryconnect_cb: connection failed : %s\n", iscsi_get_error(iscsi)); exit(10); } if (iscsi_login_async(iscsi, discoverylogin_cb, private_data) != 0) { fprintf(stderr, "iscsi_login_async failed : %s\n", iscsi_get_error(iscsi)); exit(10); } } void print_usage(void) { fprintf(stderr, "Usage: iscsi-ls [-?|--help] [--usage] [-i|--initiator-name=iqn-name]\n" "\t\t[-s|--show-luns] \n"); } void print_help(void) { fprintf(stderr, "Usage: iscsi-ls [OPTION...] \n"); fprintf(stderr, " -i, --initiator-name=iqn-name Initiatorname to use\n"); fprintf(stderr, " -s, --show-luns Show the luns for each target\n"); fprintf(stderr, "\n"); fprintf(stderr, "Help options:\n"); fprintf(stderr, " -?, --help Show this help message\n"); fprintf(stderr, " --usage Display brief usage message\n"); fprintf(stderr, "\n"); fprintf(stderr, "iSCSI Portal URL format : %s\n", ISCSI_PORTAL_URL_SYNTAX); fprintf(stderr, "\n"); fprintf(stderr, " is either of:\n"); fprintf(stderr, " \"hostname\" iscsi.example\n"); fprintf(stderr, " \"ipv4-address\" 10.1.1.27\n"); fprintf(stderr, " \"ipv6-address\" [fce0::1]\n"); } int main(int argc, char *argv[]) { struct iscsi_context *iscsi; struct iscsi_url *iscsi_url = NULL; struct client_state state; const char *url = NULL; int c; static int show_help = 0, show_usage = 0, debug = 0; while ((c = getopt(argc, argv, "h?udi:s")) != -1) { switch (c) { case 'h': case '?': show_help = 1; break; case 'u': show_usage = 1; break; case 'd': debug = 1; break; case 'i': initiator = optarg; break; case 's': showluns = 1; break; default: fprintf(stderr, "Unrecognized option '%c'\n\n", c); print_help(); exit(0); } } if (show_help != 0) { print_help(); exit(0); } if (show_usage != 0) { print_usage(); exit(0); } if (optind != argc -1) { print_usage(); exit(0); } memset(&state, 0, sizeof(state)); url = strdup(argv[optind]); if (url == NULL) { fprintf(stderr, "You must specify iscsi target portal.\n"); print_usage(); exit(10); } iscsi = iscsi_create_context(initiator); if (iscsi == NULL) { printf("Failed to create context\n"); exit(10); } if (debug > 0) { iscsi_set_log_level(iscsi, debug); iscsi_set_log_fn(iscsi, iscsi_log_to_stderr); } iscsi_url = iscsi_parse_portal_url(iscsi, url); if (url) { free(discard_const(url)); } if (iscsi_url == NULL) { fprintf(stderr, "Failed to parse URL: %s\n", iscsi_get_error(iscsi)); exit(10); } iscsi_set_session_type(iscsi, ISCSI_SESSION_DISCOVERY); state.username = iscsi_url->user; state.password = iscsi_url->passwd; if (iscsi_connect_async(iscsi, iscsi_url->portal, discoveryconnect_cb, &state) != 0) { fprintf(stderr, "iscsi_connect failed. %s\n", iscsi_get_error(iscsi)); exit(10); } event_loop(iscsi, &state); iscsi_destroy_url(iscsi_url); iscsi_destroy_context(iscsi); return 0; } libiscsi-1.17.0/autogen.sh000077500000000000000000000013041271371262700154100ustar00rootroot00000000000000#!/bin/sh set -e # Commands this script needs needed='rm mkdir autoreconf echo' if ! type $needed >/dev/null 2>&1 then for cmd in $needed do if ! type $cmd >/dev/null 2>&1 then # Have type print an error message for each missing command type $cmd || true fi done echo A required command is missing. Unable to continue. exit 1 fi rm -rf autom4te.cache rm -f depcomp aclocal.m4 missing config.guess config.sub install-sh rm -f configure config.h.in config.h.in~ m4/libtool.m4 m4/lt*.m4 Makefile.in mkdir -p m4 autoreconf -fvi (cd m4 && for i in libtool.m4 lt*.m4; do echo /$i done) > m4/.gitignore rm -rf autom4te.cache echo "Now run ./configure and then make." exit 0 libiscsi-1.17.0/configure.ac000066400000000000000000000106671271371262700157110ustar00rootroot00000000000000AC_PREREQ(2.50) AC_INIT(libiscsi, m4_esyscmd([grep 'Version:' ./packaging/RPM/libiscsi.spec.in 2>/dev/null | head -1 | sed -e 's/[ \t]*Version:[ \t]*\([^ \t]*\)[ \t]*.*/\1/' | tr -d '\n'])) AC_CONFIG_SRCDIR([lib/init.c]) AC_CONFIG_MACRO_DIR([m4]) AM_INIT_AUTOMAKE([-Wall -Werror subdir-objects]) m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES]) m4_ifdef([AM_PROG_AR], [AM_PROG_AR]) AC_PROG_LIBTOOL LT_INIT AC_CANONICAL_HOST AM_PROG_CC_C_O enable_write_strings="yes" case $host in *solaris*) enable_write_strings="no" AC_CHECK_LIB([socket], [main], , [AC_MSG_ERROR([Can not find required library])]) AC_CHECK_LIB([nsl], [main], , [AC_MSG_ERROR([Can not find required library])]) ;; *) ;; esac AC_ARG_ENABLE([werror], [AS_HELP_STRING([--disable-werror], [Disables building with -Werror by default])]) if test "$ac_cv_prog_gcc" = yes; then WARN_CFLAGS="-Wall -W -Wshadow -Wstrict-prototypes -Wpointer-arith -Wcast-align -Wno-strict-aliasing" if test "x$enable_werror" != "xno"; then WARN_CFLAGS="$WARN_CFLAGS -Werror" fi if test "x$enable_write_strings" != "xno"; then WARN_CFLAGS="$WARN_CFLAGS -Wwrite-strings" fi fi AC_SUBST(WARN_CFLAGS) AC_ARG_ENABLE([manpages], [AC_HELP_STRING([--enable-manpages], [Enable building the manpages])], [ENABLE_MANPAGES=$enableval], [ENABLE_MANPAGES="no"]) AM_CONDITIONAL(BUILD_MANPAGES, [expr "$ENABLE_MANPAGES" : yes > /dev/null 2>&1]) AC_CONFIG_HEADER(config.h) AC_CHECK_LIB([gcrypt], [gcry_control]) AM_CONDITIONAL([HAVE_LIBGCRYPT], [test $ac_cv_lib_gcrypt_gcry_control = yes]) AC_CACHE_CHECK([for sin_len in sock],libiscsi_cv_HAVE_SOCK_SIN_LEN,[ AC_TRY_COMPILE([#include #include #include ], [struct sockaddr_in sock; sock.sin_len = sizeof(sock);], libiscsi_cv_HAVE_SOCK_SIN_LEN=yes,libiscsi_cv_HAVE_SOCK_SIN_LEN=no)]) if test x"$libiscsi_cv_HAVE_SOCK_SIN_LEN" = x"yes"; then AC_DEFINE(HAVE_SOCK_SIN_LEN,1,[Whether the sockaddr_in struct has a sin_len property]) fi AC_CACHE_CHECK([for sys filio.h],libiscsi_cv_NEED_SYS_FILIO_H,[ AC_TRY_COMPILE([#include #include ], [int foo = FIONREAD], libiscsi_cv_NEED_SYS_FILIO_H=yes,libiscsi_cv_NEED_SYS_FILIO_H=no)]) if test x"$libiscsi_cv_NEED_SYS_FILIO_H" = x"yes"; then AC_DEFINE(NEED_SYS_FILIO_H,1,[Whether we need sys/filio.h]) fi # check for arpa/inet.h dnl Check for arpa/inet.h AC_CHECK_HEADERS([arpa/inet.h]) # check for poll.h dnl Check for poll.h AC_CHECK_HEADERS([poll.h]) AC_CACHE_CHECK([for sockaddr_in6 support],libiscsi_cv_HAVE_SOCKADDR_IN6,[ AC_TRY_COMPILE([#include #include #include ], [struct sockaddr_in6 sock; int len = sizeof(sock);], libiscsi_cv_HAVE_SOCKADDR_IN6=yes,libiscsi_cv_HAVE_SOCKADDR_IN6=no)]) if test x"$libiscsi_cv_HAVE_SOCKADDR_IN6" = x"yes"; then AC_DEFINE(HAVE_SOCKADDR_IN6,1,[Whether we have IPv6 support]) fi AC_CACHE_CHECK([for SG_IO support],libiscsi_cv_HAVE_SG_IO,[ AC_TRY_COMPILE([ #include #include ], [int sgio = SG_IO;], libiscsi_cv_HAVE_SG_IO=yes,libiscsi_cv_HAVE_SG_IO=no)]) if test x"$libiscsi_cv_HAVE_SG_IO" = x"yes"; then AC_DEFINE(HAVE_SG_IO,1,[Whether we have SG_IO support]) fi AC_MSG_CHECKING(whether libcunit is available) ac_save_CFLAGS="$CFLAGS" ac_save_LIBS="$LIBS" CFLAGS="$CFLAGS $GLIB_CFLAGS" LIBS="$GLIB_LIBS $LIBS -lcunit" AC_TRY_LINK([ #include ], [], [ac_cv_have_cunit=yes], [ac_cv_have_cunit=no]) CFLAGS="$ac_save_CFLAGS" LIBS="$ac_save_LIBS" if test "$ac_cv_have_cunit" = yes ; then AC_MSG_RESULT(yes) else AC_MSG_RESULT(no) AC_MSG_NOTICE(You need libcunit to build the test suite.) AC_MSG_NOTICE(The scsi/iscsi test suite will not be built.) fi AM_CONDITIONAL(ISCSITEST, [test "$ac_cv_have_cunit" = yes -a "$enable_shared" = "yes"]) AM_CONDITIONAL(LD_ISCSI, [expr "(" "$host_os" : "linux" ")" "&" "$enable_shared" "=" "yes"]) AC_CHECK_MEMBER([struct CU_SuiteInfo.pSetUpFunc], [AC_DEFINE([HAVE_CU_SUITEINFO_PSETUPFUNC], 1, [Define to 1 if struct CU_SuiteInfo has a member called pSetUpFunc])], [], [[ #include #include ]]) AC_SEARCH_LIBS(clock_gettime, rt, [ AC_DEFINE([HAVE_CLOCK_GETTIME],1,[Define if clock_gettime is available])]) AC_CONFIG_FILES([Makefile] [doc/Makefile] [examples/Makefile] [lib/Makefile] [test-tool/Makefile] [tests/Makefile] [utils/Makefile] [libiscsi.pc]) AC_OUTPUT libiscsi-1.17.0/doc/000077500000000000000000000000001271371262700141565ustar00rootroot00000000000000libiscsi-1.17.0/doc/Makefile.am000066400000000000000000000007031271371262700162120ustar00rootroot00000000000000XSLTPROC = /usr/bin/xsltproc # Manpages man1_MANS = iscsi-inq.1 iscsi-ls.1 iscsi-swp.1 iscsi-test-cu.1 EXTRA_DIST = iscsi-inq.1 iscsi-inq.xml \ iscsi-ls.1 iscsi-ls.xml \ iscsi-swp.1 iscsi-swp.xml \ iscsi-test-cu.1 iscsi-test-cu.xml .xml.1: if BUILD_MANPAGES $(XSLTPROC) -o $@ http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl $< else echo "Building without manpages. Use pre-compiled version of" $< endif libiscsi-1.17.0/doc/iscsi-inq.1000066400000000000000000000103261271371262700161410ustar00rootroot00000000000000'\" t .\" Title: iscsi-inq .\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 03/22/2014 .\" Manual: iscsi-inq: Perform INQUIRY on an iSCSI LUN .\" Source: iscsi-inq .\" Language: English .\" .TH "ISCSI\-INQ" "1" "03/22/2014" "iscsi\-inq" "iscsi\-inq: Perform INQUIRY on" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" iscsi-inq \- Utility to request INQUIRY data from an iSCSI LUN .SH "SYNOPSIS" .HP \w'\fBiscsi\-inq\ [\ OPTIONS\ ]\ \fR\ 'u \fBiscsi\-inq [ OPTIONS ] \fR .HP \w'\fBiscsi\-inq\fR\ 'u \fBiscsi\-inq\fR [\-i\ \-\-initiator\-name=] [\-e\ \-\-evpd=] [\-c\ \-\-pagecode=] [\-d\ \-\-debug=] [\-?\ \-\-help] [\-\-usage] [] .SH "DESCRIPTION" .PP iscsi\-inq is a utility to request INQUIRY data from an iSCSI LUN\&. .SH "ISCSI URL FORMAT" .PP iSCSI URL format is \*(Aqiscsi://[[%]@][:]//\*(Aq .PP Username and password are only required if the target requires CHAP authentication\&. Optionally you can specify the username and password via the environment variables LIBISCSI_CHAP_USERNAME and LIBISCSI_CHAP_PASSWORD\&. .PP Host can be specified either as a hostname, an IPv4 address or an IPv6 address\&. Examples: .sp .if n \{\ .RS 4 .\} .nf iscsi://192\&.0\&.2\&.1/iqn\&.ronnie\&.test/1 iscsi://[2001:DB8::1]:3261/iqn\&.ronnie\&.test/1 iscsi://ronnie%password@iscsi\&.example\&.com/iqn\&.ronnie\&.test/1 .fi .if n \{\ .RE .\} .PP Port is the TCP port on the target to connect to\&. Default is 3260\&. .PP Target\-iqn\-name is the iqn name of the target\&. An iSCSI portal can have multiple targets defined\&. Use iscsi\-ls to list all targets available on one specific portal\&. .PP LUN is the LUN number to report the INQUIRY data for\&. .SH "OPTIONS" .PP \-i \-\-initiator\-name= .RS 4 This specifies the initiator\-name that iscsi\-inq will use when logging in to the target\&. .sp The default name is \*(Aqiqn\&.2007\-10\&.com\&.github:sahlberg:libiscsi:iscsi\-inq\*(Aq but you can use this argument to override this\&. This is mainly needed for cases where the target is configured with access\-control to only allow discovery logins from known initiator\-names\&. .RE .PP \-e \-\-evpd= .RS 4 By default iscsi\-inq will display the standard inquiry data\&. Specify evpd as 1 to display extended VPD pages\&. .RE .PP \-c \-\-pagecode= .RS 4 This argument specifies which VPD page to display when evpd is requested\&. Default is 0x00 which represents SUPPORTED VPD PAGES\&. .sp This argument can only be used when evpd is set to 1\&. .RE .PP \-d \-\-debug= .RS 4 Debug level\&. .RE .PP \-? \-\-help .RS 4 Display basic help text\&. .RE .PP \-\-usage .RS 4 Display basic usage text\&. .RE .SH "EXAMPLES" .PP To print the standard INQUIRY page: .sp .if n \{\ .RS 4 .\} .nf iscsi\-inq iscsi://192\&.0\&.2\&.1/iqn\&.ronnie\&.test/1 .fi .if n \{\ .RE .\} .PP To list available pages: .sp .if n \{\ .RS 4 .\} .nf iscsi\-inq \-e 1 iscsi://192\&.0\&.2\&.1/iqn\&.ronnie\&.test/1 .fi .if n \{\ .RE .\} .PP To show a specific page: .sp .if n \{\ .RS 4 .\} .nf iscsi\-inq \-e 1 \-c 0x80 iscsi://192\&.0\&.2\&.1/iqn\&.ronnie\&.test/1 .fi .if n \{\ .RE .\} .sp .SH "SEE ALSO" .PP iscsi\-ls(1) \m[blue]\fB\%http://github.com/sahlberg/libiscsi\fR\m[] libiscsi-1.17.0/doc/iscsi-inq.xml000066400000000000000000000113371271371262700166040ustar00rootroot00000000000000 iscsi-inq 1 iscsi-inq iscsi-inq: Perform INQUIRY on an iSCSI LUN iscsi-inq Utility to request INQUIRY data from an iSCSI LUN iscsi-inq [ OPTIONS ] <ISCSI-URL> iscsi-inq -i --initiator-name=<IQN> -e --evpd=<INTEGER> -c --pagecode=<INTEGER> -d --debug=<INTEGER> -? --help --usage <ISCSI-URL> DESCRIPTION iscsi-inq is a utility to request INQUIRY data from an iSCSI LUN. ISCSI URL FORMAT iSCSI URL format is 'iscsi://[<username>[%<password>]@]<host>[:<port>]/<target-iqn-name>/<lun>' Username and password are only required if the target requires CHAP authentication. Optionally you can specify the username and password via the environment variables LIBISCSI_CHAP_USERNAME and LIBISCSI_CHAP_PASSWORD. Host can be specified either as a hostname, an IPv4 address or an IPv6 address. Examples: iscsi://192.0.2.1/iqn.ronnie.test/1 iscsi://[2001:DB8::1]:3261/iqn.ronnie.test/1 iscsi://ronnie%password@iscsi.example.com/iqn.ronnie.test/1 Port is the TCP port on the target to connect to. Default is 3260. Target-iqn-name is the iqn name of the target. An iSCSI portal can have multiple targets defined. Use iscsi-ls to list all targets available on one specific portal. LUN is the LUN number to report the INQUIRY data for. OPTIONS -i --initiator-name=<IQN> This specifies the initiator-name that iscsi-inq will use when logging in to the target. The default name is 'iqn.2007-10.com.github:sahlberg:libiscsi:iscsi-inq' but you can use this argument to override this. This is mainly needed for cases where the target is configured with access-control to only allow discovery logins from known initiator-names. -e --evpd=<INTEGER> By default iscsi-inq will display the standard inquiry data. Specify evpd as 1 to display extended VPD pages. -c --pagecode=<INTEGER> This argument specifies which VPD page to display when evpd is requested. Default is 0x00 which represents SUPPORTED VPD PAGES. This argument can only be used when evpd is set to 1. -d --debug=<INTEGER> Debug level. -? --help Display basic help text. --usage Display basic usage text. Examples To print the standard INQUIRY page: iscsi-inq iscsi://192.0.2.1/iqn.ronnie.test/1 To list available pages: iscsi-inq -e 1 iscsi://192.0.2.1/iqn.ronnie.test/1 To show a specific page: iscsi-inq -e 1 -c 0x80 iscsi://192.0.2.1/iqn.ronnie.test/1 SEE ALSO iscsi-ls(1) libiscsi-1.17.0/doc/iscsi-ls.1000066400000000000000000000066021271371262700157720ustar00rootroot00000000000000'\" t .\" Title: iscsi-ls .\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 03/24/2014 .\" Manual: iscsi-ls: list iSCSI targets and LUNs .\" Source: iscsi-ls .\" Language: English .\" .TH "ISCSI\-LS" "1" "03/24/2014" "iscsi\-ls" "iscsi\-ls: list iSCSI targets" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" iscsi-ls \- Utility to list iSCSI targets and LUNs .SH "SYNOPSIS" .HP \w'\fBiscsi\-ls\ [\ OPTIONS\ ]\ \fR\ 'u \fBiscsi\-ls [ OPTIONS ] \fR .HP \w'\fBiscsi\-ls\fR\ 'u \fBiscsi\-ls\fR [\-i\ \-\-initiator\-name=] [\-s\ \-\-show\-luns] [\-d\ \-\-debug=] [\-?\ \-\-help] [\-\-usage] .SH "DESCRIPTION" .PP iscsi\-ls is a utility to list all targets and LUNs for an iSCSI portal\&. .SH "ISCSI PORTAL URL FORMAT" .PP iSCSI portal format is \*(Aqiscsi://[[%]@][:]\*(Aq .PP Username and password are only required if the target requires CHAP authentication\&. Optionally you can specify the username and password via the environment variables LIBISCSI_CHAP_USERNAME and LIBISCSI_CHAP_PASSWORD\&. .PP Host can be specified either as a hostname, an IPv4 address or an IPv6 address\&. Examples: .sp .if n \{\ .RS 4 .\} .nf iscsi://192\&.0\&.2\&.1 iscsi://[2001:DB8::1]:3261 iscsi://ronnie%password@iscsi\&.example\&.com .fi .if n \{\ .RE .\} .PP Port is the TCP port on the target to connect to\&. Default is 3260\&. .SH "OPTIONS" .PP \-i \-\-initiator\-name= .RS 4 This specifies the initiator\-name that iscsi\-ls will use when logging in to the target\&. .sp The default name is \*(Aqiqn\&.2007\-10\&.com\&.github:sahlberg:libiscsi:iscsi\-ls\*(Aq but you can use this argument to override this\&. This is mainly needed for cases where the target is configured with access\-control to only allow discovery logins from known initiator\-names\&. .RE .PP \-s \-\-show\-luns .RS 4 In addition to listing all the targets at the specified portal also list all the LUNs and their types on each discovered target\&. .sp In order to display the type of LUN iscsi\-ls need to be able to perform a normal login on the targets\&. If the target is using access\-control you will need to specify an initiator\-name that allows normal logins to the target\&. .RE .PP \-d \-\-debug= .RS 4 Debug level\&. .RE .PP \-? \-\-help .RS 4 Display basic help text\&. .RE .PP \-\-usage .RS 4 Display basic usage text\&. .RE .SH "SEE ALSO" .PP iscsi\-inq(1) \m[blue]\fB\%http://github.com/sahlberg/libiscsi\fR\m[] libiscsi-1.17.0/doc/iscsi-ls.xml000066400000000000000000000071661271371262700164400ustar00rootroot00000000000000 iscsi-ls 1 iscsi-ls iscsi-ls: list iSCSI targets and LUNs iscsi-ls Utility to list iSCSI targets and LUNs iscsi-ls [ OPTIONS ] <ISCSI-PORTAL> iscsi-ls -i --initiator-name=<IQN> -s --show-luns -d --debug=<INTEGER> -? --help --usage DESCRIPTION iscsi-ls is a utility to list all targets and LUNs for an iSCSI portal. ISCSI PORTAL URL FORMAT iSCSI portal format is 'iscsi://[<username>[%<password>]@]<host>[:<port>]' Username and password are only required if the target requires CHAP authentication. Optionally you can specify the username and password via the environment variables LIBISCSI_CHAP_USERNAME and LIBISCSI_CHAP_PASSWORD. Host can be specified either as a hostname, an IPv4 address or an IPv6 address. Examples: iscsi://192.0.2.1 iscsi://[2001:DB8::1]:3261 iscsi://ronnie%password@iscsi.example.com Port is the TCP port on the target to connect to. Default is 3260. OPTIONS -i --initiator-name=<IQN> This specifies the initiator-name that iscsi-ls will use when logging in to the target. The default name is 'iqn.2007-10.com.github:sahlberg:libiscsi:iscsi-ls' but you can use this argument to override this. This is mainly needed for cases where the target is configured with access-control to only allow discovery logins from known initiator-names. -s --show-luns In addition to listing all the targets at the specified portal also list all the LUNs and their types on each discovered target. In order to display the type of LUN iscsi-ls need to be able to perform a normal login on the targets. If the target is using access-control you will need to specify an initiator-name that allows normal logins to the target. -d --debug=<INTEGER> Debug level. -? --help Display basic help text. --usage Display basic usage text. SEE ALSO iscsi-inq(1) libiscsi-1.17.0/doc/iscsi-swp.1000066400000000000000000000070631271371262700161670ustar00rootroot00000000000000'\" t .\" Title: iscsi-swp .\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 03/22/2014 .\" Manual: iscsi-swp: get/set software write protect .\" Source: iscsi-swp .\" Language: English .\" .TH "ISCSI\-SWP" "1" "03/22/2014" "iscsi\-swp" "iscsi\-swp: get/set software w" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" iscsi-swp \- Utility to get/set software write protect on an iSCSI LUN .SH "SYNOPSIS" .HP \w'\fBiscsi\-swp\ [\ OPTIONS\ ]\ \fR\ 'u \fBiscsi\-swp [ OPTIONS ] \fR .HP \w'\fBiscsi\-ls\fR\ 'u \fBiscsi\-ls\fR [\-i\ \-\-initiator\-name=] [\-s\ \-\-swp\ {on|off}] [\-d\ \-\-debug=] [\-?\ \-\-help] [\-\-usage] .SH "DESCRIPTION" .PP iscsi\-swp is a utility to get or set the software write protect on an iSCSI LUN\&. .SH "ISCSI PORTAL URL FORMAT" .PP iSCSI portal format is \*(Aqiscsi://[[%]@][:]\*(Aq .PP Username and password are only required if the target requires CHAP authentication\&. Optionally you can specify the username and password via the environment variables LIBISCSI_CHAP_USERNAME and LIBISCSI_CHAP_PASSWORD\&. .PP Host can be specified either as a hostname, an IPv4 address or an IPv6 address\&. Examples: .sp .if n \{\ .RS 4 .\} .nf iscsi://192\&.0\&.2\&.1 iscsi://[2001:DB8::1]:3261 iscsi://ronnie%password@iscsi\&.example\&.com .fi .if n \{\ .RE .\} .PP Port is the TCP port on the target to connect to\&. Default is 3260\&. .SH "OPTIONS" .PP \-i \-\-initiator\-name= .RS 4 This specifies the initiator\-name that iscsi\-ls will use when logging in to the target\&. .sp The default name is \*(Aqiqn\&.2007\-10\&.com\&.github:sahlberg:libiscsi:iscsi\-ls\*(Aq but you can use this argument to override this\&. This is mainly needed for cases where the target is configured with access\-control to only allow discovery logins from known initiator\-names\&. .RE .PP \-s \-\-swp {on|off} .RS 4 By default iscsi\-swp will only print the current setting of the software write protect bit\&. By using this argument iscsi\-swp will also try to set/clear the flag on the target LUN\&. .sp .if n \{\ .RS 4 .\} .nf iscsi\-swp iscsi://127\&.0\&.0\&.1/iqn\&.ronnie\&.test/1 SWP:0 iscsi\-swp iscsi://127\&.0\&.0\&.1/iqn\&.ronnie\&.test/1 \-\-swp on SWP:0 Turning SWP ON iscsi\-swp iscsi://127\&.0\&.0\&.1/iqn\&.ronnie\&.test/1 \-\-swp off SWP:0 Turning SWP OFF .fi .if n \{\ .RE .\} .RE .PP \-d \-\-debug= .RS 4 Debug level\&. .RE .PP \-? \-\-help .RS 4 Display basic help text\&. .RE .PP \-\-usage .RS 4 Display basic usage text\&. .RE .SH "SEE ALSO" .PP iscsi\-inq(1), iscsi\-ls(1) \m[blue]\fB\%http://github.com/sahlberg/libiscsi\fR\m[] libiscsi-1.17.0/doc/iscsi-swp.xml000066400000000000000000000073241271371262700166270ustar00rootroot00000000000000 iscsi-swp 1 iscsi-swp iscsi-swp: get/set software write protect iscsi-swp Utility to get/set software write protect on an iSCSI LUN iscsi-swp [ OPTIONS ] <ISCSI-PORTAL> iscsi-ls -i --initiator-name=<IQN> -s --swp {on|off} -d --debug=<INTEGER> -? --help --usage DESCRIPTION iscsi-swp is a utility to get or set the software write protect on an iSCSI LUN. ISCSI PORTAL URL FORMAT iSCSI portal format is 'iscsi://[<username>[%<password>]@]<host>[:<port>]' Username and password are only required if the target requires CHAP authentication. Optionally you can specify the username and password via the environment variables LIBISCSI_CHAP_USERNAME and LIBISCSI_CHAP_PASSWORD. Host can be specified either as a hostname, an IPv4 address or an IPv6 address. Examples: iscsi://192.0.2.1 iscsi://[2001:DB8::1]:3261 iscsi://ronnie%password@iscsi.example.com Port is the TCP port on the target to connect to. Default is 3260. OPTIONS -i --initiator-name=<IQN> This specifies the initiator-name that iscsi-ls will use when logging in to the target. The default name is 'iqn.2007-10.com.github:sahlberg:libiscsi:iscsi-ls' but you can use this argument to override this. This is mainly needed for cases where the target is configured with access-control to only allow discovery logins from known initiator-names. -s --swp {on|off} By default iscsi-swp will only print the current setting of the software write protect bit. By using this argument iscsi-swp will also try to set/clear the flag on the target LUN. iscsi-swp iscsi://127.0.0.1/iqn.ronnie.test/1 SWP:0 iscsi-swp iscsi://127.0.0.1/iqn.ronnie.test/1 --swp on SWP:0 Turning SWP ON iscsi-swp iscsi://127.0.0.1/iqn.ronnie.test/1 --swp off SWP:0 Turning SWP OFF -d --debug=<INTEGER> Debug level. -? --help Display basic help text. --usage Display basic usage text. SEE ALSO iscsi-inq(1), iscsi-ls(1) libiscsi-1.17.0/doc/iscsi-test-cu.1000066400000000000000000000166521271371262700167460ustar00rootroot00000000000000'\" t .\" Title: iscsi-test-cu .\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 02/16/2015 .\" Manual: iscsi-test-cu: iSCSI/SCSI protocol test suite .\" Source: iscsi-test-cu .\" Language: English .\" .TH "ISCSI\-TEST\-CU" "1" "02/16/2015" "iscsi\-test\-cu" "iscsi\-test\-cu: iSCSI/SCSI pr" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" iscsi-test-cu \- iSCSI/SCSI test suite .SH "SYNOPSIS" .HP \w'\fBiscsi\-test\-cu\ [\ OPTIONS\ ]\ \fR\ 'u \fBiscsi\-test\-cu [ OPTIONS ] \fR .HP \w'\fBiscsi\-test\-cu\fR\ 'u \fBiscsi\-test\-cu\fR [\-i\ \-\-initiator\-name=] [\-I\ \-\-initiator\-name\-2=] [\-l\ \-\-list] [\-t\ \-\-test=|*[\&.|*[\&.|*]]] [\-d\ \-\-dataloss] [\-s\ \-\-allow\-sanitize] [\-V\ \-\-Verbose\-scsi] [\-x\ \-\-xml] [\-?\ \-\-help] .SH "DESCRIPTION" .PP iscsi\-test\-cu is a test suite for iSCSI/SCSI\&. .SH "ISCSI PORTAL URL FORMAT" .PP iSCSI portal format is \*(Aqiscsi://[[%]@][:]//\*(Aq .PP Port is the TCP port on the target to connect to\&. Default is 3260\&. .PP Username and password are only required if the target requires CHAP authentication\&. Optionally you can specify the username and password via the environment variables LIBISCSI_CHAP_USERNAME and LIBISCSI_CHAP_PASSWORD\&. .PP Host can be specified either as a hostname, an IPv4 address or an IPv6 address\&. Examples: .sp .if n \{\ .RS 4 .\} .nf iscsi://192\&.0\&.2\&.1/iqn\&.ronnie\&.test/1 iscsi://[2001:DB8::1]:3261/iqn\&.ronnie\&.test/1 iscsi://ronnie%password@iscsi\&.example\&.com/iqn\&.ronnie\&.test/1 .fi .if n \{\ .RE .\} .sp .SH "OPTIONS" .PP \-i \-\-initiator\-name= .RS 4 All tests use at least one iSCSI connection to the target and this is the initiator name used for that primary session and it defaults to \*(Aqiqn\&.2007\-10\&.com\&.github:sahlberg:libiscsi:iscsi\-test\*(Aq .sp This argument is used to change what initiator name to use for the primary session\&. .RE .PP \-I \-\-initiator\-name\-2= .RS 4 Some tests use a second connection to the target, such as the \*(Aqit nexus loss tests\*(Aq\&. The default name used for this second connection is \*(Aqiqn\&.2007\-10\&.com\&.github:sahlberg:libiscsi:iscsi\-test\-2\*(Aq .sp This argument is used to change what initiator name to use for the secondary session\&. .RE .PP \-l \-\-list .RS 4 This argument lists all available tests\&. .sp The tests are divided up into \*(Aqfamilies\*(Aq, \*(Aqsuites\*(Aq and \*(Aqtests\*(Aq separated by \*(Aq\&.\*(Aq\&. .sp .if n \{\ .RS 4 .\} .nf iscsi\-test\-cu \-l ALL ALL\&.CompareAndWrite ALL\&.CompareAndWrite\&.Simple ALL\&.CompareAndWrite\&.Miscompare ALL\&.GetLBAStatus ALL\&.GetLBAStatus\&.Simple ALL\&.GetLBAStatus\&.BeyondEol \&.\&.\&. .fi .if n \{\ .RE .\} .sp .RE .PP \-t \-\-test=|*[\&.|*[\&.|*]] .RS 4 This argument specifies a comma\-separated list of the test expressions to run\&. If this argument is omitted then all tests will be executed\&. You can also just specify a filename from which to read the list of tests\&. .sp .if n \{\ .RS 4 .\} .nf To run all tests: iscsi\-test\-cu \-\-test=ALL iscsi://192\&.0\&.2\&.1/iqn\&.ronnie/test/1 To run the READ16 test suite: iscsi\-test\-cu \-\-test=SCSI\&.Read16\&.* \e iscsi://192\&.0\&.2\&.1/iqn\&.ronnie/test/1 To just run a single specific test: iscsi\-test\-cu \-\-test=iSCSI\&.iSCSIResiduals\&.Read10Invalid \e iscsi://192\&.0\&.2\&.1/iqn\&.ronnie/test/1 Run all tests in the TestUnitReady suite and also the Reaqd16\&.Flags test iscsi\-test\-cu \-\-test=SCSI\&.TestUnitReady\&.*,SCSI\&.Read16\&.Flags \e iscsi://192\&.0\&.2\&.1/iqn\&.ronnie/test/1 Run all tests listed in the file ~/tests\-to\-run iscsi\-test\-cu \-\-test=~/tests\-to\-run iscsi://192\&.0\&.2\&.1/iqn\&.ronnie/test/1 .fi .if n \{\ .RE .\} .sp .RE .PP \-d \-\-dataloss .RS 4 By default the test tool will only run non\-destructive tests\&. To test commands that mutates the data, such as the Write* tests you must specify the \-\-dataloss flag\&. .sp The tests that requre \-\-dataloss will overwrite the data on the LUN and any and all data on that LUN will be destroyed when running these tests\&. .RE .PP \-s \-\-allow\-sanitize .RS 4 The SBC SANITIZE opcode is expected to take significant time before it completes and thus tests for this opcode are not suitable for normal test runs\&. .sp By default any SBC SANITIZE tests will be skipped\&. Use these arguments if you really want to perform SANITIZE tests\&. .RE .PP \-V \-\-Verbose\-scsi .RS 4 The tests aims to be self\-documenting\&. By specifying the \-V flag the test tool will print all SCSI commands that are sent to the device, the arguments and the expected result\&. .sp If a test fails, re\-run the failing test with the \-V argument to see why it failed\&. If that also fails it is time to pull out wireshark and have a look at what happened\&. .sp .if n \{\ .RS 4 .\} .nf iscsi\-test\-cu \-\-test SCSI\&.Read10\&.BeyondEol iscsi://192\&.0\&.2\&.1/iqn\&.ronnie\&.test/1 \-V Suite: Read10 Test: BeyondEol \&.\&.\&. Test READ10 1\-256 blocks one block beyond the end Send READ10 (Expecting LBA_OUT_OF_RANGE) LBA:2097152 blocks:1 rdprotect:0 dpo:0 fua:0 fua_nv:0 group:0 [OK] READ10 returned ILLEGAL_REQUEST/LBA_OUT_OF_RANGE\&. \&.\&.\&. .fi .if n \{\ .RE .\} .sp .RE .PP \-x \-\-xml .RS 4 This option to produce test results in machine readable format for automated testing\&. .sp iscsi\-test\-cu can produce machine\-readable test results for consumption by your CI server\&. Use the \-\-xml option with any test suite(s), and a file called CUnitAutomated\-Results\&.xml will be written to your current working directory\&. These results can be converted to JUnit format using this script: http://git\&.cyrusimap\&.org/cyrus\-imapd/plain/cunit/cunit\-to\-junit\&.pl .sp The return status of the script reflects whether the tests were successful or not\&. If you would rather have the script return a status to reflect whether the conversion from cunit to junit was successful you can patch the script with this: .sp .if n \{\ .RS 4 .\} .nf diff \-\-git a/cunit\-to\-junit\&.pl\&.orig b/cunit\-to\-junit\&.pl index 7cf9320\&.\&.9182ff0 100644 \-\-\- a/cunit\-to\-junit\&.pl\&.orig +++ b/cunit\-to\-junit\&.pl @@ \-226,4 +226,4 @@ foreach my $s (@suites) } print "$0: ran $nrun tests, $nfailed failed\en"; \-exit(1) if ($nfailed > 0); +#exit(1) if ($nfailed > 0); .fi .if n \{\ .RE .\} .sp .RE .PP \-? \-\-help .RS 4 Display basic help text\&. .RE .SH "SEE ALSO" .PP \m[blue]\fB\%http://github.com/sahlberg/libiscsi\fR\m[] libiscsi-1.17.0/doc/iscsi-test-cu.xml000066400000000000000000000177701271371262700174100ustar00rootroot00000000000000 iscsi-test-cu 1 iscsi-test-cu iscsi-test-cu: iSCSI/SCSI protocol test suite iscsi-test-cu iSCSI/SCSI test suite iscsi-test-cu [ OPTIONS ] <ISCSI-PORTAL> iscsi-test-cu -i --initiator-name=<IQN> -I --initiator-name-2=<IQN> -l --list -t --test=<family>|*[.<suite>|*[.<test>|*]] -d --dataloss -s --allow-sanitize -V --Verbose-scsi -x --xml -? --help DESCRIPTION iscsi-test-cu is a test suite for iSCSI/SCSI. ISCSI PORTAL URL FORMAT iSCSI portal format is 'iscsi://[<username>[%<password>]@]<host>[:<port>]/<iqn-name>/<lun>' Port is the TCP port on the target to connect to. Default is 3260. Username and password are only required if the target requires CHAP authentication. Optionally you can specify the username and password via the environment variables LIBISCSI_CHAP_USERNAME and LIBISCSI_CHAP_PASSWORD. Host can be specified either as a hostname, an IPv4 address or an IPv6 address. Examples: iscsi://192.0.2.1/iqn.ronnie.test/1 iscsi://[2001:DB8::1]:3261/iqn.ronnie.test/1 iscsi://ronnie%password@iscsi.example.com/iqn.ronnie.test/1 OPTIONS -i --initiator-name=<IQN> All tests use at least one iSCSI connection to the target and this is the initiator name used for that primary session and it defaults to 'iqn.2007-10.com.github:sahlberg:libiscsi:iscsi-test' This argument is used to change what initiator name to use for the primary session. -I --initiator-name-2=<IQN> Some tests use a second connection to the target, such as the 'it nexus loss tests'. The default name used for this second connection is 'iqn.2007-10.com.github:sahlberg:libiscsi:iscsi-test-2' This argument is used to change what initiator name to use for the secondary session. -l --list This argument lists all available tests. The tests are divided up into 'families', 'suites' and 'tests' separated by '.'. iscsi-test-cu -l ALL ALL.CompareAndWrite ALL.CompareAndWrite.Simple ALL.CompareAndWrite.Miscompare ALL.GetLBAStatus ALL.GetLBAStatus.Simple ALL.GetLBAStatus.BeyondEol ... -t --test=<family>|*[.<suite>|*[.<test>|*]] This argument specifies a comma-separated list of the test expressions to run. If this argument is omitted then all tests will be executed. You can also just specify a filename from which to read the list of tests. To run all tests: iscsi-test-cu --test=ALL iscsi://192.0.2.1/iqn.ronnie/test/1 To run the READ16 test suite: iscsi-test-cu --test=SCSI.Read16.* \ iscsi://192.0.2.1/iqn.ronnie/test/1 To just run a single specific test: iscsi-test-cu --test=iSCSI.iSCSIResiduals.Read10Invalid \ iscsi://192.0.2.1/iqn.ronnie/test/1 Run all tests in the TestUnitReady suite and also the Reaqd16.Flags test iscsi-test-cu --test=SCSI.TestUnitReady.*,SCSI.Read16.Flags \ iscsi://192.0.2.1/iqn.ronnie/test/1 Run all tests listed in the file ~/tests-to-run iscsi-test-cu --test=~/tests-to-run iscsi://192.0.2.1/iqn.ronnie/test/1 -d --dataloss By default the test tool will only run non-destructive tests. To test commands that mutates the data, such as the Write* tests you must specify the --dataloss flag. The tests that requre --dataloss will overwrite the data on the LUN and any and all data on that LUN will be destroyed when running these tests. -s --allow-sanitize The SBC SANITIZE opcode is expected to take significant time before it completes and thus tests for this opcode are not suitable for normal test runs. By default any SBC SANITIZE tests will be skipped. Use these arguments if you really want to perform SANITIZE tests. -V --Verbose-scsi The tests aims to be self-documenting. By specifying the -V flag the test tool will print all SCSI commands that are sent to the device, the arguments and the expected result. If a test fails, re-run the failing test with the -V argument to see why it failed. If that also fails it is time to pull out wireshark and have a look at what happened. iscsi-test-cu --test SCSI.Read10.BeyondEol iscsi://192.0.2.1/iqn.ronnie.test/1 -V Suite: Read10 Test: BeyondEol ... Test READ10 1-256 blocks one block beyond the end Send READ10 (Expecting LBA_OUT_OF_RANGE) LBA:2097152 blocks:1 rdprotect:0 dpo:0 fua:0 fua_nv:0 group:0 [OK] READ10 returned ILLEGAL_REQUEST/LBA_OUT_OF_RANGE. ... -x --xml This option to produce test results in machine readable format for automated testing. iscsi-test-cu can produce machine-readable test results for consumption by your CI server. Use the --xml option with any test suite(s), and a file called CUnitAutomated-Results.xml will be written to your current working directory. These results can be converted to JUnit format using this script: http://git.cyrusimap.org/cyrus-imapd/plain/cunit/cunit-to-junit.pl The return status of the script reflects whether the tests were successful or not. If you would rather have the script return a status to reflect whether the conversion from cunit to junit was successful you can patch the script with this: diff --git a/cunit-to-junit.pl.orig b/cunit-to-junit.pl index 7cf9320..9182ff0 100644 --- a/cunit-to-junit.pl.orig +++ b/cunit-to-junit.pl @@ -226,4 +226,4 @@ foreach my $s (@suites) } print "$0: ran $nrun tests, $nfailed failed\n"; -exit(1) if ($nfailed > 0); +#exit(1) if ($nfailed > 0); -? --help Display basic help text. SEE ALSO libiscsi-1.17.0/examples/000077500000000000000000000000001271371262700152275ustar00rootroot00000000000000libiscsi-1.17.0/examples/Makefile.am000066400000000000000000000021031271371262700172570ustar00rootroot00000000000000AM_CPPFLAGS=-I. -I${srcdir}/../include "-D_U_=__attribute__((unused))" \ "-D_R_(A,B)=__attribute__((format(printf,A,B)))" AM_CFLAGS=$(WARN_CFLAGS) LDADD = ../lib/libiscsi.la noinst_PROGRAMS = iscsiclient iscsi-dd # # LD_PRELOAD library. # if LD_ISCSI EXTRA_PROGRAMS = ld_iscsi CLEANFILES = ld_iscsi.o ld_iscsi.so lib/* # # This gets a bit messy: # # 1) let automake compile the sources ld_iscsi_SOURCES = ld_iscsi.c ld_iscsi_CFLAGS = $(AM_CFLAGS) -fPIC # 2) let libtool link in the static version of the library noinst_LTLIBRARIES = lib/libiscsi_convenience.la lib_libiscsi_convenience_la_SOURCES = \ ../lib/connect.c ../lib/iscsi-command.c ../lib/nop.c \ ../lib/sync.c ../lib/crc32c.c ../lib/logging.c ../lib/pdu.c \ ../lib/task_mgmt.c ../lib/discovery.c ../lib/login.c \ ../lib/scsi-lowlevel.c ../lib/init.c ../lib/md5.c \ ../lib/socket.c ld_iscsi.o: ld_iscsi-ld_iscsi.o lib/libiscsi_convenience.la $(LIBTOOL) --mode=link $(CC) -o $@ $^ # 3) Manually create the .so file. bin_SCRIPTS = ld_iscsi.so ld_iscsi.so: ld_iscsi.o $(CC) -shared -o ld_iscsi.so ld_iscsi.o -ldl endif libiscsi-1.17.0/examples/iscsi-dd.c000066400000000000000000000267411271371262700171040ustar00rootroot00000000000000/* Copyright (C) 2010 by Ronnie Sahlberg 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, see . */ #include #include #include #include #include #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" const char *initiator = "iqn.2010-11.ronnie:iscsi-inq"; int max_in_flight = 50; int blocks_per_io = 200; struct client { int finished; int in_flight; struct iscsi_context *src_iscsi; int src_lun; int src_blocksize; uint64_t src_num_blocks; uint64_t pos; struct iscsi_context *dst_iscsi; int dst_lun; int dst_blocksize; uint64_t dst_num_blocks; int use_16_for_rw; int progress; int ignore_errors; }; void fill_read_queue(struct client *client); struct write_task { struct scsi_task *rt; struct client *client; }; void write_cb(struct iscsi_context *iscsi, int status, void *command_data, void *private_data) { struct write_task *wt = (struct write_task *)private_data; struct scsi_task *task = command_data; struct client *client = wt->client; if (status == SCSI_STATUS_CHECK_CONDITION) { printf("Write10/16 failed with sense key:%d ascq:%04x\n", task->sense.key, task->sense.ascq); scsi_free_scsi_task(task); exit(10); } if (status != SCSI_STATUS_GOOD) { printf("Write10/16 failed with %s\n", iscsi_get_error(iscsi)); if (!client->ignore_errors) { scsi_free_scsi_task(task); exit(10); } } client->in_flight--; fill_read_queue(client); if (client->progress) { printf("\r%"PRIu64" of %"PRIu64" blocks transferred.", client->pos, client->src_num_blocks); } if ((client->in_flight == 0) && (client->pos == client->src_num_blocks)) { client->finished = 1; if (client->progress) { printf("\n"); } } scsi_free_scsi_task(wt->rt); scsi_free_scsi_task(task); free(wt); } void read_cb(struct iscsi_context *iscsi _U_, int status, void *command_data, void *private_data) { struct client *client = (struct client *)private_data; struct scsi_task *task = command_data; struct write_task *wt; struct scsi_read10_cdb *read10_cdb = NULL; struct scsi_read16_cdb *read16_cdb = NULL; struct scsi_task *task2; if (status == SCSI_STATUS_CHECK_CONDITION) { printf("Read10/16 failed with sense key:%d ascq:%04x\n", task->sense.key, task->sense.ascq); scsi_free_scsi_task(task); exit(10); } if (status != SCSI_STATUS_GOOD) { printf("Read10/16 failed with %s\n", iscsi_get_error(iscsi)); if (!client->ignore_errors) { scsi_free_scsi_task(task); exit(10); } } wt = malloc(sizeof(struct write_task)); wt->rt = task; wt->client = client; if (client->use_16_for_rw) { read16_cdb = scsi_cdb_unmarshall(task, SCSI_OPCODE_READ16); if (read16_cdb == NULL) { printf("Failed to unmarshall READ16 CDB.\n"); exit(10); } task2 = iscsi_write16_task(client->dst_iscsi, client->dst_lun, read16_cdb->lba, task->datain.data, task->datain.size, client->dst_blocksize, 0, 0, 0, 0, 0, write_cb, wt); } else { read10_cdb = scsi_cdb_unmarshall(task, SCSI_OPCODE_READ10); if (read10_cdb == NULL) { printf("Failed to unmarshall READ16 CDB.\n"); exit(10); } task2 = iscsi_write10_task(client->dst_iscsi, client->dst_lun, read10_cdb->lba, task->datain.data, task->datain.size, client->dst_blocksize, 0, 0, 0, 0, 0, write_cb, wt); } if (task2 == NULL) { printf("failed to send read16 command\n"); scsi_free_scsi_task(task); exit(10); } } void fill_read_queue(struct client *client) { int num_blocks; while(client->in_flight < max_in_flight && client->pos < client->src_num_blocks) { struct scsi_task *task; client->in_flight++; num_blocks = client->src_num_blocks - client->pos; if (num_blocks > blocks_per_io) { num_blocks = blocks_per_io; } if (client->use_16_for_rw) { task = iscsi_read16_task(client->src_iscsi, client->src_lun, client->pos, num_blocks * client->src_blocksize, client->src_blocksize, 0, 0, 0, 0, 0, read_cb, client); } else { task = iscsi_read10_task(client->src_iscsi, client->src_lun, client->pos, num_blocks * client->src_blocksize, client->src_blocksize, 0, 0, 0, 0, 0, read_cb, client); } if (task == NULL) { printf("failed to send read10/16 command\n"); exit(10); } client->pos += num_blocks; } } int main(int argc, char *argv[]) { char *src_url = NULL; char *dst_url = NULL; struct iscsi_url *iscsi_url; struct scsi_task *task; struct scsi_readcapacity10 *rc10; struct scsi_readcapacity16 *rc16; int c; struct pollfd pfd[2]; struct client client; static struct option long_options[] = { {"dst", required_argument, NULL, 'd'}, {"src", required_argument, NULL, 's'}, {"initiator-name", required_argument, NULL, 'i'}, {"progress", no_argument, NULL, 'p'}, {"16", no_argument, NULL, '6'}, {"max", required_argument, NULL, 'm'}, {"blocks", required_argument, NULL, 'b'}, {"ignore-errors", no_argument, NULL, 'n'}, {0, 0, 0, 0} }; int option_index; memset(&client, 0, sizeof(client)); while ((c = getopt_long(argc, argv, "d:s:i:m:b:p6n", long_options, &option_index)) != -1) { switch (c) { case 'd': dst_url = optarg; break; case 's': src_url = optarg; break; case 'i': initiator = optarg; break; case 'p': client.progress = 1; break; case '6': client.use_16_for_rw = 1; break; case 'm': max_in_flight = atoi(optarg); break; case 'b': blocks_per_io = atoi(optarg); break; case 'n': client.ignore_errors = 1; break; default: fprintf(stderr, "Unrecognized option '%c'\n\n", c); exit(1); } } if (src_url == NULL) { fprintf(stderr, "You must specify source url\n"); fprintf(stderr, " --src iscsi://[:]//\n"); exit(10); } if (dst_url == NULL) { fprintf(stderr, "You must specify destination url\n"); fprintf(stderr, " --dst iscsi://[:]//\n"); exit(10); } client.src_iscsi = iscsi_create_context(initiator); if (client.src_iscsi == NULL) { fprintf(stderr, "Failed to create context\n"); exit(10); } iscsi_url = iscsi_parse_full_url(client.src_iscsi, src_url); if (iscsi_url == NULL) { fprintf(stderr, "Failed to parse URL: %s\n", iscsi_get_error(client.src_iscsi)); exit(10); } iscsi_set_session_type(client.src_iscsi, ISCSI_SESSION_NORMAL); iscsi_set_header_digest(client.src_iscsi, ISCSI_HEADER_DIGEST_NONE_CRC32C); if (iscsi_full_connect_sync(client.src_iscsi, iscsi_url->portal, iscsi_url->lun) != 0) { fprintf(stderr, "Login Failed. %s\n", iscsi_get_error(client.src_iscsi)); iscsi_destroy_url(iscsi_url); iscsi_destroy_context(client.src_iscsi); exit(10); } client.src_lun = iscsi_url->lun; iscsi_destroy_url(iscsi_url); if (client.use_16_for_rw) { task = iscsi_readcapacity16_sync(client.src_iscsi, client.src_lun); if (task == NULL || task->status != SCSI_STATUS_GOOD) { fprintf(stderr, "failed to send readcapacity command\n"); exit(10); } rc16 = scsi_datain_unmarshall(task); if (rc16 == NULL) { fprintf(stderr, "failed to unmarshall readcapacity16 data\n"); exit(10); } client.src_blocksize = rc16->block_length; client.src_num_blocks = rc16->returned_lba + 1; scsi_free_scsi_task(task); } else { task = iscsi_readcapacity10_sync(client.src_iscsi, client.src_lun, 0, 0); if (task == NULL || task->status != SCSI_STATUS_GOOD) { fprintf(stderr, "failed to send readcapacity command\n"); exit(10); } rc10 = scsi_datain_unmarshall(task); if (rc10 == NULL) { fprintf(stderr, "failed to unmarshall readcapacity10 data\n"); exit(10); } client.src_blocksize = rc10->block_size; client.src_num_blocks = rc10->lba; scsi_free_scsi_task(task); } client.dst_iscsi = iscsi_create_context(initiator); if (client.dst_iscsi == NULL) { fprintf(stderr, "Failed to create context\n"); exit(10); } iscsi_url = iscsi_parse_full_url(client.dst_iscsi, dst_url); if (iscsi_url == NULL) { fprintf(stderr, "Failed to parse URL: %s\n", iscsi_get_error(client.dst_iscsi)); exit(10); } iscsi_set_session_type(client.dst_iscsi, ISCSI_SESSION_NORMAL); iscsi_set_header_digest(client.dst_iscsi, ISCSI_HEADER_DIGEST_NONE_CRC32C); if (iscsi_full_connect_sync(client.dst_iscsi, iscsi_url->portal, iscsi_url->lun) != 0) { fprintf(stderr, "Login Failed. %s\n", iscsi_get_error(client.dst_iscsi)); iscsi_destroy_url(iscsi_url); iscsi_destroy_context(client.dst_iscsi); exit(10); } client.dst_lun = iscsi_url->lun; iscsi_destroy_url(iscsi_url); if (client.use_16_for_rw) { task = iscsi_readcapacity16_sync(client.dst_iscsi, client.dst_lun); if (task == NULL || task->status != SCSI_STATUS_GOOD) { fprintf(stderr, "failed to send readcapacity command\n"); exit(10); } rc16 = scsi_datain_unmarshall(task); if (rc16 == NULL) { fprintf(stderr, "failed to unmarshall readcapacity16 data\n"); exit(10); } client.dst_blocksize = rc16->block_length; client.dst_num_blocks = rc16->returned_lba + 1; scsi_free_scsi_task(task); } else { task = iscsi_readcapacity10_sync(client.dst_iscsi, client.dst_lun, 0, 0); if (task == NULL || task->status != SCSI_STATUS_GOOD) { fprintf(stderr, "failed to send readcapacity command\n"); exit(10); } rc10 = scsi_datain_unmarshall(task); if (rc10 == NULL) { fprintf(stderr, "failed to unmarshall readcapacity10 data\n"); exit(10); } client.dst_blocksize = rc10->block_size; client.dst_num_blocks = rc10->lba; scsi_free_scsi_task(task); } if (client.src_blocksize != client.dst_blocksize) { fprintf(stderr, "source LUN has different blocksize than destination than destination (%d != %d sectors)\n", client.src_blocksize, client.dst_blocksize); exit(10); } if (client.src_num_blocks > client.dst_num_blocks) { fprintf(stderr, "source LUN is bigger than destination (%"PRIu64" > %"PRIu64" sectors)\n", client.src_num_blocks, client.dst_num_blocks); exit(10); } fill_read_queue(&client); while (client.finished == 0) { pfd[0].fd = iscsi_get_fd(client.src_iscsi); pfd[0].events = iscsi_which_events(client.src_iscsi); pfd[1].fd = iscsi_get_fd(client.dst_iscsi); pfd[1].events = iscsi_which_events(client.dst_iscsi); if (!pfd[0].events && !pfd[1].events) { sleep(1); continue; } if (poll(&pfd[0], 2, -1) < 0) { printf("Poll failed"); exit(10); } if (iscsi_service(client.src_iscsi, pfd[0].revents) < 0) { printf("iscsi_service failed with : %s\n", iscsi_get_error(client.src_iscsi)); break; } if (iscsi_service(client.dst_iscsi, pfd[1].revents) < 0) { printf("iscsi_service failed with : %s\n", iscsi_get_error(client.dst_iscsi)); break; } } iscsi_logout_sync(client.src_iscsi); iscsi_destroy_context(client.src_iscsi); iscsi_logout_sync(client.dst_iscsi); iscsi_destroy_context(client.dst_iscsi); return 0; } libiscsi-1.17.0/examples/iscsiclient.c000066400000000000000000000477661271371262700177300ustar00rootroot00000000000000/* Copyright (C) 2012 by Ronnie Sahlberg 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, see . */ /* This is an example of using libiscsi. * It basically logs in to the the target and performs a discovery. * It then selects the last target in the returned list and * starts a normal login to that target. * Once logged in it issues a REPORTLUNS call and selects the last returned lun in the list. * This LUN is then used to send INQUIRY, READCAPACITY10 and READ10 test calls to. */ /* The reason why we have to specify an allocation length and sometimes probe, starting with a small value, probing how big the buffer * should be, and asking again with a bigger buffer. * Why not just always ask with a buffer that is big enough? * The reason is that a lot of scsi targets are "sensitive" and ""buggy"" * many targets will just fail the operation completely if they thing alloc len is unreasonably big. */ /* This is the host/port we connect to.*/ #define TARGET "127.0.0.1:3260" #if defined(WIN32) #include #include "win32/win32_compat.h" #pragma comment(lib, "ws2_32.lib") WSADATA wsaData; #else #include #endif #include #include #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" struct client_state { int finished; const char *message; int has_discovered_target; char *target_name; char *target_address; int lun; int block_size; }; unsigned char small_buffer[512]; void tm_at_cb(struct iscsi_context *iscsi _U_, int status _U_, void *command_data _U_, void *private_data) { struct client_state *clnt = (struct client_state *)private_data; printf("tm at cb !\n"); printf("response : %d\n", *((uint32_t *)command_data)); clnt->finished = 1; } void synccache10_cb(struct iscsi_context *iscsi _U_, int status, void *command_data _U_, void *private_data _U_) { printf("SYNCCACHE10 status:%d\n", status); } void nop_out_cb(struct iscsi_context *iscsi, int status, void *command_data, void *private_data) { struct iscsi_data *data = command_data; struct scsi_task *task; printf("NOP-IN status:%d\n", status); if (data->size > 0) { printf("NOP-IN data:%s\n", data->data); } printf("Send SYNCHRONIZECACHE10\n"); task = iscsi_synchronizecache10_task(iscsi, 2, 0, 0, 0, 0, synccache10_cb, private_data); if (task == NULL) { printf("failed to send sync cache10\n"); exit(10); } printf("send task management to try to abort the sync10 task\n"); if (iscsi_task_mgmt_abort_task_async(iscsi, task, tm_at_cb, private_data) != 0) { printf("failed to send task management to abort the sync10 task\n"); exit(10); } } void write10_1_cb(struct iscsi_context *iscsi _U_, int status, void *command_data, void *private_data _U_) { struct scsi_task *task = command_data; if (status == SCSI_STATUS_CHECK_CONDITION) { printf("Write10 failed with sense key:%d ascq:%04x\n", task->sense.key, task->sense.ascq); scsi_free_scsi_task(task); exit(10); } if (status != SCSI_STATUS_GOOD) { printf("Write10 failed with %s\n", iscsi_get_error(iscsi)); scsi_free_scsi_task(task); exit(10); } printf("Write successful :%d\n", status); scsi_free_scsi_task(task); exit(10); } void write10_cb(struct iscsi_context *iscsi _U_, int status, void *command_data, void *private_data _U_) { struct client_state *clnt = (struct client_state *)private_data; struct scsi_task *task = command_data; int i; static unsigned char wb[512]; static struct scsi_iovec iov[3]; if (status == SCSI_STATUS_CHECK_CONDITION) { printf("Write10 failed with sense key:%d ascq:%04x\n", task->sense.key, task->sense.ascq); scsi_free_scsi_task(task); exit(10); } if (status != SCSI_STATUS_GOOD) { printf("Write10 failed with %s\n", iscsi_get_error(iscsi)); scsi_free_scsi_task(task); exit(10); } printf("Write successful :%d\n", status); scsi_free_scsi_task(task); printf("write the block using an iovector\n"); for (i = 0;i < 512; i++) { wb[i] = (511 - i) & 0xff; } task = iscsi_write10_task(iscsi, clnt->lun, 0, NULL, 512, 512, 0, 0, 0, 0, 0, write10_1_cb, private_data); if (task == NULL) { printf("failed to send write10 command\n"); exit(10); } /* provide iovectors where to read the data. */ iov[0].iov_base = &wb[0]; iov[0].iov_len = 4; iov[1].iov_base = &wb[4]; iov[1].iov_len = 11; iov[2].iov_base = &wb[15]; iov[2].iov_len = 512 - 15; scsi_task_set_iov_out(task, &iov[0], 3); } void read10_1_cb(struct iscsi_context *iscsi, int status, void *command_data, void *private_data) { struct client_state *clnt = (struct client_state *)private_data; struct scsi_task *task = command_data; int i; static unsigned char wb[512]; if (status == SCSI_STATUS_CHECK_CONDITION) { printf("Read10 failed with sense key:%d ascq:%04x\n", task->sense.key, task->sense.ascq); scsi_free_scsi_task(task); exit(10); } printf("READ10 using scsi_task_set_iov_in() successful. Block content:\n"); for (i=0;i<512;i++) { printf("%02x ", small_buffer[i]); if (i%16==15) printf("\n"); if (i==69) break; } printf("...\n"); scsi_free_scsi_task(task); #if 0 printf("Finished, wont try to write data since that will likely destroy your LUN :-(\n"); printf("Send NOP-OUT\n"); if (iscsi_nop_out_async(iscsi, nop_out_cb, (unsigned char *)"Ping!", 6, private_data) != 0) { printf("failed to send nop-out\n"); scsi_free_scsi_task(task); exit(10); } #else printf("write the block normally\n"); for (i = 0;i < 512; i++) { wb[i] = i & 0xff; } task = iscsi_write10_task(iscsi, clnt->lun, 0, wb, 512, 512, 0, 0, 0, 0, 0, write10_cb, private_data); if (task == NULL) { printf("failed to send write10 command\n"); exit(10); } #endif } void read10_cb(struct iscsi_context *iscsi, int status, void *command_data, void *private_data) { struct client_state *clnt = (struct client_state *)private_data; struct scsi_task *task = command_data; int i; static struct scsi_iovec iov[3]; if (status == SCSI_STATUS_CHECK_CONDITION) { printf("Read10 failed with sense key:%d ascq:%04x\n", task->sense.key, task->sense.ascq); scsi_free_scsi_task(task); exit(10); } printf("READ10 using scsi_task_add_data_in_buffer() successful. Block content:\n"); for (i=0;i<512;i++) { printf("%02x ", small_buffer[i]); if (i%16==15) printf("\n"); if (i==69) break; } printf("...\n"); scsi_free_scsi_task(task); memset(&small_buffer[0], 0, 512); if ((task = iscsi_read10_task(iscsi, clnt->lun, 0, clnt->block_size, clnt->block_size, 0, 0, 0, 0, 0, read10_1_cb, private_data)) == NULL) { printf("failed to send read10 command\n"); exit(10); } /* provide iovectors where to read the data. */ iov[0].iov_base = &small_buffer[0]; iov[0].iov_len = 7; iov[1].iov_base = &small_buffer[7]; iov[1].iov_len = 8; iov[2].iov_base = &small_buffer[15]; iov[2].iov_len = 512 - 15; scsi_task_set_iov_in(task, &iov[0], 3); } void read6_cb(struct iscsi_context *iscsi, int status, void *command_data, void *private_data) { struct client_state *clnt = (struct client_state *)private_data; struct scsi_task *task = command_data; int i; if (status == SCSI_STATUS_CHECK_CONDITION) { printf("Read6 failed with sense key:%d ascq:%04x\n", task->sense.key, task->sense.ascq); scsi_free_scsi_task(task); exit(10); } printf("READ6 successful. Block content:\n"); for (i=0;idatain.size;i++) { printf("%02x ", task->datain.data[i]); if (i%16==15) printf("\n"); if (i==69) break; } printf("...\n"); scsi_free_scsi_task(task); if ((task = iscsi_read10_task(iscsi, clnt->lun, 0, clnt->block_size, clnt->block_size, 0, 0, 0, 0, 0, read10_cb, private_data)) == NULL) { printf("failed to send read10 command\n"); exit(10); } /* provide a buffer from the application to read into instead * of copying and linearizing the data. This saves two copies * of the data. One in libiscsi and one in the application * callback. */ scsi_task_add_data_in_buffer(task, 7, &small_buffer[0]); scsi_task_add_data_in_buffer(task, 8, &small_buffer[7]); scsi_task_add_data_in_buffer(task, 512-15, &small_buffer[15]); } void readcapacity10_cb(struct iscsi_context *iscsi, int status, void *command_data, void *private_data) { struct client_state *clnt = (struct client_state *)private_data; struct scsi_task *task = command_data; struct scsi_readcapacity10 *rc10; int full_size; if (status == SCSI_STATUS_CHECK_CONDITION) { printf("Readcapacity10 failed with sense key:%d ascq:%04x\n", task->sense.key, task->sense.ascq); scsi_free_scsi_task(task); exit(10); } full_size = scsi_datain_getfullsize(task); if (full_size < task->datain.size) { printf("not enough data for full size readcapacity10\n"); scsi_free_scsi_task(task); exit(10); } rc10 = scsi_datain_unmarshall(task); if (rc10 == NULL) { printf("failed to unmarshall readcapacity10 data\n"); scsi_free_scsi_task(task); exit(10); } clnt->block_size = rc10->block_size; printf("READCAPACITY10 successful. Size:%d blocks blocksize:%d. Read first block\n", rc10->lba, rc10->block_size); if (iscsi_read6_task(iscsi, clnt->lun, 0, clnt->block_size, clnt->block_size, read6_cb, private_data) == NULL) { printf("failed to send read6 command\n"); scsi_free_scsi_task(task); exit(10); } scsi_free_scsi_task(task); } void modesense6_cb(struct iscsi_context *iscsi, int status, void *command_data, void *private_data) { struct client_state *clnt = (struct client_state *)private_data; struct scsi_task *task = command_data; struct scsi_mode_sense *ms; int full_size; if (status == SCSI_STATUS_CHECK_CONDITION) { printf("Modesense6 failed with sense key:%d ascq:%04x\n", task->sense.key, task->sense.ascq); exit(10); } else { full_size = scsi_datain_getfullsize(task); if (full_size > task->datain.size) { printf("did not get enough data for mode sense, sening modesense again asking for bigger buffer\n"); if (iscsi_modesense6_task(iscsi, clnt->lun, 0, SCSI_MODESENSE_PC_CURRENT, SCSI_MODEPAGE_RETURN_ALL_PAGES, 0, full_size, modesense6_cb, private_data) == NULL) { printf("failed to send modesense6 command\n"); scsi_free_scsi_task(task); exit(10); } scsi_free_scsi_task(task); return; } } printf("MODESENSE6 successful.\n"); ms = scsi_datain_unmarshall(task); if (ms == NULL) { printf("failed to unmarshall mode sense datain blob\n"); scsi_free_scsi_task(task); exit(10); } printf("Send READCAPACITY10\n"); if (iscsi_readcapacity10_task(iscsi, clnt->lun, 0, 0, readcapacity10_cb, private_data) == NULL) { printf("failed to send readcapacity command\n"); scsi_free_scsi_task(task); exit(10); } scsi_free_scsi_task(task); } void inquiry_cb(struct iscsi_context *iscsi, int status, void *command_data, void *private_data) { struct client_state *clnt = (struct client_state *)private_data; struct scsi_task *task = command_data; struct scsi_inquiry_standard *inq; if (status == SCSI_STATUS_CHECK_CONDITION) { printf("Inquiry failed with sense key:%d ascq:%04x\n", task->sense.key, task->sense.ascq); scsi_free_scsi_task(task); exit(10); } printf("INQUIRY successful for standard data.\n"); inq = scsi_datain_unmarshall(task); if (inq == NULL) { printf("failed to unmarshall inquiry datain blob\n"); scsi_free_scsi_task(task); exit(10); } printf("Device Type is %d. VendorId:%s ProductId:%s\n", inq->device_type, inq->vendor_identification, inq->product_identification); printf("Send MODESENSE6\n"); if (iscsi_modesense6_task(iscsi, clnt->lun, 0, SCSI_MODESENSE_PC_CURRENT, SCSI_MODEPAGE_RETURN_ALL_PAGES, 0, 4, modesense6_cb, private_data) == NULL) { printf("failed to send modesense6 command\n"); scsi_free_scsi_task(task); exit(10); } scsi_free_scsi_task(task); } void testunitready_cb(struct iscsi_context *iscsi, int status, void *command_data, void *private_data) { struct client_state *clnt = (struct client_state *)private_data; struct scsi_task *task = command_data; if (status == SCSI_STATUS_CHECK_CONDITION) { printf("First testunitready failed with sense key:%d ascq:%04x\n", task->sense.key, task->sense.ascq); if (task->sense.key == SCSI_SENSE_UNIT_ATTENTION && task->sense.ascq == SCSI_SENSE_ASCQ_BUS_RESET) { printf("target device just came online, try again\n"); if (iscsi_testunitready_task(iscsi, clnt->lun, testunitready_cb, private_data) == NULL) { printf("failed to send testunitready command\n"); scsi_free_scsi_task(task); exit(10); } } scsi_free_scsi_task(task); return; } printf("TESTUNITREADY successful, do an inquiry on lun:%d\n", clnt->lun); if (iscsi_inquiry_task(iscsi, clnt->lun, 0, 0, 64, inquiry_cb, private_data) == NULL) { printf("failed to send inquiry command : %s\n", iscsi_get_error(iscsi)); scsi_free_scsi_task(task); exit(10); } scsi_free_scsi_task(task); } void reportluns_cb(struct iscsi_context *iscsi, int status, void *command_data, void *private_data) { struct client_state *clnt = (struct client_state *)private_data; struct scsi_task *task = command_data; struct scsi_reportluns_list *list; int full_report_size; int i; if (status != SCSI_STATUS_GOOD) { printf("Reportluns failed with : %s\n", iscsi_get_error(iscsi)); scsi_free_scsi_task(task); return; } full_report_size = scsi_datain_getfullsize(task); printf("REPORTLUNS status:%d data size:%d, full reports luns data size:%d\n", status, task->datain.size, full_report_size); if (full_report_size > task->datain.size) { printf("We did not get all the data we need in reportluns, ask again\n"); if (iscsi_reportluns_task(iscsi, 0, full_report_size, reportluns_cb, private_data) == NULL) { printf("failed to send reportluns command\n"); scsi_free_scsi_task(task); exit(10); } scsi_free_scsi_task(task); return; } list = scsi_datain_unmarshall(task); if (list == NULL) { printf("failed to unmarshall reportluns datain blob\n"); scsi_free_scsi_task(task); exit(10); } for (i=0; i < (int)list->num; i++) { printf("LUN:%d found\n", list->luns[i]); clnt->lun = list->luns[i]; } printf("Will use LUN:%d\n", clnt->lun); printf("Send testunitready to lun %d\n", clnt->lun); if (iscsi_testunitready_task(iscsi, clnt->lun, testunitready_cb, private_data) == NULL) { printf("failed to send testunitready command : %s\n", iscsi_get_error(iscsi)); scsi_free_scsi_task(task); exit(10); } scsi_free_scsi_task(task); } void normallogin_cb(struct iscsi_context *iscsi, int status, void *command_data _U_, void *private_data) { if (status != 0) { printf("Failed to log in to target : %s\n", iscsi_get_error(iscsi)); exit(10); } printf("Logged in normal session, send reportluns\n"); if (iscsi_reportluns_task(iscsi, 0, 16, reportluns_cb, private_data) == NULL) { printf("failed to send reportluns command : %s\n", iscsi_get_error(iscsi)); exit(10); } } void normalconnect_cb(struct iscsi_context *iscsi, int status, void *command_data _U_, void *private_data) { printf("Connected to iscsi socket\n"); if (status != 0) { printf("normalconnect_cb: connection failed status:%d\n", status); exit(10); } printf("connected, send login command\n"); iscsi_set_session_type(iscsi, ISCSI_SESSION_NORMAL); iscsi_set_header_digest(iscsi, ISCSI_HEADER_DIGEST_CRC32C_NONE); if (iscsi_login_async(iscsi, normallogin_cb, private_data) != 0) { printf("iscsi_login_async failed\n"); exit(10); } } void discoverylogout_cb(struct iscsi_context *iscsi, int status, void *command_data _U_, void *private_data) { struct client_state *clnt = (struct client_state *)private_data; printf("discovery session logged out, Message from main() was:[%s]\n", clnt->message); if (status != 0) { printf("Failed to logout from target. : %s\n", iscsi_get_error(iscsi)); exit(10); } printf("disconnect socket\n"); if (iscsi_disconnect(iscsi) != 0) { printf("Failed to disconnect old socket\n"); exit(10); } printf("reconnect with normal login to [%s]\n", clnt->target_address); printf("Use targetname [%s] when connecting\n", clnt->target_name); if (iscsi_set_targetname(iscsi, clnt->target_name)) { printf("Failed to set target name\n"); exit(10); } if (iscsi_set_alias(iscsi, "ronnie") != 0) { printf("Failed to add alias\n"); exit(10); } if (iscsi_set_session_type(iscsi, ISCSI_SESSION_NORMAL) != 0) { printf("Failed to set settion type to normal\n"); exit(10); } if (iscsi_connect_async(iscsi, clnt->target_address, normalconnect_cb, clnt) != 0) { printf("iscsi_connect failed : %s\n", iscsi_get_error(iscsi)); exit(10); } } void discovery_cb(struct iscsi_context *iscsi, int status, void *command_data, void *private_data) { struct client_state *clnt = (struct client_state *)private_data; struct iscsi_discovery_address *addr; printf("discovery callback status:%04x\n", status); if (status != 0 || command_data == NULL) { printf("Failed to do discovery on target. : %s\n", iscsi_get_error(iscsi)); exit(10); } for(addr = command_data; addr; addr = addr->next) { printf("Target:%s Address:%s\n", addr->target_name, addr->portals->portal); } addr=command_data; clnt->has_discovered_target = 1; clnt->target_name = strdup(addr->target_name); clnt->target_address = strdup(addr->portals->portal); printf("discovery complete, send logout command\n"); if (iscsi_logout_async(iscsi, discoverylogout_cb, private_data) != 0) { printf("iscsi_logout_async failed : %s\n", iscsi_get_error(iscsi)); exit(10); } } void discoverylogin_cb(struct iscsi_context *iscsi, int status, void *command_data _U_, void *private_data) { if (status != 0) { printf("Failed to log in to target. : %s\n", iscsi_get_error(iscsi)); exit(10); } printf("Logged in to target, send discovery command\n"); if (iscsi_discovery_async(iscsi, discovery_cb, private_data) != 0) { printf("failed to send discovery command : %s\n", iscsi_get_error(iscsi)); exit(10); } } void discoveryconnect_cb(struct iscsi_context *iscsi, int status, void *command_data _U_, void *private_data) { printf("Connected to iscsi socket status:0x%08x\n", status); if (status != 0) { printf("discoveryconnect_cb: connection failed : %s\n", iscsi_get_error(iscsi)); exit(10); } printf("connected, send login command\n"); iscsi_set_session_type(iscsi, ISCSI_SESSION_DISCOVERY); if (iscsi_login_async(iscsi, discoverylogin_cb, private_data) != 0) { printf("iscsi_login_async failed : %s\n", iscsi_get_error(iscsi)); exit(10); } } int main(int argc _U_, char *argv[] _U_) { struct iscsi_context *iscsi; struct pollfd pfd; struct client_state clnt; printf("iscsi client\n"); #if defined(WIN32) if (WSAStartup(MAKEWORD(2,2), &wsaData) != 0) { printf("Failed to start Winsock2\n"); exit(10); } #endif memset(&clnt, 0, sizeof(clnt)); iscsi = iscsi_create_context("iqn.2002-10.com.ronnie:client"); if (iscsi == NULL) { printf("Failed to create context\n"); exit(10); } if (iscsi_set_alias(iscsi, "ronnie") != 0) { printf("Failed to add alias\n"); exit(10); } clnt.message = "Hello iSCSI"; clnt.has_discovered_target = 0; if (iscsi_connect_async(iscsi, TARGET, discoveryconnect_cb, &clnt) != 0) { printf("iscsi_connect failed. %s\n", iscsi_get_error(iscsi)); exit(10); } while (clnt.finished == 0) { pfd.fd = iscsi_get_fd(iscsi); pfd.events = iscsi_which_events(iscsi); if (poll(&pfd, 1, -1) < 0) { printf("Poll failed"); exit(10); } if (iscsi_service(iscsi, pfd.revents) < 0) { printf("iscsi_service failed with : %s\n", iscsi_get_error(iscsi)); break; } } iscsi_destroy_context(iscsi); if (clnt.target_name != NULL) { free(clnt.target_name); } if (clnt.target_address != NULL) { free(clnt.target_address); } printf("ok\n"); return 0; } libiscsi-1.17.0/examples/ld_iscsi.c000066400000000000000000000451541271371262700171750ustar00rootroot00000000000000/* Copyright (C) 2011, 2012 by Ronnie Sahlberg This program 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 program is distributed in the hope that 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 program; if not, see . */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include "iscsi.h" #include "iscsi-private.h" #include "scsi-lowlevel.h" #include #include #include static const char *initiator = "iqn.2007-10.com.github:sahlberg:libiscsi:ld-iscsi"; #define ISCSI_MAX_FD 255 static int debug = 0; #define LD_ISCSI_DPRINTF(level,fmt,args...) do { if ((debug) >= level) {fprintf(stderr,"ld_iscsi: ");fprintf(stderr, (fmt), ##args); fprintf(stderr,"\n");} } while (0); struct iscsi_fd_list { int is_iscsi; int dup2fd; int in_flight; struct iscsi_context *iscsi; int lun; uint32_t block_size; uint64_t num_blocks; off_t offset; mode_t mode; int get_lba_status; struct scsi_lba_status_descriptor lbasd_cached; int lbasd_cache_valid; }; static struct iscsi_fd_list iscsi_fd_list[ISCSI_MAX_FD]; int (*real_open)(__const char *path, int flags, mode_t mode); int open(const char *path, int flags, mode_t mode) { int fd; if (!strncmp(path, "iscsi:", 6)) { struct iscsi_context *iscsi; struct iscsi_url *iscsi_url; struct scsi_task *task; struct scsi_readcapacity16 *rc16; if (mode & O_NONBLOCK) { LD_ISCSI_DPRINTF(0,"Non-blocking I/O is currently not supported"); errno = EINVAL; return -1; } iscsi = iscsi_create_context(initiator); if (iscsi == NULL) { LD_ISCSI_DPRINTF(0,"Failed to create context"); errno = ENOMEM; return -1; } iscsi_url = iscsi_parse_full_url(iscsi, path); if (iscsi_url == NULL) { LD_ISCSI_DPRINTF(0,"Failed to parse URL: %s\n", iscsi_get_error(iscsi)); iscsi_destroy_context(iscsi); errno = EINVAL; return -1; } iscsi_set_session_type(iscsi, ISCSI_SESSION_NORMAL); iscsi_set_header_digest(iscsi, ISCSI_HEADER_DIGEST_NONE_CRC32C); if (iscsi_full_connect_sync(iscsi, iscsi_url->portal, iscsi_url->lun) != 0) { LD_ISCSI_DPRINTF(0,"Login Failed. %s\n", iscsi_get_error(iscsi)); iscsi_destroy_url(iscsi_url); iscsi_destroy_context(iscsi); errno = EIO; return -1; } task = iscsi_readcapacity16_sync(iscsi, iscsi_url->lun); if (task == NULL || task->status != SCSI_STATUS_GOOD) { LD_ISCSI_DPRINTF(0,"failed to send readcapacity command"); iscsi_destroy_url(iscsi_url); iscsi_destroy_context(iscsi); errno = EIO; return -1; } rc16 = scsi_datain_unmarshall(task); if (rc16 == NULL) { LD_ISCSI_DPRINTF(0,"failed to unmarshall readcapacity10 data"); scsi_free_scsi_task(task); iscsi_destroy_url(iscsi_url); iscsi_destroy_context(iscsi); errno = EIO; return -1; } LD_ISCSI_DPRINTF(4,"readcapacity16_sync: block_size: %d, num_blocks: %"PRIu64,rc16->block_length,rc16->returned_lba + 1); fd = iscsi_get_fd(iscsi); if (fd >= ISCSI_MAX_FD) { LD_ISCSI_DPRINTF(0,"Too many files open"); iscsi_destroy_url(iscsi_url); iscsi_destroy_context(iscsi); errno = ENFILE; return -1; } iscsi_fd_list[fd].is_iscsi = 1; iscsi_fd_list[fd].dup2fd = -1; iscsi_fd_list[fd].iscsi = iscsi; iscsi_fd_list[fd].block_size = rc16->block_length; iscsi_fd_list[fd].num_blocks = rc16->returned_lba + 1; iscsi_fd_list[fd].offset = 0; iscsi_fd_list[fd].lun = iscsi_url->lun; iscsi_fd_list[fd].mode = mode; if (getenv("LD_ISCSI_GET_LBA_STATUS") != NULL) { iscsi_fd_list[fd].get_lba_status = atoi(getenv("LD_ISCSI_GET_LBA_STATUS")); if (rc16->lbpme == 0){ LD_ISCSI_DPRINTF(1,"Logical unit is fully provisioned. Will skip get_lba_status tasks"); iscsi_fd_list[fd].get_lba_status = 0; } } scsi_free_scsi_task(task); iscsi_destroy_url(iscsi_url); return fd; } return real_open(path, flags, mode); } int open64(const char *path, int flags, mode_t mode) { return open(path, flags | O_LARGEFILE, mode); } int (*real_close)(int fd); int close(int fd) { if (iscsi_fd_list[fd].is_iscsi == 1) { int i; if (iscsi_fd_list[fd].dup2fd >= 0) { iscsi_fd_list[fd].is_iscsi = 0; iscsi_fd_list[fd].dup2fd = -1; real_close(fd); return 0; } /* are there any FDs dup2ed onto this ? */ for(i = 0; i < ISCSI_MAX_FD; i++) { if (iscsi_fd_list[i].dup2fd == fd) { break; } } if (i < ISCSI_MAX_FD) { int j; /* yes there are DUPs onto fd, make i the new real device and repoint all other * duplicates */ memcpy(&iscsi_fd_list[i], &iscsi_fd_list[fd], sizeof(struct iscsi_fd_list)); iscsi_fd_list[i].dup2fd = -1; memset(&iscsi_fd_list[fd], 0, sizeof(struct iscsi_fd_list)); iscsi_fd_list[fd].dup2fd = -1; iscsi_fd_list[i].iscsi->fd = i; real_close(fd); for(j = 0; j < ISCSI_MAX_FD; j++) { if (j != i && iscsi_fd_list[j].dup2fd == fd) { iscsi_fd_list[j].dup2fd = i; } } return 0; } iscsi_fd_list[fd].is_iscsi = 0; iscsi_fd_list[fd].dup2fd = -1; iscsi_destroy_context(iscsi_fd_list[fd].iscsi); iscsi_fd_list[fd].iscsi = NULL; return 0; } return real_close(fd); } int (*real_fxstat)(int ver, int fd, struct stat *buf); int __fxstat(int ver, int fd, struct stat *buf) { if (iscsi_fd_list[fd].is_iscsi == 1) { if (iscsi_fd_list[fd].dup2fd >= 0) { return __fxstat(ver, iscsi_fd_list[fd].dup2fd, buf); } memset(buf, 0, sizeof(struct stat)); buf->st_mode = S_IRUSR | S_IRGRP | S_IROTH | S_IFREG; buf->st_size = iscsi_fd_list[fd].num_blocks * iscsi_fd_list[fd].block_size; return 0; } return real_fxstat(ver, fd, buf); } int (*real_lxstat)(int ver, __const char *path, struct stat *buf); int __lxstat(int ver, const char *path, struct stat *buf) { if (!strncmp(path, "iscsi:", 6)) { int fd, ret; fd = open(path, 0, 0); if (fd == -1) { return fd; } ret = __fxstat(ver, fd, buf); close(fd); return ret; } return real_lxstat(ver, path, buf); } int (*real_xstat)(int ver, __const char *path, struct stat *buf); int __xstat(int ver, const char *path, struct stat *buf) { return __lxstat(ver, path, buf); } off_t (*real_lseek)(int fd, off_t offset, int whence); off_t lseek(int fd, off_t offset, int whence) { if (iscsi_fd_list[fd].is_iscsi == 1) { off_t new_offset; off_t size = iscsi_fd_list[fd].num_blocks*iscsi_fd_list[fd].block_size; switch (whence) { case SEEK_SET: new_offset = offset; break; case SEEK_CUR: new_offset = iscsi_fd_list[fd].offset+offset; break; case SEEK_END: new_offset = size + offset; break; default: errno = EINVAL; return -1; } if (new_offset < 0 || new_offset > size) { errno = EINVAL; return -1; } iscsi_fd_list[fd].offset=new_offset; return iscsi_fd_list[fd].offset; } return real_lseek(fd, offset, whence); } ssize_t (*real_read)(int fd, void *buf, size_t count); ssize_t read(int fd, void *buf, size_t count) { if ((iscsi_fd_list[fd].is_iscsi == 1) && (iscsi_fd_list[fd].in_flight == 0)) { uint64_t offset; uint64_t num_blocks, lba; struct scsi_task *task; struct scsi_get_lba_status *lbas; if (iscsi_fd_list[fd].dup2fd >= 0) { return read(iscsi_fd_list[fd].dup2fd, buf, count); } offset = iscsi_fd_list[fd].offset / iscsi_fd_list[fd].block_size * iscsi_fd_list[fd].block_size; num_blocks = (iscsi_fd_list[fd].offset - offset + count + iscsi_fd_list[fd].block_size - 1) / iscsi_fd_list[fd].block_size; lba = offset / iscsi_fd_list[fd].block_size; /* Don't try to read beyond the last LBA */ if (lba >= iscsi_fd_list[fd].num_blocks) { return 0; } /* Trim num_blocks requested to last lba */ if ((lba + num_blocks) > iscsi_fd_list[fd].num_blocks) { num_blocks = iscsi_fd_list[fd].num_blocks - lba; count = num_blocks * iscsi_fd_list[fd].block_size; } iscsi_fd_list[fd].in_flight = 1; if (iscsi_fd_list[fd].get_lba_status != 0) { uint32_t i; uint32_t _num_allocated=0; uint32_t _num_blocks=0; if (iscsi_fd_list[fd].lbasd_cache_valid==1) { LD_ISCSI_DPRINTF(5,"cached get_lba_status_descriptor is lba %"PRIu64", num_blocks %d, provisioning %d",iscsi_fd_list[fd].lbasd_cached.lba,iscsi_fd_list[fd].lbasd_cached.num_blocks,iscsi_fd_list[fd].lbasd_cached.provisioning); if (iscsi_fd_list[fd].lbasd_cached.provisioning != 0x00 && lba >= iscsi_fd_list[fd].lbasd_cached.lba && lba+num_blocks < iscsi_fd_list[fd].lbasd_cached.lba+iscsi_fd_list[fd].lbasd_cached.num_blocks) { LD_ISCSI_DPRINTF(4,"skipped read16_sync for non-allocated blocks: lun %d, lba %"PRIu64", num_blocks: %"PRIu64", block_size: %d, offset: %"PRIu64" count: %lu",iscsi_fd_list[fd].lun,lba,num_blocks,iscsi_fd_list[fd].block_size,offset,(unsigned long)count); memset(buf, 0x00, count); iscsi_fd_list[fd].offset += count; iscsi_fd_list[fd].in_flight = 0; return count; } } LD_ISCSI_DPRINTF(4,"get_lba_status_sync: lun %d, lba %"PRIu64", num_blocks: %"PRIu64,iscsi_fd_list[fd].lun,lba,num_blocks); task = iscsi_get_lba_status_sync(iscsi_fd_list[fd].iscsi, iscsi_fd_list[fd].lun, lba, 8+16); if (task == NULL || task->status != SCSI_STATUS_GOOD) { LD_ISCSI_DPRINTF(0,"failed to send get_lba_status command"); iscsi_fd_list[fd].in_flight = 0; errno = EIO; return -1; } lbas = scsi_datain_unmarshall(task); if (lbas == NULL) { LD_ISCSI_DPRINTF(0,"failed to unmarshall get_lba_status data"); scsi_free_scsi_task(task); iscsi_fd_list[fd].in_flight = 0; errno = EIO; return -1; } LD_ISCSI_DPRINTF(5,"get_lba_status: num_descriptors: %d",lbas->num_descriptors); for (i=0;inum_descriptors;i++) { struct scsi_lba_status_descriptor *lbasd = &lbas->descriptors[i]; LD_ISCSI_DPRINTF(5,"get_lba_status_descriptor %d, lba %"PRIu64", num_blocks %d, provisioning %d",i,lbasd->lba,lbasd->num_blocks,lbasd->provisioning); if (lbasd->lba != _num_blocks+lba) { LD_ISCSI_DPRINTF(0,"get_lba_status response is non-continuous"); scsi_free_scsi_task(task); iscsi_fd_list[fd].in_flight = 0; errno = EIO; return -1; } _num_allocated+=(lbasd->provisioning==0x00)?lbasd->num_blocks:0; _num_blocks+=lbasd->num_blocks; iscsi_fd_list[fd].lbasd_cached=lbas->descriptors[i]; iscsi_fd_list[fd].lbasd_cache_valid=1; } scsi_free_scsi_task(task); if (_num_allocated == 0 && _num_blocks >= num_blocks) { LD_ISCSI_DPRINTF(4,"skipped read16_sync for non-allocated blocks: lun %d, lba %"PRIu64", num_blocks: %"PRIu64", block_size: %d, offset: %"PRIu64" count: %lu",iscsi_fd_list[fd].lun,lba,num_blocks,iscsi_fd_list[fd].block_size,offset,(unsigned long)count); memset(buf, 0x00, count); iscsi_fd_list[fd].offset += count; iscsi_fd_list[fd].in_flight = 0; return count; } } LD_ISCSI_DPRINTF(4,"read16_sync: lun %d, lba %"PRIu64", num_blocks: %"PRIu64", block_size: %d, offset: %"PRIu64" count: %lu",iscsi_fd_list[fd].lun,lba,num_blocks,iscsi_fd_list[fd].block_size,offset,(unsigned long)count); task = iscsi_read16_sync(iscsi_fd_list[fd].iscsi, iscsi_fd_list[fd].lun, lba, num_blocks * iscsi_fd_list[fd].block_size, iscsi_fd_list[fd].block_size, 0, 0, 0, 0, 0); iscsi_fd_list[fd].in_flight = 0; if (task == NULL || task->status != SCSI_STATUS_GOOD) { LD_ISCSI_DPRINTF(0,"failed to send read16 command"); errno = EIO; return -1; } memcpy(buf, &task->datain.data[iscsi_fd_list[fd].offset - offset], count); iscsi_fd_list[fd].offset += count; scsi_free_scsi_task(task); return count; } return real_read(fd, buf, count); } ssize_t (*real_pread)(int fd, void *buf, size_t count, off_t offset); ssize_t pread(int fd, void *buf, size_t count, off_t offset) { if ((iscsi_fd_list[fd].is_iscsi == 1 && iscsi_fd_list[fd].in_flight == 0)) { off_t old_offset; if ((old_offset = lseek(fd, 0, SEEK_CUR)) < 0) { errno = EIO; return -1; } if (lseek(fd, offset, SEEK_SET) < 0) { return -1; } if (read(fd, buf, count) < 0) { lseek(fd, old_offset, SEEK_SET); return -1; } lseek(fd, old_offset, SEEK_SET); return count; } return real_pread(fd, buf, count, offset); } ssize_t (*real_write)(int fd, const void *buf, size_t count); ssize_t write(int fd, const void *buf, size_t count) { if ((iscsi_fd_list[fd].is_iscsi == 1) && (iscsi_fd_list[fd].in_flight == 0)) { uint64_t offset; uint64_t num_blocks, lba; struct scsi_task *task; if (iscsi_fd_list[fd].dup2fd >= 0) { return write(iscsi_fd_list[fd].dup2fd, buf, count); } if (iscsi_fd_list[fd].offset%iscsi_fd_list[fd].block_size) { errno = EINVAL; return -1; } if (count%iscsi_fd_list[fd].block_size) { errno = EINVAL; return -1; } iscsi_fd_list[fd].lbasd_cache_valid = 0; offset = iscsi_fd_list[fd].offset; num_blocks = count/iscsi_fd_list[fd].block_size; lba = offset / iscsi_fd_list[fd].block_size; /* Don't try to read beyond the last LBA */ if (lba >= iscsi_fd_list[fd].num_blocks) { return 0; } /* Trim num_blocks requested to last lba */ if ((lba + num_blocks) > iscsi_fd_list[fd].num_blocks) { num_blocks = iscsi_fd_list[fd].num_blocks - lba; count = num_blocks * iscsi_fd_list[fd].block_size; } iscsi_fd_list[fd].in_flight = 1; LD_ISCSI_DPRINTF(4,"write16_sync: lun %d, lba %"PRIu64", num_blocks: %"PRIu64", block_size: %d, offset: %"PRIu64" count: %lu",iscsi_fd_list[fd].lun,lba,num_blocks,iscsi_fd_list[fd].block_size,offset,(unsigned long)count); task = iscsi_write16_sync(iscsi_fd_list[fd].iscsi, iscsi_fd_list[fd].lun, lba, (unsigned char *) buf, count, iscsi_fd_list[fd].block_size, 0, 0, 0, 0, 0); iscsi_fd_list[fd].in_flight = 0; if (task == NULL || task->status != SCSI_STATUS_GOOD) { LD_ISCSI_DPRINTF(0,"failed to send write16 command"); errno = EIO; return -1; } iscsi_fd_list[fd].offset += count; scsi_free_scsi_task(task); return count; } return real_write(fd, buf, count); } ssize_t (*real_pwrite)(int fd, const void *buf, size_t count, off_t offset); ssize_t pwrite(int fd, const void *buf, size_t count, off_t offset) { if ((iscsi_fd_list[fd].is_iscsi == 1 && iscsi_fd_list[fd].in_flight == 0)) { off_t old_offset; if ((old_offset = lseek(fd, 0, SEEK_CUR)) < 0) { errno = EIO; return -1; } if (lseek(fd, offset, SEEK_SET) < 0) { return -1; } if (write(fd, buf, count) < 0) { lseek(fd, old_offset, SEEK_SET); return -1; } lseek(fd, old_offset, SEEK_SET); return count; } return real_pwrite(fd, buf, count, offset); } int (*real_dup2)(int oldfd, int newfd); int dup2(int oldfd, int newfd) { if (iscsi_fd_list[newfd].is_iscsi) { return real_dup2(oldfd, newfd); } close(newfd); if (iscsi_fd_list[oldfd].is_iscsi == 1) { int ret; if (iscsi_fd_list[oldfd].dup2fd >= 0) { return dup2(iscsi_fd_list[oldfd].dup2fd, newfd); } ret = real_dup2(oldfd, newfd); if (ret < 0) { return ret; } iscsi_fd_list[newfd].is_iscsi = 1; iscsi_fd_list[newfd].dup2fd = oldfd; return newfd; } return real_dup2(oldfd, newfd); } #if defined(_LARGEFILE64_SOURCE) && _FILE_OFFSET_BITS != 64 int (*real_fxstat64)(int ver, int fd, struct stat64 *buf); int __fxstat64(int ver, int fd, struct stat64 *buf) { if (iscsi_fd_list[fd].is_iscsi == 1) { if (iscsi_fd_list[fd].dup2fd >= 0) { return __fxstat64(ver, iscsi_fd_list[fd].dup2fd, buf); } memset(buf, 0, sizeof(struct stat64)); buf->st_mode = S_IRUSR | S_IRGRP | S_IROTH | S_IFREG; buf->st_size = iscsi_fd_list[fd].num_blocks * iscsi_fd_list[fd].block_size; return 0; } return real_fxstat64(ver, fd, buf); } int (*real_lxstat64)(int ver, __const char *path, struct stat64 *buf); int __lxstat64(int ver, const char *path, struct stat64 *buf) { if (!strncmp(path, "iscsi:", 6)) { int fd, ret; fd = open64(path, 0, 0); if (fd == -1) { return fd; } ret = __fxstat64(ver, fd, buf); close(fd); return ret; } return real_lxstat64(ver, path, buf); } int (*real_xstat64)(int ver, __const char *path, struct stat64 *buf); int __xstat64(int ver, const char *path, struct stat64 *buf) { return __lxstat64(ver, path, buf); } #endif static void __attribute__((constructor)) _init(void) { int i; for(i = 0; i < ISCSI_MAX_FD; i++) { iscsi_fd_list[i].dup2fd = -1; } if (getenv("LD_ISCSI_DEBUG") != NULL) { debug = atoi(getenv("LD_ISCSI_DEBUG")); } real_open = dlsym(RTLD_NEXT, "open"); if (real_open == NULL) { LD_ISCSI_DPRINTF(0,"Failed to dlsym(open)"); exit(10); } real_close = dlsym(RTLD_NEXT, "close"); if (real_close == NULL) { LD_ISCSI_DPRINTF(0,"Failed to dlsym(close)"); exit(10); } real_fxstat = dlsym(RTLD_NEXT, "__fxstat"); if (real_fxstat == NULL) { LD_ISCSI_DPRINTF(0,"Failed to dlsym(__fxstat)"); exit(10); } real_lxstat = dlsym(RTLD_NEXT, "__lxstat"); if (real_lxstat == NULL) { LD_ISCSI_DPRINTF(0,"Failed to dlsym(__lxstat)"); exit(10); } real_xstat = dlsym(RTLD_NEXT, "__xstat"); if (real_xstat == NULL) { LD_ISCSI_DPRINTF(0,"Failed to dlsym(__xstat)"); exit(10); } real_lseek = dlsym(RTLD_NEXT, "lseek"); if (real_lseek == NULL) { LD_ISCSI_DPRINTF(0,"Failed to dlsym(lseek)"); exit(10); } real_read = dlsym(RTLD_NEXT, "read"); if (real_read == NULL) { LD_ISCSI_DPRINTF(0,"Failed to dlsym(read)"); exit(10); } real_pread = dlsym(RTLD_NEXT, "pread"); if (real_pread == NULL) { LD_ISCSI_DPRINTF(0,"Failed to dlsym(pread)"); exit(10); } real_write = dlsym(RTLD_NEXT, "write"); if (real_write == NULL) { LD_ISCSI_DPRINTF(0,"Failed to dlsym(write)"); exit(10); } real_pwrite = dlsym(RTLD_NEXT, "pwrite"); if (real_pwrite == NULL) { LD_ISCSI_DPRINTF(0,"Failed to dlsym(pwrite)"); exit(10); } real_dup2 = dlsym(RTLD_NEXT, "dup2"); if (real_dup2 == NULL) { LD_ISCSI_DPRINTF(0,"Failed to dlsym(dup2)"); exit(10); } #if defined(_LARGEFILE64_SOURCE) && _FILE_OFFSET_BITS != 64 real_fxstat64 = dlsym(RTLD_NEXT, "__fxstat64"); if (real_fxstat64 == NULL) { LD_ISCSI_DPRINTF(0,"Failed to dlsym(__fxstat64)"); } real_lxstat64 = dlsym(RTLD_NEXT, "__lxstat64"); if (real_lxstat64 == NULL) { LD_ISCSI_DPRINTF(0,"Failed to dlsym(_lxstat64)"); } real_xstat64 = dlsym(RTLD_NEXT, "__xstat64"); if (real_xstat64 == NULL) { LD_ISCSI_DPRINTF(0,"Failed to dlsym(__xstat64)"); } #endif } libiscsi-1.17.0/include/000077500000000000000000000000001271371262700150345ustar00rootroot00000000000000libiscsi-1.17.0/include/iscsi-private.h000066400000000000000000000303341271371262700177720ustar00rootroot00000000000000/* Copyright (C) 2010 by Ronnie Sahlberg This program 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 program is distributed in the hope that 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 program; if not, see . */ #ifndef __iscsi_private_h__ #define __iscsi_private_h__ #include #include #if defined(WIN32) #include #define ssize_t SSIZE_T #endif #ifdef __cplusplus extern "C" { #endif #ifndef discard_const #define discard_const(ptr) ((void *)((intptr_t)(ptr))) #endif #ifndef MIN #define MIN(a, b) (((a) < (b)) ? (a) : (b)) #endif #ifndef MAX #define MAX(a, b) (((a) > (b)) ? (a) : (b)) #endif #define ISCSI_RAW_HEADER_SIZE 48 #define ISCSI_DIGEST_SIZE 4 #define ISCSI_HEADER_SIZE (ISCSI_RAW_HEADER_SIZE \ + (iscsi->header_digest == ISCSI_HEADER_DIGEST_NONE?0:ISCSI_DIGEST_SIZE)) #define SMALL_ALLOC_MAX_FREE (128) /* must be power of 2 */ struct iscsi_in_pdu { struct iscsi_in_pdu *next; long long hdr_pos; unsigned char hdr[ISCSI_RAW_HEADER_SIZE + ISCSI_DIGEST_SIZE]; long long data_pos; unsigned char *data; }; void iscsi_free_iscsi_in_pdu(struct iscsi_context *iscsi, struct iscsi_in_pdu *in); void iscsi_free_iscsi_inqueue(struct iscsi_context *iscsi, struct iscsi_in_pdu *inqueue); /* size of chap response field */ #define CHAP_R_SIZE 16 /* max length of chap challange */ #define MAX_CHAP_C_LENGTH 2048 struct iscsi_context { char initiator_name[MAX_STRING_SIZE+1]; char target_name[MAX_STRING_SIZE+1]; char target_address[MAX_STRING_SIZE+1]; /* If a redirect */ char connected_portal[MAX_STRING_SIZE+1]; char portal[MAX_STRING_SIZE+1]; char alias[MAX_STRING_SIZE+1]; char bind_interfaces[MAX_STRING_SIZE+1]; char user[MAX_STRING_SIZE+1]; char passwd[MAX_STRING_SIZE+1]; char chap_c[MAX_CHAP_C_LENGTH+1]; char target_user[MAX_STRING_SIZE+1]; char target_passwd[MAX_STRING_SIZE+1]; uint32_t target_chap_i; unsigned char target_chap_r[CHAP_R_SIZE]; char error_string[MAX_STRING_SIZE+1]; enum iscsi_session_type session_type; unsigned char isid[6]; uint32_t itt; uint32_t cmdsn; uint32_t min_cmdsn_waiting; uint32_t expcmdsn; uint32_t maxcmdsn; uint32_t statsn; enum iscsi_header_digest want_header_digest; enum iscsi_header_digest header_digest; int fd; int is_connected; int is_corked; int tcp_user_timeout; int tcp_keepcnt; int tcp_keepintvl; int tcp_keepidle; int tcp_syncnt; int current_phase; int next_phase; #define ISCSI_LOGIN_SECNEG_PHASE_OFFER_CHAP 0 #define ISCSI_LOGIN_SECNEG_PHASE_SELECT_ALGORITHM 1 #define ISCSI_LOGIN_SECNEG_PHASE_SEND_RESPONSE 2 int secneg_phase; int login_attempts; int is_loggedin; int bind_interfaces_cnt; int nops_in_flight; int chap_a; int chap_i; iscsi_command_cb socket_status_cb; void *connect_data; struct iscsi_pdu *outqueue; struct iscsi_pdu *outqueue_current; struct iscsi_pdu *waitpdu; struct iscsi_in_pdu *incoming; struct iscsi_in_pdu *inqueue; uint32_t max_burst_length; uint32_t first_burst_length; uint32_t initiator_max_recv_data_segment_length; uint32_t target_max_recv_data_segment_length; enum iscsi_initial_r2t want_initial_r2t; enum iscsi_initial_r2t use_initial_r2t; enum iscsi_immediate_data want_immediate_data; enum iscsi_immediate_data use_immediate_data; int lun; int no_auto_reconnect; int reconnect_deferred; int reconnect_max_retries; int pending_reconnect; int log_level; iscsi_log_fn log_fn; int mallocs; int reallocs; int frees; int smallocs; void* smalloc_ptrs[SMALL_ALLOC_MAX_FREE]; int smalloc_free; size_t smalloc_size; int cache_allocations; time_t next_reconnect; int scsi_timeout; struct iscsi_context *old_iscsi; int retry_cnt; int no_ua_on_reconnect; }; #define ISCSI_PDU_IMMEDIATE 0x40 #define ISCSI_PDU_TEXT_FINAL 0x80 #define ISCSI_PDU_TEXT_CONTINUE 0x40 #define ISCSI_PDU_LOGIN_TRANSIT 0x80 #define ISCSI_PDU_LOGIN_CONTINUE 0x40 #define ISCSI_PDU_LOGIN_CSG_SECNEG 0x00 #define ISCSI_PDU_LOGIN_CSG_OPNEG 0x04 #define ISCSI_PDU_LOGIN_CSG_FF 0x0c #define ISCSI_PDU_LOGIN_NSG_SECNEG 0x00 #define ISCSI_PDU_LOGIN_NSG_OPNEG 0x01 #define ISCSI_PDU_LOGIN_NSG_FF 0x03 #define ISCSI_PDU_SCSI_FINAL 0x80 #define ISCSI_PDU_SCSI_READ 0x40 #define ISCSI_PDU_SCSI_WRITE 0x20 #define ISCSI_PDU_SCSI_ATTR_UNTAGGED 0x00 #define ISCSI_PDU_SCSI_ATTR_SIMPLE 0x01 #define ISCSI_PDU_SCSI_ATTR_ORDERED 0x02 #define ISCSI_PDU_SCSI_ATTR_HEADOFQUEUE 0x03 #define ISCSI_PDU_SCSI_ATTR_ACA 0x04 #define ISCSI_PDU_DATA_FINAL 0x80 #define ISCSI_PDU_DATA_ACK_REQUESTED 0x40 #define ISCSI_PDU_DATA_BIDIR_OVERFLOW 0x10 #define ISCSI_PDU_DATA_BIDIR_UNDERFLOW 0x08 #define ISCSI_PDU_DATA_RESIDUAL_OVERFLOW 0x04 #define ISCSI_PDU_DATA_RESIDUAL_UNDERFLOW 0x02 #define ISCSI_PDU_DATA_CONTAINS_STATUS 0x01 enum iscsi_opcode { ISCSI_PDU_NOP_OUT = 0x00, ISCSI_PDU_SCSI_REQUEST = 0x01, ISCSI_PDU_SCSI_TASK_MANAGEMENT_REQUEST = 0x02, ISCSI_PDU_LOGIN_REQUEST = 0x03, ISCSI_PDU_TEXT_REQUEST = 0x04, ISCSI_PDU_DATA_OUT = 0x05, ISCSI_PDU_LOGOUT_REQUEST = 0x06, ISCSI_PDU_NOP_IN = 0x20, ISCSI_PDU_SCSI_RESPONSE = 0x21, ISCSI_PDU_SCSI_TASK_MANAGEMENT_RESPONSE = 0x22, ISCSI_PDU_LOGIN_RESPONSE = 0x23, ISCSI_PDU_TEXT_RESPONSE = 0x24, ISCSI_PDU_DATA_IN = 0x25, ISCSI_PDU_LOGOUT_RESPONSE = 0x26, ISCSI_PDU_R2T = 0x31, ISCSI_PDU_ASYNC_MSG = 0x32, ISCSI_PDU_REJECT = 0x3f, ISCSI_PDU_NO_PDU = 0xff }; struct iscsi_scsi_cbdata { iscsi_command_cb callback; void *private_data; struct scsi_task *task; }; struct iscsi_pdu { struct iscsi_pdu *next; /* There will not be a response to this pdu, so delete it once it is sent on the wire. Don't put it on the wait-queue */ #define ISCSI_PDU_DELETE_WHEN_SENT 0x00000001 /* When reconnecting, just drop all these PDUs. Don't re-queue them. * This includes any DATA-OUT PDU as well as all NOPs. */ #define ISCSI_PDU_DROP_ON_RECONNECT 0x00000004 /* stop sending after this PDU has been sent */ #define ISCSI_PDU_CORK_WHEN_SENT 0x00000008 uint32_t flags; uint32_t lun; uint32_t itt; uint32_t cmdsn; uint32_t datasn; enum iscsi_opcode response_opcode; iscsi_command_cb callback; void *private_data; /* Used to track writing the iscsi header to the socket */ struct iscsi_data outdata; /* Header for PDU to send */ size_t outdata_written; /* How much of the header we have written */ /* Used to track writing the payload data to the socket */ uint32_t payload_offset; /* Offset of payload data to write */ uint32_t payload_len; /* Amount of payload data to write */ uint32_t payload_written; /* How much of the payload we have written */ struct iscsi_data indata; struct iscsi_scsi_cbdata scsi_cbdata; time_t scsi_timeout; uint32_t expxferlen; }; struct iscsi_pdu *iscsi_allocate_pdu(struct iscsi_context *iscsi, enum iscsi_opcode opcode, enum iscsi_opcode response_opcode, uint32_t itt, uint32_t flags); void iscsi_free_pdu(struct iscsi_context *iscsi, struct iscsi_pdu *pdu); void iscsi_pdu_set_pduflags(struct iscsi_pdu *pdu, unsigned char flags); void iscsi_pdu_set_immediate(struct iscsi_pdu *pdu); void iscsi_pdu_set_ttt(struct iscsi_pdu *pdu, uint32_t ttt); void iscsi_pdu_set_cmdsn(struct iscsi_pdu *pdu, uint32_t cmdsn); void iscsi_pdu_set_rcmdsn(struct iscsi_pdu *pdu, uint32_t rcmdsn); void iscsi_pdu_set_lun(struct iscsi_pdu *pdu, uint32_t lun); void iscsi_pdu_set_expstatsn(struct iscsi_pdu *pdu, uint32_t expstatsnsn); void iscsi_pdu_set_expxferlen(struct iscsi_pdu *pdu, uint32_t expxferlen); void iscsi_pdu_set_itt(struct iscsi_pdu *pdu, uint32_t itt); void iscsi_pdu_set_ritt(struct iscsi_pdu *pdu, uint32_t ritt); void iscsi_pdu_set_datasn(struct iscsi_pdu *pdu, uint32_t datasn); void iscsi_pdu_set_bufferoffset(struct iscsi_pdu *pdu, uint32_t bufferoffset); int iscsi_pdu_add_data(struct iscsi_context *iscsi, struct iscsi_pdu *pdu, unsigned char *dptr, int dsize); int iscsi_queue_pdu(struct iscsi_context *iscsi, struct iscsi_pdu *pdu); int iscsi_add_data(struct iscsi_context *iscsi, struct iscsi_data *data, unsigned char *dptr, int dsize, int pdualignment); struct scsi_task; void iscsi_pdu_set_cdb(struct iscsi_pdu *pdu, struct scsi_task *task); int iscsi_get_pdu_data_size(const unsigned char *hdr); int iscsi_get_pdu_padding_size(const unsigned char *hdr); int iscsi_process_pdu(struct iscsi_context *iscsi, struct iscsi_in_pdu *in); int iscsi_process_login_reply(struct iscsi_context *iscsi, struct iscsi_pdu *pdu, struct iscsi_in_pdu *in); int iscsi_process_text_reply(struct iscsi_context *iscsi, struct iscsi_pdu *pdu, struct iscsi_in_pdu *in); int iscsi_process_logout_reply(struct iscsi_context *iscsi, struct iscsi_pdu *pdu, struct iscsi_in_pdu *in); int iscsi_process_scsi_reply(struct iscsi_context *iscsi, struct iscsi_pdu *pdu, struct iscsi_in_pdu *in); int iscsi_process_scsi_data_in(struct iscsi_context *iscsi, struct iscsi_pdu *pdu, struct iscsi_in_pdu *in, int *is_finished); int iscsi_process_nop_out_reply(struct iscsi_context *iscsi, struct iscsi_pdu *pdu, struct iscsi_in_pdu *in); int iscsi_process_task_mgmt_reply(struct iscsi_context *iscsi, struct iscsi_pdu *pdu, struct iscsi_in_pdu *in); int iscsi_process_r2t(struct iscsi_context *iscsi, struct iscsi_pdu *pdu, struct iscsi_in_pdu *in); int iscsi_process_reject(struct iscsi_context *iscsi, struct iscsi_in_pdu *in); int iscsi_send_target_nop_out(struct iscsi_context *iscsi, uint32_t ttt, uint32_t lun); #if defined(WIN32) void iscsi_set_error(struct iscsi_context *iscsi, const char *error_string, ...); #else void iscsi_set_error(struct iscsi_context *iscsi, const char *error_string, ...) __attribute__((format(printf, 2, 3))); #endif struct scsi_iovector *iscsi_get_scsi_task_iovector_in(struct iscsi_context *iscsi, struct iscsi_in_pdu *in); struct scsi_iovector *iscsi_get_scsi_task_iovector_out(struct iscsi_context *iscsi, struct iscsi_pdu *pdu); void scsi_task_reset_iov(struct scsi_iovector *iovector); void* iscsi_malloc(struct iscsi_context *iscsi, size_t size); void* iscsi_zmalloc(struct iscsi_context *iscsi, size_t size); void* iscsi_realloc(struct iscsi_context *iscsi, void* ptr, size_t size); void iscsi_free(struct iscsi_context *iscsi, void* ptr); char* iscsi_strdup(struct iscsi_context *iscsi, const char* str); void* iscsi_szmalloc(struct iscsi_context *iscsi, size_t size); void iscsi_sfree(struct iscsi_context *iscsi, void* ptr); unsigned long crc32c(char *buf, int len); struct scsi_task *iscsi_scsi_get_task_from_pdu(struct iscsi_pdu *pdu); void iscsi_set_noautoreconnect(struct iscsi_context *iscsi, int state); void iscsi_decrement_iface_rr(void); #define ISCSI_LOG(iscsi, level, format, ...) \ do { \ if (level <= iscsi->log_level && iscsi->log_fn) { \ iscsi_log_message(iscsi, level, format, ## __VA_ARGS__); \ } \ } while (0) void iscsi_log_message(struct iscsi_context *iscsi, int level, const char *format, ...); void iscsi_add_to_outqueue(struct iscsi_context *iscsi, struct iscsi_pdu *pdu); int iscsi_serial32_compare(uint32_t s1, uint32_t s2); uint32_t iscsi_itt_post_increment(struct iscsi_context *iscsi); void iscsi_timeout_scan(struct iscsi_context *iscsi); void iscsi_reconnect_cb(struct iscsi_context *iscsi _U_, int status, void *command_data, void *private_data); #ifdef __cplusplus } #endif #endif /* __iscsi_private_h__ */ libiscsi-1.17.0/include/iscsi.h000066400000000000000000001347301271371262700163270ustar00rootroot00000000000000/* Copyright (C) 2010 by Ronnie Sahlberg This program 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 program is distributed in the hope that 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 program; if not, see . */ #ifndef __iscsi_h__ #define __iscsi_h__ #include #include #if defined(WIN32) #define EXTERN __declspec( dllexport ) #else #define EXTERN #endif #ifdef __cplusplus extern "C" { #endif struct iscsi_context; struct sockaddr; /* API VERSION */ #define LIBISCSI_API_VERSION (20160501) /* FEATURES */ #define LIBISCSI_FEATURE_IOVECTOR (1) #define LIBISCSI_FEATURE_NOP_COUNTER (1) #define MAX_STRING_SIZE (255) /* * Syntax for normal and portal/discovery URLs. */ #define ISCSI_URL_SYNTAX "\"iscsi://[[%]@]" \ "[:]//\"" #define ISCSI_PORTAL_URL_SYNTAX "\"iscsi://[[%]@]" \ "[:]\"" EXTERN void iscsi_set_cache_allocations(struct iscsi_context *iscsi, int ca); /* * The following three functions are used to integrate libiscsi in an event * system. */ /* * Returns the file descriptor that libiscsi uses. */ EXTERN int iscsi_get_fd(struct iscsi_context *iscsi); /* * Returns which events that we need to poll for for the iscsi file descriptor. * * This function can return 0 which means that there are no events to * poll for at this time. In that case the application should wait some time * before calling iscsi_which_events() again. This could for example happen * if we fail to reconnect the TCP session during an automatic session * reconnect. * When this function returns 0, the application should wait >=100ms * before trying again. */ EXTERN int iscsi_which_events(struct iscsi_context *iscsi); /* * Called to process the events when events become available for the iscsi * file descriptor. */ EXTERN int iscsi_service(struct iscsi_context *iscsi, int revents); /* * How many commands are in flight. */ EXTERN int iscsi_queue_length(struct iscsi_context *iscsi); /* * How many commands are queued for dispatch. */ EXTERN int iscsi_out_queue_length(struct iscsi_context *iscsi); /************************************************************ * Timeout Handling. * Libiscsi does not use or interface with any system timers. * Instead all timeout processing in libiscsi is done as part * of the iscsi_service() processing. * * This means that if you use the timeout function below you must * device your application to call out to iscsi_service() at regular * intervals. * An easy way to do this is calling iscsi_service(iscsi, 0), i.e. * by passing 0 as the revents arguments once every second or so. ************************************************************/ /* * Set the timeout in seconds after which a task/pdu will timeout. * This timeout applies to SCSI task PDUs as well as normal iSCSI * PDUs such as login/task management/logout/... * * Each PDU is assigned its timeout value upon creation and can not be * changed afterwards. I.e. When you change the default timeout, it will * only affect any commands that are issued in the future but will not * affect the timeouts for any commands already in flight. * * The recommended usecase is to set to a default value for all PDUs * and only change the default temporarily when a specific task needs * a different timeout. * * // Set default to 5 seconds for all commands at beginning of program. * iscsi_set_timeout(iscsi, 5); * * ... * // SANITIZE command will take long so set it to no tiemout. * iscsi_set_timeout(iscsi, 0); * iscsi_sanitize_task(iscsi, ... * iscsi_set_timeout(iscsi, ); * ... * * * Default is 0 == no timeout. */ EXTERN int iscsi_set_timeout(struct iscsi_context *iscsi, int timeout); /* * To set tcp keepalive for the session. * Only options supported by given platform (if any) are set. */ EXTERN int iscsi_set_tcp_keepalive(struct iscsi_context *iscsi, int idle, int count, int interval); struct iscsi_url { char portal[MAX_STRING_SIZE + 1]; char target[MAX_STRING_SIZE + 1]; char user[MAX_STRING_SIZE + 1]; char passwd[MAX_STRING_SIZE + 1]; char target_user[MAX_STRING_SIZE + 1]; char target_passwd[MAX_STRING_SIZE + 1]; int lun; struct iscsi_context *iscsi; }; /* * This function is used to set the desired mode for immediate data. * This can be set on a context before it has been logged in to the target * and controls how the initiator will try to negotiate the immediate data. * * Default is for libiscsi to try to negotiate ISCSI_IMMEDIATE_DATA_YES */ enum iscsi_immediate_data { ISCSI_IMMEDIATE_DATA_NO = 0, ISCSI_IMMEDIATE_DATA_YES = 1 }; int iscsi_set_immediate_data(struct iscsi_context *iscsi, enum iscsi_immediate_data immediate_data); /* * This function is used to set the desired mode for initial_r2t * This can be set on a context before it has been logged in to the target * and controls how the initiator will try to negotiate the initial r2t. * * Default is for libiscsi to try to negotiate ISCSI_INITIAL_R2T_NO */ enum iscsi_initial_r2t { ISCSI_INITIAL_R2T_NO = 0, ISCSI_INITIAL_R2T_YES = 1 }; int iscsi_set_initial_r2t(struct iscsi_context *iscsi, enum iscsi_initial_r2t initial_r2t); /* * This function is used to parse an iSCSI URL into a iscsi_url structure. * iSCSI URL format : * iscsi://[[%]@][:]// * * Target names are url encoded with '%' as a special character. * Example: * "iqn.ronnie.test%3A1234" will be translated to "iqn.ronnie.test:1234" * * Function will return a pointer to an iscsi url structure if successful, * or it will return NULL and set iscsi_get_error() accordingly if there was a problem * with the URL. * * CHAP username/password can also be provided via environment variables * LIBISCSI_CHAP_USERNAME=ronnie * LIBISCSI_CHAP_PASSWORD=password * * The returned structure is freed by calling iscsi_destroy_url() */ EXTERN struct iscsi_url *iscsi_parse_full_url(struct iscsi_context *iscsi, const char *url); EXTERN void iscsi_destroy_url(struct iscsi_url *iscsi_url); /* * This function is used to parse an iSCSI Portal URL into a iscsi_url structure. * iSCSI Portal URL format : * iscsi://[[%]@][:] * * iSCSI Portal URL is used when describing a discovery session, where the target-iqn and the * lun is not yet known. * * Function will return a pointer to an iscsi url structure if successful, * or it will return NULL and set iscsi_get_error() accordingly if there was a problem * with the URL. * * CHAP username/password can also be provided via environment variables * LIBISCSI_CHAP_USERNAME=ronnie * LIBISCSI_CHAP_PASSWORD=password * * The returned structure is freed by calling iscsi_destroy_url() */ EXTERN struct iscsi_url * iscsi_parse_portal_url(struct iscsi_context *iscsi, const char *url); /* * This function returns a description of the last encountered error. */ EXTERN const char *iscsi_get_error(struct iscsi_context *iscsi); /* * Create a context for an ISCSI session. * Initiator_name is the iqn name we want to identify to the target as. * * Returns: * non-NULL: success * NULL: error */ EXTERN struct iscsi_context *iscsi_create_context(const char *initiator_name); /* * Destroy an existing ISCSI context and tear down any existing connection. * Callbacks for any command in flight will be invoked with * SCSI_STATUS_CANCELLED. * * Returns: * 0: success * <0: error */ EXTERN int iscsi_destroy_context(struct iscsi_context *iscsi); /* * Set an optional alias name to identify with when connecting to the target * * Returns: * 0: success * <0: error */ EXTERN int iscsi_set_alias(struct iscsi_context *iscsi, const char *alias); /* * Set the iqn name of the taqget to login to. * The target name must be set before a normal-login can be initiated. * Only discovery-logins are possible without setting the target iqn name. * * Returns: * 0: success * <0: error */ EXTERN int iscsi_set_targetname(struct iscsi_context *iscsi, const char *targetname); /* * This function returns any target address supplied in a login response when * the target has moved. */ EXTERN const char *iscsi_get_target_address(struct iscsi_context *iscsi); /* Type of iscsi sessions. Discovery sessions are used to query for what * targets exist behind the portal connected to. Normal sessions are used to * log in and do I/O to the SCSI LUNs */ enum iscsi_session_type { ISCSI_SESSION_DISCOVERY = 1, ISCSI_SESSION_NORMAL = 2 }; /* * Set the session type for a scsi context. * Session type can only be set/changed before the context * is logged in to the target. * * Returns: * 0: success * <0: error */ EXTERN int iscsi_set_session_type(struct iscsi_context *iscsi, enum iscsi_session_type session_type); /* * Types of header digest we support. Default is NONE */ enum iscsi_header_digest { ISCSI_HEADER_DIGEST_NONE = 0, ISCSI_HEADER_DIGEST_NONE_CRC32C = 1, ISCSI_HEADER_DIGEST_CRC32C_NONE = 2, ISCSI_HEADER_DIGEST_CRC32C = 3, ISCSI_HEADER_DIGEST_LAST = ISCSI_HEADER_DIGEST_CRC32C }; /* * Set the desired header digest for a scsi context. * Header digest can only be set/changed before the context * is logged in to the target. * * Returns: * 0: success * <0: error */ EXTERN int iscsi_set_header_digest(struct iscsi_context *iscsi, enum iscsi_header_digest header_digest); /* * Specify the username and password to use for chap authentication */ EXTERN int iscsi_set_initiator_username_pwd(struct iscsi_context *iscsi, const char *user, const char *passwd); /* * Specify the username and password to use for target chap authentication. * Target/bidirectional CHAP is only supported if you also have normal * CHAP authentication. * You must configure CHAP first using iscsi_set_initiator_username_pwd() `* before you can set up target authentication. */ EXTERN int iscsi_set_target_username_pwd(struct iscsi_context *iscsi, const char *user, const char *passwd); /* * check if the context is logged in or not */ EXTERN int iscsi_is_logged_in(struct iscsi_context *iscsi); enum scsi_status { SCSI_STATUS_GOOD = 0, SCSI_STATUS_CHECK_CONDITION = 2, SCSI_STATUS_CONDITION_MET = 4, SCSI_STATUS_BUSY = 8, SCSI_STATUS_RESERVATION_CONFLICT = 0x18, SCSI_STATUS_TASK_SET_FULL = 0x28, SCSI_STATUS_ACA_ACTIVE = 0x30, SCSI_STATUS_TASK_ABORTED = 0x40, SCSI_STATUS_REDIRECT = 0x101, SCSI_STATUS_CANCELLED = 0x0f000000, SCSI_STATUS_ERROR = 0x0f000001, SCSI_STATUS_TIMEOUT = 0x0f000002 }; /* * Generic callback for completion of iscsi_*_async(). * command_data depends on status. */ typedef void (*iscsi_command_cb)(struct iscsi_context *iscsi, int status, void *command_data, void *private_data); /* * Asynchronous call to connect a TCP connection to the target-host/port * * Returns: * 0 if the call was initiated and a connection will be attempted. Result of * the connection will be reported through the callback function. * <0 if there was an error. The callback function will not be invoked. * * This command is unique in that the callback can be invoked twice. * * Callback parameters : * status can be either of : * SCSI_STATUS_GOOD : Connection was successful. Command_data is NULL. * In this case the callback will be invoked a * second time once the connection is torn down. * * SCSI_STATUS_ERROR : Either failed to establish the connection, or * an already established connection has failed * with an error. * * The callback will NOT be invoked if the session is explicitely torn down * through a call to iscsi_disconnect() or iscsi_destroy_context(). */ EXTERN int iscsi_connect_async(struct iscsi_context *iscsi, const char *portal, iscsi_command_cb cb, void *private_data); /* * Synchronous call to connect a TCP connection to the target-host/port * * Returns: * 0 if connected successfully. * <0 if there was an error. * */ EXTERN int iscsi_connect_sync(struct iscsi_context *iscsi, const char *portal); /* * Asynchronous call to connect a lun * This function will connect to the portal, login, and verify that the lun * is available. * * Returns: * 0 if the call was initiated and a connection will be attempted. Result * of the connection will be reported through the callback function. * <0 if there was an error. The callback function will not be invoked. * * This command is unique in that the callback can be invoked twice. * * Callback parameters : * status can be either of : * SCSI_STATUS_GOOD : Connection was successful. Command_data is NULL. * In this case the callback will be invoked a * second time once the connection is torn down. * * SCSI_STATUS_ERROR : Either failed to establish the connection, or * an already established connection has failed * with an error. * * The callback will NOT be invoked if the session is explicitely torn down * through a call to iscsi_disconnect() or iscsi_destroy_context(). */ EXTERN int iscsi_full_connect_async(struct iscsi_context *iscsi, const char *portal, int lun, iscsi_command_cb cb, void *private_data); /* * Synchronous call to connect a lun * This function will connect to the portal, login, and verify that the lun * is available. * * Returns: * 0 if the cconnect was successful. * <0 if there was an error. */ EXTERN int iscsi_full_connect_sync(struct iscsi_context *iscsi, const char *portal, int lun); /* * Disconnect a connection to a target. * You can not disconnect while being logged in to a target. * * Returns: * 0 disconnect was successful * <0 error */ EXTERN int iscsi_disconnect(struct iscsi_context *iscsi); /* * Disconnect a connection to a target and try to reconnect (async version). * This call returns immediately and the reconnect is processed in the * background. Commands send to this connection will be queued and not * processed until we have successfully reconnected. * * Returns: * 0 reconnect was successful * <0 error */ EXTERN int iscsi_reconnect(struct iscsi_context *iscsi); /* * Disconnect a connection to a target and try to reconnect (sync version). * This call will block until the connection is reestablished. * * Returns: * 0 reconnect was successful * <0 error */ EXTERN int iscsi_reconnect_sync(struct iscsi_context *iscsi); /* * Asynchronous call to perform an ISCSI login. * * Returns: * 0 if the call was initiated and a login will be attempted. Result of the * login will be reported through the callback function. * <0 if there was an error. The callback function will not be invoked. * * Callback parameters : * status can be either of : * SCSI_STATUS_GOOD : login was successful. Command_data is always * NULL. * SCSI_STATUS_CANCELLED: login was aborted. Command_data is NULL. * SCSI_STATUS_ERROR : login failed. Command_data is NULL. */ EXTERN int iscsi_login_async(struct iscsi_context *iscsi, iscsi_command_cb cb, void *private_data); /* * Synchronous call to perform an ISCSI login. * * Returns: * 0 if the login was successful * <0 if there was an error. */ EXTERN int iscsi_login_sync(struct iscsi_context *iscsi); /* * Asynchronous call to perform an ISCSI logout. * * Returns: * 0 if the call was initiated and a logout will be attempted. Result of the * logout will be reported through the callback function. * <0 if there was an error. The callback function will not be invoked. * * Callback parameters : * status can be either of : * SCSI_STATUS_GOOD : logout was successful. Command_data is always * NULL. * SCSI_STATUS_CANCELLED: logout was aborted. Command_data is NULL. */ EXTERN int iscsi_logout_async(struct iscsi_context *iscsi, iscsi_command_cb cb, void *private_data); /* * Synchronous call to perform an ISCSI logout. * * Returns: * 0 if the logout was successful * <0 if there was an error. */ EXTERN int iscsi_logout_sync(struct iscsi_context *iscsi); /* * Asynchronous call to perform an ISCSI discovery. * * discoveries can only be done on connected and logged in discovery sessions. * * Returns: * 0 if the call was initiated and a discovery will be attempted. Result * will be reported through the callback function. * <0 if there was an error. The callback function will not be invoked. * * Callback parameters : * status can be either of : * SCSI_STATUS_GOOD : Discovery was successful. Command_data is a * pointer to a iscsi_discovery_address list of * structures. * This list of structures is only valid for the * duration of the callback and all data will be * freed once the callback returns. * SCSI_STATUS_CANCELLED : Discovery was aborted. Command_data is NULL. */ EXTERN int iscsi_discovery_async(struct iscsi_context *iscsi, iscsi_command_cb cb, void *private_data); struct iscsi_target_portal { struct iscsi_target_portal *next; const char *portal; }; struct iscsi_discovery_address { struct iscsi_discovery_address *next; const char *target_name; struct iscsi_target_portal *portals; }; /* * Asynchronous call to perform an ISCSI NOP-OUT call * * Returns: * 0 if the call was initiated and a nop-out will be attempted. Result will * be reported through the callback function. * <0 if there was an error. The callback function will not be invoked. * * Callback parameters : * status can be either of : * SCSI_STATUS_GOOD : NOP-OUT was successful and the server responded * with a NOP-IN callback_data is a iscsi_data * structure containing the data returned from * the server. * SCSI_STATUS_CANCELLED : Discovery was aborted. Command_data is NULL. * * The callback may be NULL if you only want to let libiscsi count the in-flight * NOPs. */ EXTERN int iscsi_nop_out_async(struct iscsi_context *iscsi, iscsi_command_cb cb, unsigned char *data, int len, void *private_data); /* read out the number of consecutive nop outs that did not receive an answer */ EXTERN int iscsi_get_nops_in_flight(struct iscsi_context *iscsi); struct scsi_task; struct scsi_sense; enum iscsi_task_mgmt_funcs { ISCSI_TM_ABORT_TASK = 0x01, ISCSI_TM_ABORT_TASK_SET = 0x02, ISCSI_TM_CLEAR_ACA = 0x03, ISCSI_TM_CLEAR_TASK_SET = 0x04, ISCSI_TM_LUN_RESET = 0x05, ISCSI_TM_TARGET_WARM_RESET = 0x06, ISCSI_TM_TARGET_COLD_RESET = 0x07, ISCSI_TM_TASK_REASSIGN = 0x08 }; enum iscsi_task_mgmt_response { ISCSI_TMR_FUNC_COMPLETE = 0x0, ISCSI_TMR_TASK_DOES_NOT_EXIST = 0x1, ISCSI_TMR_LUN_DOES_NOT_EXIST = 0x2, ISCSI_TMR_TASK_STILL_ALLEGIANT = 0x3, ISCSI_TMR_TASK_ALLEGIANCE_REASS_NOT_SUPPORTED = 0x4, ISCSI_TMR_TMF_NOT_SUPPORTED = 0x5, ISCSI_TMR_FUNC_AUTH_FAILED = 0x6, ISCSI_TMR_FUNC_REJECTED = 0xFF }; /* * Asynchronous call for task management * * Returns: * 0 if the call was initiated and the task mgmt function will be invoked. * The result will be reported through the callback function. * <0 if there was an error. The callback function will not be invoked. * * Callback parameters : * status can be either of : * SCSI_STATUS_GOOD : Connection was successful. Command_data is a pointer to uint32_t * containing the response code as per RFC3720/10.6.1 * * SCSI_STATUS_ERROR : Error. * * The callback will NOT be invoked if the session is explicitely torn down * through a call to iscsi_disconnect() or iscsi_destroy_context(). * * abort_task will also cancel the scsi task. The callback for the scsi task will be invoked with * SCSI_STATUS_CANCELLED * abort_task_set, lun_reset, target_warn_reset, target_cold_reset will cancel all tasks. The callback for * all tasks will be invoked with SCSI_STATUS_CANCELLED */ EXTERN int iscsi_task_mgmt_async(struct iscsi_context *iscsi, int lun, enum iscsi_task_mgmt_funcs function, uint32_t ritt, uint32_t rcmdscn, iscsi_command_cb cb, void *private_data); EXTERN int iscsi_task_mgmt_abort_task_async(struct iscsi_context *iscsi, struct scsi_task *task, iscsi_command_cb cb, void *private_data); EXTERN int iscsi_task_mgmt_abort_task_set_async(struct iscsi_context *iscsi, uint32_t lun, iscsi_command_cb cb, void *private_data); EXTERN int iscsi_task_mgmt_lun_reset_async(struct iscsi_context *iscsi, uint32_t lun, iscsi_command_cb cb, void *private_data); EXTERN int iscsi_task_mgmt_target_warm_reset_async(struct iscsi_context *iscsi, iscsi_command_cb cb, void *private_data); EXTERN int iscsi_task_mgmt_target_cold_reset_async(struct iscsi_context *iscsi, iscsi_command_cb cb, void *private_data); /* * Synchronous calls for task management * * Returns: * 0 success. * <0 error. */ EXTERN int iscsi_task_mgmt_sync(struct iscsi_context *iscsi, int lun, enum iscsi_task_mgmt_funcs function, uint32_t ritt, uint32_t rcmdscn); EXTERN int iscsi_task_mgmt_abort_task_sync(struct iscsi_context *iscsi, struct scsi_task *task); EXTERN int iscsi_task_mgmt_abort_task_set_sync(struct iscsi_context *iscsi, uint32_t lun); EXTERN int iscsi_task_mgmt_lun_reset_sync(struct iscsi_context *iscsi, uint32_t lun); EXTERN int iscsi_task_mgmt_target_warm_reset_sync(struct iscsi_context *iscsi); EXTERN int iscsi_task_mgmt_target_cold_reset_sync(struct iscsi_context *iscsi); /* These are the possible status values for the callbacks for scsi commands. * The content of command_data depends on the status type. * * status : * SCSI_STATUS_GOOD the scsi command completed successfullt on the target. * If this scsi command returns DATA-IN, that data is stored in an scsi_task * structure returned in the command_data parameter. This buffer will be * automatically freed once the callback returns. * * SCSI_STATUS_CHECK_CONDITION the scsi command failed with a scsi sense. * Command_data contains a struct scsi_task. When the callback returns, * this buffer will automatically become freed. * * SCSI_STATUS_CANCELLED the scsi command was aborted. Command_data is * NULL. * * SCSI_STATUS_ERROR the command failed. Command_data is NULL. */ struct iscsi_data { size_t size; unsigned char *data; }; /* * These functions will set the ISID type and value. * By default, contexts will automatically be assigned a 'random' * type and value on creation, but this can be overridden * by an appplication using these functions. * * Setting the ISID can only be done before loggin in to the target. */ EXTERN int iscsi_set_isid_oui(struct iscsi_context *iscsi, uint32_t oui, uint32_t qualifier); EXTERN int iscsi_set_isid_en(struct iscsi_context *iscsi, uint32_t en, uint32_t qualifier); EXTERN int iscsi_set_isid_random(struct iscsi_context *iscsi, uint32_t rnd, uint32_t qualifier); EXTERN int iscsi_set_isid_reserved(struct iscsi_context *iscsi); struct scsi_mode_page; /* * The scsi commands use/return a scsi_task structure when invoked * and also through the callback. * * You must release this structure when you are finished with the task * by calling scsi_free_scsi_task(). * Most of the time this means you should call this function before returning * from the callback. */ EXTERN int iscsi_scsi_command_async(struct iscsi_context *iscsi, int lun, struct scsi_task *task, iscsi_command_cb cb, struct iscsi_data *data, void *private_data); /* * Async commands for SCSI * * These async functions return a scsi_task structure, or NULL if the command failed. * This structure can be used by task management functions to abort the task or a whole task set. */ EXTERN struct scsi_task * iscsi_reportluns_task(struct iscsi_context *iscsi, int report_type, int alloc_len, iscsi_command_cb cb, void *private_data); EXTERN struct scsi_task * iscsi_testunitready_task(struct iscsi_context *iscsi, int lun, iscsi_command_cb cb, void *private_data); EXTERN struct scsi_task * iscsi_inquiry_task(struct iscsi_context *iscsi, int lun, int evpd, int page_code, int maxsize, iscsi_command_cb cb, void *private_data); EXTERN struct scsi_task * iscsi_readcapacity10_task(struct iscsi_context *iscsi, int lun, int lba, int pmi, iscsi_command_cb cb, void *private_data); EXTERN struct scsi_task * iscsi_readcapacity16_task(struct iscsi_context *iscsi, int lun, iscsi_command_cb cb, void *private_data); EXTERN struct scsi_task * iscsi_sanitize_task(struct iscsi_context *iscsi, int lun, int immed, int ause, int sa, int param_len, struct iscsi_data *data, iscsi_command_cb cb, void *private_data); EXTERN struct scsi_task * iscsi_sanitize_block_erase_task(struct iscsi_context *iscsi, int lun, int immed, int ause, iscsi_command_cb cb, void *private_data); EXTERN struct scsi_task * iscsi_sanitize_crypto_erase_task(struct iscsi_context *iscsi, int lun, int immed, int ause, iscsi_command_cb cb, void *private_data); EXTERN struct scsi_task * iscsi_sanitize_exit_failure_mode_task(struct iscsi_context *iscsi, int lun, int immed, int ause, iscsi_command_cb cb, void *private_data); EXTERN struct scsi_task * iscsi_get_lba_status_task(struct iscsi_context *iscsi, int lun, uint64_t starting_lba, uint32_t alloc_len, iscsi_command_cb cb, void *private_data); EXTERN struct scsi_task * iscsi_synchronizecache10_task(struct iscsi_context *iscsi, int lun, int lba, int num_blocks, int syncnv, int immed, iscsi_command_cb cb, void *private_data); EXTERN struct scsi_task * iscsi_synchronizecache16_task(struct iscsi_context *iscsi, int lun, uint64_t lba, uint32_t num_blocks, int syncnv, int immed, iscsi_command_cb cb, void *private_data); EXTERN struct scsi_task * iscsi_prefetch10_task(struct iscsi_context *iscsi, int lun, uint32_t lba, int num_blocks, int immed, int group, iscsi_command_cb cb, void *private_data); EXTERN struct scsi_task * iscsi_prefetch16_task(struct iscsi_context *iscsi, int lun, uint64_t lba, int num_blocks, int immed, int group, iscsi_command_cb cb, void *private_data); EXTERN struct scsi_task * iscsi_read6_task(struct iscsi_context *iscsi, int lun, uint32_t lba, uint32_t datalen, int blocksize, iscsi_command_cb cb, void *private_data); EXTERN struct scsi_task * iscsi_read10_task(struct iscsi_context *iscsi, int lun, uint32_t lba, uint32_t datalen, int blocksize, int rdprotect, int dpo, int fua, int fua_nv, int group_number, iscsi_command_cb cb, void *private_data); EXTERN struct scsi_task * iscsi_write10_task(struct iscsi_context *iscsi, int lun, uint32_t lba, unsigned char *data, uint32_t datalen, int blocksize, int wrprotect, int dpo, int fua, int fua_nv, int group_number, iscsi_command_cb cb, void *private_data); EXTERN struct scsi_task * iscsi_writeverify10_task(struct iscsi_context *iscsi, int lun, uint32_t lba, unsigned char *data, uint32_t datalen, int blocksize, int wrprotect, int dpo, int bytchk, int group_number, iscsi_command_cb cb, void *private_data); EXTERN struct scsi_task * iscsi_read12_task(struct iscsi_context *iscsi, int lun, uint32_t lba, uint32_t datalen, int blocksize, int rdprotect, int dpo, int fua, int fua_nv, int group_number, iscsi_command_cb cb, void *private_data); EXTERN struct scsi_task * iscsi_write12_task(struct iscsi_context *iscsi, int lun, uint32_t lba, unsigned char *data, uint32_t datalen, int blocksize, int wrprotect, int dpo, int fua, int fua_nv, int group_number, iscsi_command_cb cb, void *private_data); EXTERN struct scsi_task * iscsi_writeverify12_task(struct iscsi_context *iscsi, int lun, uint32_t lba, unsigned char *data, uint32_t datalen, int blocksize, int wrprotect, int dpo, int bytchk, int group_number, iscsi_command_cb cb, void *private_data); EXTERN struct scsi_task * iscsi_read16_task(struct iscsi_context *iscsi, int lun, uint64_t lba, uint32_t datalen, int blocksize, int rdprotect, int dpo, int fua, int fua_nv, int group_number, iscsi_command_cb cb, void *private_data); EXTERN struct scsi_task * iscsi_write16_task(struct iscsi_context *iscsi, int lun, uint64_t lba, unsigned char *data, uint32_t datalen, int blocksize, int wrprotect, int dpo, int fua, int fua_nv, int group_number, iscsi_command_cb cb, void *private_data); EXTERN struct scsi_task * iscsi_writeatomic16_task(struct iscsi_context *iscsi, int lun, uint64_t lba, unsigned char *data, uint32_t datalen, int blocksize, int wrprotect, int dpo, int fua, int group_number, iscsi_command_cb cb, void *private_data); EXTERN struct scsi_task * iscsi_orwrite_task(struct iscsi_context *iscsi, int lun, uint64_t lba, unsigned char *data, uint32_t datalen, int blocksize, int wrprotect, int dpo, int fua, int fua_nv, int group_number, iscsi_command_cb cb, void *private_data); EXTERN struct scsi_task * iscsi_startstopunit_task(struct iscsi_context *iscsi, int lun, int immed, int pcm, int pc, int no_flush, int loej, int start, iscsi_command_cb cb, void *private_data); EXTERN struct scsi_task * iscsi_preventallow_task(struct iscsi_context *iscsi, int lun, int prevent, iscsi_command_cb cb, void *private_data); EXTERN struct scsi_task * iscsi_compareandwrite_task(struct iscsi_context *iscsi, int lun, uint64_t lba, unsigned char *data, uint32_t datalen, int blocksize, int wrprotect, int dpo, int fua, int fua_nv, int group_number, iscsi_command_cb cb, void *private_data); EXTERN struct scsi_task * iscsi_writeverify16_task(struct iscsi_context *iscsi, int lun, uint64_t lba, unsigned char *data, uint32_t datalen, int blocksize, int wrprotect, int dpo, int bytchk, int group_number, iscsi_command_cb cb, void *private_data); EXTERN struct scsi_task * iscsi_verify10_task(struct iscsi_context *iscsi, int lun, unsigned char *data, uint32_t datalen, uint32_t lba, int vprotect, int dpo, int bytchk, int blocksize, iscsi_command_cb cb, void *private_data); EXTERN struct scsi_task * iscsi_verify12_task(struct iscsi_context *iscsi, int lun, unsigned char *data, uint32_t datalen, uint32_t lba, int vprotect, int dpo, int bytchk, int blocksize, iscsi_command_cb cb, void *private_data); EXTERN struct scsi_task * iscsi_verify16_task(struct iscsi_context *iscsi, int lun, unsigned char *data, uint32_t datalen, uint64_t lba, int vprotect, int dpo, int bytchk, int blocksize, iscsi_command_cb cb, void *private_data); EXTERN struct scsi_task * iscsi_writesame10_task(struct iscsi_context *iscsi, int lun, uint32_t lba, unsigned char *data, uint32_t datalen, uint16_t num_blocks, int anchor, int unmap, int wrprotect, int group, iscsi_command_cb cb, void *private_data); EXTERN struct scsi_task * iscsi_writesame16_task(struct iscsi_context *iscsi, int lun, uint64_t lba, unsigned char *data, uint32_t datalen, uint32_t num_blocks, int anchor, int unmap, int wrprotect, int group, iscsi_command_cb cb, void *private_data); EXTERN struct scsi_task * iscsi_modeselect6_task(struct iscsi_context *iscsi, int lun, int pf, int sp, struct scsi_mode_page *mp, iscsi_command_cb cb, void *private_data); EXTERN struct scsi_task * iscsi_modeselect10_task(struct iscsi_context *iscsi, int lun, int pf, int sp, struct scsi_mode_page *mp, iscsi_command_cb cb, void *private_data); EXTERN struct scsi_task * iscsi_modesense6_task(struct iscsi_context *iscsi, int lun, int dbd, int pc, int page_code, int sub_page_code, unsigned char alloc_len, iscsi_command_cb cb, void *private_data); EXTERN struct scsi_task * iscsi_modesense10_task(struct iscsi_context *iscsi, int lun, int llbaa, int dbd, int pc, int page_code, int sub_page_code, unsigned char alloc_len, iscsi_command_cb cb, void *private_data); struct unmap_list { uint64_t lba; uint32_t num; }; EXTERN struct scsi_task * iscsi_persistent_reserve_in_task(struct iscsi_context *iscsi, int lun, int sa, uint16_t xferlen, iscsi_command_cb cb, void *private_data); EXTERN struct scsi_task * iscsi_persistent_reserve_out_task(struct iscsi_context *iscsi, int lun, int sa, int scope, int type, void *params, iscsi_command_cb cb, void *private_data); EXTERN struct scsi_task * iscsi_unmap_task(struct iscsi_context *iscsi, int lun, int anchor, int group, struct unmap_list *list, int list_len, iscsi_command_cb cb, void *private_data); EXTERN struct scsi_task * iscsi_readtoc_task(struct iscsi_context *iscsi, int lun, int msf, int format, int track_session, int maxsize, iscsi_command_cb cb, void *private_data); EXTERN struct scsi_task * iscsi_reserve6_task(struct iscsi_context *iscsi, int lun, iscsi_command_cb cb, void *private_data); EXTERN struct scsi_task * iscsi_release6_task(struct iscsi_context *iscsi, int lun, iscsi_command_cb cb, void *private_data); EXTERN struct scsi_task * iscsi_report_supported_opcodes_task(struct iscsi_context *iscsi, int lun, int rctd, int options, int opcode, int sa, uint32_t alloc_len, iscsi_command_cb cb, void *private_data); /* * Sync commands for SCSI */ EXTERN struct scsi_task * iscsi_scsi_command_sync(struct iscsi_context *iscsi, int lun, struct scsi_task *task, struct iscsi_data *data); EXTERN struct scsi_task * iscsi_modeselect6_sync(struct iscsi_context *iscsi, int lun, int pf, int sp, struct scsi_mode_page *mp); EXTERN struct scsi_task * iscsi_modeselect10_sync(struct iscsi_context *iscsi, int lun, int pf, int sp, struct scsi_mode_page *mp); EXTERN struct scsi_task * iscsi_modesense6_sync(struct iscsi_context *iscsi, int lun, int dbd, int pc, int page_code, int sub_page_code, unsigned char alloc_len); EXTERN struct scsi_task * iscsi_modesense10_sync(struct iscsi_context *iscsi, int lun, int llbaa, int dbd, int pc, int page_code, int sub_page_code, unsigned char alloc_len); EXTERN struct scsi_task * iscsi_reportluns_sync(struct iscsi_context *iscsi, int report_type, int alloc_len); EXTERN struct scsi_task * iscsi_testunitready_sync(struct iscsi_context *iscsi, int lun); EXTERN struct scsi_task * iscsi_inquiry_sync(struct iscsi_context *iscsi, int lun, int evpd, int page_code, int maxsize); EXTERN struct scsi_task * iscsi_read6_sync(struct iscsi_context *iscsi, int lun, uint32_t lba, uint32_t datalen, int blocksize); EXTERN struct scsi_task * iscsi_read10_sync(struct iscsi_context *iscsi, int lun, uint32_t lba, uint32_t datalen, int blocksize, int rdprotect, int dpo, int fua, int fua_nv, int group_number); EXTERN struct scsi_task * iscsi_write10_sync(struct iscsi_context *iscsi, int lun, uint32_t lba, unsigned char *data, uint32_t datalen, int blocksize, int wrprotect, int dpo, int fua, int fua_nv, int group_number); EXTERN struct scsi_task * iscsi_writeverify10_sync(struct iscsi_context *iscsi, int lun, uint32_t lba, unsigned char *data, uint32_t datalen, int blocksize, int wrprotect, int dpo, int bytchk, int group_number); EXTERN struct scsi_task * iscsi_read12_sync(struct iscsi_context *iscsi, int lun, uint32_t lba, uint32_t datalen, int blocksize, int rdprotect, int dpo, int fua, int fua_nv, int group_number); EXTERN struct scsi_task * iscsi_write12_sync(struct iscsi_context *iscsi, int lun, uint32_t lba, unsigned char *data, uint32_t datalen, int blocksize, int wrprotect, int dpo, int fua, int fua_nv, int group_number); EXTERN struct scsi_task * iscsi_writeverify12_sync(struct iscsi_context *iscsi, int lun, uint32_t lba, unsigned char *data, uint32_t datalen, int blocksize, int wrprotect, int dpo, int bytchk, int group_number); EXTERN struct scsi_task * iscsi_read16_sync(struct iscsi_context *iscsi, int lun, uint64_t lba, uint32_t datalen, int blocksize, int rdprotect, int dpo, int fua, int fua_nv, int group_number); EXTERN struct scsi_task * iscsi_write16_sync(struct iscsi_context *iscsi, int lun, uint64_t lba, unsigned char *data, uint32_t datalen, int blocksize, int wrprotect, int dpo, int fua, int fua_nv, int group_number); EXTERN struct scsi_task * iscsi_writeatomic16_sync(struct iscsi_context *iscsi, int lun, uint64_t lba, unsigned char *data, uint32_t datalen, int blocksize, int wrprotect, int dpo, int fua, int group_number); EXTERN struct scsi_task * iscsi_orwrite_sync(struct iscsi_context *iscsi, int lun, uint64_t lba, unsigned char *data, uint32_t datalen, int blocksize, int wrprotect, int dpo, int fua, int fua_nv, int group_number); EXTERN struct scsi_task * iscsi_startstopunit_sync(struct iscsi_context *iscsi, int lun, int immed, int pcm, int pc, int no_flush, int loej, int start); EXTERN struct scsi_task * iscsi_preventallow_sync(struct iscsi_context *iscsi, int lun, int prevent); EXTERN struct scsi_task * iscsi_compareandwrite_sync(struct iscsi_context *iscsi, int lun, uint64_t lba, unsigned char *data, uint32_t datalen, int blocksize, int wrprotect, int dpo, int fua, int fua_nv, int group_number); EXTERN struct scsi_task * iscsi_writeverify16_sync(struct iscsi_context *iscsi, int lun, uint64_t lba, unsigned char *data, uint32_t datalen, int blocksize, int wrprotect, int dpo, int bytchk, int group_number); EXTERN struct scsi_task * iscsi_readcapacity10_sync(struct iscsi_context *iscsi, int lun, int lba, int pmi); EXTERN struct scsi_task * iscsi_readcapacity16_sync(struct iscsi_context *iscsi, int lun); EXTERN struct scsi_task * iscsi_get_lba_status_sync(struct iscsi_context *iscsi, int lun, uint64_t starting_lba, uint32_t alloc_len); EXTERN struct scsi_task * iscsi_sanitize_sync(struct iscsi_context *iscsi, int lun, int immed, int ause, int sa, int param_len, struct iscsi_data *data); EXTERN struct scsi_task * iscsi_sanitize_block_erase_sync(struct iscsi_context *iscsi, int lun, int immed, int ause); EXTERN struct scsi_task * iscsi_sanitize_crypto_erase_sync(struct iscsi_context *iscsi, int lun, int immed, int ause); EXTERN struct scsi_task * iscsi_sanitize_exit_failure_mode_sync(struct iscsi_context *iscsi, int lun, int immed, int ause); EXTERN struct scsi_task * iscsi_synchronizecache10_sync(struct iscsi_context *iscsi, int lun, int lba, int num_blocks, int syncnv, int immed); EXTERN struct scsi_task * iscsi_synchronizecache16_sync(struct iscsi_context *iscsi, int lun, uint64_t lba, uint32_t num_blocks, int syncnv, int immed); EXTERN struct scsi_task * iscsi_prefetch10_sync(struct iscsi_context *iscsi, int lun, uint32_t lba, int num_blocks, int immed, int group); EXTERN struct scsi_task * iscsi_prefetch16_sync(struct iscsi_context *iscsi, int lun, uint64_t lba, int num_blocks, int immed, int group); EXTERN struct scsi_task * iscsi_verify10_sync(struct iscsi_context *iscsi, int lun, unsigned char *data, uint32_t datalen, uint32_t lba, int vprotect, int dpo, int bytchk, int blocksize); EXTERN struct scsi_task * iscsi_verify12_sync(struct iscsi_context *iscsi, int lun, unsigned char *data, uint32_t datalen, uint32_t lba, int vprotect, int dpo, int bytchk, int blocksize); EXTERN struct scsi_task * iscsi_verify16_sync(struct iscsi_context *iscsi, int lun, unsigned char *data, uint32_t datalen, uint64_t lba, int vprotect, int dpo, int bytchk, int blocksize); EXTERN struct scsi_task * iscsi_writesame10_sync(struct iscsi_context *iscsi, int lun, uint32_t lba, unsigned char *data, uint32_t datalen, uint16_t num_blocks, int anchor, int unmap, int wrprotect, int group); EXTERN struct scsi_task * iscsi_writesame16_sync(struct iscsi_context *iscsi, int lun, uint64_t lba, unsigned char *data, uint32_t datalen, uint32_t num_blocks, int anchor, int unmap, int wrprotect, int group); EXTERN struct scsi_task * iscsi_persistent_reserve_in_sync(struct iscsi_context *iscsi, int lun, int sa, uint16_t xferlen); EXTERN struct scsi_task * iscsi_persistent_reserve_out_sync(struct iscsi_context *iscsi, int lun, int sa, int scope, int type, void *params); EXTERN struct scsi_task * iscsi_unmap_sync(struct iscsi_context *iscsi, int lun, int anchor, int group, struct unmap_list *list, int list_len); EXTERN struct scsi_task * iscsi_readtoc_sync(struct iscsi_context *iscsi, int lun, int msf, int format, int track_session, int maxsize); EXTERN struct scsi_task * iscsi_reserve6_sync(struct iscsi_context *iscsi, int lun); EXTERN struct scsi_task * iscsi_release6_sync(struct iscsi_context *iscsi, int lun); EXTERN struct scsi_task * iscsi_report_supported_opcodes_sync(struct iscsi_context *iscsi, int lun, int rctd, int options, int opcode, int sa, uint32_t alloc_len); /* * These functions are used when the application wants to specify its own buffers to read the data * from the DATA-IN PDUs into, or write the data to DATA-OUT PDUs from. * The main use is for SCSI READ10/12/16 WRITE10/12/16 operations to have them read/write directly from * the applications buffer, avoiding coying the data. * * This also supports reading into a vector of buffers by calling this function multiple times. * The individual buffers will be filled in the same order as they were created. * * Example READ10: * task = iscsi_read10_task( ( 2 512byte blocks into two buffers) * scsi_task_add_data_in_buffer(task, first_buffer, 512 * scsi_task_add_data_in_buffer(task, second_buffer, 512 * * * If you use this function you can not use task->datain in the READ callback. * task->datain.size will be 0 and * task->datain.data will be NULL * * Example WRITE10: (write 2 blocks) * static struct scsi_iovec iov[2]; * * task = iscsi_write10_task(iscsi, lun, 0, NULL, 512, 512, 0, 0, 0, 0, 0, callback, private_data); * iov[0].iov_base = first_buffer; * iov[0].iov_len = 512; * iov[1].iov_base = second_buffer; * iov[1].iov_len = 512; * scsi_task_set_iov_out(task, &iov[0], 2); */ EXTERN int scsi_task_add_data_in_buffer(struct scsi_task *task, int len, unsigned char *buf); EXTERN int scsi_task_add_data_out_buffer(struct scsi_task *task, int len, unsigned char *buf); struct scsi_iovec; EXTERN void scsi_task_set_iov_out(struct scsi_task *task, struct scsi_iovec *iov, int niov); EXTERN void scsi_task_set_iov_in(struct scsi_task *task, struct scsi_iovec *iov, int niov); EXTERN int scsi_task_get_status(struct scsi_task *task, struct scsi_sense *sense); /* * This function is used when you want to cancel a scsi task. * The callback for the task will immediately be invoked with SCSI_STATUS_CANCELLED. * The cancellation is only local in libiscsi. If the task is already in-flight * this call will not cancel the task at the target. * To cancel the task also a the target you need to call the task management functions. */ EXTERN int iscsi_scsi_cancel_task(struct iscsi_context *iscsi, struct scsi_task *task); /* * This function is used when you want to cancel all scsi tasks. * The callback for the tasks will immediately be invoked with SCSI_STATUS_CANCELLED. * The cancellation is only local in libiscsi. If the tasks are already in-flight * this call will not cancel the tasks at the target. * To cancel the tasks also a the target you need to call the task management functions. */ EXTERN void iscsi_scsi_cancel_all_tasks(struct iscsi_context *iscsi); /* * This function is to set the debugging level where level is * * 0 = disabled (default) * 1 = errors only * 2 = connection related info * 3 = user set variables * 4 = function calls * 5 = ... * 10 = everything */ EXTERN void iscsi_set_log_level(struct iscsi_context *iscsi, int level); typedef void (*iscsi_log_fn)(int level, const char *mesage); /* Set the logging function to use */ EXTERN void iscsi_set_log_fn(struct iscsi_context *iscsi, iscsi_log_fn fn); /* predefined log function that just writes to stderr */ EXTERN void iscsi_log_to_stderr(int level, const char *message); /* * This function is to set the TCP_USER_TIMEOUT option. It has to be called after iscsi * context creation. The value given in ms is then applied each time a new socket is created. */ EXTERN void iscsi_set_tcp_user_timeout(struct iscsi_context *iscsi, int timeout_ms); /* * This function is to set the TCP_KEEPIDLE option. It has to be called after iscsi * context creation. */ EXTERN void iscsi_set_tcp_keepidle(struct iscsi_context *iscsi, int value); /* * This function is to set the TCP_KEEPCNT option. It has to be called after iscsi * context creation. */ EXTERN void iscsi_set_tcp_keepcnt(struct iscsi_context *iscsi, int value); /* * This function is to set the TCP_KEEPINTVL option. It has to be called after iscsi * context creation. */ EXTERN void iscsi_set_tcp_keepintvl(struct iscsi_context *iscsi, int value); /* * This function is to set the TCP_SYNCNT option. It has to be called after iscsi * context creation. */ EXTERN void iscsi_set_tcp_syncnt(struct iscsi_context *iscsi, int value); /* * This function is to set the interface that outbound connections for this socket are bound to. * You max specify more than one interface here separated by comma. */ EXTERN void iscsi_set_bind_interfaces(struct iscsi_context *iscsi, char * interfaces); /* This function is to set if we should retry a failed reconnect count is defined as follows: -1 -> retry forever (default) 0 -> never retry n -> retry n times */ EXTERN void iscsi_set_reconnect_max_retries(struct iscsi_context *iscsi, int count); /* Set to true to have libiscsi use TESTUNITREADY and consume any/all UnitAttentions that may have triggered in the target. */ EXTERN void iscsi_set_no_ua_on_reconnect(struct iscsi_context *iscsi, int state); #ifdef __cplusplus } #endif #endif /* __iscsi_h__ */ libiscsi-1.17.0/include/md5.h000066400000000000000000000031071271371262700156730ustar00rootroot00000000000000/* * This is the header file for the MD5 message-digest algorithm. * The algorithm is due to Ron Rivest. This code was * written by Colin Plumb in 1993, no copyright is claimed. * This code is in the public domain; do with it what you wish. * * Equivalent code is available from RSA Data Security, Inc. * This code has been tested against that, and is equivalent, * except that you don't need to include two pages of legalese * with every copy. * * To compute the message digest of a chunk of bytes, declare an * MD5Context structure, pass it to MD5Init, call MD5Update as * needed on buffers full of bytes, and then call MD5Final, which * will fill a supplied 16-byte array with the digest. * * Changed so as no longer to depend on Colin Plumb's `usual.h' * header definitions; now uses stuff from dpkg's config.h * - Ian Jackson . * Still in the public domain. */ #ifndef MD5_H #define MD5_H #if defined(WIN32) #else #include #endif #include #include #include #if (__BYTE_ORDER == __BIG_ENDIAN) # define WORDS_BIGENDIAN 1 #endif typedef uint32_t UWORD32; #ifdef __cplusplus extern "C" { #endif #define md5byte unsigned char struct MD5Context { UWORD32 buf[4]; UWORD32 bytes[2]; UWORD32 in[16]; }; void MD5Init(struct MD5Context *context); void MD5Update(struct MD5Context *context, md5byte const *buf, unsigned len); void MD5Final(unsigned char digest[16], struct MD5Context *context); void MD5Transform(UWORD32 buf[4], UWORD32 const in[16]); #ifdef __cplusplus } #endif #endif /* !MD5_H */ libiscsi-1.17.0/include/scsi-lowlevel.h000066400000000000000000001113521271371262700200000ustar00rootroot00000000000000/* Copyright (C) 2010 by Ronnie Sahlberg This program 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 program is distributed in the hope that 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 program; if not, see . */ #ifndef __scsi_lowlevel_h__ #define __scsi_lowlevel_h__ #if defined(WIN32) #define EXTERN __declspec( dllexport ) #else #define EXTERN #endif #ifdef __cplusplus extern "C" { #endif #define SCSI_CDB_MAX_SIZE 16 enum scsi_opcode { SCSI_OPCODE_TESTUNITREADY = 0x00, SCSI_OPCODE_READ6 = 0x08, SCSI_OPCODE_INQUIRY = 0x12, SCSI_OPCODE_MODESELECT6 = 0x15, SCSI_OPCODE_RESERVE6 = 0x16, SCSI_OPCODE_RELEASE6 = 0x17, SCSI_OPCODE_MODESENSE6 = 0x1a, SCSI_OPCODE_STARTSTOPUNIT = 0x1b, SCSI_OPCODE_PREVENTALLOW = 0x1e, SCSI_OPCODE_READCAPACITY10 = 0x25, SCSI_OPCODE_READ10 = 0x28, SCSI_OPCODE_WRITE10 = 0x2A, SCSI_OPCODE_WRITE_VERIFY10 = 0x2E, SCSI_OPCODE_VERIFY10 = 0x2F, SCSI_OPCODE_PREFETCH10 = 0x34, SCSI_OPCODE_SYNCHRONIZECACHE10 = 0x35, SCSI_OPCODE_WRITE_SAME10 = 0x41, SCSI_OPCODE_UNMAP = 0x42, SCSI_OPCODE_READTOC = 0x43, SCSI_OPCODE_SANITIZE = 0x48, SCSI_OPCODE_MODESELECT10 = 0x55, SCSI_OPCODE_MODESENSE10 = 0x5A, SCSI_OPCODE_PERSISTENT_RESERVE_IN = 0x5E, SCSI_OPCODE_PERSISTENT_RESERVE_OUT = 0x5F, SCSI_OPCODE_EXTENDED_COPY = 0x83, SCSI_OPCODE_RECEIVE_COPY_RESULTS = 0x84, SCSI_OPCODE_READ16 = 0x88, SCSI_OPCODE_COMPARE_AND_WRITE = 0x89, SCSI_OPCODE_WRITE16 = 0x8A, SCSI_OPCODE_ORWRITE = 0x8B, SCSI_OPCODE_WRITE_VERIFY16 = 0x8E, SCSI_OPCODE_VERIFY16 = 0x8F, SCSI_OPCODE_PREFETCH16 = 0x90, SCSI_OPCODE_SYNCHRONIZECACHE16 = 0x91, SCSI_OPCODE_WRITE_SAME16 = 0x93, SCSI_OPCODE_WRITE_ATOMIC16 = 0x9C, SCSI_OPCODE_SERVICE_ACTION_IN = 0x9E, SCSI_OPCODE_REPORTLUNS = 0xA0, SCSI_OPCODE_MAINTENANCE_IN = 0xA3, SCSI_OPCODE_READ12 = 0xA8, SCSI_OPCODE_WRITE12 = 0xAA, SCSI_OPCODE_WRITE_VERIFY12 = 0xAE, SCSI_OPCODE_VERIFY12 = 0xAF }; enum scsi_persistent_in_sa { SCSI_PERSISTENT_RESERVE_READ_KEYS = 0, SCSI_PERSISTENT_RESERVE_READ_RESERVATION = 1, SCSI_PERSISTENT_RESERVE_REPORT_CAPABILITIES = 2, SCSI_PERSISTENT_RESERVE_READ_FULL_STATUS = 3 }; enum scsi_service_action_in { SCSI_READCAPACITY16 = 0x10, SCSI_GET_LBA_STATUS = 0x12 }; enum scsi_persistent_out_sa { SCSI_PERSISTENT_RESERVE_REGISTER = 0, SCSI_PERSISTENT_RESERVE_RESERVE = 1, SCSI_PERSISTENT_RESERVE_RELEASE = 2, SCSI_PERSISTENT_RESERVE_CLEAR = 3, SCSI_PERSISTENT_RESERVE_PREEMPT = 4, SCSI_PERSISTENT_RESERVE_PREEMPT_AND_ABORT = 5, SCSI_PERSISTENT_RESERVE_REGISTER_AND_IGNORE_EXISTING_KEY = 6, SCSI_PERSISTENT_RESERVE_REGISTER_AND_MOVE = 7 }; enum scsi_persistent_out_scope { SCSI_PERSISTENT_RESERVE_SCOPE_LU = 0 }; enum scsi_persistent_out_type { SCSI_PERSISTENT_RESERVE_TYPE_WRITE_EXCLUSIVE = 1, SCSI_PERSISTENT_RESERVE_TYPE_EXCLUSIVE_ACCESS = 3, SCSI_PERSISTENT_RESERVE_TYPE_WRITE_EXCLUSIVE_REGISTRANTS_ONLY = 5, SCSI_PERSISTENT_RESERVE_TYPE_EXCLUSIVE_ACCESS_REGISTRANTS_ONLY = 6, SCSI_PERSISTENT_RESERVE_TYPE_WRITE_EXCLUSIVE_ALL_REGISTRANTS = 7, SCSI_PERSISTENT_RESERVE_TYPE_EXCLUSIVE_ACCESS_ALL_REGISTRANTS = 8 }; struct scsi_persistent_reserve_out_basic { uint64_t reservation_key; uint64_t service_action_reservation_key; uint8_t spec_i_pt; uint8_t all_tg_pt; uint8_t aptpl; }; enum scsi_maintenance_in { SCSI_REPORT_SUPPORTED_OP_CODES = 0x0c }; enum scsi_op_code_reporting_options { SCSI_REPORT_SUPPORTING_OPS_ALL = 0x00, SCSI_REPORT_SUPPORTING_OPCODE = 0x01, SCSI_REPORT_SUPPORTING_SERVICEACTION = 0x02 }; /* sense keys */ enum scsi_sense_key { SCSI_SENSE_NO_SENSE = 0x00, SCSI_SENSE_RECOVERED_ERROR = 0x01, SCSI_SENSE_NOT_READY = 0x02, SCSI_SENSE_MEDIUM_ERROR = 0x03, SCSI_SENSE_HARDWARE_ERROR = 0x04, SCSI_SENSE_ILLEGAL_REQUEST = 0x05, SCSI_SENSE_UNIT_ATTENTION = 0x06, SCSI_SENSE_DATA_PROTECTION = 0x07, SCSI_SENSE_BLANK_CHECK = 0x08, SCSI_SENSE_VENDOR_SPECIFIC = 0x09, SCSI_SENSE_COPY_ABORTED = 0x0a, SCSI_SENSE_COMMAND_ABORTED = 0x0b, SCSI_SENSE_OBSOLETE_ERROR_CODE = 0x0c, SCSI_SENSE_OVERFLOW_COMMAND = 0x0d, SCSI_SENSE_MISCOMPARE = 0x0e }; EXTERN const char *scsi_sense_key_str(int key); /* ascq */ #define SCSI_SENSE_ASCQ_NO_ADDL_SENSE 0x0000 #define SCSI_SENSE_ASCQ_SANITIZE_IN_PROGRESS 0x041b #define SCSI_SENSE_ASCQ_UNREACHABLE_COPY_TARGET 0x0804 #define SCSI_SENSE_ASCQ_COPY_TARGET_DEVICE_NOT_REACHABLE 0x0d02 #define SCSI_SENSE_ASCQ_WRITE_AFTER_SANITIZE_REQUIRED 0x1115 #define SCSI_SENSE_ASCQ_PARAMETER_LIST_LENGTH_ERROR 0x1a00 #define SCSI_SENSE_ASCQ_MISCOMPARE_DURING_VERIFY 0x1d00 #define SCSI_SENSE_ASCQ_MISCOMPARE_VERIFY_OF_UNMAPPED_LBA 0x1d01 #define SCSI_SENSE_ASCQ_INVALID_OPERATION_CODE 0x2000 #define SCSI_SENSE_ASCQ_LBA_OUT_OF_RANGE 0x2100 #define SCSI_SENSE_ASCQ_INVALID_FIELD_IN_CDB 0x2400 #define SCSI_SENSE_ASCQ_LOGICAL_UNIT_NOT_SUPPORTED 0x2500 #define SCSI_SENSE_ASCQ_INVALID_FIELD_IN_PARAMETER_LIST 0x2600 #define SCSI_SENSE_ASCQ_TOO_MANY_TARGET_DESCRIPTORS 0x2606 #define SCSI_SENSE_ASCQ_UNSUPPORTED_TARGET_DESCRIPTOR_TYPE_CODE 0x2607 #define SCSI_SENSE_ASCQ_TOO_MANY_SEGMENT_DESCRIPTORS 0x2608 #define SCSI_SENSE_ASCQ_UNSUPPORTED_SEGMENT_DESCRIPTOR_TYPE_CODE 0x2609 #define SCSI_SENSE_ASCQ_WRITE_PROTECTED 0x2700 #define SCSI_SENSE_ASCQ_HARDWARE_WRITE_PROTECTED 0x2701 #define SCSI_SENSE_ASCQ_SOFTWARE_WRITE_PROTECTED 0x2702 #define SCSI_SENSE_ASCQ_BUS_RESET 0x2900 #define SCSI_SENSE_ASCQ_POWER_ON_OCCURED 0x2901 #define SCSI_SENSE_ASCQ_SCSI_BUS_RESET_OCCURED 0x2902 #define SCSI_SENSE_ASCQ_BUS_DEVICE_RESET_FUNCTION_OCCURED 0x2903 #define SCSI_SENSE_ASCQ_DEVICE_INTERNAL_RESET 0x2904 #define SCSI_SENSE_ASCQ_TRANSCEIVER_MODE_CHANGED_TO_SINGLE_ENDED 0x2905 #define SCSI_SENSE_ASCQ_TRANSCEIVER_MODE_CHANGED_TO_LVD 0x2906 #define SCSI_SENSE_ASCQ_NEXUS_LOSS 0x2907 #define SCSI_SENSE_ASCQ_MODE_PARAMETERS_CHANGED 0x2a01 #define SCSI_SENSE_ASCQ_CAPACITY_DATA_HAS_CHANGED 0x2a09 #define SCSI_SENSE_ASCQ_THIN_PROVISION_SOFT_THRES_REACHED 0x3807 #define SCSI_SENSE_ASCQ_MEDIUM_NOT_PRESENT 0x3a00 #define SCSI_SENSE_ASCQ_MEDIUM_NOT_PRESENT_TRAY_CLOSED 0x3a01 #define SCSI_SENSE_ASCQ_MEDIUM_NOT_PRESENT_TRAY_OPEN 0x3a02 #define SCSI_SENSE_ASCQ_INQUIRY_DATA_HAS_CHANGED 0x3f03 #define SCSI_SENSE_ASCQ_INTERNAL_TARGET_FAILURE 0x4400 #define SCSI_SENSE_ASCQ_MEDIUM_LOAD_OR_EJECT_FAILED 0x5300 #define SCSI_SENSE_ASCQ_MEDIUM_REMOVAL_PREVENTED 0x5302 #define SCSI_SENSE_ASCQ_INVALID_FIELD_IN_INFORMATION_UNIT 0x0e03 EXTERN const char *scsi_sense_ascq_str(int ascq); EXTERN const char *scsi_pr_type_str(enum scsi_persistent_out_type pr_type); enum scsi_xfer_dir { SCSI_XFER_NONE = 0, SCSI_XFER_READ = 1, SCSI_XFER_WRITE = 2 }; /* * READTOC */ EXTERN struct scsi_task *scsi_cdb_readtoc(int msf, int format, int track_session, uint16_t alloc_len); enum scsi_readtoc_fmt { SCSI_READ_TOC = 0, SCSI_READ_SESSION_INFO = 1, SCSI_READ_FULL_TOC = 2, SCSI_READ_PMA = 3, SCSI_READ_ATIP = 4 }; struct scsi_readtoc_desc { union { struct scsi_toc_desc { int adr; int control; int track; uint32_t lba; } toc; struct scsi_session_desc { int adr; int control; int first_in_last; uint32_t lba; } ses; struct scsi_fulltoc_desc { int session; int adr; int control; int tno; int point; int min; int sec; int frame; int zero; int pmin; int psec; int pframe; } full; } desc; }; struct scsi_readtoc_list { int num; int first; int last; struct scsi_readtoc_desc desc[0]; }; struct scsi_report_supported_params { int return_timeouts; }; #define SCSI_SENSE_FIXED_CURRENT 0x70 #define SCSI_SENSE_FIXED_DEFERRED_ERRORS 0x71 #define SCSI_SENSE_DESCRIPTOR_CURRENT 0x72 #define SCSI_SENSE_DESCRIPTOR_DEFERRED_ERRORS 0x73 struct scsi_sense { unsigned char error_type; enum scsi_sense_key key; int ascq; /* * Sense specific descriptor. See also paragraph "Sense key specific * sense data descriptor" in SPC. */ unsigned sense_specific:1; unsigned ill_param_in_cdb:1; unsigned bit_pointer_valid:1; unsigned char bit_pointer; uint16_t field_pointer; }; struct scsi_data { int size; unsigned char *data; }; enum scsi_residual { SCSI_RESIDUAL_NO_RESIDUAL = 0, SCSI_RESIDUAL_UNDERFLOW, SCSI_RESIDUAL_OVERFLOW }; /* struct scsi_iovec follows the POSIX struct iovec definition and *MUST* never change. */ struct scsi_iovec { void *iov_base; size_t iov_len; }; struct scsi_iovector { struct scsi_iovec *iov; int niov; int nalloc; size_t offset; int consumed; }; struct scsi_task { int status; int cdb_size; int xfer_dir; int expxferlen; unsigned char cdb[SCSI_CDB_MAX_SIZE]; enum scsi_residual residual_status; size_t residual; struct scsi_sense sense; struct scsi_data datain; struct scsi_allocated_memory *mem; void *ptr; uint32_t itt; uint32_t cmdsn; uint32_t lun; struct scsi_iovector iovector_in; struct scsi_iovector iovector_out; }; /* Create a task using a pre-built CDB which can later be passed to iscsi_scsi_command_[a]sync() */ EXTERN struct scsi_task *scsi_create_task(int cdb_size, unsigned char *cdb, int xfer_dir, int expxferlen); /* This function will free a scsi task structure. You may NOT cancel a task until the callback has been invoked and the command has completed on the transport layer. */ EXTERN void scsi_free_scsi_task(struct scsi_task *task); EXTERN void scsi_set_task_private_ptr(struct scsi_task *task, void *ptr); EXTERN void *scsi_get_task_private_ptr(struct scsi_task *task); /* * TESTUNITREADY */ EXTERN struct scsi_task *scsi_cdb_testunitready(void); /* * SANITIZE */ #define SCSI_SANITIZE_OVERWRITE 0x01 #define SCSI_SANITIZE_BLOCK_ERASE 0x02 #define SCSI_SANITIZE_CRYPTO_ERASE 0x03 #define SCSI_SANITIZE_EXIT_FAILURE_MODE 0x1f EXTERN struct scsi_task *scsi_cdb_sanitize(int immed, int ause, int sa, int param_len); /* * REPORTLUNS */ #define SCSI_REPORTLUNS_REPORT_ALL_LUNS 0x00 #define SCSI_REPORTLUNS_REPORT_WELL_KNOWN_ONLY 0x01 #define SCSI_REPORTLUNS_REPORT_AVAILABLE_LUNS_ONLY 0x02 struct scsi_reportluns_list { uint32_t num; uint16_t luns[0]; }; EXTERN struct scsi_task *scsi_reportluns_cdb(int report_type, int alloc_len); /* * RESERVE6 */ EXTERN struct scsi_task *scsi_cdb_reserve6(void); /* * RELEASE6 */ EXTERN struct scsi_task *scsi_cdb_release6(void); /* * READCAPACITY10 */ struct scsi_readcapacity10 { uint32_t lba; uint32_t block_size; }; EXTERN struct scsi_task *scsi_cdb_readcapacity10(int lba, int pmi); /* * INQUIRY */ enum scsi_inquiry_peripheral_qualifier { SCSI_INQUIRY_PERIPHERAL_QUALIFIER_CONNECTED = 0x00, SCSI_INQUIRY_PERIPHERAL_QUALIFIER_DISCONNECTED = 0x01, SCSI_INQUIRY_PERIPHERAL_QUALIFIER_NOT_SUPPORTED = 0x03 }; const char *scsi_devqualifier_to_str( enum scsi_inquiry_peripheral_qualifier qualifier); enum scsi_inquiry_peripheral_device_type { SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_DIRECT_ACCESS = 0x00, SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_SEQUENTIAL_ACCESS = 0x01, SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_PRINTER = 0x02, SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_PROCESSOR = 0x03, SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_WRITE_ONCE = 0x04, SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_MMC = 0x05, SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_SCANNER = 0x06, SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_OPTICAL_MEMORY = 0x07, SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_MEDIA_CHANGER = 0x08, SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_COMMUNICATIONS = 0x09, SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_STORAGE_ARRAY_CONTROLLER = 0x0c, SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_ENCLOSURE_SERVICES = 0x0d, SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_SIMPLIFIED_DIRECT_ACCESS = 0x0e, SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_OPTICAL_CARD_READER = 0x0f, SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_BRIDGE_CONTROLLER = 0x10, SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_OSD = 0x11, SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_AUTOMATION = 0x12, SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_SEQURITY_MANAGER = 0x13, SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_WELL_KNOWN_LUN = 0x1e, SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_UNKNOWN = 0x1f }; EXTERN const char *scsi_devtype_to_str(enum scsi_inquiry_peripheral_device_type type); enum scsi_version { SCSI_VERSION_SPC = 0x03, SCSI_VERSION_SPC2 = 0x04, SCSI_VERSION_SPC3 = 0x05 }; EXTERN const char *scsi_version_to_str(enum scsi_version version); enum scsi_version_descriptor { SCSI_VERSION_DESCRIPTOR_ISCSI = 0x0960, SCSI_VERSION_DESCRIPTOR_SBC = 0x0180, SCSI_VERSION_DESCRIPTOR_SBC_ANSI_INCITS_306_1998 = 0x019C, SCSI_VERSION_DESCRIPTOR_SBC_T10_0996_D_R08C = 0x019B, SCSI_VERSION_DESCRIPTOR_SBC_2 = 0x0320, SCSI_VERSION_DESCRIPTOR_SBC_2_ISO_IEC_14776_322 = 0x033E, SCSI_VERSION_DESCRIPTOR_SBC_2_ANSI_INCITS_405_2005 = 0x033D, SCSI_VERSION_DESCRIPTOR_SBC_2_T10_1417_D_R16 = 0x033B, SCSI_VERSION_DESCRIPTOR_SBC_2_T10_1417_D_R5A = 0x0322, SCSI_VERSION_DESCRIPTOR_SBC_2_T10_1417_D_R15 = 0x0324, SCSI_VERSION_DESCRIPTOR_SBC_3 = 0x04C0, SCSI_VERSION_DESCRIPTOR_SPC = 0x0120, SCSI_VERSION_DESCRIPTOR_SPC_ANSI_INCITS_301_1997 = 0x013C, SCSI_VERSION_DESCRIPTOR_SPC_T10_0995_D_R11A = 0x013B, SCSI_VERSION_DESCRIPTOR_SPC_2 = 0x0260, SCSI_VERSION_DESCRIPTOR_SPC_2_ISO_IEC_14776_452 = 0x0278, SCSI_VERSION_DESCRIPTOR_SPC_2_ANSI_INCITS_351_2001 = 0x0277, SCSI_VERSION_DESCRIPTOR_SPC_2_T10_1236_D_R20 = 0x0276, SCSI_VERSION_DESCRIPTOR_SPC_2_T10_1236_D_R12 = 0x0267, SCSI_VERSION_DESCRIPTOR_SPC_2_T10_1236_D_R18 = 0x0269, SCSI_VERSION_DESCRIPTOR_SPC_2_T10_1236_D_R19 = 0x0275, SCSI_VERSION_DESCRIPTOR_SPC_3 = 0x0300, SCSI_VERSION_DESCRIPTOR_SPC_3_ISO_IEC_14776_453 = 0x0316, SCSI_VERSION_DESCRIPTOR_SPC_3_ANSI_INCITS_408_2005 = 0x0314, SCSI_VERSION_DESCRIPTOR_SPC_3_T10_1416_D_R7 = 0x0301, SCSI_VERSION_DESCRIPTOR_SPC_3_T10_1416_D_R21 = 0x0307, SCSI_VERSION_DESCRIPTOR_SPC_3_T10_1416_D_R22 = 0x030F, SCSI_VERSION_DESCRIPTOR_SPC_3_T10_1416_D_R23 = 0x0312, SCSI_VERSION_DESCRIPTOR_SPC_4 = 0x0460, SCSI_VERSION_DESCRIPTOR_SPC_4_T10_1731_D_R16 = 0x0461, SCSI_VERSION_DESCRIPTOR_SPC_4_T10_1731_D_R18 = 0x0462, SCSI_VERSION_DESCRIPTOR_SPC_4_T10_1731_D_R23 = 0x0463, SCSI_VERSION_DESCRIPTOR_SSC = 0x0200, SCSI_VERSION_DESCRIPTOR_UAS_T10_2095D_R04 = 0x1747 }; EXTERN const char *scsi_version_descriptor_to_str(enum scsi_version_descriptor version_descriptor); enum scsi_inquiry_tpgs { SCSI_INQUIRY_TPGS_NO_SUPPORT = 0x00, SCSI_INQUIRY_TPGS_IMPLICIT = 0x01, SCSI_INQUIRY_TPGS_EXPLICIT = 0x02, SCSI_INQUIRY_TPGS_IMPLICIT_AND_EXPLICIT = 0x03 }; /* fix typos, leave old names for backward compatibility */ #define periperal_qualifier qualifier #define periperal_device_type device_type struct scsi_inquiry_standard { enum scsi_inquiry_peripheral_qualifier qualifier; enum scsi_inquiry_peripheral_device_type device_type; int rmb; int version; int normaca; int hisup; int response_data_format; int additional_length; int sccs; int acc; int tpgs; int threepc; int protect; int encserv; int multip; int addr16; int wbus16; int sync; int cmdque; int clocking; int qas; int ius; char vendor_identification[8+1]; char product_identification[16+1]; char product_revision_level[4+1]; uint16_t version_descriptor[8]; }; enum scsi_inquiry_pagecode { SCSI_INQUIRY_PAGECODE_SUPPORTED_VPD_PAGES = 0x00, SCSI_INQUIRY_PAGECODE_UNIT_SERIAL_NUMBER = 0x80, SCSI_INQUIRY_PAGECODE_DEVICE_IDENTIFICATION = 0x83, SCSI_INQUIRY_PAGECODE_BLOCK_LIMITS = 0xB0, SCSI_INQUIRY_PAGECODE_BLOCK_DEVICE_CHARACTERISTICS = 0xB1, SCSI_INQUIRY_PAGECODE_LOGICAL_BLOCK_PROVISIONING = 0xB2 }; EXTERN const char *scsi_inquiry_pagecode_to_str(int pagecode); struct scsi_inquiry_supported_pages { enum scsi_inquiry_peripheral_qualifier qualifier; enum scsi_inquiry_peripheral_device_type device_type; enum scsi_inquiry_pagecode pagecode; int num_pages; unsigned char *pages; }; struct scsi_inquiry_block_limits { enum scsi_inquiry_peripheral_qualifier qualifier; enum scsi_inquiry_peripheral_device_type device_type; enum scsi_inquiry_pagecode pagecode; int wsnz; /* write same no zero */ uint8_t max_cmp; /* maximum_compare_and_write_length */ uint16_t opt_gran; /* optimal_transfer_length_granularity */ uint32_t max_xfer_len; /* maximum_transfer_length */ uint32_t opt_xfer_len; /* optimal_transfer_length */ uint32_t max_prefetch; /* maximum_prefetched_xdread_xdwrite_transfer_length */ uint32_t max_unmap; /* maximum_unmap_lba_count */ uint32_t max_unmap_bdc; /* maximum_unmap_block_descriptor_count */ uint32_t opt_unmap_gran; /* optimal_unmap_granularity */ int ugavalid; uint32_t unmap_gran_align; /* unmap_granularity_alignment */ uint64_t max_ws_len; /* maximum_write_same_length */ /* SBC-4 */ uint32_t max_atomic_xfer_len; uint32_t atomic_align; uint32_t atomic_gran; uint32_t max_atomic_tl_with_atomic_boundary; uint32_t max_atomic_boundary_size; }; struct scsi_inquiry_block_device_characteristics { enum scsi_inquiry_peripheral_qualifier qualifier; enum scsi_inquiry_peripheral_device_type device_type; enum scsi_inquiry_pagecode pagecode; int medium_rotation_rate; int product_type; int wabereq; int wacereq; int nominal_form_factor; int fuab; int vbuls; }; enum scsi_inquiry_provisioning_type { PROVISIONING_TYPE_NONE = 0, PROVISIONING_TYPE_RESOURCE = 1, PROVISIONING_TYPE_THIN = 2 }; struct scsi_inquiry_logical_block_provisioning { enum scsi_inquiry_peripheral_qualifier qualifier; enum scsi_inquiry_peripheral_device_type device_type; enum scsi_inquiry_pagecode pagecode; int threshold_exponent; int lbpu; int lbpws; int lbpws10; int lbprz; int anc_sup; int dp; enum scsi_inquiry_provisioning_type provisioning_type; }; EXTERN struct scsi_task *scsi_cdb_inquiry(int evpd, int page_code, int alloc_len); struct scsi_inquiry_unit_serial_number { enum scsi_inquiry_peripheral_qualifier qualifier; enum scsi_inquiry_peripheral_device_type device_type; enum scsi_inquiry_pagecode pagecode; char *usn; }; enum scsi_protocol_identifier { SCSI_PROTOCOL_IDENTIFIER_FIBRE_CHANNEL = 0x00, SCSI_PROTOCOL_IDENTIFIER_PARALLEL_SCSI = 0x01, SCSI_PROTOCOL_IDENTIFIER_SSA = 0x02, SCSI_PROTOCOL_IDENTIFIER_IEEE_1394 = 0x03, SCSI_PROTOCOL_IDENTIFIER_RDMA = 0x04, SCSI_PROTOCOL_IDENTIFIER_ISCSI = 0x05, SCSI_PROTOCOL_IDENTIFIER_SAS = 0x06, SCSI_PROTOCOL_IDENTIFIER_ADT = 0x07, SCSI_PROTOCOL_IDENTIFIER_ATA = 0x08 }; EXTERN const char *scsi_protocol_identifier_to_str(int identifier); enum scsi_codeset { SCSI_CODESET_BINARY = 0x01, SCSI_CODESET_ASCII = 0x02, SCSI_CODESET_UTF8 = 0x03 }; EXTERN const char *scsi_codeset_to_str(int codeset); enum scsi_association { SCSI_ASSOCIATION_LOGICAL_UNIT = 0x00, SCSI_ASSOCIATION_TARGET_PORT = 0x01, SCSI_ASSOCIATION_TARGET_DEVICE = 0x02 }; EXTERN const char *scsi_association_to_str(int association); enum scsi_designator_type { SCSI_DESIGNATOR_TYPE_VENDOR_SPECIFIC = 0x00, SCSI_DESIGNATOR_TYPE_T10_VENDORT_ID = 0x01, SCSI_DESIGNATOR_TYPE_EUI_64 = 0x02, SCSI_DESIGNATOR_TYPE_NAA = 0x03, SCSI_DESIGNATOR_TYPE_RELATIVE_TARGET_PORT = 0x04, SCSI_DESIGNATOR_TYPE_TARGET_PORT_GROUP = 0x05, SCSI_DESIGNATOR_TYPE_LOGICAL_UNIT_GROUP = 0x06, SCSI_DESIGNATOR_TYPE_MD5_LOGICAL_UNIT_IDENTIFIER = 0x07, SCSI_DESIGNATOR_TYPE_SCSI_NAME_STRING = 0x08 }; EXTERN const char *scsi_designator_type_to_str(int association); struct scsi_inquiry_device_designator { struct scsi_inquiry_device_designator *next; enum scsi_protocol_identifier protocol_identifier; enum scsi_codeset code_set; int piv; enum scsi_association association; enum scsi_designator_type designator_type; int designator_length; char *designator; }; struct scsi_inquiry_device_identification { enum scsi_inquiry_peripheral_qualifier qualifier; enum scsi_inquiry_peripheral_device_type device_type; enum scsi_inquiry_pagecode pagecode; struct scsi_inquiry_device_designator *designators; }; /* * MODESENSE */ enum scsi_modesense_page_control { SCSI_MODESENSE_PC_CURRENT = 0x00, SCSI_MODESENSE_PC_CHANGEABLE = 0x01, SCSI_MODESENSE_PC_DEFAULT = 0x02, SCSI_MODESENSE_PC_SAVED = 0x03 }; struct scsi_mode_page_caching { int ic; int abpf; int cap; int disc; int size; int wce; int mf; int rcd; int demand_read_retention_priority; int write_retention_priority; int disable_prefetch_transfer_length; int minimum_prefetch; int maximum_prefetch; int maximum_prefetch_ceiling; int fsw; int lbcss; int dra; int nv_dis; int number_of_cache_segments; int cache_segment_size; }; struct scsi_mode_page_power_condition { int pm_bg_precedence; int standby_y; int idle_c; int idle_b; int idle_a; int standby_z; uint32_t idle_a_condition_timer; uint32_t standby_z_condition_timer; uint32_t idle_b_condition_timer; uint32_t idle_c_condition_timer; uint32_t standby_y_condition_timer; int ccf_idle; int ccf_standby; int ccf_stopped; }; struct scsi_mode_page_control { int tst; int tmf_only; int dpicz; int d_sense; int gltsd; int rlec; int queue_algorithm_modifier; int nuar; int qerr; int vs; int rac; int ua_intlck_ctrl; int swp; int ato; int tas; int atmpe; int rwwp; int autoload_mode; int busy_timeout_period; int extended_selftest_completion_time; }; struct scsi_mode_page_disconnect_reconnect { int buffer_full_ratio; int buffer_empty_ratio; int bus_inactivity_limit; int disconnect_time_limit; int connect_time_limit; int maximum_burst_size; int emdp; int fair_arbitration; int dimm; int dtdc; int first_burst_size; }; struct scsi_mode_page_informational_exceptions_control { int perf; int ebf; int ewasc; int dexcpt; int test; int ebackerr; int logerr; int mrie; int interval_timer; int report_count; }; enum scsi_modesense_page_code { SCSI_MODEPAGE_READ_WRITE_ERROR_RECOVERY = 0x01, SCSI_MODEPAGE_DISCONNECT_RECONNECT = 0x02, SCSI_MODEPAGE_VERIFY_ERROR_RECOVERY = 0x07, SCSI_MODEPAGE_CACHING = 0x08, SCSI_MODEPAGE_XOR_CONTROL = 0x10, SCSI_MODEPAGE_CONTROL = 0x0a, SCSI_MODEPAGE_POWER_CONDITION = 0x1a, SCSI_MODEPAGE_INFORMATIONAL_EXCEPTIONS_CONTROL = 0x1c, SCSI_MODEPAGE_RETURN_ALL_PAGES = 0x3f }; /* Do not use in new code. * Backward compatibility macros */ #define SCSI_MODESENSE_PAGECODE_READ_WRITE_ERROR_RECOVERY SCSI_MODEPAGE_READ_WRITE_ERROR_RECOVERY #define SCSI_MODESENSE_PAGECODE_DISCONNECT_RECONNECT SCSI_MODEPAGE_DISCONNECT_RECONNECT #define SCSI_MODESENSE_PAGECODE_VERIFY_ERROR_RECOVERY SCSI_MODEPAGE_VERIFY_ERROR_RECOVERY #define SCSI_MODESENSE_PAGECODE_CACHING SCSI_MODEPAGE_CACHING #define SCSI_MODESENSE_PAGECODE_XOR_CONTROL SCSI_MODEPAGE_XOR_CONTROL #define SCSI_MODESENSE_PAGECODE_CONTROL SCSI_MODEPAGE_CONTROL #define SCSI_MODESENSE_PAGECODE_INFORMATIONAL_EXCEPTIONS_CONTROL SCSI_MODEPAGE_INFORMATIONAL_EXCEPTIONS_CONTROL #define SCSI_MODESENSE_PAGECODE_RETURN_ALL_PAGES SCSI_MODEPAGE_RETURN_ALL_PAGES struct scsi_mode_page { struct scsi_mode_page *next; int ps; int spf; enum scsi_modesense_page_code page_code; int subpage_code; int len; union { struct scsi_mode_page_caching caching; struct scsi_mode_page_control control; struct scsi_mode_page_disconnect_reconnect disconnect_reconnect; struct scsi_mode_page_informational_exceptions_control iec; struct scsi_mode_page_power_condition power_condition; }; }; struct scsi_mode_sense { uint16_t mode_data_length; uint8_t medium_type; uint8_t device_specific_parameter; uint8_t longlba; uint8_t block_descriptor_length; struct scsi_mode_page *pages; }; EXTERN struct scsi_mode_page * scsi_modesense_get_page(struct scsi_mode_sense *ms, enum scsi_modesense_page_code page_code, int subpage_code); EXTERN struct scsi_task *scsi_cdb_modesense6(int dbd, enum scsi_modesense_page_control pc, enum scsi_modesense_page_code page_code, int sub_page_code, unsigned char alloc_len); EXTERN struct scsi_task *scsi_cdb_modesense10(int llbaa, int dbd, enum scsi_modesense_page_control pc, enum scsi_modesense_page_code page_code, int sub_page_code, unsigned char alloc_len); EXTERN struct scsi_task *scsi_cdb_modeselect6(int pf, int sp, int param_len); EXTERN struct scsi_task *scsi_cdb_modeselect10(int pf, int sp, int param_len); EXTERN struct scsi_data * scsi_modesense_dataout_marshall(struct scsi_task *task, struct scsi_mode_page *mp, int is_modeselect6); struct scsi_readcapacity16 { uint64_t returned_lba; uint32_t block_length; uint8_t p_type; uint8_t prot_en; uint8_t p_i_exp; uint8_t lbppbe; uint8_t lbpme; uint8_t lbprz; uint16_t lalba; }; enum scsi_provisioning_type { SCSI_PROVISIONING_TYPE_MAPPED = 0x00, SCSI_PROVISIONING_TYPE_DEALLOCATED = 0x01, SCSI_PROVISIONING_TYPE_ANCHORED = 0x02 }; struct scsi_lba_status_descriptor { uint64_t lba; uint32_t num_blocks; enum scsi_provisioning_type provisioning; }; struct scsi_get_lba_status { uint32_t num_descriptors; struct scsi_lba_status_descriptor *descriptors; }; struct scsi_op_timeout_descriptor { uint16_t descriptor_length; uint8_t command_specific; uint32_t nominal_processing_timeout; uint32_t recommended_timeout; }; struct scsi_command_descriptor { uint8_t opcode; uint16_t sa; uint8_t ctdp; uint8_t servactv; uint16_t cdb_len; /* only present if CTDP==1 */ struct scsi_op_timeout_descriptor to; }; struct scsi_report_supported_op_codes { int num_descriptors; struct scsi_command_descriptor descriptors[0]; }; struct scsi_report_supported_op_codes_one_command { uint8_t ctdp; uint8_t support; uint8_t cdb_length; uint8_t cdb_usage_data[16]; /* only present if CTDP==1 */ struct scsi_op_timeout_descriptor to; }; struct scsi_persistent_reserve_in_read_keys { uint32_t prgeneration; uint32_t additional_length; int num_keys; uint64_t keys[0]; }; struct scsi_persistent_reserve_in_read_reservation { uint32_t prgeneration; uint32_t additional_length; int reserved; uint64_t reservation_key; unsigned char pr_scope; unsigned char pr_type; }; enum scsi_persistent_reservation_type_mask { SCSI_PR_TYPE_MASK_EX_AC_AR = (1 << 0), SCSI_PR_TYPE_MASK_WR_EX = (1 << 9), SCSI_PR_TYPE_MASK_EX_AC = (1 << 11), SCSI_PR_TYPE_MASK_WR_EX_RO = (1 << 13), SCSI_PR_TYPE_MASK_EX_AC_RO = (1 << 14), SCSI_PR_TYPE_MASK_WR_EX_AR = (1 << 15), SCSI_PR_TYPE_MASK_ALL = (SCSI_PR_TYPE_MASK_EX_AC_AR | SCSI_PR_TYPE_MASK_WR_EX | SCSI_PR_TYPE_MASK_EX_AC | SCSI_PR_TYPE_MASK_WR_EX_RO | SCSI_PR_TYPE_MASK_EX_AC_RO | SCSI_PR_TYPE_MASK_WR_EX_AR) }; struct scsi_persistent_reserve_in_report_capabilities { uint16_t length; uint8_t crh; uint8_t sip_c; uint8_t atp_c; uint8_t ptpl_c; uint8_t tmv; uint8_t allow_commands; uint8_t ptpl_a; uint16_t persistent_reservation_type_mask; }; struct scsi_read6_cdb { enum scsi_opcode opcode; uint32_t lba; uint16_t transfer_length; uint8_t control; }; struct scsi_read10_cdb { enum scsi_opcode opcode; uint8_t rdprotect; uint8_t dpo; uint8_t fua; uint8_t fua_nv; uint32_t lba; uint8_t group; uint16_t transfer_length; uint8_t control; }; struct scsi_read12_cdb { enum scsi_opcode opcode; uint8_t rdprotect; uint8_t dpo; uint8_t fua; uint8_t rarc; uint8_t fua_nv; uint32_t lba; uint32_t transfer_length; uint8_t group; uint8_t control; }; struct scsi_read16_cdb { enum scsi_opcode opcode; uint8_t rdprotect; uint8_t dpo; uint8_t fua; uint8_t rarc; uint8_t fua_nv; uint64_t lba; uint32_t transfer_length; uint8_t group; uint8_t control; }; struct scsi_verify10_cdb { enum scsi_opcode opcode; uint8_t vrprotect; uint8_t dpo; uint8_t bytchk; uint32_t lba; uint8_t group; uint16_t verification_length; uint8_t control; }; struct scsi_verify12_cdb { enum scsi_opcode opcode; uint8_t vrprotect; uint8_t dpo; uint8_t bytchk; uint32_t lba; uint32_t verification_length; uint8_t group; uint8_t control; }; struct scsi_verify16_cdb { enum scsi_opcode opcode; uint8_t vrprotect; uint8_t dpo; uint8_t bytchk; uint64_t lba; uint32_t verification_length; uint8_t group; uint8_t control; }; struct scsi_write10_cdb { enum scsi_opcode opcode; uint8_t wrprotect; uint8_t dpo; uint8_t fua; uint8_t fua_nv; uint32_t lba; uint8_t group; uint16_t transfer_length; uint8_t control; }; struct scsi_write12_cdb { enum scsi_opcode opcode; uint8_t wrprotect; uint8_t dpo; uint8_t fua; uint8_t fua_nv; uint32_t lba; uint32_t transfer_length; uint8_t group; uint8_t control; }; struct scsi_write16_cdb { enum scsi_opcode opcode; uint8_t wrprotect; uint8_t dpo; uint8_t fua; uint8_t fua_nv; uint32_t lba; uint32_t transfer_length; uint8_t group; uint8_t control; }; struct scsi_writeatomic16_cdb { enum scsi_opcode opcode; uint8_t wrprotect; uint8_t dpo; uint8_t fua; uint32_t lba; uint16_t transfer_length; uint8_t group; uint8_t control; }; EXTERN int scsi_datain_getfullsize(struct scsi_task *task); EXTERN void *scsi_datain_unmarshall(struct scsi_task *task); EXTERN void *scsi_cdb_unmarshall(struct scsi_task *task, enum scsi_opcode opcode); EXTERN void scsi_parse_sense_data(struct scsi_sense *sense, const uint8_t *sb); EXTERN struct scsi_task *scsi_cdb_compareandwrite(uint64_t lba, uint32_t xferlen, int blocksize, int wrprotect, int dpo, int fua, int fua_nv, int group_number); EXTERN struct scsi_task *scsi_cdb_get_lba_status(uint64_t starting_lba, uint32_t alloc_len); EXTERN struct scsi_task *scsi_cdb_orwrite(uint64_t lba, uint32_t xferlen, int blocksize, int wrprotect, int dpo, int fua, int fua_nv, int group_number); EXTERN struct scsi_task *scsi_cdb_persistent_reserve_in(enum scsi_persistent_in_sa sa, uint16_t xferlen); EXTERN struct scsi_task *scsi_cdb_persistent_reserve_out(enum scsi_persistent_out_sa sa, enum scsi_persistent_out_scope scope, enum scsi_persistent_out_type type, void *params); EXTERN struct scsi_task *scsi_cdb_prefetch10(uint32_t lba, int num_blocks, int immed, int group); EXTERN struct scsi_task *scsi_cdb_prefetch16(uint64_t lba, int num_blocks, int immed, int group); EXTERN struct scsi_task *scsi_cdb_preventallow(int prevent); EXTERN struct scsi_task *scsi_cdb_read6(uint32_t lba, uint32_t xferlen, int blocksize); EXTERN struct scsi_task *scsi_cdb_read10(uint32_t lba, uint32_t xferlen, int blocksize, int rdprotect, int dpo, int fua, int fua_nv, int group_number); EXTERN struct scsi_task *scsi_cdb_read12(uint32_t lba, uint32_t xferlen, int blocksize, int rdprotect, int dpo, int fua, int fua_nv, int group_number); EXTERN struct scsi_task *scsi_cdb_read16(uint64_t lba, uint32_t xferlen, int blocksize, int rdprotect, int dpo, int fua, int fua_nv, int group_number); EXTERN struct scsi_task *scsi_cdb_readcapacity16(void); EXTERN struct scsi_task *scsi_cdb_report_supported_opcodes(int rctd, int options, enum scsi_opcode opcode, int sa, uint32_t alloc_len); EXTERN struct scsi_task *scsi_cdb_serviceactionin16(enum scsi_service_action_in sa, uint32_t xferlen); EXTERN struct scsi_task *scsi_cdb_startstopunit(int immed, int pcm, int pc, int no_flush, int loej, int start); EXTERN struct scsi_task *scsi_cdb_synchronizecache10(int lba, int num_blocks, int syncnv, int immed); EXTERN struct scsi_task *scsi_cdb_synchronizecache16(uint64_t lba, uint32_t num_blocks, int syncnv, int immed); EXTERN struct scsi_task *scsi_cdb_unmap(int anchor, int group, uint16_t xferlen); EXTERN struct scsi_task *scsi_cdb_verify10(uint32_t lba, uint32_t xferlen, int vprotect, int dpo, int bytchk, int blocksize); EXTERN struct scsi_task *scsi_cdb_verify12(uint32_t lba, uint32_t xferlen, int vprotect, int dpo, int bytchk, int blocksize); EXTERN struct scsi_task *scsi_cdb_verify16(uint64_t lba, uint32_t xferlen, int vprotect, int dpo, int bytchk, int blocksize); EXTERN struct scsi_task *scsi_cdb_write10(uint32_t lba, uint32_t xferlen, int blocksize, int wrprotect, int dpo, int fua, int fua_nv, int group_number); EXTERN struct scsi_task *scsi_cdb_write12(uint32_t lba, uint32_t xferlen, int blocksize, int wrprotect, int dpo, int fua, int fua_nv, int group_number); EXTERN struct scsi_task *scsi_cdb_write16(uint64_t lba, uint32_t xferlen, int blocksize, int wrprotect, int dpo, int fua, int fua_nv, int group_number); EXTERN struct scsi_task *scsi_cdb_writeatomic16(uint64_t lba, uint32_t xferlen, int blocksize, int wrprotect, int dpo, int fua, int group_number); EXTERN struct scsi_task *scsi_cdb_writesame10(int wrprotect, int anchor, int unmap, uint32_t lba, int group, uint16_t num_blocks, uint32_t datalen); EXTERN struct scsi_task *scsi_cdb_writesame16(int wrprotect, int anchor, int unmap, uint64_t lba, int group, uint32_t num_blocks, uint32_t datalen); EXTERN struct scsi_task *scsi_cdb_writeverify10(uint32_t lba, uint32_t xferlen, int blocksize, int wrprotect, int dpo, int bytchk, int group_number); EXTERN struct scsi_task *scsi_cdb_writeverify12(uint32_t lba, uint32_t xferlen, int blocksize, int wrprotect, int dpo, int bytchk, int group_number); EXTERN struct scsi_task *scsi_cdb_writeverify16(uint64_t lba, uint32_t xferlen, int blocksize, int wrprotect, int dpo, int bytchk, int group_number); /* * EXTENDED COPY */ #define XCOPY_DESC_OFFSET 16 #define SEG_DESC_SRC_INDEX_OFFSET 4 enum list_id_usage { LIST_ID_USAGE_HOLD = 0, LIST_ID_USAGE_DISCARD = 2, LIST_ID_USAGE_DISABLE = 3 }; enum ec_descr_type_code { /* Segment descriptors : 0x00 to 0xBF */ BLK_TO_STRM_SEG_DESCR = 0x00, STRM_TO_BLK_SEG_DESCR = 0x01, BLK_TO_BLK_SEG_DESCR = 0x02, STRM_TO_STRM_SEG_DESCR = 0x03, /* Target descriptors : 0xEO to 0xFE */ IDENT_DESCR_TGT_DESCR = 0xE4, IPV4_TGT_DESCR = 0xE5, IPV6_TGT_DESCR = 0xEA, IP_COPY_SVC_TGT_DESCR = 0xEB }; enum lu_id_type { LU_ID_TYPE_LUN = 0x00, LU_ID_TYPE_PROXY_TOKEN = 0x01, LU_ID_TYPE_RSVD = 0x02, LU_ID_TYPE_RSVD1 = 0x03 }; EXTERN struct scsi_task *scsi_cdb_extended_copy(int immed); /* * RECEIVE COPY RESULTS */ enum scsi_copy_results_sa { SCSI_COPY_RESULTS_COPY_STATUS = 0, SCSI_COPY_RESULTS_RECEIVE_DATA = 1, SCSI_COPY_RESULTS_OP_PARAMS = 3, SCSI_COPY_RESULTS_FAILED_SEGMENT = 4, }; EXTERN struct scsi_task *scsi_cdb_receive_copy_results(enum scsi_copy_results_sa sa, int list_id, int xferlen); struct scsi_copy_results_copy_status { uint32_t available_data; uint8_t copy_manager_status; uint8_t hdd; uint16_t segments_processed; uint8_t transfer_count_units; uint32_t transfer_count; }; struct scsi_copy_results_op_params { uint32_t available_data; uint16_t max_target_desc_count; uint16_t max_segment_desc_count; uint32_t max_desc_list_length; uint32_t max_segment_length; uint32_t max_inline_data_length; uint32_t held_data_limit; uint32_t max_stream_device_transfer_size; uint16_t total_concurrent_copies; uint8_t max_concurrent_copies; uint8_t data_segment_granularity; uint8_t inline_data_granularity; uint8_t held_data_granularity; uint8_t impl_desc_list_length; uint8_t imp_desc_type_codes[0]; }; void *scsi_malloc(struct scsi_task *task, size_t size); uint64_t scsi_get_uint64(const unsigned char *c); uint32_t scsi_get_uint32(const unsigned char *c); uint16_t scsi_get_uint16(const unsigned char *c); void scsi_set_uint64(unsigned char *c, uint64_t val); void scsi_set_uint32(unsigned char *c, uint32_t val); void scsi_set_uint16(unsigned char *c, uint16_t val); #ifdef __cplusplus } #endif #endif /* __scsi_lowlevel_h__ */ libiscsi-1.17.0/include/slist.h000066400000000000000000000035131271371262700163450ustar00rootroot00000000000000/* Copyright (C) 2010 by Ronnie Sahlberg This program 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 program is distributed in the hope that 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 program; if not, see . */ #ifndef __iscsi_slist_h__ #define __iscsi_slist_h__ #define ISCSI_LIST_ADD(list, item) \ do { \ (item)->next = (*list); \ (*list) = (item); \ } while (0); #define ISCSI_LIST_ADD_END(list, item) \ if ((*list) == NULL) { \ ISCSI_LIST_ADD((list), (item)); \ } else { \ void *head = (*list); \ while ((*list)->next) \ (*list) = (*list)->next; \ (*list)->next = (item); \ (item)->next = NULL; \ (*list) = head; \ } #define ISCSI_LIST_REMOVE(list, item) \ if ((*list) == (item)) { \ (*list) = (item)->next; \ } else { \ void *head = (*list); \ while ((*list)->next && (*list)->next != (item)) \ (*list) = (*list)->next; \ if ((*list)->next != NULL) { \ (*list)->next = (*list)->next->next; \ } \ (*list) = head; \ } #define ISCSI_LIST_LENGTH(list,length) \ do { \ (length) = 0; \ void *head = (*list); \ while ((*list)) { \ (*list) = (*list)->next; \ (length)++; \ } \ (*list) = head; \ } while (0); #endif /* __iscsi_slist_h__ */ libiscsi-1.17.0/lib/000077500000000000000000000000001271371262700141575ustar00rootroot00000000000000libiscsi-1.17.0/lib/Makefile.am000066400000000000000000000012221271371262700162100ustar00rootroot00000000000000lib_LTLIBRARIES = libiscsi.la libiscsi_la_SOURCES = \ connect.c crc32c.c discovery.c init.c \ login.c nop.c pdu.c iscsi-command.c \ scsi-lowlevel.c socket.c sync.c task_mgmt.c \ logging.c if !HAVE_LIBGCRYPT libiscsi_la_SOURCES += md5.c endif SOCURRENT=7 SOREVISON=2 SOAGE=0 libiscsi_la_LDFLAGS = \ -version-info $(SOCURRENT):$(SOREVISON):$(SOAGE) -bindir $(bindir) \ -no-undefined -export-symbols ${srcdir}/libiscsi.syms libiscsi_la_CPPFLAGS = -I${srcdir}/../include -I$(srcdir)/include \ "-D_U_=__attribute__((unused))" \ "-D_R_(A,B)=__attribute__((format(printf,A,B)))" AM_CFLAGS=$(WARN_CFLAGS) dist_noinst_DATA = libiscsi.syms libiscsi.def libiscsi-1.17.0/lib/connect.c000066400000000000000000000340721271371262700157620ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2010 by Ronnie Sahlberg This program 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 program is distributed in the hope that 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 program; if not, see . */ #if defined(WIN32) #include "win32/win32_compat.h" #else #include #endif #include #include #include #include #include #include "slist.h" #include "iscsi.h" #include "iscsi-private.h" #include "scsi-lowlevel.h" struct connect_task { iscsi_command_cb cb; void *private_data; int lun; int num_uas; }; static void iscsi_connect_cb(struct iscsi_context *iscsi, int status, void *command_data _U_, void *private_data); /* During a reconnect all new SCSI commands are normally deferred to the * old context and not actually issued until we have completed the re-connect * and switched the contexts. * This is what we want most of the time. However, IF we want to send TURs * during the reconnect to eat all the UAs, then we want to send them out * on this temporary context and NOT just queue them for until later. * Hence this function. * * By setting ->old_iscsi temporarily to NULL when we are creating the TUR * we avoid the check in iscsi_scsi_command_async() that otehrwise will try * to defer this command until later. */ static struct scsi_task * iscsi_testunitready_connect(struct iscsi_context *iscsi, int lun, iscsi_command_cb cb, void *private_data) { struct scsi_task *task; struct iscsi_context *old_iscsi = iscsi->old_iscsi; iscsi->old_iscsi = NULL; task = iscsi_testunitready_task(iscsi, lun, cb, private_data); iscsi->old_iscsi = old_iscsi; return task; } static void iscsi_testunitready_cb(struct iscsi_context *iscsi, int status, void *command_data, void *private_data) { struct connect_task *ct = private_data; struct scsi_task *task = command_data; if (status != 0) { if (task->sense.key == SCSI_SENSE_UNIT_ATTENTION) { /* This is just the normal unitattention/busreset * you always get just after a fresh login. Try * again. Instead of enumerating all of them we * assume that there will be at most 10 or else * there is something broken. */ ct->num_uas++; if (ct->num_uas > 10) { iscsi_set_error(iscsi, "iscsi_testunitready " "Too many UnitAttentions " "during login."); ct->cb(iscsi, SCSI_STATUS_ERROR, NULL, ct->private_data); iscsi_free(iscsi, ct); scsi_free_scsi_task(task); return; } if (iscsi_testunitready_connect(iscsi, ct->lun, iscsi_testunitready_cb, ct) == NULL) { iscsi_set_error(iscsi, "iscsi_testunitready " "failed."); ct->cb(iscsi, SCSI_STATUS_ERROR, NULL, ct->private_data); iscsi_free(iscsi, ct); } scsi_free_scsi_task(task); return; } } /* Don't fail the login just because there is no medium in the device */ if (status != 0 && task->sense.key == SCSI_SENSE_NOT_READY && (task->sense.ascq == SCSI_SENSE_ASCQ_MEDIUM_NOT_PRESENT || task->sense.ascq == SCSI_SENSE_ASCQ_MEDIUM_NOT_PRESENT_TRAY_CLOSED || task->sense.ascq == SCSI_SENSE_ASCQ_MEDIUM_NOT_PRESENT_TRAY_OPEN)) { status = 0; } /* Don't fail the login just because the medium is reserved */ if (status == SCSI_STATUS_RESERVATION_CONFLICT) { status = 0; } /* Don't fail the login just because there is a sanitize in progress */ if (status != 0 && task->sense.key == SCSI_SENSE_NOT_READY && task->sense.ascq == SCSI_SENSE_ASCQ_SANITIZE_IN_PROGRESS) { status = 0; } ct->cb(iscsi, status?SCSI_STATUS_ERROR:SCSI_STATUS_GOOD, NULL, ct->private_data); scsi_free_scsi_task(task); iscsi_free(iscsi, ct); } static void iscsi_login_cb(struct iscsi_context *iscsi, int status, void *command_data _U_, void *private_data) { struct connect_task *ct = private_data; if (status == SCSI_STATUS_REDIRECT && iscsi->target_address[0]) { iscsi_disconnect(iscsi); if (iscsi->bind_interfaces[0]) iscsi_decrement_iface_rr(); if (iscsi_connect_async(iscsi, iscsi->target_address, iscsi_connect_cb, iscsi->connect_data) != 0) { iscsi_free(iscsi, ct); return; } return; } if (status != 0) { ct->cb(iscsi, SCSI_STATUS_ERROR, NULL, ct->private_data); iscsi_free(iscsi, ct); return; } /* If the application has requested no UA on reconnect OR if this is the initial connection attempt then we need to consume any/all UAs that might be present. */ if (iscsi->no_ua_on_reconnect || (ct->lun != -1 && !iscsi->old_iscsi)) { if (iscsi_testunitready_connect(iscsi, ct->lun, iscsi_testunitready_cb, ct) == NULL) { iscsi_set_error(iscsi, "iscsi_testunitready_async failed."); ct->cb(iscsi, SCSI_STATUS_ERROR, NULL, ct->private_data); iscsi_free(iscsi, ct); } } else { ct->cb(iscsi, SCSI_STATUS_GOOD, NULL, ct->private_data); iscsi_free(iscsi, ct); } } static void iscsi_connect_cb(struct iscsi_context *iscsi, int status, void *command_data _U_, void *private_data) { struct connect_task *ct = private_data; if (status != 0) { iscsi_set_error(iscsi, "Failed to connect to iSCSI socket. " "%s", iscsi_get_error(iscsi)); ct->cb(iscsi, SCSI_STATUS_ERROR, NULL, ct->private_data); iscsi_free(iscsi, ct); return; } if (iscsi_login_async(iscsi, iscsi_login_cb, ct) != 0) { iscsi_set_error(iscsi, "iscsi_login_async failed: %s", iscsi_get_error(iscsi)); ct->cb(iscsi, SCSI_STATUS_ERROR, NULL, ct->private_data); iscsi_free(iscsi, ct); } } int iscsi_full_connect_async(struct iscsi_context *iscsi, const char *portal, int lun, iscsi_command_cb cb, void *private_data) { struct connect_task *ct; iscsi->lun = lun; if (iscsi->portal != portal) { strncpy(iscsi->portal, portal, MAX_STRING_SIZE); } ct = iscsi_malloc(iscsi, sizeof(struct connect_task)); if (ct == NULL) { iscsi_set_error(iscsi, "Out-of-memory. Failed to allocate " "connect_task structure."); return -ENOMEM; } ct->cb = cb; ct->lun = lun; ct->num_uas = 0; ct->private_data = private_data; if (iscsi_connect_async(iscsi, portal, iscsi_connect_cb, ct) != 0) { iscsi_free(iscsi, ct); return -ENOMEM; } return 0; } /* Set auto reconnect status. If state !0 then we will not reconnect automatically upon session failure. */ void iscsi_set_noautoreconnect(struct iscsi_context *iscsi, int state) { iscsi->no_auto_reconnect = state; /* If the session was dropped while auto reconnect was disabled then we explicitely reconnect here again. */ if (!state && iscsi->reconnect_deferred) { iscsi->reconnect_deferred = 0; iscsi_reconnect(iscsi); } } /* Set ua reconnect status. The default is that we just reconnect and then any/all UAs that are generated by the target will be passed back to the application. For test applications it can be more convenient to just reconnect and have any UAs be consumed and ignored by the library. */ void iscsi_set_no_ua_on_reconnect(struct iscsi_context *iscsi, int state) { iscsi->no_ua_on_reconnect = state; } void iscsi_set_reconnect_max_retries(struct iscsi_context *iscsi, int count) { iscsi->reconnect_max_retries = count; } void iscsi_defer_reconnect(struct iscsi_context *iscsi) { struct iscsi_pdu *pdu; iscsi->reconnect_deferred = 1; ISCSI_LOG(iscsi, 2, "reconnect deferred, cancelling all tasks"); while ((pdu = iscsi->outqueue)) { ISCSI_LIST_REMOVE(&iscsi->outqueue, pdu); if (iscsi->is_loggedin && pdu->callback) { /* If an error happened during connect/login, we don't want to call any of the callbacks. */ pdu->callback(iscsi, SCSI_STATUS_CANCELLED, NULL, pdu->private_data); } iscsi_free_pdu(iscsi, pdu); } while ((pdu = iscsi->waitpdu)) { ISCSI_LIST_REMOVE(&iscsi->waitpdu, pdu); if (iscsi->is_loggedin && pdu->callback) { /* If an error happened during connect/login, we don't want to call any of the callbacks. */ pdu->callback(iscsi, SCSI_STATUS_CANCELLED, NULL, pdu->private_data); } iscsi_free_pdu(iscsi, pdu); } } void iscsi_reconnect_cb(struct iscsi_context *iscsi _U_, int status, void *command_data _U_, void *private_data _U_) { struct iscsi_context *old_iscsi; int i; if (status != SCSI_STATUS_GOOD) { int backoff = ++iscsi->old_iscsi->retry_cnt; if (backoff > 10) { backoff += rand() % 10; backoff -= 5; } if (backoff > 30) { backoff = 30; } if (iscsi->reconnect_max_retries != -1 && iscsi->old_iscsi->retry_cnt > iscsi->reconnect_max_retries) { /* we will exit iscsi_service with -1 the next time we enter it. */ backoff = 0; } ISCSI_LOG(iscsi, 1, "reconnect try %d failed, waiting %d seconds", iscsi->old_iscsi->retry_cnt, backoff); iscsi->next_reconnect = time(NULL) + backoff; iscsi->pending_reconnect = 1; return; } old_iscsi = iscsi->old_iscsi; iscsi->old_iscsi = NULL; while (old_iscsi->outqueue) { struct iscsi_pdu *pdu = old_iscsi->outqueue; ISCSI_LIST_REMOVE(&old_iscsi->outqueue, pdu); ISCSI_LIST_ADD_END(&old_iscsi->waitpdu, pdu); } while (old_iscsi->waitpdu) { struct iscsi_pdu *pdu = old_iscsi->waitpdu; ISCSI_LIST_REMOVE(&old_iscsi->waitpdu, pdu); if (pdu->itt == 0xffffffff) { iscsi_free_pdu(old_iscsi, pdu); continue; } if (pdu->flags & ISCSI_PDU_DROP_ON_RECONNECT) { /* * We only want to re-queue SCSI COMMAND PDUs. * All other PDUs are discarded at this point. * This includes DATA-OUT, NOP and task management. */ if (pdu->callback) { pdu->callback(iscsi, SCSI_STATUS_CANCELLED, NULL, pdu->private_data); } iscsi_free_pdu(old_iscsi, pdu); continue; } scsi_task_reset_iov(&pdu->scsi_cbdata.task->iovector_in); scsi_task_reset_iov(&pdu->scsi_cbdata.task->iovector_out); /* We pass NULL as 'd' since any databuffer has already * been converted to a task-> iovector first time this * PDU was sent. */ if (iscsi_scsi_command_async(iscsi, pdu->lun, pdu->scsi_cbdata.task, pdu->scsi_cbdata.callback, NULL, pdu->scsi_cbdata.private_data)) { /* not much we can really do at this point */ } iscsi_free_pdu(old_iscsi, pdu); } if (old_iscsi->incoming != NULL) { iscsi_free_iscsi_in_pdu(old_iscsi, old_iscsi->incoming); } if (old_iscsi->inqueue != NULL) { iscsi_free_iscsi_inqueue(old_iscsi, old_iscsi->inqueue); } if (old_iscsi->outqueue_current != NULL && old_iscsi->outqueue_current->flags & ISCSI_PDU_DELETE_WHEN_SENT) { iscsi_free_pdu(old_iscsi, old_iscsi->outqueue_current); } for (i = 0; i < old_iscsi->smalloc_free; i++) { iscsi_free(old_iscsi, old_iscsi->smalloc_ptrs[i]); } iscsi->mallocs += old_iscsi->mallocs; iscsi->frees += old_iscsi->frees; free(old_iscsi); /* avoid a reconnect faster than 3 seconds */ iscsi->next_reconnect = time(NULL) + 3; ISCSI_LOG(iscsi, 2, "reconnect was successful"); iscsi->pending_reconnect = 0; } int iscsi_reconnect(struct iscsi_context *old_iscsi) { struct iscsi_context *iscsi; /* if there is already a deferred reconnect do not try again */ if (old_iscsi->reconnect_deferred) { ISCSI_LOG(old_iscsi, 2, "reconnect initiated, but reconnect is already deferred"); return -1; } /* This is mainly for tests, where we do not want to automatically reconnect but rather want the commands to fail with an error if the target drops the session. */ if (old_iscsi->no_auto_reconnect) { iscsi_defer_reconnect(old_iscsi); return 0; } if (old_iscsi->old_iscsi && !old_iscsi->pending_reconnect) { return 0; } if (time(NULL) < old_iscsi->next_reconnect) { old_iscsi->pending_reconnect = 1; return 0; } if (old_iscsi->reconnect_max_retries != -1 && old_iscsi->old_iscsi && old_iscsi->old_iscsi->retry_cnt > old_iscsi->reconnect_max_retries) { iscsi_defer_reconnect(old_iscsi); return -1; } iscsi = iscsi_create_context(old_iscsi->initiator_name); if (iscsi == NULL) { ISCSI_LOG(old_iscsi, 2, "failed to create new context for reconnection"); return -1; } ISCSI_LOG(old_iscsi, 2, "reconnect initiated"); iscsi_set_targetname(iscsi, old_iscsi->target_name); iscsi_set_header_digest(iscsi, old_iscsi->want_header_digest); iscsi_set_initiator_username_pwd(iscsi, old_iscsi->user, old_iscsi->passwd); iscsi_set_target_username_pwd(iscsi, old_iscsi->target_user, old_iscsi->target_passwd); iscsi_set_session_type(iscsi, ISCSI_SESSION_NORMAL); iscsi->lun = old_iscsi->lun; strncpy(iscsi->portal,old_iscsi->portal,MAX_STRING_SIZE); strncpy(iscsi->bind_interfaces,old_iscsi->bind_interfaces,MAX_STRING_SIZE); iscsi->bind_interfaces_cnt = old_iscsi->bind_interfaces_cnt; iscsi->log_level = old_iscsi->log_level; iscsi->log_fn = old_iscsi->log_fn; iscsi->tcp_user_timeout = old_iscsi->tcp_user_timeout; iscsi->tcp_keepidle = old_iscsi->tcp_keepidle; iscsi->tcp_keepcnt = old_iscsi->tcp_keepcnt; iscsi->tcp_keepintvl = old_iscsi->tcp_keepintvl; iscsi->tcp_syncnt = old_iscsi->tcp_syncnt; iscsi->cache_allocations = old_iscsi->cache_allocations; iscsi->scsi_timeout = old_iscsi->scsi_timeout; iscsi->no_ua_on_reconnect = old_iscsi->no_ua_on_reconnect; iscsi->reconnect_max_retries = old_iscsi->reconnect_max_retries; if (old_iscsi->old_iscsi) { int i; for (i = 0; i < old_iscsi->smalloc_free; i++) { iscsi_free(old_iscsi, old_iscsi->smalloc_ptrs[i]); } iscsi->old_iscsi = old_iscsi->old_iscsi; } else { iscsi->old_iscsi = malloc(sizeof(struct iscsi_context)); memcpy(iscsi->old_iscsi, old_iscsi, sizeof(struct iscsi_context)); } memcpy(old_iscsi, iscsi, sizeof(struct iscsi_context)); free(iscsi); return iscsi_full_connect_async(old_iscsi, old_iscsi->portal, old_iscsi->lun, iscsi_reconnect_cb, NULL); } libiscsi-1.17.0/lib/crc32c.c000066400000000000000000000133001271371262700153770ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2010 by Ronnie Sahlberg This program 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 program is distributed in the hope that 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 program; if not, see . */ #if defined(WIN32) #else #include #endif #include "iscsi.h" #include "iscsi-private.h" /*****************************************************************/ /* */ /* CRC LOOKUP TABLE */ /* ================ */ /* The following CRC lookup table was generated automagically */ /* by the Rocksoft^tm Model CRC Algorithm Table Generation */ /* Program V1.0 using the following model parameters: */ /* */ /* Width : 4 bytes. */ /* Poly : 0x1EDC6F41L */ /* Reverse : TRUE. */ /* */ /* For more information on the Rocksoft^tm Model CRC Algorithm, */ /* see the document titled "A Painless Guide to CRC Error */ /* Detection Algorithms" by Ross Williams */ /* (ross@guest.adelaide.edu.au.). This document is likely to be */ /* in the FTP archive "ftp.adelaide.edu.au/pub/rocksoft". */ /* */ /*****************************************************************/ static unsigned long crctable[256] = { 0x00000000L, 0xF26B8303L, 0xE13B70F7L, 0x1350F3F4L, 0xC79A971FL, 0x35F1141CL, 0x26A1E7E8L, 0xD4CA64EBL, 0x8AD958CFL, 0x78B2DBCCL, 0x6BE22838L, 0x9989AB3BL, 0x4D43CFD0L, 0xBF284CD3L, 0xAC78BF27L, 0x5E133C24L, 0x105EC76FL, 0xE235446CL, 0xF165B798L, 0x030E349BL, 0xD7C45070L, 0x25AFD373L, 0x36FF2087L, 0xC494A384L, 0x9A879FA0L, 0x68EC1CA3L, 0x7BBCEF57L, 0x89D76C54L, 0x5D1D08BFL, 0xAF768BBCL, 0xBC267848L, 0x4E4DFB4BL, 0x20BD8EDEL, 0xD2D60DDDL, 0xC186FE29L, 0x33ED7D2AL, 0xE72719C1L, 0x154C9AC2L, 0x061C6936L, 0xF477EA35L, 0xAA64D611L, 0x580F5512L, 0x4B5FA6E6L, 0xB93425E5L, 0x6DFE410EL, 0x9F95C20DL, 0x8CC531F9L, 0x7EAEB2FAL, 0x30E349B1L, 0xC288CAB2L, 0xD1D83946L, 0x23B3BA45L, 0xF779DEAEL, 0x05125DADL, 0x1642AE59L, 0xE4292D5AL, 0xBA3A117EL, 0x4851927DL, 0x5B016189L, 0xA96AE28AL, 0x7DA08661L, 0x8FCB0562L, 0x9C9BF696L, 0x6EF07595L, 0x417B1DBCL, 0xB3109EBFL, 0xA0406D4BL, 0x522BEE48L, 0x86E18AA3L, 0x748A09A0L, 0x67DAFA54L, 0x95B17957L, 0xCBA24573L, 0x39C9C670L, 0x2A993584L, 0xD8F2B687L, 0x0C38D26CL, 0xFE53516FL, 0xED03A29BL, 0x1F682198L, 0x5125DAD3L, 0xA34E59D0L, 0xB01EAA24L, 0x42752927L, 0x96BF4DCCL, 0x64D4CECFL, 0x77843D3BL, 0x85EFBE38L, 0xDBFC821CL, 0x2997011FL, 0x3AC7F2EBL, 0xC8AC71E8L, 0x1C661503L, 0xEE0D9600L, 0xFD5D65F4L, 0x0F36E6F7L, 0x61C69362L, 0x93AD1061L, 0x80FDE395L, 0x72966096L, 0xA65C047DL, 0x5437877EL, 0x4767748AL, 0xB50CF789L, 0xEB1FCBADL, 0x197448AEL, 0x0A24BB5AL, 0xF84F3859L, 0x2C855CB2L, 0xDEEEDFB1L, 0xCDBE2C45L, 0x3FD5AF46L, 0x7198540DL, 0x83F3D70EL, 0x90A324FAL, 0x62C8A7F9L, 0xB602C312L, 0x44694011L, 0x5739B3E5L, 0xA55230E6L, 0xFB410CC2L, 0x092A8FC1L, 0x1A7A7C35L, 0xE811FF36L, 0x3CDB9BDDL, 0xCEB018DEL, 0xDDE0EB2AL, 0x2F8B6829L, 0x82F63B78L, 0x709DB87BL, 0x63CD4B8FL, 0x91A6C88CL, 0x456CAC67L, 0xB7072F64L, 0xA457DC90L, 0x563C5F93L, 0x082F63B7L, 0xFA44E0B4L, 0xE9141340L, 0x1B7F9043L, 0xCFB5F4A8L, 0x3DDE77ABL, 0x2E8E845FL, 0xDCE5075CL, 0x92A8FC17L, 0x60C37F14L, 0x73938CE0L, 0x81F80FE3L, 0x55326B08L, 0xA759E80BL, 0xB4091BFFL, 0x466298FCL, 0x1871A4D8L, 0xEA1A27DBL, 0xF94AD42FL, 0x0B21572CL, 0xDFEB33C7L, 0x2D80B0C4L, 0x3ED04330L, 0xCCBBC033L, 0xA24BB5A6L, 0x502036A5L, 0x4370C551L, 0xB11B4652L, 0x65D122B9L, 0x97BAA1BAL, 0x84EA524EL, 0x7681D14DL, 0x2892ED69L, 0xDAF96E6AL, 0xC9A99D9EL, 0x3BC21E9DL, 0xEF087A76L, 0x1D63F975L, 0x0E330A81L, 0xFC588982L, 0xB21572C9L, 0x407EF1CAL, 0x532E023EL, 0xA145813DL, 0x758FE5D6L, 0x87E466D5L, 0x94B49521L, 0x66DF1622L, 0x38CC2A06L, 0xCAA7A905L, 0xD9F75AF1L, 0x2B9CD9F2L, 0xFF56BD19L, 0x0D3D3E1AL, 0x1E6DCDEEL, 0xEC064EEDL, 0xC38D26C4L, 0x31E6A5C7L, 0x22B65633L, 0xD0DDD530L, 0x0417B1DBL, 0xF67C32D8L, 0xE52CC12CL, 0x1747422FL, 0x49547E0BL, 0xBB3FFD08L, 0xA86F0EFCL, 0x5A048DFFL, 0x8ECEE914L, 0x7CA56A17L, 0x6FF599E3L, 0x9D9E1AE0L, 0xD3D3E1ABL, 0x21B862A8L, 0x32E8915CL, 0xC083125FL, 0x144976B4L, 0xE622F5B7L, 0xF5720643L, 0x07198540L, 0x590AB964L, 0xAB613A67L, 0xB831C993L, 0x4A5A4A90L, 0x9E902E7BL, 0x6CFBAD78L, 0x7FAB5E8CL, 0x8DC0DD8FL, 0xE330A81AL, 0x115B2B19L, 0x020BD8EDL, 0xF0605BEEL, 0x24AA3F05L, 0xD6C1BC06L, 0xC5914FF2L, 0x37FACCF1L, 0x69E9F0D5L, 0x9B8273D6L, 0x88D28022L, 0x7AB90321L, 0xAE7367CAL, 0x5C18E4C9L, 0x4F48173DL, 0xBD23943EL, 0xF36E6F75L, 0x0105EC76L, 0x12551F82L, 0xE03E9C81L, 0x34F4F86AL, 0xC69F7B69L, 0xD5CF889DL, 0x27A40B9EL, 0x79B737BAL, 0x8BDCB4B9L, 0x988C474DL, 0x6AE7C44EL, 0xBE2DA0A5L, 0x4C4623A6L, 0x5F16D052L, 0xAD7D5351L }; unsigned long crc32c(char *buf, int len) { unsigned long crc = 0xffffffff; while (len-- > 0) { crc = (crc>>8) ^ crctable[(crc ^ (*buf++)) & 0xFF]; } return crc^0xffffffff; } libiscsi-1.17.0/lib/discovery.c000066400000000000000000000152161271371262700163370ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2010 by Ronnie Sahlberg This program 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 program is distributed in the hope that 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 program; if not, see . */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #ifdef HAVE_SYS_TYPES_H #include #endif #ifdef HAVE_ARPA_INET_H #include #endif #include #include #include #include "iscsi.h" #include "iscsi-private.h" int iscsi_discovery_async(struct iscsi_context *iscsi, iscsi_command_cb cb, void *private_data) { struct iscsi_pdu *pdu; char *str; if (iscsi->session_type != ISCSI_SESSION_DISCOVERY) { iscsi_set_error(iscsi, "Trying to do discovery on " "non-discovery session."); return -1; } pdu = iscsi_allocate_pdu(iscsi, ISCSI_PDU_TEXT_REQUEST, ISCSI_PDU_TEXT_RESPONSE, iscsi_itt_post_increment(iscsi), ISCSI_PDU_DROP_ON_RECONNECT); if (pdu == NULL) { iscsi_set_error(iscsi, "Out-of-memory: Failed to allocate " "text pdu."); return -1; } /* immediate */ iscsi_pdu_set_immediate(pdu); /* cmdsn is not increased if Immediate delivery*/ iscsi_pdu_set_cmdsn(pdu, iscsi->cmdsn); /* flags */ iscsi_pdu_set_pduflags(pdu, ISCSI_PDU_TEXT_FINAL); /* target transfer tag */ iscsi_pdu_set_ttt(pdu, 0xffffffff); /* sendtargets */ str = (char *)"SendTargets=All"; if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1) != 0) { iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed."); iscsi_free_pdu(iscsi, pdu); return -1; } pdu->callback = cb; pdu->private_data = private_data; if (iscsi_queue_pdu(iscsi, pdu) != 0) { iscsi_set_error(iscsi, "Out-of-memory: failed to queue iscsi " "text pdu."); iscsi_free_pdu(iscsi, pdu); return -1; } return 0; } static void iscsi_free_discovery_addresses(struct iscsi_context *iscsi, struct iscsi_discovery_address *addresses) { while (addresses != NULL) { struct iscsi_discovery_address *next = addresses->next; iscsi_free(iscsi, discard_const(addresses->target_name)); addresses->target_name = NULL; while (addresses->portals != NULL) { struct iscsi_target_portal *next_portal = addresses->portals->next; iscsi_free(iscsi, discard_const(addresses->portals->portal)); iscsi_free(iscsi, discard_const(addresses->portals)); addresses->portals = next_portal; } addresses->portals = NULL; addresses->next = NULL; iscsi_free(iscsi, addresses); addresses = next; } } int iscsi_process_text_reply(struct iscsi_context *iscsi, struct iscsi_pdu *pdu, struct iscsi_in_pdu *in) { struct iscsi_discovery_address *targets = NULL; unsigned char *ptr = in->data; int size = in->data_pos; /* verify the response looks sane */ if (in->hdr[1] != ISCSI_PDU_TEXT_FINAL) { iscsi_set_error(iscsi, "unsupported flags in text " "reply %02x", in->hdr[1]); if (pdu->callback) { pdu->callback(iscsi, SCSI_STATUS_ERROR, NULL, pdu->private_data); } return -1; } while (size > 0) { unsigned char *end; int len; end = memchr(ptr, 0, size); if (end == NULL) { iscsi_set_error(iscsi, "NUL not found after offset %ld " "when parsing discovery data", (long)(ptr - in->data)); if (pdu->callback) { pdu->callback(iscsi, SCSI_STATUS_ERROR, NULL, pdu->private_data); } iscsi_free_discovery_addresses(iscsi, targets); return -1; } len = end - ptr; if (len == 0) { break; } /* parse the strings */ if (!strncmp((char *)ptr, "TargetName=", 11)) { struct iscsi_discovery_address *target; target = iscsi_zmalloc(iscsi, sizeof(struct iscsi_discovery_address)); if (target == NULL) { iscsi_set_error(iscsi, "Failed to allocate " "data for new discovered " "target"); if (pdu->callback) { pdu->callback(iscsi, SCSI_STATUS_ERROR, NULL, pdu->private_data); } iscsi_free_discovery_addresses(iscsi, targets); return -1; } target->target_name = iscsi_strdup(iscsi,(char *)ptr+11); if (target->target_name == NULL) { iscsi_set_error(iscsi, "Failed to allocate " "data for new discovered " "target name"); if (pdu->callback) { pdu->callback(iscsi, SCSI_STATUS_ERROR, NULL, pdu->private_data); } iscsi_free(iscsi, target); target = NULL; iscsi_free_discovery_addresses(iscsi, targets); return -1; } target->next = targets; targets = target; } else if (!strncmp((char *)ptr, "TargetAddress=", 14)) { struct iscsi_target_portal *portal; if (targets == NULL) { iscsi_set_error(iscsi, "Invalid discovery " "reply"); if (pdu->callback) { pdu->callback(iscsi, SCSI_STATUS_ERROR, NULL, pdu->private_data); } iscsi_free_discovery_addresses(iscsi, targets); return -1; } portal = iscsi_zmalloc(iscsi, sizeof(struct iscsi_target_portal)); if (portal == NULL) { iscsi_set_error(iscsi, "Failed to malloc " "portal structure"); if (pdu->callback) { pdu->callback(iscsi, SCSI_STATUS_ERROR, NULL, pdu->private_data); } iscsi_free_discovery_addresses(iscsi, targets); return -1; } portal->next = targets->portals; targets->portals = portal; portal->portal = iscsi_strdup(iscsi, (char *)ptr+14); if (portal->portal == NULL) { iscsi_set_error(iscsi, "Failed to allocate " "data for new discovered " "target address"); if (pdu->callback) { pdu->callback(iscsi, SCSI_STATUS_ERROR, NULL, pdu->private_data); } iscsi_free_discovery_addresses(iscsi, targets); return -1; } } else { iscsi_set_error(iscsi, "Don't know how to handle " "discovery string : %s", ptr); if (pdu->callback) { pdu->callback(iscsi, SCSI_STATUS_ERROR, NULL, pdu->private_data); } iscsi_free_discovery_addresses(iscsi, targets); return -1; } ptr += len + 1; size -= len + 1; } if (pdu->callback) { pdu->callback(iscsi, SCSI_STATUS_GOOD, targets, pdu->private_data); } iscsi_free_discovery_addresses(iscsi, targets); return 0; } libiscsi-1.17.0/lib/init.c000066400000000000000000000421031271371262700152660ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2010 by Ronnie Sahlberg This program 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 program is distributed in the hope that 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 program; if not, see . */ #define _GNU_SOURCE #if defined(WIN32) #else #include #include #endif #include #include #include #include #include #include #include #include "iscsi.h" #include "iscsi-private.h" #include "slist.h" /** * Whether or not the internal memory allocator caches allocations. Disable * memory allocation caching to improve the accuracy of Valgrind reports. */ void iscsi_set_cache_allocations(struct iscsi_context *iscsi, int ca) { iscsi->cache_allocations = ca; } void* iscsi_malloc(struct iscsi_context *iscsi, size_t size) { void * ptr = malloc(size); if (ptr != NULL) iscsi->mallocs++; return ptr; } void* iscsi_zmalloc(struct iscsi_context *iscsi, size_t size) { void * ptr = malloc(size); if (ptr != NULL) { memset(ptr,0x00,size); iscsi->mallocs++; } return ptr; } void* iscsi_realloc(struct iscsi_context *iscsi, void* ptr, size_t size) { void * _ptr = realloc(ptr, size); if (_ptr != NULL) { iscsi->reallocs++; } return _ptr; } void iscsi_free(struct iscsi_context *iscsi, void* ptr) { if (ptr == NULL) return; free(ptr); iscsi->frees++; } char* iscsi_strdup(struct iscsi_context *iscsi, const char* str) { char *str2 = strdup(str); if (str2 != NULL) iscsi->mallocs++; return str2; } void* iscsi_szmalloc(struct iscsi_context *iscsi, size_t size) { void *ptr; if (size > iscsi->smalloc_size) return NULL; if (iscsi->smalloc_free > 0) { ptr = iscsi->smalloc_ptrs[--iscsi->smalloc_free]; memset(ptr, 0, iscsi->smalloc_size); iscsi->smallocs++; } else { ptr = iscsi_zmalloc(iscsi, iscsi->smalloc_size); } return ptr; } void iscsi_sfree(struct iscsi_context *iscsi, void* ptr) { if (ptr == NULL) { return; } if (!iscsi->cache_allocations) { iscsi_free(iscsi, ptr); } else if (iscsi->smalloc_free == SMALL_ALLOC_MAX_FREE) { /* SMALL_ALLOC_MAX_FREE should be adjusted that this */ /* happens rarely */ ISCSI_LOG(iscsi, 6, "smalloc free == SMALLOC_MAX_FREE"); iscsi_free(iscsi, ptr); } else { iscsi->smalloc_ptrs[iscsi->smalloc_free++] = ptr; } } struct iscsi_context * iscsi_create_context(const char *initiator_name) { struct iscsi_context *iscsi; size_t required = ISCSI_RAW_HEADER_SIZE + ISCSI_DIGEST_SIZE; char *ca; if (!initiator_name[0]) { return NULL; } iscsi = malloc(sizeof(struct iscsi_context)); if (iscsi == NULL) { return NULL; } memset(iscsi, 0, sizeof(struct iscsi_context)); strncpy(iscsi->initiator_name,initiator_name,MAX_STRING_SIZE); iscsi->fd = -1; srand(time(NULL) ^ getpid() ^ (uint32_t) ((uintptr_t) iscsi)); /* initialize to a "random" isid */ iscsi_set_isid_random(iscsi, rand(), 0); /* assume we start in security negotiation phase */ iscsi->current_phase = ISCSI_PDU_LOGIN_CSG_SECNEG; iscsi->next_phase = ISCSI_PDU_LOGIN_NSG_OPNEG; iscsi->secneg_phase = ISCSI_LOGIN_SECNEG_PHASE_OFFER_CHAP; iscsi->max_burst_length = 262144; iscsi->first_burst_length = 262144; iscsi->initiator_max_recv_data_segment_length = 262144; iscsi->target_max_recv_data_segment_length = 8192; iscsi->want_initial_r2t = ISCSI_INITIAL_R2T_NO; iscsi->use_initial_r2t = ISCSI_INITIAL_R2T_YES; iscsi->want_immediate_data = ISCSI_IMMEDIATE_DATA_YES; iscsi->use_immediate_data = ISCSI_IMMEDIATE_DATA_YES; iscsi->want_header_digest = ISCSI_HEADER_DIGEST_NONE_CRC32C; iscsi->tcp_keepcnt=3; iscsi->tcp_keepintvl=30; iscsi->tcp_keepidle=30; iscsi->reconnect_max_retries = -1; if (getenv("LIBISCSI_DEBUG") != NULL) { iscsi_set_log_level(iscsi, atoi(getenv("LIBISCSI_DEBUG"))); iscsi_set_log_fn(iscsi, iscsi_log_to_stderr); } if (getenv("LIBISCSI_TCP_USER_TIMEOUT") != NULL) { iscsi_set_tcp_user_timeout(iscsi,atoi(getenv("LIBISCSI_TCP_USER_TIMEOUT"))); } if (getenv("LIBISCSI_TCP_KEEPCNT") != NULL) { iscsi_set_tcp_keepcnt(iscsi,atoi(getenv("LIBISCSI_TCP_KEEPCNT"))); } if (getenv("LIBISCSI_TCP_KEEPINTVL") != NULL) { iscsi_set_tcp_keepintvl(iscsi,atoi(getenv("LIBISCSI_TCP_KEEPINTVL"))); } if (getenv("LIBISCSI_TCP_KEEPIDLE") != NULL) { iscsi_set_tcp_keepidle(iscsi,atoi(getenv("LIBISCSI_TCP_KEEPIDLE"))); } if (getenv("LIBISCSI_TCP_SYNCNT") != NULL) { iscsi_set_tcp_syncnt(iscsi,atoi(getenv("LIBISCSI_TCP_SYNCNT"))); } if (getenv("LIBISCSI_BIND_INTERFACES") != NULL) { iscsi_set_bind_interfaces(iscsi,getenv("LIBISCSI_BIND_INTERFACES")); } /* iscsi->smalloc_size is the size for small allocations. this should be max(ISCSI_HEADER_SIZE, sizeof(struct iscsi_pdu), sizeof(struct iscsi_in_pdu)) rounded up to the next power of 2. */ required = MAX(required, sizeof(struct iscsi_pdu)); required = MAX(required, sizeof(struct iscsi_in_pdu)); iscsi->smalloc_size = 1; while (iscsi->smalloc_size < required) { iscsi->smalloc_size <<= 1; } ISCSI_LOG(iscsi,5,"small allocation size is %d byte", iscsi->smalloc_size); ca = getenv("LIBISCSI_CACHE_ALLOCATIONS"); if (!ca || atoi(ca) != 0) { iscsi->cache_allocations = 1; } return iscsi; } int iscsi_set_isid_oui(struct iscsi_context *iscsi, uint32_t oui, uint32_t qualifier) { iscsi->isid[0] = (oui >> 16) & 0x3f; iscsi->isid[1] = (oui >> 8) & 0xff; iscsi->isid[2] = (oui ) & 0xff; iscsi->isid[3] = (qualifier >> 16) & 0xff; iscsi->isid[4] = (qualifier >> 8) & 0xff; iscsi->isid[5] = (qualifier ) & 0xff; return 0; } int iscsi_set_isid_en(struct iscsi_context *iscsi, uint32_t en, uint32_t qualifier) { iscsi->isid[0] = 0x40; iscsi->isid[1] = (en >> 16) & 0xff; iscsi->isid[2] = (en >> 8) & 0xff; iscsi->isid[3] = (en ) & 0xff; iscsi->isid[4] = (qualifier >> 8) & 0xff; iscsi->isid[5] = (qualifier ) & 0xff; return 0; } int iscsi_set_isid_random(struct iscsi_context *iscsi, uint32_t rnd, uint32_t qualifier) { iscsi->isid[0] = 0x80; iscsi->isid[1] = (rnd >> 16) & 0xff; iscsi->isid[2] = (rnd >> 8) & 0xff; iscsi->isid[3] = (rnd ) & 0xff; iscsi->isid[4] = (qualifier >> 8) & 0xff; iscsi->isid[5] = (qualifier ) & 0xff; return 0; } int iscsi_set_isid_reserved(struct iscsi_context *iscsi) { iscsi->isid[0] = 0xc0; iscsi->isid[1] = 0x00; iscsi->isid[2] = 0x00; iscsi->isid[3] = 0x00; iscsi->isid[4] = 0x00; iscsi->isid[5] = 0x00; return 0; } int iscsi_set_alias(struct iscsi_context *iscsi, const char *alias) { if (iscsi->is_loggedin != 0) { iscsi_set_error(iscsi, "Already logged in when adding alias"); return -1; } strncpy(iscsi->alias,alias,MAX_STRING_SIZE); return 0; } int iscsi_set_targetname(struct iscsi_context *iscsi, const char *target_name) { if (iscsi->is_loggedin != 0) { iscsi_set_error(iscsi, "Already logged in when adding " "targetname"); return -1; } strncpy(iscsi->target_name,target_name,MAX_STRING_SIZE); return 0; } int iscsi_destroy_context(struct iscsi_context *iscsi) { struct iscsi_pdu *pdu; int i; if (iscsi == NULL) { return 0; } if (iscsi->fd != -1) { iscsi_disconnect(iscsi); } while ((pdu = iscsi->outqueue)) { ISCSI_LIST_REMOVE(&iscsi->outqueue, pdu); if (iscsi->is_loggedin && pdu->callback) { /* If an error happened during connect/login, we don't want to call any of the callbacks. */ pdu->callback(iscsi, SCSI_STATUS_CANCELLED, NULL, pdu->private_data); } iscsi_free_pdu(iscsi, pdu); } while ((pdu = iscsi->waitpdu)) { ISCSI_LIST_REMOVE(&iscsi->waitpdu, pdu); if (iscsi->is_loggedin && pdu->callback) { /* If an error happened during connect/login, we don't want to call any of the callbacks. */ pdu->callback(iscsi, SCSI_STATUS_CANCELLED, NULL, pdu->private_data); } iscsi_free_pdu(iscsi, pdu); } if (iscsi->outqueue_current != NULL && iscsi->outqueue_current->flags & ISCSI_PDU_DELETE_WHEN_SENT) { iscsi_free_pdu(iscsi, iscsi->outqueue_current); } if (iscsi->incoming != NULL) { iscsi_free_iscsi_in_pdu(iscsi, iscsi->incoming); } if (iscsi->inqueue != NULL) { iscsi_free_iscsi_inqueue(iscsi, iscsi->inqueue); } iscsi->connect_data = NULL; for (i=0;ismalloc_free;i++) { iscsi_free(iscsi, iscsi->smalloc_ptrs[i]); } if (iscsi->mallocs != iscsi->frees) { ISCSI_LOG(iscsi,1,"%d memory blocks lost at iscsi_destroy_context() after %d malloc(s), %d realloc(s), %d free(s) and %d reused small allocations",iscsi->mallocs-iscsi->frees,iscsi->mallocs,iscsi->reallocs,iscsi->frees,iscsi->smallocs); } else { ISCSI_LOG(iscsi,5,"memory is clean at iscsi_destroy_context() after %d mallocs, %d realloc(s), %d free(s) and %d reused small allocations",iscsi->mallocs,iscsi->reallocs,iscsi->frees,iscsi->smallocs); } if (iscsi->old_iscsi) { iscsi->old_iscsi->fd = -1; iscsi_destroy_context(iscsi->old_iscsi); } memset(iscsi, 0, sizeof(struct iscsi_context)); free(iscsi); return 0; } void iscsi_set_error(struct iscsi_context *iscsi, const char *error_string, ...) { va_list ap; char errstr[MAX_STRING_SIZE + 1] = {0}; va_start(ap, error_string); if (vsnprintf(errstr, MAX_STRING_SIZE, error_string, ap) < 0) { strncpy(errstr, "could not format error string!", MAX_STRING_SIZE); } va_end(ap); if (iscsi != NULL) { strncpy(iscsi->error_string, errstr,MAX_STRING_SIZE); ISCSI_LOG(iscsi, 1, "%s",iscsi->error_string); } } void iscsi_set_log_level(struct iscsi_context *iscsi, int level) { iscsi->log_level = level; ISCSI_LOG(iscsi, 2, "set log level to %d", level); } const char * iscsi_get_error(struct iscsi_context *iscsi) { return iscsi ? iscsi->error_string : ""; } const char * iscsi_get_target_address(struct iscsi_context *iscsi) { return iscsi->target_address; } int iscsi_set_header_digest(struct iscsi_context *iscsi, enum iscsi_header_digest header_digest) { if (iscsi->is_loggedin) { iscsi_set_error(iscsi, "trying to set header digest while " "logged in"); return -1; } if ((unsigned)header_digest > ISCSI_HEADER_DIGEST_LAST) { iscsi_set_error(iscsi, "invalid header digest value"); return -1; } iscsi->want_header_digest = header_digest; return 0; } int iscsi_is_logged_in(struct iscsi_context *iscsi) { return iscsi->is_loggedin; } static int h2i(int h) { if (h >= 'a' && h <= 'f') { return h - 'a' + 10; } if (h >= 'A' && h <= 'F') { return h - 'A' + 10; } return h - '0'; } static void iscsi_decode_url_string(char *str) { while (*str) { char *tmp = str; char c; if (*str++ != '%') { continue; } if (*str == 0) { return; } c = h2i(*str++) << 4; if (*str == 0) { return; } c |= h2i(*str++); *tmp++ = c; memmove(tmp, str, strlen(str)); tmp[strlen(str)] = 0; } } struct iscsi_url * iscsi_parse_url(struct iscsi_context *iscsi, const char *url, int full) { struct iscsi_url *iscsi_url; char str[MAX_STRING_SIZE+1]; char *portal; char *user = NULL; char *passwd = NULL; char *target_user = NULL; char *target_passwd = NULL; char *target = NULL; char *lun; char *tmp; int l = 0; if (strncmp(url, "iscsi://", 8)) { if (full) { iscsi_set_error(iscsi, "Invalid URL %s\niSCSI URL must " "be of the form: %s", url, ISCSI_URL_SYNTAX); } else { iscsi_set_error(iscsi, "Invalid URL %s\niSCSI Portal " "URL must be of the form: %s", url, ISCSI_PORTAL_URL_SYNTAX); } return NULL; } strncpy(str,url + 8, MAX_STRING_SIZE); portal = str; user = getenv("LIBISCSI_CHAP_USERNAME"); passwd = getenv("LIBISCSI_CHAP_PASSWORD"); target_user = getenv("LIBISCSI_CHAP_TARGET_USERNAME"); target_passwd = getenv("LIBISCSI_CHAP_TARGET_PASSWORD"); tmp = strchr(portal, '?'); if (tmp) { *tmp++ = 0; while (tmp && *tmp) { char *next = strchr(tmp, '&'); char *key, *value; if (next != NULL) { *next++ = 0; } key = tmp; value = strchr(key, '='); if (value != NULL) { *value++ = 0; } if (!strcmp(key, "target_user")) { target_user = value; } else if (!strcmp(key, "target_password")) { target_passwd = value; } tmp = next; } } tmp = strchr(portal, '@'); if (tmp != NULL) { user = portal; *tmp++ = 0; portal = tmp; tmp = strchr(user, '%'); if (tmp == NULL) { tmp = strchr(user, ':'); } if (tmp != NULL) { *tmp++ = 0; passwd = tmp; } } if (full) { target = strchr(portal, '/'); if (target == NULL) { iscsi_set_error(iscsi, "Invalid URL %s\nCould not " "parse ''\niSCSI URL must be of " "the form: %s", url, ISCSI_URL_SYNTAX); return NULL; } *target++ = 0; if (*target == 0) { iscsi_set_error(iscsi, "Invalid URL %s\nCould not " "parse \niSCSI URL must be of the " "form: %s", url, ISCSI_URL_SYNTAX); return NULL; } lun = strchr(target, '/'); if (lun == NULL) { iscsi_set_error(iscsi, "Invalid URL %s\nCould not " "parse \niSCSI URL must be of the form: " "%s", url, ISCSI_URL_SYNTAX); return NULL; } *lun++ = 0; l = strtol(lun, &tmp, 10); if (*lun == 0 || *tmp != 0) { iscsi_set_error(iscsi, "Invalid URL %s\nCould not " "parse \niSCSI URL must be of the form: " "%s", url, ISCSI_URL_SYNTAX); return NULL; } } else { tmp=strchr(portal,'/'); if (tmp) { *tmp=0; } } if (iscsi != NULL) { iscsi_url = iscsi_malloc(iscsi, sizeof(struct iscsi_url)); } else { iscsi_url = malloc(sizeof(struct iscsi_url)); } if (iscsi_url == NULL) { iscsi_set_error(iscsi, "Out-of-memory: Failed to allocate " "iscsi_url structure"); return NULL; } memset(iscsi_url, 0, sizeof(struct iscsi_url)); iscsi_url->iscsi= iscsi; strncpy(iscsi_url->portal,portal,MAX_STRING_SIZE); if (user && passwd && user[0] && passwd[0]) { strncpy(iscsi_url->user, user, MAX_STRING_SIZE); strncpy(iscsi_url->passwd, passwd, MAX_STRING_SIZE); /* if we do not have normal CHAP, that means we do not have * bidirectional either. */ if (target_user && target_passwd && target_user[0] && target_passwd[0]) { strncpy(iscsi_url->target_user, target_user, MAX_STRING_SIZE); strncpy(iscsi_url->target_passwd, target_passwd, MAX_STRING_SIZE); } } if (full) { strncpy(iscsi_url->target, target, MAX_STRING_SIZE); iscsi_url->lun = l; } iscsi_decode_url_string(&iscsi_url->target[0]); /* NOTE: iscsi is allowed to be NULL. Especially qemu does call us with iscsi == NULL. * If we receive iscsi != NULL we apply the parsed settings to the context. */ if (iscsi) { iscsi_set_targetname(iscsi, iscsi_url->target); iscsi_set_initiator_username_pwd(iscsi, iscsi_url->user, iscsi_url->passwd); iscsi_set_target_username_pwd(iscsi, iscsi_url->target_user, iscsi_url->target_passwd); } return iscsi_url; } struct iscsi_url * iscsi_parse_full_url(struct iscsi_context *iscsi, const char *url) { return iscsi_parse_url(iscsi,url,1); } struct iscsi_url * iscsi_parse_portal_url(struct iscsi_context *iscsi, const char *url) { return iscsi_parse_url(iscsi,url,0); } void iscsi_destroy_url(struct iscsi_url *iscsi_url) { struct iscsi_context *iscsi = iscsi_url->iscsi; memset(iscsi_url, 0, sizeof(struct iscsi_url)); if (iscsi != NULL) iscsi_free(iscsi, iscsi_url); else free(iscsi_url); } int iscsi_set_initiator_username_pwd(struct iscsi_context *iscsi, const char *user, const char *passwd) { if (!user || !passwd || !user[0] || !passwd[0]) { iscsi->user[0] = 0; iscsi->passwd[0] = 0; return 0; } strncpy(iscsi->user,user,MAX_STRING_SIZE); strncpy(iscsi->passwd,passwd,MAX_STRING_SIZE); return 0; } int iscsi_set_target_username_pwd(struct iscsi_context *iscsi, const char *user, const char *passwd) { if (!user || !passwd || !user[0] || !passwd[0]) { iscsi->target_user[0] = 0; iscsi->target_passwd[0] = 0; return 0; } strncpy(iscsi->target_user, user, MAX_STRING_SIZE); strncpy(iscsi->target_passwd, passwd, MAX_STRING_SIZE); return 0; } int iscsi_set_immediate_data(struct iscsi_context *iscsi, enum iscsi_immediate_data immediate_data) { if (iscsi->is_loggedin != 0) { iscsi_set_error(iscsi, "Already logged in when trying to set immediate_data"); return -1; } iscsi->want_immediate_data = immediate_data; iscsi->use_immediate_data = immediate_data; return 0; } int iscsi_set_initial_r2t(struct iscsi_context *iscsi, enum iscsi_initial_r2t initial_r2t) { if (iscsi->is_loggedin != 0) { iscsi_set_error(iscsi, "Already logged in when trying to set initial_r2t"); return -1; } iscsi->want_initial_r2t = initial_r2t; return 0; } int iscsi_set_timeout(struct iscsi_context *iscsi, int timeout) { iscsi->scsi_timeout = timeout; return 0; } libiscsi-1.17.0/lib/iscsi-command.c000066400000000000000000001412521271371262700170560ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2010 by Ronnie Sahlberg This program 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 program is distributed in the hope that 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 program; if not, see . */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #ifdef HAVE_SYS_TYPES_H #include #endif #ifdef HAVE_ARPA_INET_H #include #endif #if defined(WIN32) #include #endif #include #include #include #include "iscsi.h" #include "iscsi-private.h" #include "scsi-lowlevel.h" #include "slist.h" static void iscsi_scsi_response_cb(struct iscsi_context *iscsi, int status, void *command_data _U_, void *private_data) { struct iscsi_scsi_cbdata *scsi_cbdata = (struct iscsi_scsi_cbdata *)private_data; switch (status) { case SCSI_STATUS_RESERVATION_CONFLICT: case SCSI_STATUS_CHECK_CONDITION: case SCSI_STATUS_GOOD: case SCSI_STATUS_BUSY: case SCSI_STATUS_CONDITION_MET: case SCSI_STATUS_TASK_SET_FULL: case SCSI_STATUS_ACA_ACTIVE: case SCSI_STATUS_TASK_ABORTED: case SCSI_STATUS_ERROR: case SCSI_STATUS_CANCELLED: case SCSI_STATUS_TIMEOUT: scsi_cbdata->task->status = status; if (scsi_cbdata->callback) { scsi_cbdata->callback(iscsi, status, scsi_cbdata->task, scsi_cbdata->private_data); } return; default: scsi_cbdata->task->status = SCSI_STATUS_ERROR; iscsi_set_error(iscsi, "Cant handle scsi status %d yet.", status); if (scsi_cbdata->callback) { scsi_cbdata->callback(iscsi, SCSI_STATUS_ERROR, scsi_cbdata->task, scsi_cbdata->private_data); } } } static int iscsi_send_data_out(struct iscsi_context *iscsi, struct iscsi_pdu *cmd_pdu, uint32_t ttt, uint32_t offset, uint32_t tot_len) { while (tot_len > 0) { uint32_t len = tot_len; struct iscsi_pdu *pdu; int flags; len = MIN(len, iscsi->target_max_recv_data_segment_length); pdu = iscsi_allocate_pdu(iscsi, ISCSI_PDU_DATA_OUT, ISCSI_PDU_NO_PDU, cmd_pdu->itt, ISCSI_PDU_DROP_ON_RECONNECT|ISCSI_PDU_DELETE_WHEN_SENT); if (pdu == NULL) { iscsi_set_error(iscsi, "Out-of-memory, Failed to allocate " "scsi data out pdu."); goto error; } pdu->scsi_cbdata.task = cmd_pdu->scsi_cbdata.task; /* set the cmdsn in the pdu struct so we can compare with * maxcmdsn when sending to socket even if data-out pdus * do not carry a cmdsn on the wire */ pdu->cmdsn = cmd_pdu->cmdsn; if (tot_len == len) { flags = ISCSI_PDU_SCSI_FINAL; } else { flags = 0; } /* flags */ iscsi_pdu_set_pduflags(pdu, flags); /* lun */ iscsi_pdu_set_lun(pdu, cmd_pdu->lun); /* ttt */ iscsi_pdu_set_ttt(pdu, ttt); /* data sn */ iscsi_pdu_set_datasn(pdu, cmd_pdu->datasn++); /* buffer offset */ iscsi_pdu_set_bufferoffset(pdu, offset); pdu->payload_offset = offset; pdu->payload_len = len; /* update data segment length */ scsi_set_uint32(&pdu->outdata.data[4], pdu->payload_len); if (iscsi_queue_pdu(iscsi, pdu) != 0) { iscsi_set_error(iscsi, "Out-of-memory: failed to queue iscsi " "scsi pdu."); goto error; } tot_len -= len; offset += len; } return 0; error: ISCSI_LIST_REMOVE(&iscsi->outqueue, cmd_pdu); ISCSI_LIST_REMOVE(&iscsi->waitpdu, cmd_pdu); if (cmd_pdu->callback) { cmd_pdu->callback(iscsi, SCSI_STATUS_ERROR, NULL, cmd_pdu->private_data); } iscsi_free_pdu(iscsi, cmd_pdu); return -1; } static int iscsi_send_unsolicited_data_out(struct iscsi_context *iscsi, struct iscsi_pdu *pdu) { uint32_t len = MIN(pdu->expxferlen, iscsi->first_burst_length) - pdu->payload_len; return iscsi_send_data_out(iscsi, pdu, 0xffffffff, pdu->payload_len, len); } /* Using 'struct iscsi_data *d' for data-out is optional * and will be converted into a one element data-out iovector. */ int iscsi_scsi_command_async(struct iscsi_context *iscsi, int lun, struct scsi_task *task, iscsi_command_cb cb, struct iscsi_data *d, void *private_data) { struct iscsi_pdu *pdu; int flags; if (iscsi->old_iscsi) { iscsi = iscsi->old_iscsi; ISCSI_LOG(iscsi, 2, "iscsi_scsi_command_async: queuing cmd to old_iscsi while reconnecting"); } if (iscsi->session_type != ISCSI_SESSION_NORMAL) { iscsi_set_error(iscsi, "Trying to send command on " "discovery session."); return -1; } if (iscsi->is_loggedin == 0 && !iscsi->pending_reconnect) { iscsi_set_error(iscsi, "Trying to send command while " "not logged in."); return -1; } /* We got an actual buffer from the application. Convert it to * a data-out iovector. */ if (d != NULL && d->data != NULL) { struct scsi_iovec *iov; iov = scsi_malloc(task, sizeof(struct scsi_iovec)); if (iov == NULL) { return -1; } iov->iov_base = d->data; iov->iov_len = d->size; scsi_task_set_iov_out(task, iov, 1); } pdu = iscsi_allocate_pdu(iscsi, ISCSI_PDU_SCSI_REQUEST, ISCSI_PDU_SCSI_RESPONSE, iscsi_itt_post_increment(iscsi), 0); if (pdu == NULL) { iscsi_set_error(iscsi, "Out-of-memory, Failed to allocate " "scsi pdu."); return -1; } pdu->scsi_cbdata.task = task; pdu->scsi_cbdata.callback = cb; pdu->scsi_cbdata.private_data = private_data; pdu->payload_offset = 0; pdu->payload_len = 0; scsi_set_task_private_ptr(task, &pdu->scsi_cbdata); /* flags */ flags = ISCSI_PDU_SCSI_FINAL|ISCSI_PDU_SCSI_ATTR_SIMPLE; switch (task->xfer_dir) { case SCSI_XFER_NONE: break; case SCSI_XFER_READ: flags |= ISCSI_PDU_SCSI_READ; break; case SCSI_XFER_WRITE: flags |= ISCSI_PDU_SCSI_WRITE; /* If we can send immediate data, send as much as we can */ if (iscsi->use_immediate_data == ISCSI_IMMEDIATE_DATA_YES) { uint32_t len = task->expxferlen; len = MIN(len, iscsi->first_burst_length); len = MIN(len, iscsi->target_max_recv_data_segment_length); pdu->payload_offset = 0; pdu->payload_len = len; /* update data segment length */ scsi_set_uint32(&pdu->outdata.data[4], pdu->payload_len); } /* We have (more) data to send and we are allowed to send * it as unsolicited data-out segments. * Drop the F-flag from the pdu and start sending a train * of data-out further below. */ if (iscsi->use_initial_r2t == ISCSI_INITIAL_R2T_NO && pdu->payload_len < (uint32_t)task->expxferlen && pdu->payload_len < iscsi->first_burst_length) { /* We have more data to send, and we are allowed to send * unsolicited data, so don't flag this PDU as final. */ flags &= ~ISCSI_PDU_SCSI_FINAL; } break; } iscsi_pdu_set_pduflags(pdu, flags); /* lun */ iscsi_pdu_set_lun(pdu, lun); pdu->lun = lun; /* expxferlen */ iscsi_pdu_set_expxferlen(pdu, task->expxferlen); /* cmdsn */ iscsi_pdu_set_cmdsn(pdu, iscsi->cmdsn++); /* cdb */ iscsi_pdu_set_cdb(pdu, task); pdu->callback = iscsi_scsi_response_cb; pdu->private_data = &pdu->scsi_cbdata; if (iscsi_queue_pdu(iscsi, pdu) != 0) { iscsi_set_error(iscsi, "Out-of-memory: failed to queue iscsi " "scsi pdu."); iscsi_free_pdu(iscsi, pdu); return -1; } /* The F flag is not set. This means we haven't sent all the unsolicited * data yet. Sent as much as we are allowed as a train of DATA-OUT PDUs. * We might already have sent some data as immediate data, which we must * subtract from first_burst_length. */ if (!(flags & ISCSI_PDU_SCSI_FINAL)) { iscsi_send_unsolicited_data_out(iscsi, pdu); } /* remember cmdsn and itt so we can use task management */ task->cmdsn = pdu->cmdsn; task->itt = pdu->itt; task->lun = lun; return 0; } /* Parse a sense key specific sense data descriptor */ static void parse_sense_spec(struct scsi_sense *sense, const uint8_t inf[3]) { if (!(inf[0] & 0x80)) /* SKSV */ return; sense->sense_specific = 1; sense->ill_param_in_cdb = !!(inf[0] & 0x40); /* C/D flag */ if (inf[0] & 8) { /* BPV */ sense->bit_pointer_valid = 1; sense->bit_pointer = inf[0] & 7; } sense->field_pointer = scsi_get_uint16(&inf[1]); } /* Parse descriptor format sense data */ static void parse_sense_descriptors(struct scsi_sense *sense, const uint8_t *sb, unsigned sb_len) { const unsigned char *p, *const end = sb + sb_len; for (p = sb; p < end; p += p[1]) { if (p[1] < 4) /* length */ break; if (!(p[2] & 0x80)) /* VALID bit */ break; switch (p[0]) { case 2: /* Sense key specific sense data descriptor */ parse_sense_spec(sense, p + 4); break; } } } void scsi_parse_sense_data(struct scsi_sense *sense, const uint8_t *sb) { sense->error_type = sb[0] & 0x7f; switch (sense->error_type) { case 0x70: case 0x71: /* Fixed format */ sense->key = sb[2] & 0x0f; sense->ascq = scsi_get_uint16(&sb[12]); parse_sense_spec(sense, sb + 15); break; case 0x72: case 0x73: /* Descriptor format */ sense->key = sb[1] & 0x0f; sense->ascq = scsi_get_uint16(&sb[2]); parse_sense_descriptors(sense, sb + 8, sb[7]); break; } } int iscsi_process_scsi_reply(struct iscsi_context *iscsi, struct iscsi_pdu *pdu, struct iscsi_in_pdu *in) { uint8_t flags, response, status; struct iscsi_scsi_cbdata *scsi_cbdata = &pdu->scsi_cbdata; struct scsi_task *task = scsi_cbdata->task; flags = in->hdr[1]; if ((flags&ISCSI_PDU_DATA_FINAL) == 0) { iscsi_set_error(iscsi, "scsi response pdu but Final bit is " "not set: 0x%02x.", flags); if (pdu->callback) { pdu->callback(iscsi, SCSI_STATUS_ERROR, task, pdu->private_data); } return -1; } if ((flags&ISCSI_PDU_DATA_ACK_REQUESTED) != 0) { iscsi_set_error(iscsi, "scsi response asked for ACK " "0x%02x.", flags); if (pdu->callback) { pdu->callback(iscsi, SCSI_STATUS_ERROR, task, pdu->private_data); } return -1; } response = in->hdr[2]; task->residual_status = SCSI_RESIDUAL_NO_RESIDUAL; task->residual = 0; if (flags & (ISCSI_PDU_DATA_RESIDUAL_OVERFLOW| ISCSI_PDU_DATA_RESIDUAL_UNDERFLOW)) { if (response != 0) { iscsi_set_error(iscsi, "protocol error: flags %#02x;" " response %#02x.", flags, response); if (pdu->callback) { pdu->callback(iscsi, SCSI_STATUS_ERROR, task, pdu->private_data); } return -1; } task->residual = scsi_get_uint32(&in->hdr[44]); if (flags & ISCSI_PDU_DATA_RESIDUAL_UNDERFLOW) { task->residual_status = SCSI_RESIDUAL_UNDERFLOW; } else { task->residual_status = SCSI_RESIDUAL_OVERFLOW; } } status = in->hdr[3]; switch (status) { case SCSI_STATUS_GOOD: case SCSI_STATUS_CONDITION_MET: task->datain.data = pdu->indata.data; task->datain.size = pdu->indata.size; /* the pdu->datain.data was malloc'ed by iscsi_malloc, as long as we have no struct iscsi_task we cannot track the free'ing of this buffer which is currently done in scsi_free_scsi_task() */ if (pdu->indata.data != NULL) iscsi->frees++; pdu->indata.data = NULL; pdu->indata.size = 0; if (pdu->callback) { pdu->callback(iscsi, SCSI_STATUS_GOOD, task, pdu->private_data); } break; case SCSI_STATUS_CHECK_CONDITION: task->datain.size = in->data_pos; task->datain.data = malloc(task->datain.size); if (task->datain.data == NULL) { iscsi_set_error(iscsi, "failed to allocate blob for " "sense data"); break; } memcpy(task->datain.data, in->data, task->datain.size); scsi_parse_sense_data(&task->sense, &task->datain.data[2]); iscsi_set_error(iscsi, "SENSE KEY:%s(%d) ASCQ:%s(0x%04x)", scsi_sense_key_str(task->sense.key), task->sense.key, scsi_sense_ascq_str(task->sense.ascq), task->sense.ascq); if (pdu->callback) { pdu->callback(iscsi, SCSI_STATUS_CHECK_CONDITION, task, pdu->private_data); } break; case SCSI_STATUS_RESERVATION_CONFLICT: iscsi_set_error(iscsi, "RESERVATION CONFLICT"); if (pdu->callback) { pdu->callback(iscsi, SCSI_STATUS_RESERVATION_CONFLICT, task, pdu->private_data); } break; case SCSI_STATUS_TASK_SET_FULL: iscsi_set_error(iscsi, "TASK_SET_FULL"); if (pdu->callback) { pdu->callback(iscsi, SCSI_STATUS_TASK_SET_FULL, task, pdu->private_data); } break; case SCSI_STATUS_ACA_ACTIVE: iscsi_set_error(iscsi, "ACA_ACTIVE"); if (pdu->callback) { pdu->callback(iscsi, SCSI_STATUS_ACA_ACTIVE, task, pdu->private_data); } break; case SCSI_STATUS_TASK_ABORTED: iscsi_set_error(iscsi, "TASK_ABORTED"); if (pdu->callback) { pdu->callback(iscsi, SCSI_STATUS_TASK_ABORTED, task, pdu->private_data); } break; case SCSI_STATUS_BUSY: iscsi_set_error(iscsi, "BUSY"); if (pdu->callback) { pdu->callback(iscsi, SCSI_STATUS_BUSY, task, pdu->private_data); } break; default: iscsi_set_error(iscsi, "Unknown SCSI status :%d.", status); if (pdu->callback) { pdu->callback(iscsi, SCSI_STATUS_ERROR, task, pdu->private_data); } return -1; } return 0; } int iscsi_process_scsi_data_in(struct iscsi_context *iscsi, struct iscsi_pdu *pdu, struct iscsi_in_pdu *in, int *is_finished) { uint32_t flags, status; struct iscsi_scsi_cbdata *scsi_cbdata = &pdu->scsi_cbdata; struct scsi_task *task = scsi_cbdata->task; int dsl; flags = in->hdr[1]; if ((flags&ISCSI_PDU_DATA_ACK_REQUESTED) != 0) { iscsi_set_error(iscsi, "scsi response asked for ACK " "0x%02x.", flags); if (pdu->callback) { pdu->callback(iscsi, SCSI_STATUS_ERROR, task, pdu->private_data); } return -1; } dsl = scsi_get_uint32(&in->hdr[4]) & 0x00ffffff; /* Don't add to reassembly buffer if we already have a user buffer */ if (task->iovector_in.iov == NULL) { if (iscsi_add_data(iscsi, &pdu->indata, in->data, dsl, 0) != 0) { iscsi_set_error(iscsi, "Out-of-memory: failed to add data " "to pdu in buffer."); return -1; } } if ((flags&ISCSI_PDU_DATA_FINAL) == 0) { *is_finished = 0; } if ((flags&ISCSI_PDU_DATA_CONTAINS_STATUS) == 0) { *is_finished = 0; } if (*is_finished == 0) { return 0; } task->residual_status = SCSI_RESIDUAL_NO_RESIDUAL; task->residual = 0; /* * These flags should only be set if the S flag is also set */ if (flags & (ISCSI_PDU_DATA_RESIDUAL_OVERFLOW|ISCSI_PDU_DATA_RESIDUAL_UNDERFLOW)) { task->residual = scsi_get_uint32(&in->hdr[44]); if (flags & ISCSI_PDU_DATA_RESIDUAL_UNDERFLOW) { task->residual_status = SCSI_RESIDUAL_UNDERFLOW; } else { task->residual_status = SCSI_RESIDUAL_OVERFLOW; } } /* this was the final data-in packet in the sequence and it has * the s-bit set, so invoke the callback. */ status = in->hdr[3]; task->datain.data = pdu->indata.data; task->datain.size = pdu->indata.size; /* the pdu->indata.data was malloc'ed by iscsi_malloc, as long as we have no struct iscsi_task we cannot track the free'ing of this buffer which is currently done in scsi_free_scsi_task() */ if (pdu->indata.data != NULL) iscsi->frees++; pdu->indata.data = NULL; pdu->indata.size = 0; if (pdu->callback) { pdu->callback(iscsi, status, task, pdu->private_data); } return 0; } int iscsi_process_r2t(struct iscsi_context *iscsi, struct iscsi_pdu *pdu, struct iscsi_in_pdu *in) { uint32_t ttt, offset, len; ttt = scsi_get_uint32(&in->hdr[20]); offset = scsi_get_uint32(&in->hdr[40]); len = scsi_get_uint32(&in->hdr[44]); pdu->datasn = 0; iscsi_send_data_out(iscsi, pdu, ttt, offset, len); return 0; } /* * SCSI commands */ struct scsi_task * iscsi_testunitready_task(struct iscsi_context *iscsi, int lun, iscsi_command_cb cb, void *private_data) { struct scsi_task *task; task = scsi_cdb_testunitready(); if (task == NULL) { iscsi_set_error(iscsi, "Out-of-memory: Failed to create " "testunitready cdb."); return NULL; } if (iscsi_scsi_command_async(iscsi, lun, task, cb, NULL, private_data) != 0) { scsi_free_scsi_task(task); return NULL; } return task; } struct scsi_task * iscsi_reportluns_task(struct iscsi_context *iscsi, int report_type, int alloc_len, iscsi_command_cb cb, void *private_data) { struct scsi_task *task; if (alloc_len < 16) { iscsi_set_error(iscsi, "Minimum allowed alloc len for " "reportluns is 16. You specified %d.", alloc_len); return NULL; } task = scsi_reportluns_cdb(report_type, alloc_len); if (task == NULL) { iscsi_set_error(iscsi, "Out-of-memory: Failed to create " "reportluns cdb."); return NULL; } /* report luns are always sent to lun 0 */ if (iscsi_scsi_command_async(iscsi, 0, task, cb, NULL, private_data) != 0) { scsi_free_scsi_task(task); return NULL; } return task; } struct scsi_task * iscsi_inquiry_task(struct iscsi_context *iscsi, int lun, int evpd, int page_code, int maxsize, iscsi_command_cb cb, void *private_data) { struct scsi_task *task; task = scsi_cdb_inquiry(evpd, page_code, maxsize); if (task == NULL) { iscsi_set_error(iscsi, "Out-of-memory: Failed to create " "inquiry cdb."); return NULL; } if (iscsi_scsi_command_async(iscsi, lun, task, cb, NULL, private_data) != 0) { scsi_free_scsi_task(task); return NULL; } return task; } struct scsi_task * iscsi_readcapacity10_task(struct iscsi_context *iscsi, int lun, int lba, int pmi, iscsi_command_cb cb, void *private_data) { struct scsi_task *task; task = scsi_cdb_readcapacity10(lba, pmi); if (task == NULL) { iscsi_set_error(iscsi, "Out-of-memory: Failed to create " "readcapacity10 cdb."); return NULL; } if (iscsi_scsi_command_async(iscsi, lun, task, cb, NULL, private_data) != 0) { scsi_free_scsi_task(task); return NULL; } return task; } struct scsi_task * iscsi_readcapacity16_task(struct iscsi_context *iscsi, int lun, iscsi_command_cb cb, void *private_data) { struct scsi_task *task; task = scsi_cdb_readcapacity16(); if (task == NULL) { iscsi_set_error(iscsi, "Out-of-memory: Failed to create " "readcapacity16 cdb."); return NULL; } if (iscsi_scsi_command_async(iscsi, lun, task, cb, NULL, private_data) != 0) { scsi_free_scsi_task(task); return NULL; } return task; } struct scsi_task * iscsi_get_lba_status_task(struct iscsi_context *iscsi, int lun, uint64_t starting_lba, uint32_t alloc_len, iscsi_command_cb cb, void *private_data) { struct scsi_task *task; task = scsi_cdb_get_lba_status(starting_lba, alloc_len); if (task == NULL) { iscsi_set_error(iscsi, "Out-of-memory: Failed to create " "get-lba-status cdb."); return NULL; } if (iscsi_scsi_command_async(iscsi, lun, task, cb, NULL, private_data) != 0) { scsi_free_scsi_task(task); return NULL; } return task; } struct scsi_task * iscsi_read6_task(struct iscsi_context *iscsi, int lun, uint32_t lba, uint32_t datalen, int blocksize, iscsi_command_cb cb, void *private_data) { struct scsi_task *task; if (datalen % blocksize != 0) { iscsi_set_error(iscsi, "Datalen:%d is not a multiple of " "the blocksize:%d.", datalen, blocksize); return NULL; } task = scsi_cdb_read6(lba, datalen, blocksize); if (task == NULL) { iscsi_set_error(iscsi, "Out-of-memory: Failed to create " "read6 cdb."); return NULL; } if (iscsi_scsi_command_async(iscsi, lun, task, cb, NULL, private_data) != 0) { scsi_free_scsi_task(task); return NULL; } return task; } struct scsi_task * iscsi_read10_task(struct iscsi_context *iscsi, int lun, uint32_t lba, uint32_t datalen, int blocksize, int rdprotect, int dpo, int fua, int fua_nv, int group_number, iscsi_command_cb cb, void *private_data) { struct scsi_task *task; if (datalen % blocksize != 0) { iscsi_set_error(iscsi, "Datalen:%d is not a multiple of " "the blocksize:%d.", datalen, blocksize); return NULL; } task = scsi_cdb_read10(lba, datalen, blocksize, rdprotect, dpo, fua, fua_nv, group_number); if (task == NULL) { iscsi_set_error(iscsi, "Out-of-memory: Failed to create " "read10 cdb."); return NULL; } if (iscsi_scsi_command_async(iscsi, lun, task, cb, NULL, private_data) != 0) { scsi_free_scsi_task(task); return NULL; } return task; } struct scsi_task * iscsi_read12_task(struct iscsi_context *iscsi, int lun, uint32_t lba, uint32_t datalen, int blocksize, int rdprotect, int dpo, int fua, int fua_nv, int group_number, iscsi_command_cb cb, void *private_data) { struct scsi_task *task; if (datalen % blocksize != 0) { iscsi_set_error(iscsi, "Datalen:%d is not a multiple of " "the blocksize:%d.", datalen, blocksize); return NULL; } task = scsi_cdb_read12(lba, datalen, blocksize, rdprotect, dpo, fua, fua_nv, group_number); if (task == NULL) { iscsi_set_error(iscsi, "Out-of-memory: Failed to create " "read12 cdb."); return NULL; } if (iscsi_scsi_command_async(iscsi, lun, task, cb, NULL, private_data) != 0) { scsi_free_scsi_task(task); return NULL; } return task; } struct scsi_task * iscsi_read16_task(struct iscsi_context *iscsi, int lun, uint64_t lba, uint32_t datalen, int blocksize, int rdprotect, int dpo, int fua, int fua_nv, int group_number, iscsi_command_cb cb, void *private_data) { struct scsi_task *task; if (datalen % blocksize != 0) { iscsi_set_error(iscsi, "Datalen:%d is not a multiple of " "the blocksize:%d.", datalen, blocksize); return NULL; } task = scsi_cdb_read16(lba, datalen, blocksize, rdprotect, dpo, fua, fua_nv, group_number); if (task == NULL) { iscsi_set_error(iscsi, "Out-of-memory: Failed to create " "read16 cdb."); return NULL; } if (iscsi_scsi_command_async(iscsi, lun, task, cb, NULL, private_data) != 0) { scsi_free_scsi_task(task); return NULL; } return task; } struct scsi_task * iscsi_write10_task(struct iscsi_context *iscsi, int lun, uint32_t lba, unsigned char *data, uint32_t datalen, int blocksize, int wrprotect, int dpo, int fua, int fua_nv, int group_number, iscsi_command_cb cb, void *private_data) { struct scsi_task *task; struct iscsi_data d; if (datalen % blocksize != 0) { iscsi_set_error(iscsi, "Datalen:%d is not a multiple of the " "blocksize:%d.", datalen, blocksize); return NULL; } task = scsi_cdb_write10(lba, datalen, blocksize, wrprotect, dpo, fua, fua_nv, group_number); if (task == NULL) { iscsi_set_error(iscsi, "Out-of-memory: Failed to create " "write10 cdb."); return NULL; } d.data = data; d.size = datalen; if (iscsi_scsi_command_async(iscsi, lun, task, cb, &d, private_data) != 0) { scsi_free_scsi_task(task); return NULL; } return task; } struct scsi_task * iscsi_write12_task(struct iscsi_context *iscsi, int lun, uint32_t lba, unsigned char *data, uint32_t datalen, int blocksize, int wrprotect, int dpo, int fua, int fua_nv, int group_number, iscsi_command_cb cb, void *private_data) { struct scsi_task *task; struct iscsi_data d; if (datalen % blocksize != 0) { iscsi_set_error(iscsi, "Datalen:%d is not a multiple of the " "blocksize:%d.", datalen, blocksize); return NULL; } task = scsi_cdb_write12(lba, datalen, blocksize, wrprotect, dpo, fua, fua_nv, group_number); if (task == NULL) { iscsi_set_error(iscsi, "Out-of-memory: Failed to create " "write12 cdb."); return NULL; } d.data = data; d.size = datalen; if (iscsi_scsi_command_async(iscsi, lun, task, cb, &d, private_data) != 0) { scsi_free_scsi_task(task); return NULL; } return task; } struct scsi_task * iscsi_write16_task(struct iscsi_context *iscsi, int lun, uint64_t lba, unsigned char *data, uint32_t datalen, int blocksize, int wrprotect, int dpo, int fua, int fua_nv, int group_number, iscsi_command_cb cb, void *private_data) { struct scsi_task *task; struct iscsi_data d; if (datalen % blocksize != 0) { iscsi_set_error(iscsi, "Datalen:%d is not a multiple of the " "blocksize:%d.", datalen, blocksize); return NULL; } task = scsi_cdb_write16(lba, datalen, blocksize, wrprotect, dpo, fua, fua_nv, group_number); if (task == NULL) { iscsi_set_error(iscsi, "Out-of-memory: Failed to create " "write16 cdb."); return NULL; } d.data = data; d.size = datalen; if (iscsi_scsi_command_async(iscsi, lun, task, cb, &d, private_data) != 0) { scsi_free_scsi_task(task); return NULL; } return task; } struct scsi_task * iscsi_writeatomic16_task(struct iscsi_context *iscsi, int lun, uint64_t lba, unsigned char *data, uint32_t datalen, int blocksize, int wrprotect, int dpo, int fua, int group_number, iscsi_command_cb cb, void *private_data) { struct scsi_task *task; struct iscsi_data d; if (datalen % blocksize != 0) { iscsi_set_error(iscsi, "Datalen:%d is not a multiple of the " "blocksize:%d.", datalen, blocksize); return NULL; } task = scsi_cdb_writeatomic16(lba, datalen, blocksize, wrprotect, dpo, fua, group_number); if (task == NULL) { iscsi_set_error(iscsi, "Out-of-memory: Failed to create " "writeAtomic16 cdb."); return NULL; } d.data = data; d.size = datalen; if (iscsi_scsi_command_async(iscsi, lun, task, cb, &d, private_data) != 0) { scsi_free_scsi_task(task); return NULL; } return task; } struct scsi_task * iscsi_orwrite_task(struct iscsi_context *iscsi, int lun, uint64_t lba, unsigned char *data, uint32_t datalen, int blocksize, int wrprotect, int dpo, int fua, int fua_nv, int group_number, iscsi_command_cb cb, void *private_data) { struct scsi_task *task; struct iscsi_data d; if (datalen % blocksize != 0) { iscsi_set_error(iscsi, "Datalen:%d is not a multiple of the " "blocksize:%d.", datalen, blocksize); return NULL; } task = scsi_cdb_orwrite(lba, datalen, blocksize, wrprotect, dpo, fua, fua_nv, group_number); if (task == NULL) { iscsi_set_error(iscsi, "Out-of-memory: Failed to create " "orwrite cdb."); return NULL; } d.data = data; d.size = datalen; if (iscsi_scsi_command_async(iscsi, lun, task, cb, &d, private_data) != 0) { scsi_free_scsi_task(task); return NULL; } return task; } struct scsi_task * iscsi_compareandwrite_task(struct iscsi_context *iscsi, int lun, uint64_t lba, unsigned char *data, uint32_t datalen, int blocksize, int wrprotect, int dpo, int fua, int fua_nv, int group_number, iscsi_command_cb cb, void *private_data) { struct scsi_task *task; struct iscsi_data d; if (datalen % (blocksize * 2) != 0) { iscsi_set_error(iscsi, "Datalen:%d is not a multiple of the " "blocksize * 2:%d.", datalen, blocksize * 2); return NULL; } task = scsi_cdb_compareandwrite(lba, datalen, blocksize, wrprotect, dpo, fua, fua_nv, group_number); if (task == NULL) { iscsi_set_error(iscsi, "Out-of-memory: Failed to create " "compareandwrite cdb."); return NULL; } d.data = data; d.size = datalen; if (iscsi_scsi_command_async(iscsi, lun, task, cb, &d, private_data) != 0) { scsi_free_scsi_task(task); return NULL; } return task; } struct scsi_task * iscsi_writeverify10_task(struct iscsi_context *iscsi, int lun, uint32_t lba, unsigned char *data, uint32_t datalen, int blocksize, int wrprotect, int dpo, int bytchk, int group_number, iscsi_command_cb cb, void *private_data) { struct scsi_task *task; struct iscsi_data d; if (datalen % blocksize != 0) { iscsi_set_error(iscsi, "Datalen:%d is not a multiple of the " "blocksize:%d.", datalen, blocksize); return NULL; } task = scsi_cdb_writeverify10(lba, datalen, blocksize, wrprotect, dpo, bytchk, group_number); if (task == NULL) { iscsi_set_error(iscsi, "Out-of-memory: Failed to create " "writeverify10 cdb."); return NULL; } d.data = data; d.size = datalen; if (iscsi_scsi_command_async(iscsi, lun, task, cb, &d, private_data) != 0) { scsi_free_scsi_task(task); return NULL; } return task; } struct scsi_task * iscsi_writeverify12_task(struct iscsi_context *iscsi, int lun, uint32_t lba, unsigned char *data, uint32_t datalen, int blocksize, int wrprotect, int dpo, int bytchk, int group_number, iscsi_command_cb cb, void *private_data) { struct scsi_task *task; struct iscsi_data d; if (datalen % blocksize != 0) { iscsi_set_error(iscsi, "Datalen:%d is not a multiple of the " "blocksize:%d.", datalen, blocksize); return NULL; } task = scsi_cdb_writeverify12(lba, datalen, blocksize, wrprotect, dpo, bytchk, group_number); if (task == NULL) { iscsi_set_error(iscsi, "Out-of-memory: Failed to create " "writeverify12 cdb."); return NULL; } d.data = data; d.size = datalen; if (iscsi_scsi_command_async(iscsi, lun, task, cb, &d, private_data) != 0) { scsi_free_scsi_task(task); return NULL; } return task; } struct scsi_task * iscsi_writeverify16_task(struct iscsi_context *iscsi, int lun, uint64_t lba, unsigned char *data, uint32_t datalen, int blocksize, int wrprotect, int dpo, int bytchk, int group_number, iscsi_command_cb cb, void *private_data) { struct scsi_task *task; struct iscsi_data d; if (datalen % blocksize != 0) { iscsi_set_error(iscsi, "Datalen:%d is not a multiple of the " "blocksize:%d.", datalen, blocksize); return NULL; } task = scsi_cdb_writeverify16(lba, datalen, blocksize, wrprotect, dpo, bytchk, group_number); if (task == NULL) { iscsi_set_error(iscsi, "Out-of-memory: Failed to create " "writeverify16 cdb."); return NULL; } d.data = data; d.size = datalen; if (iscsi_scsi_command_async(iscsi, lun, task, cb, &d, private_data) != 0) { scsi_free_scsi_task(task); return NULL; } return task; } struct scsi_task * iscsi_verify10_task(struct iscsi_context *iscsi, int lun, unsigned char *data, uint32_t datalen, uint32_t lba, int vprotect, int dpo, int bytchk, int blocksize, iscsi_command_cb cb, void *private_data) { struct scsi_task *task; struct iscsi_data d; if (datalen % blocksize != 0) { iscsi_set_error(iscsi, "Datalen:%d is not a multiple of the " "blocksize:%d.", datalen, blocksize); return NULL; } task = scsi_cdb_verify10(lba, datalen, vprotect, dpo, bytchk, blocksize); if (task == NULL) { iscsi_set_error(iscsi, "Out-of-memory: Failed to create " "verify10 cdb."); return NULL; } d.data = data; d.size = datalen; if (iscsi_scsi_command_async(iscsi, lun, task, cb, &d, private_data) != 0) { scsi_free_scsi_task(task); return NULL; } return task; } struct scsi_task * iscsi_verify12_task(struct iscsi_context *iscsi, int lun, unsigned char *data, uint32_t datalen, uint32_t lba, int vprotect, int dpo, int bytchk, int blocksize, iscsi_command_cb cb, void *private_data) { struct scsi_task *task; struct iscsi_data d; if (datalen % blocksize != 0) { iscsi_set_error(iscsi, "Datalen:%d is not a multiple of the " "blocksize:%d.", datalen, blocksize); return NULL; } task = scsi_cdb_verify12(lba, datalen, vprotect, dpo, bytchk, blocksize); if (task == NULL) { iscsi_set_error(iscsi, "Out-of-memory: Failed to create " "verify12 cdb."); return NULL; } d.data = data; d.size = datalen; if (iscsi_scsi_command_async(iscsi, lun, task, cb, &d, private_data) != 0) { scsi_free_scsi_task(task); return NULL; } return task; } struct scsi_task * iscsi_verify16_task(struct iscsi_context *iscsi, int lun, unsigned char *data, uint32_t datalen, uint64_t lba, int vprotect, int dpo, int bytchk, int blocksize, iscsi_command_cb cb, void *private_data) { struct scsi_task *task; struct iscsi_data d; if (datalen % blocksize != 0) { iscsi_set_error(iscsi, "Datalen:%d is not a multiple of the " "blocksize:%d.", datalen, blocksize); return NULL; } task = scsi_cdb_verify16(lba, datalen, vprotect, dpo, bytchk, blocksize); if (task == NULL) { iscsi_set_error(iscsi, "Out-of-memory: Failed to create " "verify16 cdb."); return NULL; } d.data = data; d.size = datalen; if (iscsi_scsi_command_async(iscsi, lun, task, cb, &d, private_data) != 0) { scsi_free_scsi_task(task); return NULL; } return task; } struct scsi_task * iscsi_modeselect6_task(struct iscsi_context *iscsi, int lun, int pf, int sp, struct scsi_mode_page *mp, iscsi_command_cb cb, void *private_data) { struct scsi_task *task; struct scsi_data *data; struct iscsi_data d; task = scsi_cdb_modeselect6(pf, sp, 255); if (task == NULL) { iscsi_set_error(iscsi, "Out-of-memory: Failed to create " "modeselect6 cdb."); return NULL; } data = scsi_modesense_dataout_marshall(task, mp, 1); if (data == NULL) { iscsi_set_error(iscsi, "Error: Failed to marshall " "modesense dataout buffer."); scsi_free_scsi_task(task); return NULL; } d.data = data->data; d.size = data->size; task->cdb[4] = data->size; task->expxferlen = data->size; if (iscsi_scsi_command_async(iscsi, lun, task, cb, &d, private_data) != 0) { scsi_free_scsi_task(task); return NULL; } return task; } struct scsi_task * iscsi_modeselect10_task(struct iscsi_context *iscsi, int lun, int pf, int sp, struct scsi_mode_page *mp, iscsi_command_cb cb, void *private_data) { struct scsi_task *task; struct scsi_data *data; struct iscsi_data d; task = scsi_cdb_modeselect10(pf, sp, 255); if (task == NULL) { iscsi_set_error(iscsi, "Out-of-memory: Failed to create " "modeselect10 cdb."); return NULL; } data = scsi_modesense_dataout_marshall(task, mp, 0); if (data == NULL) { iscsi_set_error(iscsi, "Error: Failed to marshall " "modesense dataout buffer."); scsi_free_scsi_task(task); return NULL; } d.data = data->data; d.size = data->size; task->cdb[7] = data->size >> 8; task->cdb[8] = data->size & 0xff; task->expxferlen = data->size; if (iscsi_scsi_command_async(iscsi, lun, task, cb, &d, private_data) != 0) { scsi_free_scsi_task(task); return NULL; } return task; } struct scsi_task * iscsi_modesense6_task(struct iscsi_context *iscsi, int lun, int dbd, int pc, int page_code, int sub_page_code, unsigned char alloc_len, iscsi_command_cb cb, void *private_data) { struct scsi_task *task; task = scsi_cdb_modesense6(dbd, pc, page_code, sub_page_code, alloc_len); if (task == NULL) { iscsi_set_error(iscsi, "Out-of-memory: Failed to create " "modesense6 cdb."); return NULL; } if (iscsi_scsi_command_async(iscsi, lun, task, cb, NULL, private_data) != 0) { scsi_free_scsi_task(task); return NULL; } return task; } struct scsi_task * iscsi_modesense10_task(struct iscsi_context *iscsi, int lun, int llbaa, int dbd, int pc, int page_code, int sub_page_code, unsigned char alloc_len, iscsi_command_cb cb, void *private_data) { struct scsi_task *task; task = scsi_cdb_modesense10(llbaa, dbd, pc, page_code, sub_page_code, alloc_len); if (task == NULL) { iscsi_set_error(iscsi, "Out-of-memory: Failed to create " "modesense10 cdb."); return NULL; } if (iscsi_scsi_command_async(iscsi, lun, task, cb, NULL, private_data) != 0) { scsi_free_scsi_task(task); return NULL; } return task; } struct scsi_task * iscsi_startstopunit_task(struct iscsi_context *iscsi, int lun, int immed, int pcm, int pc, int no_flush, int loej, int start, iscsi_command_cb cb, void *private_data) { struct scsi_task *task; task = scsi_cdb_startstopunit(immed, pcm, pc, no_flush, loej, start); if (task == NULL) { iscsi_set_error(iscsi, "Out-of-memory: Failed to create " "startstopunit cdb."); return NULL; } if (iscsi_scsi_command_async(iscsi, lun, task, cb, NULL, private_data) != 0) { scsi_free_scsi_task(task); return NULL; } return task; } struct scsi_task * iscsi_preventallow_task(struct iscsi_context *iscsi, int lun, int prevent, iscsi_command_cb cb, void *private_data) { struct scsi_task *task; task = scsi_cdb_preventallow(prevent); if (task == NULL) { iscsi_set_error(iscsi, "Out-of-memory: Failed to create " "PreventAllowMediumRemoval cdb."); return NULL; } if (iscsi_scsi_command_async(iscsi, lun, task, cb, NULL, private_data) != 0) { scsi_free_scsi_task(task); return NULL; } return task; } struct scsi_task * iscsi_synchronizecache10_task(struct iscsi_context *iscsi, int lun, int lba, int num_blocks, int syncnv, int immed, iscsi_command_cb cb, void *private_data) { struct scsi_task *task; task = scsi_cdb_synchronizecache10(lba, num_blocks, syncnv, immed); if (task == NULL) { iscsi_set_error(iscsi, "Out-of-memory: Failed to create " "synchronizecache10 cdb."); return NULL; } if (iscsi_scsi_command_async(iscsi, lun, task, cb, NULL, private_data) != 0) { scsi_free_scsi_task(task); return NULL; } return task; } struct scsi_task * iscsi_synchronizecache16_task(struct iscsi_context *iscsi, int lun, uint64_t lba, uint32_t num_blocks, int syncnv, int immed, iscsi_command_cb cb, void *private_data) { struct scsi_task *task; task = scsi_cdb_synchronizecache16(lba, num_blocks, syncnv, immed); if (task == NULL) { iscsi_set_error(iscsi, "Out-of-memory: Failed to create " "synchronizecache16 cdb."); return NULL; } if (iscsi_scsi_command_async(iscsi, lun, task, cb, NULL, private_data) != 0) { scsi_free_scsi_task(task); return NULL; } return task; } struct scsi_task * iscsi_persistent_reserve_in_task(struct iscsi_context *iscsi, int lun, int sa, uint16_t xferlen, iscsi_command_cb cb, void *private_data) { struct scsi_task *task; task = scsi_cdb_persistent_reserve_in(sa, xferlen); if (task == NULL) { iscsi_set_error(iscsi, "Out-of-memory: Failed to create " "persistent-reserver-in cdb."); return NULL; } if (iscsi_scsi_command_async(iscsi, lun, task, cb, NULL, private_data) != 0) { scsi_free_scsi_task(task); return NULL; } return task; } struct scsi_task * iscsi_persistent_reserve_out_task(struct iscsi_context *iscsi, int lun, int sa, int scope, int type, void *param, iscsi_command_cb cb, void *private_data) { struct scsi_task *task; task = scsi_cdb_persistent_reserve_out(sa, scope, type, param); if (task == NULL) { iscsi_set_error(iscsi, "Out-of-memory: Failed to create " "persistent-reserver-out cdb."); return NULL; } if (iscsi_scsi_command_async(iscsi, lun, task, cb, NULL, private_data) != 0) { scsi_free_scsi_task(task); return NULL; } return task; } struct scsi_task * iscsi_prefetch10_task(struct iscsi_context *iscsi, int lun, uint32_t lba, int num_blocks, int immed, int group, iscsi_command_cb cb, void *private_data) { struct scsi_task *task; task = scsi_cdb_prefetch10(lba, num_blocks, immed, group); if (task == NULL) { iscsi_set_error(iscsi, "Out-of-memory: Failed to create " "prefetch10 cdb."); return NULL; } if (iscsi_scsi_command_async(iscsi, lun, task, cb, NULL, private_data) != 0) { scsi_free_scsi_task(task); return NULL; } return task; } struct scsi_task * iscsi_prefetch16_task(struct iscsi_context *iscsi, int lun, uint64_t lba, int num_blocks, int immed, int group, iscsi_command_cb cb, void *private_data) { struct scsi_task *task; task = scsi_cdb_prefetch16(lba, num_blocks, immed, group); if (task == NULL) { iscsi_set_error(iscsi, "Out-of-memory: Failed to create " "prefetch16 cdb."); return NULL; } if (iscsi_scsi_command_async(iscsi, lun, task, cb, NULL, private_data) != 0) { scsi_free_scsi_task(task); return NULL; } return task; } struct scsi_task * iscsi_writesame10_task(struct iscsi_context *iscsi, int lun, uint32_t lba, unsigned char *data, uint32_t datalen, uint16_t num_blocks, int anchor, int unmap, int wrprotect, int group, iscsi_command_cb cb, void *private_data) { struct scsi_task *task; struct iscsi_data d; task = scsi_cdb_writesame10(wrprotect, anchor, unmap, lba, group, num_blocks, datalen); if (task == NULL) { iscsi_set_error(iscsi, "Out-of-memory: Failed to create " "writesame10 cdb."); return NULL; } d.data = data; d.size = datalen; if (data != NULL) { task->expxferlen = datalen; } else { task->expxferlen = 0; task->xfer_dir = SCSI_XFER_NONE; } if (iscsi_scsi_command_async(iscsi, lun, task, cb, &d, private_data) != 0) { scsi_free_scsi_task(task); return NULL; } return task; } struct scsi_task * iscsi_writesame16_task(struct iscsi_context *iscsi, int lun, uint64_t lba, unsigned char *data, uint32_t datalen, uint32_t num_blocks, int anchor, int unmap, int wrprotect, int group, iscsi_command_cb cb, void *private_data) { struct scsi_task *task; struct iscsi_data d; task = scsi_cdb_writesame16(wrprotect, anchor, unmap, lba, group, num_blocks, datalen); if (task == NULL) { iscsi_set_error(iscsi, "Out-of-memory: Failed to create " "writesame16 cdb."); return NULL; } d.data = data; d.size = datalen; if (data != NULL) { task->expxferlen = datalen; } else { task->expxferlen = 0; task->xfer_dir = SCSI_XFER_NONE; } if (iscsi_scsi_command_async(iscsi, lun, task, cb, &d, private_data) != 0) { scsi_free_scsi_task(task); return NULL; } return task; } struct scsi_task * iscsi_unmap_task(struct iscsi_context *iscsi, int lun, int anchor, int group, struct unmap_list *list, int list_len, iscsi_command_cb cb, void *private_data) { struct scsi_task *task; struct scsi_iovec *iov; unsigned char *data; int xferlen; int i; xferlen = 8 + list_len * 16; task = scsi_cdb_unmap(anchor, group, xferlen); if (task == NULL) { iscsi_set_error(iscsi, "Out-of-memory: Failed to create " "unmap cdb."); return NULL; } data = scsi_malloc(task, xferlen); if (data == NULL) { iscsi_set_error(iscsi, "Out-of-memory: Failed to create " "unmap parameters."); scsi_free_scsi_task(task); return NULL; } scsi_set_uint16(&data[0], xferlen - 2); scsi_set_uint16(&data[2], xferlen - 8); for (i = 0; i < list_len; i++) { scsi_set_uint32(&data[8 + 16 * i], list[i].lba >> 32); scsi_set_uint32(&data[8 + 16 * i + 4], list[i].lba & 0xffffffff); scsi_set_uint32(&data[8 + 16 * i + 8], list[i].num); } iov = scsi_malloc(task, sizeof(struct scsi_iovec)); if (iov == NULL) { scsi_free_scsi_task(task); return NULL; } iov->iov_base = data; iov->iov_len = xferlen; scsi_task_set_iov_out(task, iov, 1); if (iscsi_scsi_command_async(iscsi, lun, task, cb, NULL, private_data) != 0) { scsi_free_scsi_task(task); return NULL; } return task; } struct scsi_iovector * iscsi_get_scsi_task_iovector_in(struct iscsi_context *iscsi, struct iscsi_in_pdu *in) { struct iscsi_pdu *pdu; uint32_t itt; if ((in->hdr[0] & 0x3f) != ISCSI_PDU_DATA_IN) { return NULL; } itt = scsi_get_uint32(&in->hdr[16]); for (pdu = iscsi->waitpdu; pdu; pdu = pdu->next) { if (pdu->itt == itt) { break; } } if (pdu == NULL) { return NULL; } if (pdu->scsi_cbdata.task->iovector_in.iov == NULL) { return NULL; } return &pdu->scsi_cbdata.task->iovector_in; } struct scsi_iovector * iscsi_get_scsi_task_iovector_out(struct iscsi_context *iscsi _U_, struct iscsi_pdu *pdu) { if (pdu->scsi_cbdata.task->iovector_out.iov == NULL) { return NULL; } return &pdu->scsi_cbdata.task->iovector_out; } struct scsi_task * iscsi_readtoc_task(struct iscsi_context *iscsi, int lun, int msf, int format, int track_session, int maxsize, iscsi_command_cb cb, void *private_data) { struct scsi_task *task; task = scsi_cdb_readtoc(msf, format, track_session, maxsize); if (task == NULL) { iscsi_set_error(iscsi, "Out-of-memory: Failed to create " "read TOC cdb."); return NULL; } if (iscsi_scsi_command_async(iscsi, lun, task, cb, NULL, private_data) != 0) { scsi_free_scsi_task(task); return NULL; } return task; } struct scsi_task * iscsi_reserve6_task(struct iscsi_context *iscsi, int lun, iscsi_command_cb cb, void *private_data) { struct scsi_task *task; task = scsi_cdb_reserve6(); if (task == NULL) { iscsi_set_error(iscsi, "Out-of-memory: Failed to create " "reserve6 cdb."); return NULL; } if (iscsi_scsi_command_async(iscsi, lun, task, cb, NULL, private_data) != 0) { scsi_free_scsi_task(task); return NULL; } return task; } struct scsi_task * iscsi_release6_task(struct iscsi_context *iscsi, int lun, iscsi_command_cb cb, void *private_data) { struct scsi_task *task; task = scsi_cdb_release6(); if (task == NULL) { iscsi_set_error(iscsi, "Out-of-memory: Failed to create " "release6 cdb."); return NULL; } if (iscsi_scsi_command_async(iscsi, lun, task, cb, NULL, private_data) != 0) { scsi_free_scsi_task(task); return NULL; } return task; } struct scsi_task * iscsi_sanitize_task(struct iscsi_context *iscsi, int lun, int immed, int ause, int sa, int param_len, struct iscsi_data *data, iscsi_command_cb cb, void *private_data) { struct scsi_task *task; task = scsi_cdb_sanitize(immed, ause, sa, param_len); if (task == NULL) { iscsi_set_error(iscsi, "Out-of-memory: Failed to create " "sanitize cdb."); return NULL; } if (iscsi_scsi_command_async(iscsi, lun, task, cb, data, private_data) != 0) { scsi_free_scsi_task(task); return NULL; } return task; } struct scsi_task * iscsi_sanitize_block_erase_task(struct iscsi_context *iscsi, int lun, int immed, int ause, iscsi_command_cb cb, void *private_data) { struct scsi_task *task; task = scsi_cdb_sanitize(immed, ause, SCSI_SANITIZE_BLOCK_ERASE, 0); if (task == NULL) { iscsi_set_error(iscsi, "Out-of-memory: Failed to create " "sanitize cdb."); return NULL; } if (iscsi_scsi_command_async(iscsi, lun, task, cb, NULL, private_data) != 0) { scsi_free_scsi_task(task); return NULL; } return task; } struct scsi_task * iscsi_sanitize_crypto_erase_task(struct iscsi_context *iscsi, int lun, int immed, int ause, iscsi_command_cb cb, void *private_data) { struct scsi_task *task; task = scsi_cdb_sanitize(immed, ause, SCSI_SANITIZE_CRYPTO_ERASE, 0); if (task == NULL) { iscsi_set_error(iscsi, "Out-of-memory: Failed to create " "sanitize cdb."); return NULL; } if (iscsi_scsi_command_async(iscsi, lun, task, cb, NULL, private_data) != 0) { scsi_free_scsi_task(task); return NULL; } return task; } struct scsi_task * iscsi_sanitize_exit_failure_mode_task(struct iscsi_context *iscsi, int lun, int immed, int ause, iscsi_command_cb cb, void *private_data) { struct scsi_task *task; task = scsi_cdb_sanitize(immed, ause, SCSI_SANITIZE_EXIT_FAILURE_MODE, 0); if (task == NULL) { iscsi_set_error(iscsi, "Out-of-memory: Failed to create " "sanitize cdb."); return NULL; } if (iscsi_scsi_command_async(iscsi, lun, task, cb, NULL, private_data) != 0) { scsi_free_scsi_task(task); return NULL; } return task; } struct scsi_task * iscsi_report_supported_opcodes_task(struct iscsi_context *iscsi, int lun, int rctd, int options, int opcode, int sa, uint32_t alloc_len, iscsi_command_cb cb, void *private_data) { struct scsi_task *task; task = scsi_cdb_report_supported_opcodes(rctd, options, opcode, sa, alloc_len); if (task == NULL) { iscsi_set_error(iscsi, "Out-of-memory: Failed to create " "Maintenance In/Read Supported Op Codes cdb."); return NULL; } if (iscsi_scsi_command_async(iscsi, lun, task, cb, NULL, private_data) != 0) { scsi_free_scsi_task(task); return NULL; } return task; } struct scsi_task * iscsi_scsi_get_task_from_pdu(struct iscsi_pdu *pdu) { return pdu->scsi_cbdata.task; } int iscsi_scsi_cancel_task(struct iscsi_context *iscsi, struct scsi_task *task) { struct iscsi_pdu *pdu; for (pdu = iscsi->waitpdu; pdu; pdu = pdu->next) { if (pdu->itt == task->itt) { ISCSI_LIST_REMOVE(&iscsi->waitpdu, pdu); if (pdu->callback) { pdu->callback(iscsi, SCSI_STATUS_CANCELLED, NULL, pdu->private_data); } iscsi_free_pdu(iscsi, pdu); return 0; } } for (pdu = iscsi->outqueue; pdu; pdu = pdu->next) { if (pdu->itt == task->itt) { ISCSI_LIST_REMOVE(&iscsi->outqueue, pdu); if (pdu->callback) { pdu->callback(iscsi, SCSI_STATUS_CANCELLED, NULL, pdu->private_data); } iscsi_free_pdu(iscsi, pdu); return 0; } } return -1; } void iscsi_scsi_cancel_all_tasks(struct iscsi_context *iscsi) { struct iscsi_pdu *pdu; while ((pdu = iscsi->waitpdu)) { ISCSI_LIST_REMOVE(&iscsi->waitpdu, pdu); if (pdu->callback) { pdu->callback(iscsi, SCSI_STATUS_CANCELLED, NULL, pdu->private_data); } iscsi_free_pdu(iscsi, pdu); } while ((pdu = iscsi->outqueue)) { ISCSI_LIST_REMOVE(&iscsi->outqueue, pdu); if (pdu->callback) { pdu->callback(iscsi, SCSI_STATUS_CANCELLED, NULL, pdu->private_data); } iscsi_free_pdu(iscsi, pdu); } } libiscsi-1.17.0/lib/libiscsi.def000066400000000000000000000126561271371262700164520ustar00rootroot00000000000000LIBRARY libiscsi EXPORTS iscsi_connect_async iscsi_connect_sync iscsi_reconnect_sync iscsi_create_context iscsi_destroy_context iscsi_destroy_url iscsi_disconnect iscsi_discovery_async iscsi_full_connect_async iscsi_full_connect_sync iscsi_get_error iscsi_get_fd iscsi_get_lba_status_sync iscsi_get_lba_status_task iscsi_get_target_address iscsi_get_nops_in_flight iscsi_inquiry_sync iscsi_inquiry_task iscsi_is_logged_in iscsi_log_to_stderr iscsi_login_async iscsi_login_sync iscsi_logout_async iscsi_logout_sync iscsi_modeselect6_sync iscsi_modeselect6_task iscsi_modeselect10_sync iscsi_modeselect10_task iscsi_modesense6_sync iscsi_modesense6_task iscsi_modesense10_sync iscsi_modesense10_task iscsi_nop_out_async iscsi_parse_full_url iscsi_parse_portal_url iscsi_persistent_reserve_in_task iscsi_persistent_reserve_in_sync iscsi_persistent_reserve_out_task iscsi_persistent_reserve_out_sync iscsi_prefetch10_sync iscsi_prefetch10_task iscsi_prefetch16_sync iscsi_prefetch16_task iscsi_preventallow_sync iscsi_preventallow_task iscsi_queue_length iscsi_out_queue_length iscsi_queue_pdu iscsi_read10_sync iscsi_read10_task iscsi_read12_sync iscsi_read12_task iscsi_read16_sync iscsi_read16_task iscsi_read6_sync iscsi_read6_task iscsi_readcapacity10_sync iscsi_readcapacity10_task iscsi_readcapacity16_sync iscsi_readcapacity16_task iscsi_readtoc_sync iscsi_readtoc_task iscsi_reserve6_sync iscsi_reserve6_task iscsi_release6_sync iscsi_release6_task iscsi_report_supported_opcodes_sync iscsi_report_supported_opcodes_task iscsi_reconnect iscsi_sanitize_sync iscsi_sanitize_task iscsi_sanitize_block_erase_sync iscsi_sanitize_block_erase_task iscsi_sanitize_crypto_erase_sync iscsi_sanitize_crypto_erase_task iscsi_sanitize_exit_failure_mode_sync iscsi_sanitize_exit_failure_mode_task iscsi_set_cache_allocations iscsi_set_noautoreconnect iscsi_set_reconnect_max_retries iscsi_set_timeout iscsi_reportluns_sync iscsi_reportluns_task iscsi_scsi_cancel_all_tasks iscsi_scsi_command_async iscsi_scsi_command_sync iscsi_scsi_cancel_task iscsi_service iscsi_set_alias iscsi_set_immediate_data iscsi_set_initial_r2t iscsi_set_log_level iscsi_set_log_fn iscsi_set_header_digest iscsi_set_initiator_username_pwd iscsi_set_isid_en iscsi_set_isid_oui iscsi_set_isid_random iscsi_set_isid_reserved iscsi_set_no_ua_on_reconnect iscsi_set_session_type iscsi_set_target_username_pwd iscsi_set_targetname iscsi_set_tcp_keepalive iscsi_set_tcp_user_timeout iscsi_set_tcp_keepidle iscsi_set_tcp_keepcnt iscsi_set_tcp_keepintvl iscsi_set_tcp_syncnt iscsi_set_bind_interfaces iscsi_startstopunit_sync iscsi_startstopunit_task iscsi_synchronizecache10_sync iscsi_synchronizecache10_task iscsi_synchronizecache16_sync iscsi_synchronizecache16_task iscsi_task_mgmt_abort_task_async iscsi_task_mgmt_abort_task_sync iscsi_task_mgmt_abort_task_set_async iscsi_task_mgmt_abort_task_set_sync iscsi_task_mgmt_async iscsi_task_mgmt_sync iscsi_task_mgmt_lun_reset_async iscsi_task_mgmt_lun_reset_sync iscsi_task_mgmt_target_cold_reset_async iscsi_task_mgmt_target_cold_reset_sync iscsi_task_mgmt_target_warm_reset_async iscsi_task_mgmt_target_warm_reset_sync iscsi_testunitready_sync iscsi_testunitready_task iscsi_unmap_sync iscsi_unmap_task iscsi_verify10_sync iscsi_verify10_task iscsi_verify12_sync iscsi_verify12_task iscsi_verify16_sync iscsi_verify16_task iscsi_which_events iscsi_write10_sync iscsi_write10_task iscsi_write12_sync iscsi_write12_task iscsi_write16_sync iscsi_write16_task iscsi_writeatomic16_sync iscsi_writeatomic16_task iscsi_orwrite_sync iscsi_orwrite_task iscsi_compareandwrite_sync iscsi_compareandwrite_task iscsi_writeverify10_sync iscsi_writeverify10_task iscsi_writeverify12_sync iscsi_writeverify12_task iscsi_writeverify16_sync iscsi_writeverify16_task iscsi_writesame10_sync iscsi_writesame10_task iscsi_writesame16_sync iscsi_writesame16_task scsi_association_to_str scsi_cdb_compareandwrite scsi_cdb_extended_copy scsi_cdb_inquiry scsi_cdb_get_lba_status scsi_cdb_modeselect6 scsi_cdb_modeselect10 scsi_cdb_modesense6 scsi_cdb_modesense10 scsi_cdb_persistent_reserve_in scsi_cdb_persistent_reserve_out scsi_cdb_prefetch10 scsi_cdb_prefetch16 scsi_cdb_preventallow scsi_cdb_read10 scsi_cdb_read12 scsi_cdb_read16 scsi_cdb_read6 scsi_cdb_readcapacity10 scsi_cdb_readcapacity16 scsi_cdb_readtoc scsi_cdb_receive_copy_results scsi_cdb_reserve6 scsi_cdb_release6 scsi_cdb_report_supported_opcodes scsi_cdb_sanitize scsi_cdb_serviceactionin16 scsi_cdb_startstopunit scsi_cdb_synchronizecache10 scsi_cdb_synchronizecache16 scsi_cdb_testunitready scsi_cdb_unmap scsi_cdb_verify10 scsi_cdb_verify12 scsi_cdb_verify16 scsi_cdb_write10 scsi_cdb_write12 scsi_cdb_write16 scsi_cdb_writeatomic16 scsi_cdb_orwrite scsi_cdb_writeverify10 scsi_cdb_writeverify12 scsi_cdb_writeverify16 scsi_cdb_writesame10 scsi_cdb_writesame16 scsi_codeset_to_str scsi_create_task scsi_datain_getfullsize scsi_datain_unmarshall scsi_cdb_unmarshall scsi_designator_type_to_str scsi_devqualifier_to_str scsi_devtype_to_str scsi_free_scsi_task scsi_get_task_private_ptr scsi_get_uint16 scsi_get_uint32 scsi_get_uint64 scsi_inquiry_pagecode_to_str scsi_malloc scsi_modesense_dataout_marshall scsi_modesense_get_page scsi_parse_sense_data scsi_protocol_identifier_to_str scsi_reportluns_cdb scsi_sense_ascq_str scsi_sense_key_str scsi_set_task_private_ptr scsi_set_uint16 scsi_set_uint32 scsi_set_uint64 scsi_task_add_data_in_buffer scsi_task_add_data_out_buffer scsi_task_get_status scsi_task_set_iov_in scsi_task_set_iov_out scsi_version_to_str scsi_version_descriptor_to_str libiscsi-1.17.0/lib/libiscsi.syms000066400000000000000000000126461271371262700167060ustar00rootroot00000000000000iscsi_connect_async iscsi_connect_sync iscsi_reconnect_sync iscsi_create_context iscsi_destroy_context iscsi_destroy_url iscsi_disconnect iscsi_discovery_async iscsi_full_connect_async iscsi_full_connect_sync iscsi_get_error iscsi_get_fd iscsi_get_lba_status_sync iscsi_get_lba_status_task iscsi_get_target_address iscsi_get_nops_in_flight iscsi_inquiry_sync iscsi_inquiry_task iscsi_is_logged_in iscsi_log_to_stderr iscsi_login_async iscsi_login_sync iscsi_logout_async iscsi_logout_sync iscsi_modeselect6_sync iscsi_modeselect6_task iscsi_modeselect10_sync iscsi_modeselect10_task iscsi_modesense6_sync iscsi_modesense6_task iscsi_modesense10_sync iscsi_modesense10_task iscsi_nop_out_async iscsi_parse_full_url iscsi_parse_portal_url iscsi_persistent_reserve_in_task iscsi_persistent_reserve_in_sync iscsi_persistent_reserve_out_task iscsi_persistent_reserve_out_sync iscsi_prefetch10_sync iscsi_prefetch10_task iscsi_prefetch16_sync iscsi_prefetch16_task iscsi_preventallow_sync iscsi_preventallow_task iscsi_queue_length iscsi_out_queue_length iscsi_queue_pdu iscsi_read10_sync iscsi_read10_task iscsi_read12_sync iscsi_read12_task iscsi_read16_sync iscsi_read16_task iscsi_read6_sync iscsi_read6_task iscsi_readcapacity10_sync iscsi_readcapacity10_task iscsi_readcapacity16_sync iscsi_readcapacity16_task iscsi_readtoc_sync iscsi_readtoc_task iscsi_reserve6_sync iscsi_reserve6_task iscsi_release6_sync iscsi_release6_task iscsi_report_supported_opcodes_sync iscsi_report_supported_opcodes_task iscsi_reconnect iscsi_sanitize_sync iscsi_sanitize_task iscsi_sanitize_block_erase_sync iscsi_sanitize_block_erase_task iscsi_sanitize_crypto_erase_sync iscsi_sanitize_crypto_erase_task iscsi_sanitize_exit_failure_mode_sync iscsi_sanitize_exit_failure_mode_task iscsi_set_cache_allocations iscsi_set_noautoreconnect iscsi_set_reconnect_max_retries iscsi_set_timeout iscsi_reportluns_sync iscsi_reportluns_task iscsi_scsi_cancel_all_tasks iscsi_scsi_command_async iscsi_scsi_command_sync iscsi_scsi_cancel_task iscsi_service iscsi_set_alias iscsi_set_immediate_data iscsi_set_initial_r2t iscsi_set_log_level iscsi_set_log_fn iscsi_set_header_digest iscsi_set_initiator_username_pwd iscsi_set_isid_en iscsi_set_isid_oui iscsi_set_isid_random iscsi_set_isid_reserved iscsi_set_no_ua_on_reconnect iscsi_set_session_type iscsi_set_target_username_pwd iscsi_set_targetname iscsi_set_tcp_keepalive iscsi_set_tcp_user_timeout iscsi_set_tcp_keepidle iscsi_set_tcp_keepcnt iscsi_set_tcp_keepintvl iscsi_set_tcp_syncnt iscsi_set_bind_interfaces iscsi_startstopunit_sync iscsi_startstopunit_task iscsi_synchronizecache10_sync iscsi_synchronizecache10_task iscsi_synchronizecache16_sync iscsi_synchronizecache16_task iscsi_task_mgmt_abort_task_async iscsi_task_mgmt_abort_task_sync iscsi_task_mgmt_abort_task_set_async iscsi_task_mgmt_abort_task_set_sync iscsi_task_mgmt_async iscsi_task_mgmt_sync iscsi_task_mgmt_lun_reset_async iscsi_task_mgmt_lun_reset_sync iscsi_task_mgmt_target_cold_reset_async iscsi_task_mgmt_target_cold_reset_sync iscsi_task_mgmt_target_warm_reset_async iscsi_task_mgmt_target_warm_reset_sync iscsi_testunitready_sync iscsi_testunitready_task iscsi_unmap_sync iscsi_unmap_task iscsi_verify10_sync iscsi_verify10_task iscsi_verify12_sync iscsi_verify12_task iscsi_verify16_sync iscsi_verify16_task iscsi_which_events iscsi_write10_sync iscsi_write10_task iscsi_write12_sync iscsi_write12_task iscsi_write16_sync iscsi_write16_task iscsi_writeatomic16_sync iscsi_writeatomic16_task iscsi_orwrite_sync iscsi_orwrite_task iscsi_compareandwrite_sync iscsi_compareandwrite_task iscsi_writeverify10_sync iscsi_writeverify10_task iscsi_writeverify12_sync iscsi_writeverify12_task iscsi_writeverify16_sync iscsi_writeverify16_task iscsi_writesame10_sync iscsi_writesame10_task iscsi_writesame16_sync iscsi_writesame16_task scsi_association_to_str scsi_cdb_compareandwrite scsi_cdb_extended_copy scsi_cdb_inquiry scsi_cdb_get_lba_status scsi_cdb_modeselect6 scsi_cdb_modeselect10 scsi_cdb_modesense6 scsi_cdb_modesense10 scsi_cdb_persistent_reserve_in scsi_cdb_persistent_reserve_out scsi_cdb_prefetch10 scsi_cdb_prefetch16 scsi_cdb_preventallow scsi_cdb_read10 scsi_cdb_read12 scsi_cdb_read16 scsi_cdb_read6 scsi_cdb_readcapacity10 scsi_cdb_readcapacity16 scsi_cdb_readtoc scsi_cdb_receive_copy_results scsi_cdb_reserve6 scsi_cdb_release6 scsi_cdb_report_supported_opcodes scsi_cdb_sanitize scsi_cdb_serviceactionin16 scsi_cdb_startstopunit scsi_cdb_synchronizecache10 scsi_cdb_synchronizecache16 scsi_cdb_testunitready scsi_cdb_unmap scsi_cdb_verify10 scsi_cdb_verify12 scsi_cdb_verify16 scsi_cdb_write10 scsi_cdb_write12 scsi_cdb_write16 scsi_cdb_writeatomic16 scsi_cdb_orwrite scsi_cdb_writeverify10 scsi_cdb_writeverify12 scsi_cdb_writeverify16 scsi_cdb_writesame10 scsi_cdb_writesame16 scsi_codeset_to_str scsi_create_task scsi_datain_getfullsize scsi_datain_unmarshall scsi_cdb_unmarshall scsi_designator_type_to_str scsi_devqualifier_to_str scsi_devtype_to_str scsi_free_scsi_task scsi_get_task_private_ptr scsi_get_uint16 scsi_get_uint32 scsi_get_uint64 scsi_inquiry_pagecode_to_str scsi_malloc scsi_modesense_dataout_marshall scsi_modesense_get_page scsi_parse_sense_data scsi_protocol_identifier_to_str scsi_reportluns_cdb scsi_sense_ascq_str scsi_pr_type_str scsi_sense_key_str scsi_set_task_private_ptr scsi_set_uint16 scsi_set_uint32 scsi_set_uint64 scsi_task_add_data_in_buffer scsi_task_add_data_out_buffer scsi_task_get_status scsi_task_set_iov_in scsi_task_set_iov_out scsi_version_to_str scsi_version_descriptor_to_str libiscsi-1.17.0/lib/logging.c000066400000000000000000000036161271371262700157570ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2012 by Ronnie Sahlberg This program 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 program is distributed in the hope that 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 program; if not, see . */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #ifdef HAVE_SYS_TYPES_H #include #endif #ifdef HAVE_ARPA_INET_H #include #endif #ifdef HAVE_UNISTD_H #include #endif #if defined(WIN32) #include "win32/win32_compat.h" #endif #include #include #include "iscsi.h" #include "iscsi-private.h" #include "scsi-lowlevel.h" void iscsi_log_to_stderr(int level, const char *message) { fprintf(stderr, "libiscsi:%d %s\n", level, message); } void iscsi_set_log_fn(struct iscsi_context *iscsi, iscsi_log_fn fn) { iscsi->log_fn = fn; } void iscsi_log_message(struct iscsi_context *iscsi, int level, const char *format, ...) { va_list ap; static char message[1024]; int ret; if (iscsi->log_fn == NULL) { return; } va_start(ap, format); ret = vsnprintf(message, 1024, format, ap); va_end(ap); if (ret < 0) { return; } if (iscsi->target_name[0]) { static char message2[1024]; snprintf(message2, 1024, "%s [%s]", message, iscsi->target_name); iscsi->log_fn(level, message2); } else iscsi->log_fn(level, message); } libiscsi-1.17.0/lib/login.c000066400000000000000000001023141271371262700154340ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2010 by Ronnie Sahlberg This program 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 program is distributed in the hope that 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 program; if not, see . */ #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #ifdef HAVE_CONFIG_H #include "config.h" #endif #ifdef HAVE_SYS_TYPES_H #include #endif #ifdef HAVE_ARPA_INET_H #include #endif #if defined(WIN32) #include #include "win32/win32_compat.h" #endif #include #include #include #include #include "iscsi.h" #include "iscsi-private.h" #include "scsi-lowlevel.h" #include "md5.h" #ifdef HAVE_LIBGCRYPT #include #endif static int iscsi_login_add_initiatorname(struct iscsi_context *iscsi, struct iscsi_pdu *pdu) { char str[MAX_STRING_SIZE+1]; /* We only send InitiatorName during opneg or the first leg of secneg */ if ((iscsi->current_phase != ISCSI_PDU_LOGIN_CSG_OPNEG && iscsi->secneg_phase != ISCSI_LOGIN_SECNEG_PHASE_OFFER_CHAP) || iscsi->secneg_phase != ISCSI_LOGIN_SECNEG_PHASE_OFFER_CHAP) { return 0; } if (snprintf(str, MAX_STRING_SIZE, "InitiatorName=%s", iscsi->initiator_name) == -1) { iscsi_set_error(iscsi, "Out-of-memory: aprintf failed."); return -1; } if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1) != 0) { iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed."); return -1; } return 0; } static int iscsi_login_add_alias(struct iscsi_context *iscsi, struct iscsi_pdu *pdu) { char str[MAX_STRING_SIZE+1]; /* We only send InitiatorAlias during opneg or the first leg of secneg */ if ((iscsi->current_phase != ISCSI_PDU_LOGIN_CSG_OPNEG && iscsi->secneg_phase != ISCSI_LOGIN_SECNEG_PHASE_OFFER_CHAP) || iscsi->secneg_phase != ISCSI_LOGIN_SECNEG_PHASE_OFFER_CHAP) { return 0; } if (snprintf(str, MAX_STRING_SIZE, "InitiatorAlias=%s", iscsi->alias) == -1) { iscsi_set_error(iscsi, "Out-of-memory: aprintf failed."); return -1; } if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1) != 0) { iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed."); return -1; } return 0; } static int iscsi_login_add_targetname(struct iscsi_context *iscsi, struct iscsi_pdu *pdu) { char str[MAX_STRING_SIZE+1]; /* We only send TargetName during opneg or the first leg of secneg */ if ((iscsi->current_phase != ISCSI_PDU_LOGIN_CSG_OPNEG && iscsi->secneg_phase != ISCSI_LOGIN_SECNEG_PHASE_OFFER_CHAP) || iscsi->secneg_phase != ISCSI_LOGIN_SECNEG_PHASE_OFFER_CHAP) { return 0; } if (!iscsi->target_name[0]) { iscsi_set_error(iscsi, "Trying normal connect but " "target name not set."); return -1; } if (snprintf(str, MAX_STRING_SIZE, "TargetName=%s", iscsi->target_name) == -1) { iscsi_set_error(iscsi, "Out-of-memory: aprintf failed."); return -1; } if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1) != 0) { iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed."); return -1; } return 0; } static int iscsi_login_add_sessiontype(struct iscsi_context *iscsi, struct iscsi_pdu *pdu) { char str[MAX_STRING_SIZE+1]; /* We only send SessionType during opneg or the first leg of secneg */ if ((iscsi->current_phase != ISCSI_PDU_LOGIN_CSG_OPNEG && iscsi->secneg_phase != ISCSI_LOGIN_SECNEG_PHASE_OFFER_CHAP) || iscsi->secneg_phase != ISCSI_LOGIN_SECNEG_PHASE_OFFER_CHAP) { return 0; } switch (iscsi->session_type) { case ISCSI_SESSION_DISCOVERY: strncpy(str,"SessionType=Discovery",MAX_STRING_SIZE); break; case ISCSI_SESSION_NORMAL: strncpy(str,"SessionType=Normal",MAX_STRING_SIZE); break; default: iscsi_set_error(iscsi, "Can not handle sessions %d yet.", iscsi->session_type); return -1; } if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1) != 0) { iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed."); return -1; } return 0; } static int iscsi_login_add_headerdigest(struct iscsi_context *iscsi, struct iscsi_pdu *pdu) { char str[MAX_STRING_SIZE+1]; /* We only send HeaderDigest during opneg */ if (iscsi->current_phase != ISCSI_PDU_LOGIN_CSG_OPNEG) { return 0; } switch (iscsi->want_header_digest) { case ISCSI_HEADER_DIGEST_NONE: strncpy(str,"HeaderDigest=None",MAX_STRING_SIZE); break; case ISCSI_HEADER_DIGEST_NONE_CRC32C: strncpy(str,"HeaderDigest=None,CRC32C",MAX_STRING_SIZE); break; case ISCSI_HEADER_DIGEST_CRC32C_NONE: strncpy(str,"HeaderDigest=CRC32C,None",MAX_STRING_SIZE); break; case ISCSI_HEADER_DIGEST_CRC32C: strncpy(str,"HeaderDigest=CRC32C",MAX_STRING_SIZE); break; default: iscsi_set_error(iscsi, "invalid header digest value"); return -1; } if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1) != 0) { iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed."); return -1; } return 0; } static int iscsi_login_add_datadigest(struct iscsi_context *iscsi, struct iscsi_pdu *pdu) { char str[MAX_STRING_SIZE+1]; /* We only send DataDigest during opneg */ if (iscsi->current_phase != ISCSI_PDU_LOGIN_CSG_OPNEG) { return 0; } strncpy(str,"DataDigest=None",MAX_STRING_SIZE); if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1) != 0) { iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed."); return -1; } return 0; } static int iscsi_login_add_initialr2t(struct iscsi_context *iscsi, struct iscsi_pdu *pdu) { char str[MAX_STRING_SIZE+1]; /* We only send InitialR2T during opneg */ if (iscsi->current_phase != ISCSI_PDU_LOGIN_CSG_OPNEG) { return 0; } if (snprintf(str, MAX_STRING_SIZE, "InitialR2T=%s", iscsi->want_initial_r2t == ISCSI_INITIAL_R2T_NO ? "No" : "Yes") == -1) { iscsi_set_error(iscsi, "Out-of-memory: aprintf failed."); return -1; } if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1) != 0) { iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed."); return -1; } return 0; } static int iscsi_login_add_immediatedata(struct iscsi_context *iscsi, struct iscsi_pdu *pdu) { char str[MAX_STRING_SIZE+1]; /* We only send ImmediateData during opneg */ if (iscsi->current_phase != ISCSI_PDU_LOGIN_CSG_OPNEG) { return 0; } if (snprintf(str, MAX_STRING_SIZE, "ImmediateData=%s", iscsi->want_immediate_data == ISCSI_IMMEDIATE_DATA_NO ? "No" : "Yes") == -1) { iscsi_set_error(iscsi, "Out-of-memory: aprintf failed."); return -1; } if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1) != 0) { iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed."); return -1; } return 0; } static int iscsi_login_add_maxburstlength(struct iscsi_context *iscsi, struct iscsi_pdu *pdu) { char str[MAX_STRING_SIZE+1]; /* We only send MaxBurstLength during opneg */ if (iscsi->current_phase != ISCSI_PDU_LOGIN_CSG_OPNEG) { return 0; } if (snprintf(str, MAX_STRING_SIZE, "MaxBurstLength=%d", iscsi->max_burst_length) == -1) { iscsi_set_error(iscsi, "Out-of-memory: aprintf failed."); return -1; } if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1) != 0) { iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed."); return -1; } return 0; } static int iscsi_login_add_firstburstlength(struct iscsi_context *iscsi, struct iscsi_pdu *pdu) { char str[MAX_STRING_SIZE+1]; /* We only send FirstBurstLength during opneg */ if (iscsi->current_phase != ISCSI_PDU_LOGIN_CSG_OPNEG) { return 0; } if (snprintf(str, MAX_STRING_SIZE, "FirstBurstLength=%d", iscsi->first_burst_length) == -1) { iscsi_set_error(iscsi, "Out-of-memory: aprintf failed."); return -1; } if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1) != 0) { iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed."); return -1; } return 0; } static int iscsi_login_add_maxrecvdatasegmentlength(struct iscsi_context *iscsi, struct iscsi_pdu *pdu) { char str[MAX_STRING_SIZE+1]; /* We only send MaxRecvDataSegmentLength during opneg */ if (iscsi->current_phase != ISCSI_PDU_LOGIN_CSG_OPNEG) { return 0; } if (snprintf(str, MAX_STRING_SIZE, "MaxRecvDataSegmentLength=%d", iscsi->initiator_max_recv_data_segment_length) == -1) { iscsi_set_error(iscsi, "Out-of-memory: aprintf failed."); return -1; } if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1) != 0) { iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed."); return -1; } return 0; } static int iscsi_login_add_datapduinorder(struct iscsi_context *iscsi, struct iscsi_pdu *pdu) { char str[MAX_STRING_SIZE+1]; /* We only send DataPduInOrder during opneg */ if (iscsi->current_phase != ISCSI_PDU_LOGIN_CSG_OPNEG) { return 0; } strncpy(str,"DataPDUInOrder=Yes",MAX_STRING_SIZE); if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1) != 0) { iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed."); return -1; } return 0; } static int iscsi_login_add_defaulttime2wait(struct iscsi_context *iscsi, struct iscsi_pdu *pdu) { char str[MAX_STRING_SIZE+1]; /* We only send DefaultTime2Wait during opneg */ if (iscsi->current_phase != ISCSI_PDU_LOGIN_CSG_OPNEG) { return 0; } strncpy(str,"DefaultTime2Wait=2",MAX_STRING_SIZE); if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1) != 0) { iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed."); return -1; } return 0; } static int iscsi_login_add_defaulttime2retain(struct iscsi_context *iscsi, struct iscsi_pdu *pdu) { char str[MAX_STRING_SIZE+1]; /* We only send DefaultTime2Retain during opneg */ if (iscsi->current_phase != ISCSI_PDU_LOGIN_CSG_OPNEG) { return 0; } strncpy(str,"DefaultTime2Retain=0",MAX_STRING_SIZE); if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1) != 0) { iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed."); return -1; } return 0; } static int iscsi_login_add_ifmarker(struct iscsi_context *iscsi, struct iscsi_pdu *pdu) { char str[MAX_STRING_SIZE+1]; /* We only send IFMarker during opneg */ if (iscsi->current_phase != ISCSI_PDU_LOGIN_CSG_OPNEG) { return 0; } strncpy(str,"IFMarker=No",MAX_STRING_SIZE); if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1) != 0) { iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed."); return -1; } return 0; } static int iscsi_login_add_ofmarker(struct iscsi_context *iscsi, struct iscsi_pdu *pdu) { char str[MAX_STRING_SIZE+1]; /* We only send OFMarker during opneg */ if (iscsi->current_phase != ISCSI_PDU_LOGIN_CSG_OPNEG) { return 0; } strncpy(str,"OFMarker=No",MAX_STRING_SIZE); if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1) != 0) { iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed."); return -1; } return 0; } static int iscsi_login_add_maxconnections(struct iscsi_context *iscsi, struct iscsi_pdu *pdu) { char str[MAX_STRING_SIZE+1]; /* We only send MaxConnections during opneg */ if (iscsi->current_phase != ISCSI_PDU_LOGIN_CSG_OPNEG) { return 0; } strncpy(str,"MaxConnections=1",MAX_STRING_SIZE); if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1) != 0) { iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed."); return -1; } return 0; } static int iscsi_login_add_maxoutstandingr2t(struct iscsi_context *iscsi, struct iscsi_pdu *pdu) { char str[MAX_STRING_SIZE+1]; /* We only send MaxOutstandingR2T during opneg */ if (iscsi->current_phase != ISCSI_PDU_LOGIN_CSG_OPNEG) { return 0; } strncpy(str,"MaxOutstandingR2T=1",MAX_STRING_SIZE); if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1) != 0) { iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed."); return -1; } return 0; } static int iscsi_login_add_errorrecoverylevel(struct iscsi_context *iscsi, struct iscsi_pdu *pdu) { char str[MAX_STRING_SIZE+1]; /* We only send ErrorRecoveryLevel during opneg */ if (iscsi->current_phase != ISCSI_PDU_LOGIN_CSG_OPNEG) { return 0; } strncpy(str,"ErrorRecoveryLevel=0",MAX_STRING_SIZE); if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1) != 0) { iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed."); return -1; } return 0; } static int iscsi_login_add_datasequenceinorder(struct iscsi_context *iscsi, struct iscsi_pdu *pdu) { char str[MAX_STRING_SIZE+1]; /* We only send DataSequenceInOrder during opneg */ if (iscsi->current_phase != ISCSI_PDU_LOGIN_CSG_OPNEG) { return 0; } strncpy(str,"DataSequenceInOrder=Yes",MAX_STRING_SIZE); if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1) != 0) { iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed."); return -1; } return 0; } static int iscsi_login_add_authmethod(struct iscsi_context *iscsi, struct iscsi_pdu *pdu) { char str[MAX_STRING_SIZE+1]; if (iscsi->current_phase != ISCSI_PDU_LOGIN_CSG_SECNEG || iscsi->secneg_phase != ISCSI_LOGIN_SECNEG_PHASE_OFFER_CHAP) { return 0; } strncpy(str,"AuthMethod=CHAP,None",MAX_STRING_SIZE); if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1) != 0) { iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed."); return -1; } return 0; } static int iscsi_login_add_authalgorithm(struct iscsi_context *iscsi, struct iscsi_pdu *pdu) { char str[MAX_STRING_SIZE+1]; if (iscsi->current_phase != ISCSI_PDU_LOGIN_CSG_SECNEG || iscsi->secneg_phase != ISCSI_LOGIN_SECNEG_PHASE_SELECT_ALGORITHM) { return 0; } strncpy(str,"CHAP_A=5",MAX_STRING_SIZE); if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1) != 0) { iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed."); return -1; } return 0; } static int iscsi_login_add_chap_username(struct iscsi_context *iscsi, struct iscsi_pdu *pdu) { char str[MAX_STRING_SIZE+1]; if (iscsi->current_phase != ISCSI_PDU_LOGIN_CSG_SECNEG || iscsi->secneg_phase != ISCSI_LOGIN_SECNEG_PHASE_SEND_RESPONSE) { return 0; } strncpy(str,"CHAP_N=",MAX_STRING_SIZE); if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)) != 0) { iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed."); return -1; } if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)iscsi->user, strlen(iscsi->user) +1) != 0) { iscsi_set_error(iscsi, "Out-of-memory: pdu add data " "failed."); return -1; } return 0; } static int h2i(int h) { if (h >= 'a' && h <= 'f') { return h - 'a' + 10; } if (h >= 'A' && h <= 'F') { return h - 'A' + 10; } return h - '0'; } static int i2h(int i) { if (i >= 10) { return i - 10 + 'A'; } return i + '0'; } #ifndef HAVE_LIBGCRYPT typedef struct MD5Context *gcry_md_hd_t; #define gcry_md_write MD5Update #define GCRY_MD_MD5 1 static void gcry_md_open(gcry_md_hd_t *hd, int algo, unsigned int flags) { assert(algo == GCRY_MD_MD5 && flags == 0); *hd = malloc(sizeof(struct MD5Context)); if (*hd) { MD5Init(*hd); } } static void gcry_md_putc(gcry_md_hd_t h, unsigned char c) { MD5Update(h, &c, 1); } static char *gcry_md_read(gcry_md_hd_t h, int algo) { unsigned char digest[16]; assert(algo == 0 || algo == GCRY_MD_MD5); MD5Final(digest, h); return memcpy(h->buf, digest, sizeof(digest)); } static void gcry_md_close(gcry_md_hd_t h) { memset(h, 0, sizeof(*h)); free(h); } #endif /* size of the challenge used for bidirectional chap */ #define TARGET_CHAP_C_SIZE 32 static int iscsi_login_add_chap_response(struct iscsi_context *iscsi, struct iscsi_pdu *pdu) { char str[MAX_STRING_SIZE+1]; char * strp; unsigned char c, cc[2]; unsigned char digest[CHAP_R_SIZE]; gcry_md_hd_t ctx; int i; if (iscsi->current_phase != ISCSI_PDU_LOGIN_CSG_SECNEG || iscsi->secneg_phase != ISCSI_LOGIN_SECNEG_PHASE_SEND_RESPONSE) { return 0; } if (!iscsi->chap_c[0]) { iscsi_set_error(iscsi, "No CHAP challenge found"); return -1; } gcry_md_open(&ctx, GCRY_MD_MD5, 0); if (ctx == NULL) { iscsi_set_error(iscsi, "Cannot create MD5 algorithm"); return -1; } gcry_md_putc(ctx, iscsi->chap_i); gcry_md_write(ctx, (unsigned char *)iscsi->passwd, strlen(iscsi->passwd)); strp = iscsi->chap_c; while (*strp != 0) { c = (h2i(strp[0]) << 4) | h2i(strp[1]); strp += 2; gcry_md_putc(ctx, c); } memcpy(digest, gcry_md_read(ctx, 0), sizeof(digest)); gcry_md_close(ctx); strncpy(str,"CHAP_R=0x",MAX_STRING_SIZE); if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)) != 0) { iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed."); return -1; } for (i = 0; i < CHAP_R_SIZE; i++) { c = digest[i]; cc[0] = i2h((c >> 4)&0x0f); cc[1] = i2h((c )&0x0f); if (iscsi_pdu_add_data(iscsi, pdu, &cc[0], 2) != 0) { iscsi_set_error(iscsi, "Out-of-memory: pdu add data " "failed."); return -1; } } c = 0; if (iscsi_pdu_add_data(iscsi, pdu, &c, 1) != 0) { iscsi_set_error(iscsi, "Out-of-memory: pdu add data " "failed."); return -1; } /* bidirectional chap */ if (iscsi->target_user[0]) { unsigned char target_chap_c[TARGET_CHAP_C_SIZE]; iscsi->target_chap_i++; snprintf(str, MAX_STRING_SIZE, "CHAP_I=%d", iscsi->target_chap_i); if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str) + 1) != 0) { iscsi_set_error(iscsi, "Out-of-memory: pdu add " "data failed."); return -1; } for (i = 0; i < TARGET_CHAP_C_SIZE; i++) { target_chap_c[i] = rand()&0xff; } strncpy(str, "CHAP_C=0x", MAX_STRING_SIZE); if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)) != 0) { iscsi_set_error(iscsi, "Out-of-memory: pdu add data " "failed."); return -1; } for (i = 0; i < TARGET_CHAP_C_SIZE; i++) { c = target_chap_c[i]; cc[0] = i2h((c >> 4)&0x0f); cc[1] = i2h((c )&0x0f); if (iscsi_pdu_add_data(iscsi, pdu, &cc[0], 2) != 0) { iscsi_set_error(iscsi, "Out-of-memory: pdu add " "data failed."); return -1; } } c = 0; if (iscsi_pdu_add_data(iscsi, pdu, &c, 1) != 0) { iscsi_set_error(iscsi, "Out-of-memory: pdu add data " "failed."); return -1; } gcry_md_open(&ctx, GCRY_MD_MD5, 0); if (ctx == NULL) { iscsi_set_error(iscsi, "Cannot create MD5 algorithm"); return -1; } gcry_md_putc(ctx, iscsi->target_chap_i); gcry_md_write(ctx, (unsigned char *)iscsi->target_passwd, strlen(iscsi->target_passwd)); gcry_md_write(ctx, (unsigned char *)target_chap_c, TARGET_CHAP_C_SIZE); memcpy(iscsi->target_chap_r, gcry_md_read(ctx, 0), sizeof(iscsi->target_chap_r)); gcry_md_close(ctx); } return 0; } int iscsi_login_async(struct iscsi_context *iscsi, iscsi_command_cb cb, void *private_data) { struct iscsi_pdu *pdu; int transit; if (iscsi->login_attempts++ > 10) { iscsi_set_error(iscsi, "login took too many tries." " giving up."); return -1; } if (iscsi->is_loggedin != 0) { iscsi_set_error(iscsi, "Trying to login while already logged " "in."); return -1; } switch (iscsi->session_type) { case ISCSI_SESSION_DISCOVERY: case ISCSI_SESSION_NORMAL: break; default: iscsi_set_error(iscsi, "trying to login without setting " "session type."); return -1; } /* randomize cmdsn and itt */ if (!iscsi->current_phase && !iscsi->secneg_phase) { iscsi->itt = (uint32_t) rand(); iscsi->cmdsn = (uint32_t) rand(); iscsi->expcmdsn = iscsi->maxcmdsn = iscsi->min_cmdsn_waiting = iscsi->cmdsn; } pdu = iscsi_allocate_pdu(iscsi, ISCSI_PDU_LOGIN_REQUEST, ISCSI_PDU_LOGIN_RESPONSE, iscsi->itt, ISCSI_PDU_DROP_ON_RECONNECT); if (pdu == NULL) { iscsi_set_error(iscsi, "Out-of-memory: Failed to allocate " "login pdu."); return -1; } /* login request */ iscsi_pdu_set_immediate(pdu); /* cmdsn is not increased if Immediate delivery*/ iscsi_pdu_set_cmdsn(pdu, iscsi->cmdsn); if (!iscsi->user[0]) { iscsi->current_phase = ISCSI_PDU_LOGIN_CSG_OPNEG; } if (iscsi->current_phase == ISCSI_PDU_LOGIN_CSG_SECNEG) { iscsi->next_phase = ISCSI_PDU_LOGIN_NSG_OPNEG; } if (iscsi->current_phase == ISCSI_PDU_LOGIN_CSG_OPNEG) { iscsi->next_phase = ISCSI_PDU_LOGIN_NSG_FF; } transit = 0; if (iscsi->current_phase == ISCSI_PDU_LOGIN_CSG_OPNEG) { transit = ISCSI_PDU_LOGIN_TRANSIT; } if (iscsi->current_phase == ISCSI_PDU_LOGIN_CSG_SECNEG) { if (iscsi->secneg_phase == ISCSI_LOGIN_SECNEG_PHASE_OFFER_CHAP) { transit = ISCSI_PDU_LOGIN_TRANSIT; } if (iscsi->secneg_phase == ISCSI_LOGIN_SECNEG_PHASE_SEND_RESPONSE) { transit = ISCSI_PDU_LOGIN_TRANSIT; } } /* flags */ iscsi_pdu_set_pduflags(pdu, transit | iscsi->current_phase | iscsi->next_phase); /* initiator name */ if (iscsi_login_add_initiatorname(iscsi, pdu) != 0) { iscsi_free_pdu(iscsi, pdu); return -1; } /* optional alias */ if (iscsi->alias[0]) { if (iscsi_login_add_alias(iscsi, pdu) != 0) { iscsi_free_pdu(iscsi, pdu); return -1; } } /* target name */ if (iscsi->session_type == ISCSI_SESSION_NORMAL) { if (iscsi_login_add_targetname(iscsi, pdu) != 0) { iscsi_free_pdu(iscsi, pdu); return -1; } } /* session type */ if (iscsi_login_add_sessiontype(iscsi, pdu) != 0) { iscsi_free_pdu(iscsi, pdu); return -1; } /* header digest */ if (iscsi_login_add_headerdigest(iscsi, pdu) != 0) { iscsi_free_pdu(iscsi, pdu); return -1; } /* auth method */ if (iscsi_login_add_authmethod(iscsi, pdu) != 0) { iscsi_free_pdu(iscsi, pdu); return -1; } /* auth algorithm */ if (iscsi_login_add_authalgorithm(iscsi, pdu) != 0) { iscsi_free_pdu(iscsi, pdu); return -1; } /* chap username */ if (iscsi_login_add_chap_username(iscsi, pdu) != 0) { iscsi_free_pdu(iscsi, pdu); return -1; } /* chap response */ if (iscsi_login_add_chap_response(iscsi, pdu) != 0) { iscsi_free_pdu(iscsi, pdu); return -1; } /* data digest */ if (iscsi_login_add_datadigest(iscsi, pdu) != 0) { iscsi_free_pdu(iscsi, pdu); return -1; } /* initial r2t */ if (iscsi_login_add_initialr2t(iscsi, pdu) != 0) { iscsi_free_pdu(iscsi, pdu); return -1; } /* immediate data */ if (iscsi_login_add_immediatedata(iscsi, pdu) != 0) { iscsi_free_pdu(iscsi, pdu); return -1; } /* max burst length */ if (iscsi_login_add_maxburstlength(iscsi, pdu) != 0) { iscsi_free_pdu(iscsi, pdu); return -1; } /* first burst length */ if (iscsi_login_add_firstburstlength(iscsi, pdu) != 0) { iscsi_free_pdu(iscsi, pdu); return -1; } /* default time 2 wait */ if (iscsi_login_add_defaulttime2wait(iscsi, pdu) != 0) { iscsi_free_pdu(iscsi, pdu); return -1; } /* default time 2 retain */ if (iscsi_login_add_defaulttime2retain(iscsi, pdu) != 0) { iscsi_free_pdu(iscsi, pdu); return -1; } /* max outstanding r2t */ if (iscsi_login_add_maxoutstandingr2t(iscsi, pdu) != 0) { iscsi_free_pdu(iscsi, pdu); return -1; } /* errorrecoverylevel */ if (iscsi_login_add_errorrecoverylevel(iscsi, pdu) != 0) { iscsi_free_pdu(iscsi, pdu); return -1; } /* ifmarker */ if (iscsi_login_add_ifmarker(iscsi, pdu) != 0) { iscsi_free_pdu(iscsi, pdu); return -1; } /* ofmarker */ if (iscsi_login_add_ofmarker(iscsi, pdu) != 0) { iscsi_free_pdu(iscsi, pdu); return -1; } /* maxconnections */ if (iscsi_login_add_maxconnections(iscsi, pdu) != 0) { iscsi_free_pdu(iscsi, pdu); return -1; } /* max recv data segment length */ if (iscsi_login_add_maxrecvdatasegmentlength(iscsi, pdu) != 0) { iscsi_free_pdu(iscsi, pdu); return -1; } /* data pdu in order */ if (iscsi_login_add_datapduinorder(iscsi, pdu) != 0) { iscsi_free_pdu(iscsi, pdu); return -1; } /* data sequence in order */ if (iscsi_login_add_datasequenceinorder(iscsi, pdu) != 0) { iscsi_free_pdu(iscsi, pdu); return -1; } pdu->callback = cb; pdu->private_data = private_data; if (iscsi_queue_pdu(iscsi, pdu) != 0) { iscsi_set_error(iscsi, "Out-of-memory: failed to queue iscsi " "pdu."); iscsi_free_pdu(iscsi, pdu); return -1; } return 0; } static const char *login_error_str(int status) { switch (status) { case 0x0100: return "Target moved (unknown)"; /* Some don't set the detail */ case 0x0101: return "Target moved temporarily"; case 0x0102: return "Target moved permanently"; case 0x0200: return "Initiator error"; case 0x0201: return "Authentication failure"; case 0x0202: return "Authorization failure"; case 0x0203: return "Target not found"; case 0x0204: return "Target removed"; case 0x0205: return "Unsupported version"; case 0x0206: return "Too many connections"; case 0x0207: return "Missing parameter"; case 0x0208: return "Can't include in session"; case 0x0209: return "Session type not supported"; case 0x020a: return "Session does not exist"; case 0x020b: return "Invalid during login"; case 0x0300: return "Target error"; case 0x0301: return "Service unavailable"; case 0x0302: return "Out of resources"; } return "Unknown login error"; } int iscsi_process_login_reply(struct iscsi_context *iscsi, struct iscsi_pdu *pdu, struct iscsi_in_pdu *in) { uint32_t status; char *ptr = (char *)in->data; int size = in->data_pos; int must_have_chap_n = 0; int must_have_chap_r = 0; status = scsi_get_uint16(&in->hdr[36]); // Status-Class is 0 if (!(status >> 8)) { if (!iscsi->current_phase && !iscsi->secneg_phase) { iscsi->statsn = scsi_get_uint32(&in->hdr[24]); } } /* Using bidirectional CHAP? Then we must see a chap_n and chap_r * field in this PDU */ if ((in->hdr[1] & ISCSI_PDU_LOGIN_TRANSIT) && (in->hdr[1] & ISCSI_PDU_LOGIN_CSG_FF) == ISCSI_PDU_LOGIN_CSG_SECNEG && iscsi->target_user[0]) { must_have_chap_n = 1; must_have_chap_r = 1; } /* XXX here we should parse the data returned in case the target * renegotiated some some parameters. * we should also do proper handshaking if the target is not yet * prepared to transition to the next stage */ while (size > 0) { char *end; int len; end = memchr(ptr, 0, size); if (end == NULL) { iscsi_set_error(iscsi, "NUL not found after offset %ld " "when parsing login data", (long)((unsigned char *)ptr - in->data)); if (pdu->callback) { pdu->callback(iscsi, SCSI_STATUS_ERROR, NULL, pdu->private_data); } return -1; } len = end - ptr; if (len == 0) { break; } /* parse the strings */ if (!strncmp(ptr, "TargetAddress=", 14)) { strncpy(iscsi->target_address,ptr+14,MAX_STRING_SIZE); } if (!strncmp(ptr, "HeaderDigest=", 13)) { if (!strcmp(ptr + 13, "CRC32C")) { iscsi->want_header_digest = ISCSI_HEADER_DIGEST_CRC32C; } else { iscsi->want_header_digest = ISCSI_HEADER_DIGEST_NONE; } } if (!strncmp(ptr, "FirstBurstLength=", 17)) { iscsi->first_burst_length = strtol(ptr + 17, NULL, 10); } if (!strncmp(ptr, "InitialR2T=", 11)) { if (!strcmp(ptr + 11, "No")) { iscsi->use_initial_r2t = ISCSI_INITIAL_R2T_NO; } else { iscsi->use_initial_r2t = ISCSI_INITIAL_R2T_YES; } } if (!strncmp(ptr, "ImmediateData=", 14)) { if (!strcmp(ptr + 14, "No")) { iscsi->use_immediate_data = ISCSI_IMMEDIATE_DATA_NO; } else if (iscsi->want_immediate_data == ISCSI_IMMEDIATE_DATA_NO) { /* If we negotiated NO, it doesnt matter what * the target said. ImmediateData is NO. */ iscsi->use_immediate_data = ISCSI_IMMEDIATE_DATA_NO; } else { iscsi->use_immediate_data = ISCSI_IMMEDIATE_DATA_YES; } } if (!strncmp(ptr, "MaxBurstLength=", 15)) { iscsi->max_burst_length = strtol(ptr + 15, NULL, 10); } if (!strncmp(ptr, "MaxRecvDataSegmentLength=", 25)) { iscsi->target_max_recv_data_segment_length = strtol(ptr + 25, NULL, 10); } if (!strncmp(ptr, "AuthMethod=", 11)) { if (!strcmp(ptr + 11, "CHAP")) { iscsi->secneg_phase = ISCSI_LOGIN_SECNEG_PHASE_SELECT_ALGORITHM; } } if (!strncmp(ptr, "CHAP_A=", 7)) { iscsi->chap_a = atoi(ptr+7); iscsi->secneg_phase = ISCSI_LOGIN_SECNEG_PHASE_SEND_RESPONSE; } if (!strncmp(ptr, "CHAP_I=", 7)) { iscsi->chap_i = atoi(ptr+7); iscsi->secneg_phase = ISCSI_LOGIN_SECNEG_PHASE_SEND_RESPONSE; } if (!strncmp(ptr, "CHAP_C=0x", 9)) { if (len-9 > MAX_CHAP_C_LENGTH) { iscsi_set_error(iscsi, "Wrong length of CHAP_C received from" " target (%d, max: %d)", len-9, MAX_CHAP_C_LENGTH); if (pdu->callback) { pdu->callback(iscsi, SCSI_STATUS_ERROR, NULL, pdu->private_data); } return 0; } *iscsi->chap_c = '\0'; strncat(iscsi->chap_c,ptr+9,len-9); iscsi->secneg_phase = ISCSI_LOGIN_SECNEG_PHASE_SEND_RESPONSE; } if (!strncmp(ptr, "CHAP_N=", 7)) { if (strcmp(iscsi->target_user, ptr + 7)) { iscsi_set_error(iscsi, "Failed to log in to" " target. Wrong CHAP targetname" " received: %s", ptr + 7); if (pdu->callback) { pdu->callback(iscsi, SCSI_STATUS_ERROR, NULL, pdu->private_data); } return 0; } must_have_chap_n = 0; } if (!strncmp(ptr, "CHAP_R=0x", 9)) { int i; if (len != 9 + 2 * CHAP_R_SIZE) { iscsi_set_error(iscsi, "Wrong size of CHAP_R" " received from target."); if (pdu->callback) { pdu->callback(iscsi, SCSI_STATUS_ERROR, NULL, pdu->private_data); } return 0; } for (i = 0; i < CHAP_R_SIZE; i++) { unsigned char c; c = ((h2i(ptr[9 + 2 * i]) << 4) | h2i(ptr[9 + 2 * i + 1])); if (c != iscsi->target_chap_r[i]) { iscsi_set_error(iscsi, "Authentication " "failed. Invalid CHAP_R " "response from the target"); if (pdu->callback) { pdu->callback(iscsi, SCSI_STATUS_ERROR, NULL, pdu->private_data); } return 0; } } must_have_chap_r = 0; } ptr += len + 1; size -= len + 1; } if (status == SCSI_STATUS_REDIRECT && iscsi->target_address[0]) { ISCSI_LOG(iscsi, 2, "target requests redirect to %s",iscsi->target_address); if (pdu->callback) { pdu->callback(iscsi, SCSI_STATUS_REDIRECT, NULL, pdu->private_data); } return 0; } if (status != 0) { iscsi_set_error(iscsi, "Failed to log in to target. Status: %s(%d)", login_error_str(status), status); if (pdu->callback) { pdu->callback(iscsi, SCSI_STATUS_ERROR, NULL, pdu->private_data); } return 0; } if (must_have_chap_n) { iscsi_set_error(iscsi, "Failed to log in to target. " "It did not return CHAP_N during SECNEG."); if (pdu->callback) { pdu->callback(iscsi, SCSI_STATUS_ERROR, NULL, pdu->private_data); } return 0; } if (must_have_chap_r) { iscsi_set_error(iscsi, "Failed to log in to target. " "It did not return CHAP_R during SECNEG."); if (pdu->callback) { pdu->callback(iscsi, SCSI_STATUS_ERROR, NULL, pdu->private_data); } return 0; } if (in->hdr[1] & ISCSI_PDU_LOGIN_TRANSIT) { iscsi->current_phase = (in->hdr[1] & ISCSI_PDU_LOGIN_NSG_FF) << 2; } if ((in->hdr[1] & ISCSI_PDU_LOGIN_TRANSIT) && (in->hdr[1] & ISCSI_PDU_LOGIN_NSG_FF) == ISCSI_PDU_LOGIN_NSG_FF) { iscsi->is_loggedin = 1; iscsi_itt_post_increment(iscsi); iscsi->header_digest = iscsi->want_header_digest; ISCSI_LOG(iscsi, 2, "login successful"); pdu->callback(iscsi, SCSI_STATUS_GOOD, NULL, pdu->private_data); } else { if (iscsi_login_async(iscsi, pdu->callback, pdu->private_data) != 0) { iscsi_set_error(iscsi, "Failed to send continuation login pdu"); if (pdu->callback) { pdu->callback(iscsi, SCSI_STATUS_ERROR, NULL, pdu->private_data); } } } return 0; } int iscsi_logout_async(struct iscsi_context *iscsi, iscsi_command_cb cb, void *private_data) { struct iscsi_pdu *pdu; iscsi->login_attempts = 0; if (iscsi->is_loggedin == 0) { iscsi_set_error(iscsi, "Trying to logout while not logged in."); return -1; } pdu = iscsi_allocate_pdu(iscsi, ISCSI_PDU_LOGOUT_REQUEST, ISCSI_PDU_LOGOUT_RESPONSE, iscsi_itt_post_increment(iscsi), ISCSI_PDU_DROP_ON_RECONNECT|ISCSI_PDU_CORK_WHEN_SENT); if (pdu == NULL) { iscsi_set_error(iscsi, "Out-of-memory: Failed to allocate " "logout pdu."); return -1; } /* logout request has the immediate flag set */ iscsi_pdu_set_immediate(pdu); /* flags : close the session */ iscsi_pdu_set_pduflags(pdu, 0x80); /* cmdsn is not increased if Immediate delivery*/ iscsi_pdu_set_cmdsn(pdu, iscsi->cmdsn); pdu->callback = cb; pdu->private_data = private_data; if (iscsi_queue_pdu(iscsi, pdu) != 0) { iscsi_set_error(iscsi, "Out-of-memory: failed to queue iscsi " "logout pdu."); iscsi_free_pdu(iscsi, pdu); return -1; } return 0; } int iscsi_process_logout_reply(struct iscsi_context *iscsi, struct iscsi_pdu *pdu, struct iscsi_in_pdu *in _U_) { iscsi->is_loggedin = 0; ISCSI_LOG(iscsi, 2, "logout successful"); if (pdu->callback) { pdu->callback(iscsi, SCSI_STATUS_GOOD, NULL, pdu->private_data); } return 0; } int iscsi_set_session_type(struct iscsi_context *iscsi, enum iscsi_session_type session_type) { if (iscsi->is_loggedin) { iscsi_set_error(iscsi, "trying to set session type while " "logged in"); return -1; } iscsi->session_type = session_type; return 0; } libiscsi-1.17.0/lib/md5.c000066400000000000000000000165721271371262700150230ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* * This code implements the MD5 message-digest algorithm. * The algorithm is due to Ron Rivest. This code was * written by Colin Plumb in 1993, no copyright is claimed. * This code is in the public domain; do with it what you wish. * * Equivalent code is available from RSA Data Security, Inc. * This code has been tested against that, and is equivalent, * except that you don't need to include two pages of legalese * with every copy. * * To compute the message digest of a chunk of bytes, declare an * MD5Context structure, pass it to MD5Init, call MD5Update as * needed on buffers full of bytes, and then call MD5Final, which * will fill a supplied 16-byte array with the digest. * * Changed so as no longer to depend on Colin Plumb's `usual.h' header * definitions; now uses stuff from dpkg's config.h. * - Ian Jackson . * Still in the public domain. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #ifdef HAVE_SYS_TYPES_H #include #endif #include "md5.h" #ifdef WORDS_BIGENDIAN void byteSwap(UWORD32 *buf, unsigned words) { md5byte *p = (md5byte *)buf; do { *buf++ = (UWORD32)((unsigned)p[3] << 8 | p[2]) << 16 | ((unsigned)p[1] << 8 | p[0]); p += 4; } while (--words); } #else #define byteSwap(buf,words) #endif /* * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious * initialization constants. */ void MD5Init(struct MD5Context *ctx) { ctx->buf[0] = 0x67452301; ctx->buf[1] = 0xefcdab89; ctx->buf[2] = 0x98badcfe; ctx->buf[3] = 0x10325476; ctx->bytes[0] = 0; ctx->bytes[1] = 0; } /* * Update context to reflect the concatenation of another buffer full * of bytes. */ void MD5Update(struct MD5Context *ctx, md5byte const *buf, unsigned len) { UWORD32 t; /* Update byte count */ t = ctx->bytes[0]; if ((ctx->bytes[0] = t + len) < t) ctx->bytes[1]++; /* Carry from low to high */ t = 64 - (t & 0x3f); /* Space available in ctx->in (at least 1) */ if (t > len) { memcpy((md5byte *)ctx->in + 64 - t, buf, len); return; } /* First chunk is an odd size */ memcpy((md5byte *)ctx->in + 64 - t, buf, t); byteSwap(ctx->in, 16); MD5Transform(ctx->buf, ctx->in); buf += t; len -= t; /* Process data in 64-byte chunks */ while (len >= 64) { memcpy(ctx->in, buf, 64); byteSwap(ctx->in, 16); MD5Transform(ctx->buf, ctx->in); buf += 64; len -= 64; } /* Handle any remaining bytes of data. */ memcpy(ctx->in, buf, len); } /* * Final wrapup - pad to 64-byte boundary with the bit pattern * 1 0* (64-bit count of bits processed, MSB-first) */ void MD5Final(md5byte digest[16], struct MD5Context *ctx) { int count = ctx->bytes[0] & 0x3f; /* Number of bytes in ctx->in */ md5byte *p = (md5byte *)ctx->in + count; /* Set the first char of padding to 0x80. There is always room. */ *p++ = 0x80; /* Bytes of padding needed to make 56 bytes (-8..55) */ count = 56 - 1 - count; if (count < 0) { /* Padding forces an extra block */ memset(p, 0, count + 8); byteSwap(ctx->in, 16); MD5Transform(ctx->buf, ctx->in); p = (md5byte *)ctx->in; count = 56; } memset(p, 0, count); byteSwap(ctx->in, 14); /* Append length in bits and transform */ ctx->in[14] = ctx->bytes[0] << 3; ctx->in[15] = ctx->bytes[1] << 3 | ctx->bytes[0] >> 29; MD5Transform(ctx->buf, ctx->in); byteSwap(ctx->buf, 4); memcpy(digest, ctx->buf, 16); memset(ctx, 0, sizeof(*ctx)); /* In case it's sensitive */ } #ifndef ASM_MD5 /* The four core functions - F1 is optimized somewhat */ /* #define F1(x, y, z) (x & y | ~x & z) */ #define F1(x, y, z) (z ^ (x & (y ^ z))) #define F2(x, y, z) F1(z, x, y) #define F3(x, y, z) (x ^ y ^ z) #define F4(x, y, z) (y ^ (x | ~z)) /* This is the central step in the MD5 algorithm. */ #define MD5STEP(f,w,x,y,z,in,s) \ (w += f(x,y,z) + in, w = (w<>(32-s)) + x) /* * The core of the MD5 algorithm, this alters an existing MD5 hash to * reflect the addition of 16 longwords of new data. MD5Update blocks * the data and converts bytes into longwords for this routine. */ void MD5Transform(UWORD32 buf[4], UWORD32 const in[16]) { register UWORD32 a, b, c, d; a = buf[0]; b = buf[1]; c = buf[2]; d = buf[3]; MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7); MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12); MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17); MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22); MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7); MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12); MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17); MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22); MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7); MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12); MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17); MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22); MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7); MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12); MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17); MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22); MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5); MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9); MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14); MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20); MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5); MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9); MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14); MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20); MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5); MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9); MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14); MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20); MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5); MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9); MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14); MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4); MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11); MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16); MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23); MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4); MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11); MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16); MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23); MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4); MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11); MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16); MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23); MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4); MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11); MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23); MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6); MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10); MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15); MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21); MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6); MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10); MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15); MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21); MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6); MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15); MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21); MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6); MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10); MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15); MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21); buf[0] += a; buf[1] += b; buf[2] += c; buf[3] += d; } #endif libiscsi-1.17.0/lib/nop.c000066400000000000000000000116751271371262700151310ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2010 by Ronnie Sahlberg This program 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 program is distributed in the hope that 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 program; if not, see . */ #if defined(WIN32) #else #include #endif #include #include #include "iscsi.h" #include "iscsi-private.h" int iscsi_nop_out_async(struct iscsi_context *iscsi, iscsi_command_cb cb, unsigned char *data, int len, void *private_data) { struct iscsi_pdu *pdu; if (iscsi->old_iscsi || iscsi->pending_reconnect) { ISCSI_LOG(iscsi, (iscsi->nops_in_flight > 1) ? 1 : 6, "NOP Out Send NOT SEND while reconnecting (nops_in_flight: %d, iscsi->maxcmdsn %08x, iscsi->expcmdsn %08x)", iscsi->nops_in_flight, iscsi->maxcmdsn, iscsi->expcmdsn); return 0; } if (iscsi->is_loggedin == 0) { iscsi_set_error(iscsi, "trying to send nop-out while not " "logged in"); return -1; } pdu = iscsi_allocate_pdu(iscsi, ISCSI_PDU_NOP_OUT, ISCSI_PDU_NOP_IN, iscsi_itt_post_increment(iscsi), ISCSI_PDU_DROP_ON_RECONNECT); if (pdu == NULL) { iscsi_set_error(iscsi, "Failed to allocate nop-out pdu"); return -1; } /* flags */ iscsi_pdu_set_pduflags(pdu, 0x80); /* ttt */ iscsi_pdu_set_ttt(pdu, 0xffffffff); /* lun */ iscsi_pdu_set_lun(pdu, 0); /* cmdsn */ iscsi_pdu_set_cmdsn(pdu, iscsi->cmdsn++); pdu->callback = cb; pdu->private_data = private_data; if (data != NULL && len > 0) { if (iscsi_pdu_add_data(iscsi, pdu, data, len) != 0) { iscsi_set_error(iscsi, "Failed to add outdata to nop-out"); iscsi_free_pdu(iscsi, pdu); return -1; } } if (iscsi_queue_pdu(iscsi, pdu) != 0) { iscsi_set_error(iscsi, "failed to queue iscsi nop-out pdu"); iscsi_free_pdu(iscsi, pdu); return -1; } iscsi->nops_in_flight++; ISCSI_LOG(iscsi, (iscsi->nops_in_flight > 1) ? 1 : 6, "NOP Out Send (nops_in_flight: %d, pdu->cmdsn %08x, pdu->itt %08x, pdu->ttt %08x, iscsi->maxcmdsn %08x, iscsi->expcmdsn %08x)", iscsi->nops_in_flight, pdu->cmdsn, pdu->itt, 0xffffffff, iscsi->maxcmdsn, iscsi->expcmdsn); return 0; } int iscsi_send_target_nop_out(struct iscsi_context *iscsi, uint32_t ttt, uint32_t lun) { struct iscsi_pdu *pdu; pdu = iscsi_allocate_pdu(iscsi, ISCSI_PDU_NOP_OUT, ISCSI_PDU_NO_PDU, 0xffffffff, ISCSI_PDU_DROP_ON_RECONNECT|ISCSI_PDU_DELETE_WHEN_SENT); if (pdu == NULL) { iscsi_set_error(iscsi, "Failed to allocate nop-out pdu"); return -1; } /* immediate flag */ iscsi_pdu_set_immediate(pdu); /* flags */ iscsi_pdu_set_pduflags(pdu, 0x80); /* ttt */ iscsi_pdu_set_ttt(pdu, ttt); /* lun */ iscsi_pdu_set_lun(pdu, lun); /* cmdsn is not increased if Immediate delivery*/ iscsi_pdu_set_cmdsn(pdu, iscsi->cmdsn); if (iscsi_queue_pdu(iscsi, pdu) != 0) { iscsi_set_error(iscsi, "failed to queue iscsi nop-out pdu"); iscsi_free_pdu(iscsi, pdu); return -1; } ISCSI_LOG(iscsi, (iscsi->nops_in_flight > 1) ? 1 : 6, "NOP Out Send (nops_in_flight: %d, pdu->cmdsn %08x, pdu->itt %08x, pdu->ttt %08x, pdu->lun %8x, iscsi->maxcmdsn %08x, iscsi->expcmdsn %08x)", iscsi->nops_in_flight, pdu->cmdsn, 0xffffffff, ttt, lun, iscsi->maxcmdsn, iscsi->expcmdsn); return 0; } int iscsi_process_nop_out_reply(struct iscsi_context *iscsi, struct iscsi_pdu *pdu, struct iscsi_in_pdu *in) { struct iscsi_data data; ISCSI_LOG(iscsi, (iscsi->nops_in_flight > 1) ? 1 : 6, "NOP-In received (pdu->itt %08x, pdu->ttt %08x, iscsi->maxcmdsn %08x, iscsi->expcmdsn %08x, iscsi->statsn %08x)", pdu->itt, 0xffffffff, iscsi->maxcmdsn, iscsi->expcmdsn, iscsi->statsn); if (iscsi->waitpdu->cmdsn == iscsi->min_cmdsn_waiting) { ISCSI_LOG(iscsi, 2, "Oldest element in waitqueue is unchanged since last NOP-In (iscsi->min_cmdsn_waiting %08x)", iscsi->min_cmdsn_waiting); if (getenv("LIBISCSI_IGNORE_NOP_OUT_ON_STUCK_WAITPDU_QUEUE") == NULL) { iscsi->nops_in_flight = 0; } } else { iscsi->nops_in_flight = 0; } iscsi->min_cmdsn_waiting = iscsi->waitpdu->cmdsn; if (pdu->callback == NULL) { return 0; } data.data = NULL; data.size = 0; if (in->data_pos > ISCSI_HEADER_SIZE) { data.data = in->data; data.size = in->data_pos; } pdu->callback(iscsi, SCSI_STATUS_GOOD, &data, pdu->private_data); return 0; } int iscsi_get_nops_in_flight(struct iscsi_context *iscsi) { return iscsi->nops_in_flight; } libiscsi-1.17.0/lib/pdu.c000066400000000000000000000454151271371262700151240ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2010 by Ronnie Sahlberg This program 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 program is distributed in the hope that 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 program; if not, see . */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #ifdef HAVE_SYS_TYPES_H #include #endif #ifdef HAVE_ARPA_INET_H #include #endif #if defined(WIN32) #include #else #include #endif #include #include #include #include "iscsi.h" #include "iscsi-private.h" #include "scsi-lowlevel.h" #include "slist.h" /* This adds 32-bit serial comparision as defined in RFC1982. * It returns 0 for equality, 1 if s1 is greater than s2 and * -1 if s1 is less than s2. According to RFC1982 section 3.2 * there are rare cases where the result of the comparision is * undefined e.g. when s1 = 0 and s2=2^31. This cases should * not happen in iSCSI protocol. */ int iscsi_serial32_compare(uint32_t s1, uint32_t s2) { if (s1 == s2) return 0; if (s1 < s2 && s2-s1 < (uint32_t)1<<31) return -1; if (s1 > s2 && s1-s2 < (uint32_t)1<<31) return 1; if (s1 > s2 && s1-s2 > (uint32_t)1<<31) return -1; if (s1 < s2 && s2-s1 > (uint32_t)1<<31) return 1; /* undefined result */ return -1; } uint32_t iscsi_itt_post_increment(struct iscsi_context *iscsi) { uint32_t old_itt = iscsi->itt; iscsi->itt++; /* 0xffffffff is a reserved value */ if (iscsi->itt == 0xffffffff) { iscsi->itt = 0; } return old_itt; } void iscsi_dump_pdu_header(struct iscsi_context *iscsi, unsigned char *data) { char dump[ISCSI_RAW_HEADER_SIZE*3+1]={0}; int i; for (i=0;ioutdata.size = ISCSI_HEADER_SIZE; pdu->outdata.data = iscsi_szmalloc(iscsi, pdu->outdata.size); if (pdu->outdata.data == NULL) { iscsi_set_error(iscsi, "failed to allocate pdu header"); iscsi_free(iscsi, pdu); return NULL; } /* opcode */ pdu->outdata.data[0] = opcode; pdu->response_opcode = response_opcode; /* isid */ if (opcode == ISCSI_PDU_LOGIN_REQUEST) { memcpy(&pdu->outdata.data[8], &iscsi->isid[0], 6); } /* itt */ iscsi_pdu_set_itt(pdu, itt); pdu->itt = itt; /* flags */ pdu->flags = flags; return pdu; } void iscsi_free_pdu(struct iscsi_context *iscsi, struct iscsi_pdu *pdu) { if (pdu == NULL) { iscsi_set_error(iscsi, "trying to free NULL pdu"); return; } if (pdu->outdata.size <= iscsi->smalloc_size) { iscsi_sfree(iscsi, pdu->outdata.data); } else { iscsi_free(iscsi, pdu->outdata.data); } pdu->outdata.data = NULL; if (pdu->indata.size <= iscsi->smalloc_size) { iscsi_sfree(iscsi, pdu->indata.data); } else { iscsi_free(iscsi, pdu->indata.data); } pdu->indata.data = NULL; if (iscsi->outqueue_current == pdu) { iscsi->outqueue_current = NULL; } iscsi_sfree(iscsi, pdu); } int iscsi_add_data(struct iscsi_context *iscsi, struct iscsi_data *data, unsigned char *dptr, int dsize, int pdualignment) { size_t len, aligned; if (dsize == 0) { iscsi_set_error(iscsi, "Trying to append zero size data to " "iscsi_data"); return -1; } len = data->size + dsize; aligned = len; if (pdualignment) { aligned = (aligned+3)&0xfffffffc; } if (data->size == 0) { if (aligned <= iscsi->smalloc_size) { data->data = iscsi_szmalloc(iscsi, aligned); } else { data->data = iscsi_malloc(iscsi, aligned); } } else { if (aligned > iscsi->smalloc_size) { data->data = iscsi_realloc(iscsi, data->data, aligned); } } if (data->data == NULL) { iscsi_set_error(iscsi, "failed to allocate buffer for %d " "bytes", (int) len); return -1; } memcpy(data->data + data->size, dptr, dsize); data->size += dsize; if (len != aligned) { /* zero out any padding at the end */ memset(data->data + len, 0, aligned - len); } return 0; } int iscsi_pdu_add_data(struct iscsi_context *iscsi, struct iscsi_pdu *pdu, unsigned char *dptr, int dsize) { if (pdu == NULL) { iscsi_set_error(iscsi, "trying to add data to NULL pdu"); return -1; } if (dsize == 0) { iscsi_set_error(iscsi, "Trying to append zero size data to " "pdu"); return -1; } if (iscsi_add_data(iscsi, &pdu->outdata, dptr, dsize, 1) != 0) { iscsi_set_error(iscsi, "failed to add data to pdu buffer"); return -1; } /* update data segment length */ scsi_set_uint32(&pdu->outdata.data[4], pdu->outdata.size - ISCSI_HEADER_SIZE); return 0; } int iscsi_get_pdu_data_size(const unsigned char *hdr) { int size; size = scsi_get_uint32(&hdr[4]) & 0x00ffffff; return size; } int iscsi_get_pdu_padding_size(const unsigned char *hdr) { int data_size, padded_size; data_size = scsi_get_uint32(&hdr[4]) & 0x00ffffff; padded_size = (data_size+3) & 0xfffffffc; return padded_size - data_size; } enum iscsi_reject_reason { ISCSI_REJECT_RESERVED = 0x01, ISCSI_REJECT_DATA_DIGEST_ERROR = 0x02, ISCSI_REJECT_SNACK_REJECT = 0x03, ISCSI_REJECT_PROTOCOL_ERROR = 0x04, ISCSI_REJECT_COMMAND_NOT_SUPPORTED = 0x05, ISCSI_REJECT_IMMEDIATE_COMMAND_REJECT = 0x06, ISCSI_REJECT_TASK_IN_PROCESS = 0x07, ISCSI_REJECT_INVALID_DATA_ACK = 0x08, ISCSI_REJECT_INVALID_PDU_FIELD = 0x09, ISCSI_REJECT_LONG_OPERATION_REJECT = 0x0a, ISCSI_REJECT_NEGOTIATION_RESET = 0x0b, ISCSI_REJECT_WAITING_FOR_LOGOUT = 0x0c }; static const char *iscsi_reject_reason_str(enum iscsi_reject_reason reason) { switch (reason) { case ISCSI_REJECT_RESERVED: return "Reserved"; case ISCSI_REJECT_DATA_DIGEST_ERROR: return "Data Digest Error"; case ISCSI_REJECT_SNACK_REJECT: return "SNACK Reject"; case ISCSI_REJECT_PROTOCOL_ERROR: return "Protocol Error"; case ISCSI_REJECT_COMMAND_NOT_SUPPORTED: return "Command Not Supported"; case ISCSI_REJECT_IMMEDIATE_COMMAND_REJECT: return "Immediate Command Reject"; case ISCSI_REJECT_TASK_IN_PROCESS: return "Task In Process"; case ISCSI_REJECT_INVALID_DATA_ACK: return "Invalid Data ACK"; case ISCSI_REJECT_INVALID_PDU_FIELD: return "Invalid PDU Field"; case ISCSI_REJECT_LONG_OPERATION_REJECT: return "Long Operation Reject"; case ISCSI_REJECT_NEGOTIATION_RESET: return "Negotiation Reset"; case ISCSI_REJECT_WAITING_FOR_LOGOUT: return "Waiting For Logout"; } return "Unknown"; } int iscsi_process_target_nop_in(struct iscsi_context *iscsi, struct iscsi_in_pdu *in) { uint32_t ttt = scsi_get_uint32(&in->hdr[20]); uint32_t itt = scsi_get_uint32(&in->hdr[16]); uint32_t lun = scsi_get_uint16(&in->hdr[8]); ISCSI_LOG(iscsi, (iscsi->nops_in_flight > 1) ? 1 : 6, "NOP-In received (pdu->itt %08x, pdu->ttt %08x, pdu->lun %8x, iscsi->maxcmdsn %08x, iscsi->expcmdsn %08x, iscsi->statsn %08x)", itt, ttt, lun, iscsi->maxcmdsn, iscsi->expcmdsn, iscsi->statsn); /* if the server does not want a response */ if (ttt == 0xffffffff) { return 0; } iscsi_send_target_nop_out(iscsi, ttt, lun); return 0; } static void iscsi_reconnect_after_logout(struct iscsi_context *iscsi, int status, void *command_data _U_, void *opaque _U_) { if (status) { ISCSI_LOG(iscsi, 1, "logout failed: %s", iscsi_get_error(iscsi)); } iscsi->pending_reconnect = 1; } int iscsi_process_reject(struct iscsi_context *iscsi, struct iscsi_in_pdu *in) { int size = in->data_pos; uint32_t itt; struct iscsi_pdu *pdu; uint8_t reason = in->hdr[2]; if (size < ISCSI_RAW_HEADER_SIZE) { iscsi_set_error(iscsi, "size of REJECT payload is too small." "Need >= %d bytes but got %d.", ISCSI_RAW_HEADER_SIZE, (int)size); return -1; } if (reason == ISCSI_REJECT_WAITING_FOR_LOGOUT) { ISCSI_LOG(iscsi, 1, "target rejects request with reason: %s", iscsi_reject_reason_str(reason)); iscsi_logout_async(iscsi, iscsi_reconnect_after_logout, NULL); return 0; } iscsi_set_error(iscsi, "Request was rejected with reason: 0x%02x (%s)", reason, iscsi_reject_reason_str(reason)); itt = scsi_get_uint32(&in->data[16]); iscsi_dump_pdu_header(iscsi, in->data); for (pdu = iscsi->waitpdu; pdu; pdu = pdu->next) { if (pdu->itt == itt) { break; } } if (pdu == NULL) { iscsi_set_error(iscsi, "Can not match REJECT with" "any outstanding pdu with itt:0x%08x", itt); return -1; } if (pdu->callback) { pdu->callback(iscsi, SCSI_STATUS_ERROR, NULL, pdu->private_data); } ISCSI_LIST_REMOVE(&iscsi->waitpdu, pdu); iscsi_free_pdu(iscsi, pdu); return 0; } static void iscsi_process_pdu_serials(struct iscsi_context *iscsi, struct iscsi_in_pdu *in) { uint32_t itt = scsi_get_uint32(&in->hdr[16]); uint32_t statsn = scsi_get_uint32(&in->hdr[24]); uint32_t maxcmdsn = scsi_get_uint32(&in->hdr[32]); uint32_t expcmdsn = scsi_get_uint32(&in->hdr[28]); uint16_t status = scsi_get_uint16(&in->hdr[36]); uint8_t flags = in->hdr[1]; enum iscsi_opcode opcode = in->hdr[0] & 0x3f; /* RFC3720 10.13.5 (serials are invalid if status class != 0) */ if (opcode == ISCSI_PDU_LOGIN_RESPONSE && (status >> 8)) { return; } if (iscsi_serial32_compare(maxcmdsn, iscsi->maxcmdsn) > 0) { iscsi->maxcmdsn = maxcmdsn; } if (iscsi_serial32_compare(expcmdsn, iscsi->expcmdsn) > 0) { iscsi->expcmdsn = expcmdsn; } /* RFC3720 10.7.3 (StatSN is invalid if S bit unset in flags) */ if (opcode == ISCSI_PDU_DATA_IN && !(flags & ISCSI_PDU_DATA_CONTAINS_STATUS)) { return; } if (itt == 0xffffffff) { /* target will not increase statsn if itt == 0xffffffff */ statsn--; } if (iscsi_serial32_compare(statsn, iscsi->statsn) > 0) { iscsi->statsn = statsn; } } int iscsi_process_pdu(struct iscsi_context *iscsi, struct iscsi_in_pdu *in) { uint32_t itt = scsi_get_uint32(&in->hdr[16]); enum iscsi_opcode opcode = in->hdr[0] & 0x3f; uint8_t ahslen = in->hdr[4]; struct iscsi_pdu *pdu; if (ahslen != 0) { iscsi_set_error(iscsi, "cant handle expanded headers yet"); return -1; } /* All target PDUs update the serials */ iscsi_process_pdu_serials(iscsi, in); if (opcode == ISCSI_PDU_ASYNC_MSG) { uint8_t event = in->hdr[36]; uint16_t param1 = scsi_get_uint16(&in->hdr[38]); uint16_t param2 = scsi_get_uint16(&in->hdr[40]); uint16_t param3 = scsi_get_uint16(&in->hdr[42]); switch (event) { case 0x0: /* Just ignore these ones for now. It could be * a UNIT_ATTENTION for some changes on the * target but we don't have an API to pass this on * to the application yet. */ ISCSI_LOG(iscsi, 2, "Ignoring received iSCSI AsyncMsg/" "SCSI Async Event"); return 0; case 0x1: ISCSI_LOG(iscsi, 2, "target requests logout within %u seconds", param3); /* this is an ugly workaround for DELL Equallogic FW 7.x bugs: * Bug_71409 - I/O errors during volume move (present before 7.0.7) * Bug_73732 - I/O errors during volume move operation (still present in 7.0.9) */ if (getenv("LIBISCSI_DROP_CONN_ON_ASYNC_EVENT1") != NULL) { ISCSI_LOG(iscsi, 2, "dropping connection to fix errors with broken DELL Equallogic firmware 7.x"); return -1; } iscsi_logout_async(iscsi, iscsi_reconnect_after_logout, NULL); return 0; case 0x2: ISCSI_LOG(iscsi, 2, "target will drop this connection. Time2Wait is %u seconds", param2); iscsi->next_reconnect = time(NULL) + param2; return 0; case 0x3: ISCSI_LOG(iscsi, 2, "target will drop all connections of this session. Time2Wait is %u seconds", param2); iscsi->next_reconnect = time(NULL) + param2; return 0; case 0x4: ISCSI_LOG(iscsi, 2, "target requests parameter renogitiation."); iscsi_logout_async(iscsi, iscsi_reconnect_after_logout, NULL); return 0; default: ISCSI_LOG(iscsi, 1, "unhandled async event %u: param1 %u param2 %u param3 %u", event, param1, param2, param3); return -1; } } if (opcode == ISCSI_PDU_REJECT) { return iscsi_process_reject(iscsi, in); } if (opcode == ISCSI_PDU_NOP_IN && itt == 0xffffffff) { if (iscsi_process_target_nop_in(iscsi, in) != 0) { return -1; } return 0; } for (pdu = iscsi->waitpdu; pdu; pdu = pdu->next) { enum iscsi_opcode expected_response = pdu->response_opcode; int is_finished = 1; if (pdu->itt != itt) { continue; } /* we have a special case with scsi-command opcodes, * they are replied to by either a scsi-response * or a data-in, or a combination of both. */ if (opcode == ISCSI_PDU_DATA_IN && expected_response == ISCSI_PDU_SCSI_RESPONSE) { expected_response = ISCSI_PDU_DATA_IN; } /* Another special case is if we get a R2T. * In this case we should find the original request and just send an additional * DATAOUT segment for this task. */ if (opcode == ISCSI_PDU_R2T) { expected_response = ISCSI_PDU_R2T; } if (opcode != expected_response) { iscsi_set_error(iscsi, "Got wrong opcode back for " "itt:%d got:%d expected %d", itt, opcode, pdu->response_opcode); return -1; } switch (opcode) { case ISCSI_PDU_LOGIN_RESPONSE: if (iscsi_process_login_reply(iscsi, pdu, in) != 0) { ISCSI_LIST_REMOVE(&iscsi->waitpdu, pdu); iscsi_free_pdu(iscsi, pdu); iscsi_set_error(iscsi, "iscsi login reply " "failed"); return -1; } break; case ISCSI_PDU_TEXT_RESPONSE: if (iscsi_process_text_reply(iscsi, pdu, in) != 0) { ISCSI_LIST_REMOVE(&iscsi->waitpdu, pdu); iscsi_free_pdu(iscsi, pdu); iscsi_set_error(iscsi, "iscsi text reply " "failed"); return -1; } break; case ISCSI_PDU_LOGOUT_RESPONSE: if (iscsi_process_logout_reply(iscsi, pdu, in) != 0) { ISCSI_LIST_REMOVE(&iscsi->waitpdu, pdu); iscsi_free_pdu(iscsi, pdu); iscsi_set_error(iscsi, "iscsi logout reply " "failed"); return -1; } break; case ISCSI_PDU_SCSI_RESPONSE: if (iscsi_process_scsi_reply(iscsi, pdu, in) != 0) { ISCSI_LIST_REMOVE(&iscsi->waitpdu, pdu); iscsi_free_pdu(iscsi, pdu); iscsi_set_error(iscsi, "iscsi response reply " "failed"); return -1; } break; case ISCSI_PDU_DATA_IN: if (iscsi_process_scsi_data_in(iscsi, pdu, in, &is_finished) != 0) { ISCSI_LIST_REMOVE(&iscsi->waitpdu, pdu); iscsi_free_pdu(iscsi, pdu); iscsi_set_error(iscsi, "iscsi data in " "failed"); return -1; } break; case ISCSI_PDU_NOP_IN: if (iscsi_process_nop_out_reply(iscsi, pdu, in) != 0) { ISCSI_LIST_REMOVE(&iscsi->waitpdu, pdu); iscsi_free_pdu(iscsi, pdu); iscsi_set_error(iscsi, "iscsi nop-in failed"); return -1; } break; case ISCSI_PDU_SCSI_TASK_MANAGEMENT_RESPONSE: if (iscsi_process_task_mgmt_reply(iscsi, pdu, in) != 0) { ISCSI_LIST_REMOVE(&iscsi->waitpdu, pdu); iscsi_free_pdu(iscsi, pdu); iscsi_set_error(iscsi, "iscsi task-mgmt failed"); return -1; } break; case ISCSI_PDU_R2T: if (iscsi_process_r2t(iscsi, pdu, in) != 0) { ISCSI_LIST_REMOVE(&iscsi->waitpdu, pdu); iscsi_free_pdu(iscsi, pdu); iscsi_set_error(iscsi, "iscsi r2t " "failed"); return -1; } is_finished = 0; break; default: iscsi_set_error(iscsi, "Don't know how to handle " "opcode 0x%02x", opcode); return -1; } if (is_finished) { ISCSI_LIST_REMOVE(&iscsi->waitpdu, pdu); iscsi_free_pdu(iscsi, pdu); } return 0; } return 0; } void iscsi_pdu_set_itt(struct iscsi_pdu *pdu, uint32_t itt) { scsi_set_uint32(&pdu->outdata.data[16], itt); } void iscsi_pdu_set_ritt(struct iscsi_pdu *pdu, uint32_t ritt) { scsi_set_uint32(&pdu->outdata.data[20], ritt); } void iscsi_pdu_set_pduflags(struct iscsi_pdu *pdu, unsigned char flags) { pdu->outdata.data[1] = flags; } void iscsi_pdu_set_immediate(struct iscsi_pdu *pdu) { pdu->outdata.data[0] |= ISCSI_PDU_IMMEDIATE; } void iscsi_pdu_set_ttt(struct iscsi_pdu *pdu, uint32_t ttt) { scsi_set_uint32(&pdu->outdata.data[20], ttt); } void iscsi_pdu_set_cmdsn(struct iscsi_pdu *pdu, uint32_t cmdsn) { scsi_set_uint32(&pdu->outdata.data[24], cmdsn); pdu->cmdsn = cmdsn; } void iscsi_pdu_set_rcmdsn(struct iscsi_pdu *pdu, uint32_t rcmdsn) { scsi_set_uint32(&pdu->outdata.data[32], rcmdsn); } void iscsi_pdu_set_datasn(struct iscsi_pdu *pdu, uint32_t datasn) { scsi_set_uint32(&pdu->outdata.data[36], datasn); } void iscsi_pdu_set_expstatsn(struct iscsi_pdu *pdu, uint32_t expstatsnsn) { scsi_set_uint32(&pdu->outdata.data[28], expstatsnsn); } void iscsi_pdu_set_bufferoffset(struct iscsi_pdu *pdu, uint32_t bufferoffset) { scsi_set_uint32(&pdu->outdata.data[40], bufferoffset); } void iscsi_pdu_set_cdb(struct iscsi_pdu *pdu, struct scsi_task *task) { memset(&pdu->outdata.data[32], 0, 16); memcpy(&pdu->outdata.data[32], task->cdb, task->cdb_size); } void iscsi_pdu_set_lun(struct iscsi_pdu *pdu, uint32_t lun) { scsi_set_uint16(&pdu->outdata.data[8], lun); } void iscsi_pdu_set_expxferlen(struct iscsi_pdu *pdu, uint32_t expxferlen) { pdu->expxferlen = expxferlen; scsi_set_uint32(&pdu->outdata.data[20], expxferlen); } void iscsi_timeout_scan(struct iscsi_context *iscsi) { struct iscsi_pdu *pdu; struct iscsi_pdu *next_pdu; time_t t = time(NULL); for (pdu = iscsi->outqueue; pdu; pdu = next_pdu) { next_pdu = pdu->next; if (pdu->scsi_timeout == 0) { /* no timeout for this pdu */ continue; } if (t < pdu->scsi_timeout) { /* not expired yet */ continue; } ISCSI_LIST_REMOVE(&iscsi->outqueue, pdu); iscsi_set_error(iscsi, "command timed out"); iscsi_dump_pdu_header(iscsi, pdu->outdata.data); if (pdu->callback) { pdu->callback(iscsi, SCSI_STATUS_TIMEOUT, NULL, pdu->private_data); } iscsi_free_pdu(iscsi, pdu); } for (pdu = iscsi->waitpdu; pdu; pdu = next_pdu) { next_pdu = pdu->next; if (pdu->scsi_timeout == 0) { /* no timeout for this pdu */ continue; } if (t < pdu->scsi_timeout) { /* not expired yet */ continue; } ISCSI_LIST_REMOVE(&iscsi->waitpdu, pdu); iscsi_set_error(iscsi, "command timed out"); iscsi_dump_pdu_header(iscsi, pdu->outdata.data); if (pdu->callback) { pdu->callback(iscsi, SCSI_STATUS_TIMEOUT, NULL, pdu->private_data); } iscsi_free_pdu(iscsi, pdu); } } libiscsi-1.17.0/lib/scsi-lowlevel.c000066400000000000000000003113211271371262700171140ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2010 by Ronnie Sahlberg This program 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 program is distributed in the hope that 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 program; if not, see . */ /* * would be nice if this could grow into a full blown library to * 1, build and unmarshall a CDB * 2, check how big a complete data-in structure needs to be * 3, unmarshall data-in into a real structure * 4, marshall a real structure into a data-out blob */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #ifdef HAVE_SYS_TYPES_H #include #endif #ifdef HAVE_ARPA_INET_H #include #endif #ifdef AROS #include "aros/aros_compat.h" #endif #if defined(WIN32) #include #include "win32/win32_compat.h" #else #include #endif #include #include #include #include #include #include #include #include "slist.h" #include "scsi-lowlevel.h" void scsi_task_set_iov_out(struct scsi_task *task, struct scsi_iovec *iov, int niov); struct scsi_allocated_memory { struct scsi_allocated_memory *next; char buf[0]; }; void scsi_free_scsi_task(struct scsi_task *task) { struct scsi_allocated_memory *mem; if (!task) return; while ((mem = task->mem)) { ISCSI_LIST_REMOVE(&task->mem, mem); free(mem); } free(task->datain.data); free(task); } struct scsi_task * scsi_create_task(int cdb_size, unsigned char *cdb, int xfer_dir, int expxferlen) { struct scsi_task *task; task = malloc(sizeof(struct scsi_task)); if (task == NULL) { return NULL; } memset(task, 0, sizeof(struct scsi_task)); memcpy(&task->cdb[0], cdb, cdb_size); task->cdb_size = cdb_size; task->xfer_dir = xfer_dir; task->expxferlen = expxferlen; return task; } void * scsi_malloc(struct scsi_task *task, size_t size) { struct scsi_allocated_memory *mem; mem = malloc(sizeof(struct scsi_allocated_memory) + size); if (mem == NULL) { return NULL; } memset(mem, 0, sizeof(struct scsi_allocated_memory) + size); ISCSI_LIST_ADD(&task->mem, mem); return &mem->buf[0]; } struct value_string { int value; const char *string; }; static const char * value_string_find(struct value_string *values, int value) { for (; values->string; values++) { if (value == values->value) { return values->string; } } return NULL; } const char * scsi_sense_key_str(int key) { struct value_string keys[] = { {SCSI_SENSE_NO_SENSE, "NO SENSE"}, {SCSI_SENSE_RECOVERED_ERROR, "RECOVERED ERROR"}, {SCSI_SENSE_NOT_READY, "NOT READY"}, {SCSI_SENSE_HARDWARE_ERROR, "HARDWARE_ERROR"}, {SCSI_SENSE_ILLEGAL_REQUEST, "ILLEGAL_REQUEST"}, {SCSI_SENSE_UNIT_ATTENTION, "UNIT_ATTENTION"}, {SCSI_SENSE_DATA_PROTECTION, "DATA PROTECTION"}, {SCSI_SENSE_BLANK_CHECK, "BLANK CHECK"}, {SCSI_SENSE_VENDOR_SPECIFIC, "VENDOR SPECIFIC"}, {SCSI_SENSE_COPY_ABORTED, "COPY ABORTED"}, {SCSI_SENSE_COMMAND_ABORTED, "COMMAND ABORTED"}, {SCSI_SENSE_OBSOLETE_ERROR_CODE, "OBSOLETE_ERROR_CODE"}, {SCSI_SENSE_OVERFLOW_COMMAND, "OVERFLOW_COMMAND"}, {SCSI_SENSE_MISCOMPARE, "MISCOMPARE"}, {0, NULL} }; return value_string_find(keys, key); } const char * scsi_sense_ascq_str(int ascq) { struct value_string ascqs[] = { {SCSI_SENSE_ASCQ_SANITIZE_IN_PROGRESS, "SANITIZE_IN_PROGRESS"}, {SCSI_SENSE_ASCQ_WRITE_AFTER_SANITIZE_REQUIRED, "WRITE_AFTER_SANITIZE_REQUIRED"}, {SCSI_SENSE_ASCQ_INVALID_OPERATION_CODE, "INVALID_OPERATION_CODE"}, {SCSI_SENSE_ASCQ_LBA_OUT_OF_RANGE, "LBA_OUT_OF_RANGE"}, {SCSI_SENSE_ASCQ_INVALID_FIELD_IN_CDB, "INVALID_FIELD_IN_CDB"}, {SCSI_SENSE_ASCQ_LOGICAL_UNIT_NOT_SUPPORTED, "LOGICAL_UNIT_NOT_SUPPORTED"}, {SCSI_SENSE_ASCQ_PARAMETER_LIST_LENGTH_ERROR, "PARAMETER_LIST_LENGTH_ERROR"}, {SCSI_SENSE_ASCQ_INVALID_FIELD_IN_PARAMETER_LIST, "INVALID_FIELD_IN_PARAMETER_LIST"}, {SCSI_SENSE_ASCQ_WRITE_PROTECTED, "WRITE_PROTECTED"}, {SCSI_SENSE_ASCQ_WRITE_PROTECTED, "WRITE_PROTECTED"}, {SCSI_SENSE_ASCQ_HARDWARE_WRITE_PROTECTED, "HARDWARE_WRITE_PROTECTED"}, {SCSI_SENSE_ASCQ_SOFTWARE_WRITE_PROTECTED, "SOFTWARE_WRITE_PROTECTED"}, {SCSI_SENSE_ASCQ_MEDIUM_NOT_PRESENT, "MEDIUM_NOT_PRESENT"}, {SCSI_SENSE_ASCQ_MEDIUM_NOT_PRESENT_TRAY_CLOSED, "MEDIUM_NOT_PRESENT-TRAY_CLOSED"}, {SCSI_SENSE_ASCQ_MEDIUM_NOT_PRESENT_TRAY_OPEN, "MEDIUM_NOT_PRESENT-TRAY_OPEN"}, {SCSI_SENSE_ASCQ_BUS_RESET, "BUS_RESET"}, {SCSI_SENSE_ASCQ_POWER_ON_OCCURED, "POWER_ON_OCCURED"}, {SCSI_SENSE_ASCQ_SCSI_BUS_RESET_OCCURED, "SCSI_BUS_RESET_OCCURED"}, {SCSI_SENSE_ASCQ_BUS_DEVICE_RESET_FUNCTION_OCCURED, "BUS_DEVICE_RESET_FUNCTION_OCCURED"}, {SCSI_SENSE_ASCQ_DEVICE_INTERNAL_RESET, "DEVICE_INTERNAL_RESET"}, {SCSI_SENSE_ASCQ_TRANSCEIVER_MODE_CHANGED_TO_SINGLE_ENDED, "TRANSCEIVER_MODE_CHANGED_TO_SINGLE_ENDED"}, {SCSI_SENSE_ASCQ_TRANSCEIVER_MODE_CHANGED_TO_LVD, "TRANSCEIVER_MODE_CHANGED_TO_LVD"}, {SCSI_SENSE_ASCQ_MODE_PARAMETERS_CHANGED, "MODE PARAMETERS CHANGED"}, {SCSI_SENSE_ASCQ_CAPACITY_DATA_HAS_CHANGED, "CAPACITY_DATA_HAS_CHANGED"}, {SCSI_SENSE_ASCQ_THIN_PROVISION_SOFT_THRES_REACHED, "THIN PROVISIONING SOFT THRESHOLD REACHED"}, {SCSI_SENSE_ASCQ_INQUIRY_DATA_HAS_CHANGED, "INQUIRY DATA HAS CHANGED"}, {SCSI_SENSE_ASCQ_INTERNAL_TARGET_FAILURE, "INTERNAL_TARGET_FAILURE"}, {SCSI_SENSE_ASCQ_MISCOMPARE_DURING_VERIFY, "MISCOMPARE_DURING_VERIFY"}, {SCSI_SENSE_ASCQ_MISCOMPARE_VERIFY_OF_UNMAPPED_LBA, "MISCOMPARE_VERIFY_OF_UNMAPPED_LBA"}, { SCSI_SENSE_ASCQ_MEDIUM_LOAD_OR_EJECT_FAILED, "MEDIUM_LOAD_OR_EJECT_FAILED" }, {SCSI_SENSE_ASCQ_MEDIUM_REMOVAL_PREVENTED, "SCSI_SENSE_ASCQ_MEDIUM_REMOVAL_PREVENTED"}, {0, NULL} }; return value_string_find(ascqs, ascq); } const char * scsi_pr_type_str(enum scsi_persistent_out_type pr_type) { struct value_string pr_type_strings[] = { {SCSI_PERSISTENT_RESERVE_TYPE_WRITE_EXCLUSIVE, "Write Exclusive"}, {SCSI_PERSISTENT_RESERVE_TYPE_EXCLUSIVE_ACCESS, "Exclusive Access"}, {SCSI_PERSISTENT_RESERVE_TYPE_WRITE_EXCLUSIVE_REGISTRANTS_ONLY, "Write Exclusive, Registrants Only"}, {SCSI_PERSISTENT_RESERVE_TYPE_EXCLUSIVE_ACCESS_REGISTRANTS_ONLY, "Exclusive Access Registrants Only"}, {SCSI_PERSISTENT_RESERVE_TYPE_WRITE_EXCLUSIVE_ALL_REGISTRANTS, "Write Exclusive, All Registrants"}, {SCSI_PERSISTENT_RESERVE_TYPE_EXCLUSIVE_ACCESS_ALL_REGISTRANTS, "Exclusive Access, All Registrants"}, {0, NULL} }; return value_string_find(pr_type_strings, pr_type); } uint64_t scsi_get_uint64(const unsigned char *c) { uint64_t val; val = scsi_get_uint32(c); val <<= 32; c += 4; val |= scsi_get_uint32(c); return val; } uint32_t scsi_get_uint32(const unsigned char *c) { uint32_t val; val = c[0]; val = (val << 8) | c[1]; val = (val << 8) | c[2]; val = (val << 8) | c[3]; return val; } uint16_t scsi_get_uint16(const unsigned char *c) { uint16_t val; val = c[0]; val = (val << 8) | c[1]; return val; } static inline uint64_t task_get_uint64(struct scsi_task *task, int offset) { if (offset <= task->datain.size - 8) { const unsigned char *c = &task->datain.data[offset]; return scsi_get_uint64(c); } else { return 0; } } static inline uint32_t task_get_uint32(struct scsi_task *task, int offset) { if (offset <= task->datain.size - 4) { const unsigned char *c = &task->datain.data[offset]; return scsi_get_uint32(c); } else { return 0; } } static inline uint16_t task_get_uint16(struct scsi_task *task, int offset) { if (offset <= task->datain.size - 2) { const unsigned char *c = &task->datain.data[offset]; return scsi_get_uint16(c); } else { return 0; } } static inline uint8_t task_get_uint8(struct scsi_task *task, int offset) { if (offset <= task->datain.size - 1) { return task->datain.data[offset]; } else { return 0; } } void scsi_set_uint64(unsigned char *c, uint64_t v) { uint32_t val; val = (v >> 32) & 0xffffffff; scsi_set_uint32(c, val); c += 4; val = v & 0xffffffff; scsi_set_uint32(c, val); } void scsi_set_uint32(unsigned char *c, uint32_t val) { c[0] = val >> 24; c[1] = val >> 16; c[2] = val >> 8; c[3] = val; } void scsi_set_uint16(unsigned char *c, uint16_t val) { c[0] = val >> 8; c[1] = val; } /* * TESTUNITREADY */ struct scsi_task * scsi_cdb_testunitready(void) { struct scsi_task *task; task = malloc(sizeof(struct scsi_task)); if (task == NULL) { return NULL; } memset(task, 0, sizeof(struct scsi_task)); task->cdb[0] = SCSI_OPCODE_TESTUNITREADY; task->cdb_size = 6; task->xfer_dir = SCSI_XFER_NONE; task->expxferlen = 0; return task; } /* * SANITIZE */ struct scsi_task * scsi_cdb_sanitize(int immed, int ause, int sa, int param_len) { struct scsi_task *task; task = malloc(sizeof(struct scsi_task)); if (task == NULL) { return NULL; } memset(task, 0, sizeof(struct scsi_task)); task->cdb[0] = SCSI_OPCODE_SANITIZE; task->cdb[1] = sa & 0x1f; if (immed) { task->cdb[1] |= 0x80; } if (ause) { task->cdb[1] |= 0x20; } scsi_set_uint16(&task->cdb[7], param_len); task->cdb_size = 10; if (param_len != 0) { task->xfer_dir = SCSI_XFER_WRITE; } else { task->xfer_dir = SCSI_XFER_NONE; } task->expxferlen = (param_len + 3) & 0xfffc; return task; } /* * REPORTLUNS */ struct scsi_task * scsi_reportluns_cdb(int report_type, int alloc_len) { struct scsi_task *task; task = malloc(sizeof(struct scsi_task)); if (task == NULL) { return NULL; } memset(task, 0, sizeof(struct scsi_task)); task->cdb[0] = SCSI_OPCODE_REPORTLUNS; task->cdb[2] = report_type; scsi_set_uint32(&task->cdb[6], alloc_len); task->cdb_size = 12; if (alloc_len != 0) { task->xfer_dir = SCSI_XFER_READ; } else { task->xfer_dir = SCSI_XFER_NONE; } task->expxferlen = alloc_len; return task; } /* * parse the data in blob and calculate the size of a full report luns * datain structure */ static int scsi_reportluns_datain_getfullsize(struct scsi_task *task) { uint32_t list_size; list_size = task_get_uint32(task, 0) + 8; return list_size; } /* * unmarshall the data in blob for reportluns into a structure */ static struct scsi_reportluns_list * scsi_reportluns_datain_unmarshall(struct scsi_task *task) { struct scsi_reportluns_list *list; int list_size; int i, num_luns; if (task->datain.size < 4) { return NULL; } list_size = task_get_uint32(task, 0) + 8; if (list_size < task->datain.size) { return NULL; } num_luns = list_size / 8 - 1; list = scsi_malloc(task, offsetof(struct scsi_reportluns_list, luns) + sizeof(uint16_t) * num_luns); if (list == NULL) { return NULL; } list->num = num_luns; for (i = 0; i < num_luns; i++) { list->luns[i] = task_get_uint16(task, i * 8 + 8); } return list; } /* * READCAPACITY10 */ struct scsi_task * scsi_cdb_readcapacity10(int lba, int pmi) { struct scsi_task *task; task = malloc(sizeof(struct scsi_task)); if (task == NULL) { return NULL; } memset(task, 0, sizeof(struct scsi_task)); task->cdb[0] = SCSI_OPCODE_READCAPACITY10; scsi_set_uint32(&task->cdb[2], lba); if (pmi) { task->cdb[8] |= 0x01; } task->cdb_size = 10; task->xfer_dir = SCSI_XFER_READ; task->expxferlen = 8; return task; } /* * READTOC */ struct scsi_task * scsi_cdb_readtoc(int msf, int format, int track_session, uint16_t alloc_len) { struct scsi_task *task; if (format != SCSI_READ_TOC && format != SCSI_READ_SESSION_INFO && format != SCSI_READ_FULL_TOC){ fprintf(stderr, "Read TOC format %d not fully supported yet\n", format); return NULL; } task = malloc(sizeof(struct scsi_task)); if (task == NULL) { return NULL; } memset(task, 0, sizeof(struct scsi_task)); task->cdb[0] = SCSI_OPCODE_READTOC; if (msf) { task->cdb[1] |= 0x02; } task->cdb[2] = format & 0xf; /* Prevent invalid setting of Track/Session Number */ if (format == SCSI_READ_TOC || format == SCSI_READ_FULL_TOC) { task->cdb[6] = 0xff & track_session; } scsi_set_uint16(&task->cdb[7], alloc_len); task->cdb_size = 10; if (alloc_len != 0) { task->xfer_dir = SCSI_XFER_READ; } else { task->xfer_dir = SCSI_XFER_NONE; } task->expxferlen = alloc_len; return task; } /* * parse the data in blob and calculate the size of a full read TOC * datain structure */ static int scsi_readtoc_datain_getfullsize(struct scsi_task *task) { uint16_t toc_data_len; toc_data_len = task_get_uint16(task, 0) + 2; return toc_data_len; } static inline enum scsi_readtoc_fmt scsi_readtoc_format(const struct scsi_task *task) { return task->cdb[2] & 0xf; } static void scsi_readtoc_desc_unmarshall(struct scsi_task *task, struct scsi_readtoc_list *list, int i) { switch(scsi_readtoc_format(task)) { case SCSI_READ_TOC: list->desc[i].desc.toc.adr = task_get_uint8(task, 4 + 8 * i + 1) & 0xf0; list->desc[i].desc.toc.control = task_get_uint8(task, 4 + 8 * i + 1) & 0x0f; list->desc[i].desc.toc.track = task_get_uint8(task, 4 + 8 * i + 2); list->desc[i].desc.toc.lba = task_get_uint32(task, 4 + 8 * i + 4); break; case SCSI_READ_SESSION_INFO: list->desc[i].desc.ses.adr = task_get_uint8(task, 4 + 8 * i + 1) & 0xf0; list->desc[i].desc.ses.control = task_get_uint8(task, 4 + 8 * i + 1) & 0x0f; list->desc[i].desc.ses.first_in_last = task_get_uint8(task, 4 + 8 * i + 2); list->desc[i].desc.ses.lba = task_get_uint32(task, 4 + 8 * i + 4); break; case SCSI_READ_FULL_TOC: list->desc[i].desc.full.session = task_get_uint8(task, 4 + 11 * i + 0) & 0xf0; list->desc[i].desc.full.adr = task_get_uint8(task, 4 + 11 * i + 1) & 0xf0; list->desc[i].desc.full.control = task_get_uint8(task, 4 + 11 * i + 1) & 0x0f; list->desc[i].desc.full.tno = task_get_uint8(task, 4 + 11 * i + 2); list->desc[i].desc.full.point = task_get_uint8(task, 4 + 11 * i + 3); list->desc[i].desc.full.min = task_get_uint8(task, 4 + 11 * i + 4); list->desc[i].desc.full.sec = task_get_uint8(task, 4 + 11 * i + 5); list->desc[i].desc.full.frame = task_get_uint8(task, 4 + 11 * i + 6); list->desc[i].desc.full.zero = task_get_uint8(task, 4 + 11 * i + 7); list->desc[i].desc.full.pmin = task_get_uint8(task, 4 + 11 * i + 8); list->desc[i].desc.full.psec = task_get_uint8(task, 4 + 11 * i + 9); list->desc[i].desc.full.pframe = task_get_uint8(task, 4 + 11 * i + 10); break; default: break; } } /* * unmarshall the data in blob for read TOC into a structure */ static struct scsi_readtoc_list * scsi_readtoc_datain_unmarshall(struct scsi_task *task) { struct scsi_readtoc_list *list; int data_len; int i, num_desc; if (task->datain.size < 4) { return NULL; } /* Do we have all data? */ data_len = scsi_readtoc_datain_getfullsize(task) - 2; if(task->datain.size < data_len) { return NULL; } /* Remove header size (4) to get bytes in descriptor list */ num_desc = (data_len - 4) / 8; list = scsi_malloc(task, offsetof(struct scsi_readtoc_list, desc) + sizeof(struct scsi_readtoc_desc) * num_desc); if (list == NULL) { return NULL; } list->num = num_desc; list->first = task_get_uint8(task, 2); list->last = task_get_uint8(task, 3); for (i = 0; i < num_desc; i++) { scsi_readtoc_desc_unmarshall(task, list, i); } return list; } /* * RESERVE6 */ struct scsi_task * scsi_cdb_reserve6(void) { struct scsi_task *task; task = malloc(sizeof(struct scsi_task)); if (task == NULL) { return NULL; } memset(task, 0, sizeof(struct scsi_task)); task->cdb[0] = SCSI_OPCODE_RESERVE6; task->cdb_size = 6; task->xfer_dir = SCSI_XFER_NONE; return task; } /* * RELEASE10 */ struct scsi_task * scsi_cdb_release6(void) { struct scsi_task *task; task = malloc(sizeof(struct scsi_task)); if (task == NULL) { return NULL; } memset(task, 0, sizeof(struct scsi_task)); task->cdb[0] = SCSI_OPCODE_RELEASE6; task->cdb_size = 6; task->xfer_dir = SCSI_XFER_NONE; return task; } static inline uint8_t scsi_serviceactionin_sa(const struct scsi_task *task) { return task->cdb[1]; } /* * service_action_in unmarshall */ static void * scsi_serviceactionin_datain_unmarshall(struct scsi_task *task) { switch (scsi_serviceactionin_sa(task)) { case SCSI_READCAPACITY16: { struct scsi_readcapacity16 *rc16 = scsi_malloc(task, sizeof(*rc16)); if (rc16 == NULL) { return NULL; } rc16->returned_lba = task_get_uint32(task, 0); rc16->returned_lba = (rc16->returned_lba << 32) | task_get_uint32(task, 4); rc16->block_length = task_get_uint32(task, 8); rc16->p_type = (task_get_uint8(task, 12) >> 1) & 0x07; rc16->prot_en = task_get_uint8(task, 12) & 0x01; rc16->p_i_exp = (task_get_uint8(task, 13) >> 4) & 0x0f; rc16->lbppbe = task_get_uint8(task, 13) & 0x0f; rc16->lbpme = !!(task_get_uint8(task, 14) & 0x80); rc16->lbprz = !!(task_get_uint8(task, 14) & 0x40); rc16->lalba = task_get_uint16(task, 14) & 0x3fff; return rc16; } case SCSI_GET_LBA_STATUS: { struct scsi_get_lba_status *gls = scsi_malloc(task, sizeof(*gls)); int32_t len = task_get_uint32(task, 0); int i; if (gls == NULL) { return NULL; } if (len > task->datain.size - 4) { len = task->datain.size - 4; } len = len / 16; gls->num_descriptors = len; gls->descriptors = scsi_malloc(task, sizeof(*gls->descriptors) * len); if (gls->descriptors == NULL) { return NULL; } for (i = 0; i < (int)gls->num_descriptors; i++) { gls->descriptors[i].lba = task_get_uint32(task, 8 + i * sizeof(struct scsi_lba_status_descriptor) + 0); gls->descriptors[i].lba <<= 32; gls->descriptors[i].lba |= task_get_uint32(task, 8 + i * sizeof(struct scsi_lba_status_descriptor) + 4); gls->descriptors[i].num_blocks = task_get_uint32(task, 8 + i * sizeof(struct scsi_lba_status_descriptor) + 8); gls->descriptors[i].provisioning = task_get_uint8(task, 8 + i * sizeof(struct scsi_lba_status_descriptor) + 12) & 0x0f; } return gls; } default: return NULL; } } /* * persistent_reserve_in unmarshall */ static inline uint8_t scsi_persistentreservein_sa(const struct scsi_task *task) { return task->cdb[1] & 0x1f; } static int scsi_persistentreservein_datain_getfullsize(struct scsi_task *task) { switch (scsi_persistentreservein_sa(task)) { case SCSI_PERSISTENT_RESERVE_READ_KEYS: return task_get_uint32(task, 4) + 8; case SCSI_PERSISTENT_RESERVE_READ_RESERVATION: return 8; case SCSI_PERSISTENT_RESERVE_REPORT_CAPABILITIES: return 8; default: return -1; } } static void * scsi_receivecopyresults_datain_unmarshall(struct scsi_task *task) { int sa = task->cdb[1] & 0x1f; int len, i; struct scsi_copy_results_copy_status *cs; struct scsi_copy_results_op_params *op; switch (sa) { case SCSI_COPY_RESULTS_COPY_STATUS: len = task_get_uint32(task, 0); if (len < 8) return NULL; cs = scsi_malloc(task, sizeof(*cs)); if (cs == NULL) { return NULL; } cs->available_data = len; cs->copy_manager_status = task_get_uint8(task, 4) & 0x7F; cs->hdd = (task_get_uint8(task, 4) & 0x80) >> 7; cs->segments_processed = task_get_uint16(task, 5); cs->transfer_count_units = task_get_uint8(task, 7); cs->transfer_count = task_get_uint32(task, 8); return cs; case SCSI_COPY_RESULTS_OP_PARAMS: len = task_get_uint32(task, 0); if (len < 40) return NULL; op = scsi_malloc(task, sizeof(*op) + task_get_uint8(task, 43)); if (op == NULL) { return NULL; } op->available_data = len; op->max_target_desc_count = task_get_uint16(task, 8); op->max_segment_desc_count = task_get_uint16(task, 10); op->max_desc_list_length = task_get_uint32(task, 12); op->max_segment_length = task_get_uint32(task, 16); op->max_inline_data_length = task_get_uint32(task, 20); op->held_data_limit = task_get_uint32(task, 24); op->max_stream_device_transfer_size = task_get_uint32(task, 28); op->total_concurrent_copies = task_get_uint16(task, 34); op->max_concurrent_copies = task_get_uint8(task, 36); op->data_segment_granularity = task_get_uint8(task, 37); op->inline_data_granularity = task_get_uint8(task, 38); op->held_data_granularity = task_get_uint8(task, 39); op->impl_desc_list_length = task_get_uint8(task, 43); for (i = 0; i < (int)op->impl_desc_list_length; i++) { op->imp_desc_type_codes[i] = task_get_uint8(task, 44+i); } return op; default: return NULL; } } static void * scsi_persistentreservein_datain_unmarshall(struct scsi_task *task) { struct scsi_persistent_reserve_in_read_keys *rk; struct scsi_persistent_reserve_in_read_reservation *rr; struct scsi_persistent_reserve_in_report_capabilities *rc; int i; switch (scsi_persistentreservein_sa(task)) { case SCSI_PERSISTENT_RESERVE_READ_KEYS: i = task_get_uint32(task, 4); rk = scsi_malloc(task, offsetof(struct scsi_persistent_reserve_in_read_keys, keys) + i); if (rk == NULL) { return NULL; } rk->prgeneration = task_get_uint32(task, 0); rk->additional_length = task_get_uint32(task, 4); rk->num_keys = rk->additional_length / 8; for (i = 0; i < (int)rk->num_keys; i++) { rk->keys[i] = task_get_uint64(task, 8 + i * 8); } return rk; case SCSI_PERSISTENT_RESERVE_READ_RESERVATION: { size_t alloc_sz; i = task_get_uint32(task, 4); alloc_sz = sizeof(struct scsi_persistent_reserve_in_read_reservation); rr = scsi_malloc(task, alloc_sz); if (rr == NULL) { return NULL; } memset(rr, 0, alloc_sz); rr->prgeneration = task_get_uint32(task, 0); if (i > 0) { rr->reserved = 1; rr->reservation_key = task_get_uint64(task, 8); rr->pr_scope = task_get_uint8(task, 21) >> 4; rr->pr_type = task_get_uint8(task, 21) & 0xf; } return rr; } case SCSI_PERSISTENT_RESERVE_REPORT_CAPABILITIES: rc = scsi_malloc(task, sizeof(struct scsi_persistent_reserve_in_report_capabilities)); if (rc == NULL) { return NULL; } rc->length = task_get_uint16(task, 0); rc->crh = !!(task_get_uint8(task, 2) & 0x10); rc->sip_c = !!(task_get_uint8(task, 2) & 0x08); rc->atp_c = !!(task_get_uint8(task, 2) & 0x04); rc->ptpl_c = !!(task_get_uint8(task, 2) & 0x01); rc->tmv = !!(task_get_uint8(task, 3) & 0x80); rc->allow_commands = (task_get_uint8(task, 3) & 0x70) >> 4; rc->persistent_reservation_type_mask = task_get_uint16(task, 4); return rc; default: return NULL; } } static inline uint8_t scsi_maintenancein_sa(const struct scsi_task *task) { return task->cdb[1]; } static inline uint8_t scsi_report_supported_opcodes_options(const struct scsi_task *task) { return task->cdb[2] & 0x07; } /* * parse the data in blob and calculate the size of a full maintenancein * datain structure */ static int scsi_maintenancein_datain_getfullsize(struct scsi_task *task) { switch (scsi_maintenancein_sa(task)) { case SCSI_REPORT_SUPPORTED_OP_CODES: switch (scsi_report_supported_opcodes_options(task)) { case SCSI_REPORT_SUPPORTING_OPS_ALL: return task_get_uint32(task, 0) + 4; case SCSI_REPORT_SUPPORTING_OPCODE: case SCSI_REPORT_SUPPORTING_SERVICEACTION: return 4 + (task_get_uint8(task, 1) & 0x80) ? 12 : 0 + task_get_uint16(task, 2); } default: return -1; } } /* * maintenance_in unmarshall */ static void * scsi_maintenancein_datain_unmarshall(struct scsi_task *task) { struct scsi_report_supported_op_codes *rsoc; struct scsi_report_supported_op_codes_one_command *rsoc_one; int len, i; switch (scsi_maintenancein_sa(task)) { case SCSI_REPORT_SUPPORTED_OP_CODES: switch (scsi_report_supported_opcodes_options(task)) { case SCSI_REPORT_SUPPORTING_OPS_ALL: if (task->datain.size < 4) { return NULL; } len = task_get_uint32(task, 0); /* len / 8 is not always correct since if CTDP==1 then * the descriptor is 20 bytes in size intead of 8. * It doesnt matter here though since it just means * we would allocate more descriptors at the end of * the structure than we strictly need. This avoids * having to traverse the datain buffer twice. */ rsoc = scsi_malloc(task, offsetof(struct scsi_report_supported_op_codes, descriptors) + len / 8 * sizeof(struct scsi_command_descriptor)); if (rsoc == NULL) { return NULL; } rsoc->num_descriptors = 0; i = 4; while (len >= 8) { struct scsi_command_descriptor *desc; desc = &rsoc->descriptors[rsoc->num_descriptors++]; desc->opcode = task_get_uint8(task, i); desc->sa = task_get_uint16(task, i + 2); desc->ctdp = !!(task_get_uint8(task, i + 5) & 0x02); desc->servactv = !!(task_get_uint8(task, i + 5) & 0x01); desc->cdb_len = task_get_uint16(task, i + 6); len -= 8; i += 8; /* No tiemout description */ if (!desc->ctdp) { continue; } desc->to.descriptor_length = task_get_uint16(task, i); desc->to.command_specific = task_get_uint8(task, i + 3); desc->to.nominal_processing_timeout = task_get_uint32(task, i + 4); desc->to.recommended_timeout = task_get_uint32(task, i + 8); len -= desc->to.descriptor_length + 2; i += desc->to.descriptor_length + 2; } return rsoc; case SCSI_REPORT_SUPPORTING_OPCODE: case SCSI_REPORT_SUPPORTING_SERVICEACTION: rsoc_one = scsi_malloc(task, sizeof(struct scsi_report_supported_op_codes_one_command)); if (rsoc_one == NULL) { return NULL; } rsoc_one->ctdp = !!(task_get_uint8(task, 1) & 0x80); rsoc_one->support = task_get_uint8(task, 1) & 0x07; rsoc_one->cdb_length = task_get_uint16(task, 2); if (rsoc_one->cdb_length <= sizeof(rsoc_one->cdb_usage_data)) { memcpy(rsoc_one->cdb_usage_data, &task->datain.data[4], rsoc_one->cdb_length); } if (rsoc_one->ctdp) { i = 4 + rsoc_one->cdb_length; rsoc_one->to.descriptor_length = task_get_uint16(task, i); rsoc_one->to.command_specific = task_get_uint8(task, i + 3); rsoc_one->to.nominal_processing_timeout = task_get_uint32(task, i + 4); rsoc_one->to.recommended_timeout = task_get_uint32(task, i + 8); } return rsoc_one; } }; return NULL; } /* * MAINTENANCE In / Read Supported Op Codes */ struct scsi_task * scsi_cdb_report_supported_opcodes(int rctd, int options, enum scsi_opcode opcode, int sa, uint32_t alloc_len) { struct scsi_task *task; task = malloc(sizeof(struct scsi_task)); if (task == NULL) { return NULL; } memset(task, 0, sizeof(struct scsi_task)); task->cdb[0] = SCSI_OPCODE_MAINTENANCE_IN; task->cdb[1] = SCSI_REPORT_SUPPORTED_OP_CODES; task->cdb[2] = options & 0x07; if (rctd) { task->cdb[2] |= 0x80; } task->cdb[3] = opcode; scsi_set_uint16(&task->cdb[4], sa); scsi_set_uint32(&task->cdb[6], alloc_len); task->cdb_size = 12; if (alloc_len != 0) { task->xfer_dir = SCSI_XFER_READ; } else { task->xfer_dir = SCSI_XFER_NONE; } task->expxferlen = alloc_len; return task; } /* * parse the data in blob and calculate the size of a full * readcapacity10 datain structure */ static int scsi_readcapacity10_datain_getfullsize(struct scsi_task *task _U_) { return 8; } /* * unmarshall the data in blob for readcapacity10 into a structure */ static struct scsi_readcapacity10 * scsi_readcapacity10_datain_unmarshall(struct scsi_task *task) { struct scsi_readcapacity10 *rc10; if (task->datain.size < 8) { return NULL; } rc10 = scsi_malloc(task, sizeof(struct scsi_readcapacity10)); if (rc10 == NULL) { return NULL; } rc10->lba = task_get_uint32(task, 0); rc10->block_size = task_get_uint32(task, 4); return rc10; } /* * INQUIRY */ struct scsi_task * scsi_cdb_inquiry(int evpd, int page_code, int alloc_len) { struct scsi_task *task; task = malloc(sizeof(struct scsi_task)); if (task == NULL) { return NULL; } memset(task, 0, sizeof(struct scsi_task)); task->cdb[0] = SCSI_OPCODE_INQUIRY; if (evpd) { task->cdb[1] |= 0x01; } task->cdb[2] = page_code; scsi_set_uint16(&task->cdb[3], alloc_len); task->cdb_size = 6; if (alloc_len != 0) { task->xfer_dir = SCSI_XFER_READ; } else { task->xfer_dir = SCSI_XFER_NONE; } task->expxferlen = alloc_len; return task; } static inline int scsi_inquiry_evpd_set(const struct scsi_task *task) { return task->cdb[1] & 0x1; } static inline uint8_t scsi_inquiry_page_code(const struct scsi_task *task) { return task->cdb[2]; } /* * parse the data in blob and calculate the size of a full * inquiry datain structure */ static int scsi_inquiry_datain_getfullsize(struct scsi_task *task) { if (scsi_inquiry_evpd_set(task) == 0) { return task_get_uint8(task, 4) + 5; } switch (scsi_inquiry_page_code(task)) { case SCSI_INQUIRY_PAGECODE_SUPPORTED_VPD_PAGES: case SCSI_INQUIRY_PAGECODE_BLOCK_DEVICE_CHARACTERISTICS: case SCSI_INQUIRY_PAGECODE_UNIT_SERIAL_NUMBER: return task_get_uint8(task, 3) + 4; case SCSI_INQUIRY_PAGECODE_DEVICE_IDENTIFICATION: case SCSI_INQUIRY_PAGECODE_BLOCK_LIMITS: case SCSI_INQUIRY_PAGECODE_LOGICAL_BLOCK_PROVISIONING: return task_get_uint16(task, 2) + 4; default: return -1; } } static struct scsi_inquiry_standard * scsi_inquiry_unmarshall_standard(struct scsi_task *task) { int i; struct scsi_inquiry_standard *inq = scsi_malloc(task, sizeof(*inq)); if (inq == NULL) { return NULL; } inq->qualifier = (task_get_uint8(task, 0) >> 5) & 0x07; inq->device_type = task_get_uint8(task, 0) & 0x1f; inq->rmb = !!(task_get_uint8(task, 1) & 0x80); inq->version = task_get_uint8(task, 2); inq->normaca = !!(task_get_uint8(task, 3) & 0x20); inq->hisup = !!(task_get_uint8(task, 3) & 0x10); inq->response_data_format = task_get_uint8(task, 3) & 0x0f; inq->additional_length = task_get_uint8(task, 4); inq->sccs = !!(task_get_uint8(task, 5) & 0x80); inq->acc = !!(task_get_uint8(task, 5) & 0x40); inq->tpgs = (task_get_uint8(task, 5) >> 4) & 0x03; inq->threepc = !!(task_get_uint8(task, 5) & 0x08); inq->protect = !!(task_get_uint8(task, 5) & 0x01); inq->encserv = !!(task_get_uint8(task, 6) & 0x40); inq->multip = !!(task_get_uint8(task, 6) & 0x10); inq->addr16 = !!(task_get_uint8(task, 6) & 0x01); inq->wbus16 = !!(task_get_uint8(task, 7) & 0x20); inq->sync = !!(task_get_uint8(task, 7) & 0x10); inq->cmdque = !!(task_get_uint8(task, 7) & 0x02); memcpy(&inq->vendor_identification[0], &task->datain.data[8], 8); memcpy(&inq->product_identification[0], &task->datain.data[16], 16); memcpy(&inq->product_revision_level[0], &task->datain.data[32], 4); inq->clocking = (task_get_uint8(task, 56) >> 2) & 0x03; inq->qas = !!(task_get_uint8(task, 56) & 0x02); inq->ius = !!(task_get_uint8(task, 56) & 0x01); for (i = 0; i < 8; i++) { inq->version_descriptor[i] = task_get_uint16(task, 58 + i * 2); } return inq; } static struct scsi_inquiry_supported_pages * scsi_inquiry_unmarshall_supported_pages(struct scsi_task *task) { struct scsi_inquiry_supported_pages *inq = scsi_malloc(task, sizeof(*inq)); if (inq == NULL) { return NULL; } inq->qualifier = (task_get_uint8(task, 0) >> 5) & 0x07; inq->device_type = task_get_uint8(task, 0) & 0x1f; inq->pagecode = task_get_uint8(task, 1); inq->num_pages = task_get_uint8(task, 3); inq->pages = scsi_malloc(task, inq->num_pages); if (inq->pages == NULL) { return NULL; } memcpy(inq->pages, &task->datain.data[4], inq->num_pages); return inq; } static struct scsi_inquiry_unit_serial_number * scsi_inquiry_unmarshall_unit_serial_number(struct scsi_task* task) { struct scsi_inquiry_unit_serial_number *inq = scsi_malloc(task, sizeof(*inq)); if (inq == NULL) { return NULL; } inq->qualifier = (task_get_uint8(task, 0) >> 5) & 0x07; inq->device_type = task_get_uint8(task, 0) & 0x1f; inq->pagecode = task_get_uint8(task, 1); inq->usn = scsi_malloc(task, task_get_uint8(task, 3) + 1); if (inq->usn == NULL) { return NULL; } memcpy(inq->usn, &task->datain.data[4], task_get_uint8(task, 3)); inq->usn[task_get_uint8(task, 3)] = 0; return inq; } static struct scsi_inquiry_device_identification * scsi_inquiry_unmarshall_device_identification(struct scsi_task *task) { struct scsi_inquiry_device_identification *inq = scsi_malloc(task, sizeof(*inq)); int remaining = task_get_uint16(task, 2); unsigned char *dptr; if (inq == NULL) { return NULL; } inq->qualifier = (task_get_uint8(task, 0) >> 5) & 0x07; inq->device_type = task_get_uint8(task, 0) & 0x1f; inq->pagecode = task_get_uint8(task, 1); dptr = &task->datain.data[4]; while (remaining > 0) { struct scsi_inquiry_device_designator *dev = scsi_malloc(task, sizeof(*dev)); if (dev == NULL) { goto err; } dev->next = inq->designators; inq->designators = dev; dev->protocol_identifier = (dptr[0]>>4) & 0x0f; dev->code_set = dptr[0] & 0x0f; dev->piv = !!(dptr[1]&0x80); dev->association = (dptr[1]>>4)&0x03; dev->designator_type = dptr[1]&0x0f; dev->designator_length = dptr[3]; dev->designator = scsi_malloc(task, dev->designator_length + 1); if (dev->designator == NULL) { goto err; } dev->designator[dev->designator_length] = 0; memcpy(dev->designator, &dptr[4], dev->designator_length); remaining -= 4; remaining -= dev->designator_length; dptr += dev->designator_length + 4; } return inq; err: while (inq->designators) { struct scsi_inquiry_device_designator *dev = inq->designators; inq->designators = dev->next; } return NULL; } static struct scsi_inquiry_block_limits * scsi_inquiry_unmarshall_block_limits(struct scsi_task *task) { struct scsi_inquiry_block_limits *inq = scsi_malloc(task, sizeof(*inq)); if (inq == NULL) { return NULL; } inq->qualifier = (task_get_uint8(task, 0) >> 5) & 0x07; inq->device_type = task_get_uint8(task, 0) & 0x1f; inq->pagecode = task_get_uint8(task, 1); inq->wsnz = task_get_uint8(task, 4) & 0x01; inq->max_cmp = task_get_uint8(task, 5); inq->opt_gran = task_get_uint16(task, 6); inq->max_xfer_len = task_get_uint32(task, 8); inq->opt_xfer_len = task_get_uint32(task, 12); inq->max_prefetch = task_get_uint32(task, 16); inq->max_unmap = task_get_uint32(task, 20); inq->max_unmap_bdc = task_get_uint32(task, 24); inq->opt_unmap_gran = task_get_uint32(task, 28); inq->ugavalid = !!(task_get_uint8(task, 32)&0x80); inq->unmap_gran_align = task_get_uint32(task, 32) & 0x7fffffff; inq->max_ws_len = task_get_uint32(task, 36); inq->max_ws_len = (inq->max_ws_len << 32) | task_get_uint32(task, 40); inq->max_atomic_xfer_len = task_get_uint32(task, 44); inq->atomic_align = task_get_uint32(task, 48); inq->atomic_gran = task_get_uint32(task, 52); inq->max_atomic_tl_with_atomic_boundary = task_get_uint32(task, 56); inq->max_atomic_boundary_size = task_get_uint32(task, 60); return inq; } static struct scsi_inquiry_block_device_characteristics * scsi_inquiry_unmarshall_block_device_characteristics(struct scsi_task *task) { struct scsi_inquiry_block_device_characteristics *inq = scsi_malloc(task, sizeof(*inq)); if (inq == NULL) { return NULL; } inq->qualifier = (task_get_uint8(task, 0) >> 5) & 0x07; inq->device_type = task_get_uint8(task, 0) & 0x1f; inq->pagecode = task_get_uint8(task, 1); inq->medium_rotation_rate = task_get_uint16(task, 4); inq->product_type = task_get_uint8(task, 6); inq->wabereq = (task_get_uint8(task, 7) >> 6) & 0x03; inq->wacereq = (task_get_uint8(task, 7) >> 4) & 0x03; inq->nominal_form_factor = task_get_uint8(task, 7) & 0x0f; inq->fuab = !!(task_get_uint8(task, 8) & 0x02); inq->vbuls = !!(task_get_uint8(task, 8) & 0x01); return inq; } struct scsi_inquiry_logical_block_provisioning * scsi_inquiry_unmarshall_logical_block_provisioning(struct scsi_task *task) { struct scsi_inquiry_logical_block_provisioning *inq = scsi_malloc(task, sizeof(*inq)); if (inq == NULL) { return NULL; } inq->qualifier = (task_get_uint8(task, 0) >> 5) & 0x07; inq->device_type = task_get_uint8(task, 0) & 0x1f; inq->pagecode = task_get_uint8(task, 1); inq->threshold_exponent = task_get_uint8(task, 4); inq->lbpu = !!(task_get_uint8(task, 5) & 0x80); inq->lbpws = !!(task_get_uint8(task, 5) & 0x40); inq->lbpws10 = !!(task_get_uint8(task, 5) & 0x20); inq->lbprz = !!(task_get_uint8(task, 5) & 0x04); inq->anc_sup = !!(task_get_uint8(task, 5) & 0x02); inq->dp = !!(task_get_uint8(task, 5) & 0x01); inq->provisioning_type = task_get_uint8(task, 6) & 0x07; return inq; } /* * unmarshall the data in blob for inquiry into a structure */ static void * scsi_inquiry_datain_unmarshall(struct scsi_task *task) { if (scsi_inquiry_evpd_set(task) == 0) { return scsi_inquiry_unmarshall_standard(task); } switch (scsi_inquiry_page_code(task)) { case SCSI_INQUIRY_PAGECODE_SUPPORTED_VPD_PAGES: return scsi_inquiry_unmarshall_supported_pages(task); case SCSI_INQUIRY_PAGECODE_UNIT_SERIAL_NUMBER: return scsi_inquiry_unmarshall_unit_serial_number(task); case SCSI_INQUIRY_PAGECODE_DEVICE_IDENTIFICATION: return scsi_inquiry_unmarshall_device_identification(task); case SCSI_INQUIRY_PAGECODE_BLOCK_LIMITS: return scsi_inquiry_unmarshall_block_limits(task); case SCSI_INQUIRY_PAGECODE_BLOCK_DEVICE_CHARACTERISTICS: return scsi_inquiry_unmarshall_block_device_characteristics(task); case SCSI_INQUIRY_PAGECODE_LOGICAL_BLOCK_PROVISIONING: return scsi_inquiry_unmarshall_logical_block_provisioning(task); default: return NULL; } } /* * READ6 */ struct scsi_task * scsi_cdb_read6(uint32_t lba, uint32_t xferlen, int blocksize) { struct scsi_task *task; int num_blocks; num_blocks = xferlen/blocksize; if (num_blocks > 256) { return NULL; } if (lba > 0x1fffff) { return NULL; } task = malloc(sizeof(struct scsi_task)); if (task == NULL) { return NULL; } memset(task, 0, sizeof(struct scsi_task)); task->cdb[0] = SCSI_OPCODE_READ6; task->cdb_size = 6; task->cdb[1] = (lba>>16)&0x1f; task->cdb[2] = (lba>> 8)&0xff; task->cdb[3] = (lba )&0xff; if (num_blocks < 256) { task->cdb[4] = num_blocks & 0xff; } if (xferlen != 0) { task->xfer_dir = SCSI_XFER_READ; } else { task->xfer_dir = SCSI_XFER_NONE; } task->expxferlen = xferlen; return task; } /* * READ10 */ struct scsi_task * scsi_cdb_read10(uint32_t lba, uint32_t xferlen, int blocksize, int rdprotect, int dpo, int fua, int fua_nv, int group_number) { struct scsi_task *task; task = malloc(sizeof(struct scsi_task)); if (task == NULL) { return NULL; } memset(task, 0, sizeof(struct scsi_task)); task->cdb[0] = SCSI_OPCODE_READ10; task->cdb[1] |= ((rdprotect & 0x07) << 5); if (dpo) { task->cdb[1] |= 0x10; } if (fua) { task->cdb[1] |= 0x08; } if (fua_nv) { task->cdb[1] |= 0x02; } scsi_set_uint32(&task->cdb[2], lba); scsi_set_uint16(&task->cdb[7], xferlen/blocksize); task->cdb[6] |= (group_number & 0x1f); task->cdb_size = 10; if (xferlen != 0) { task->xfer_dir = SCSI_XFER_READ; } else { task->xfer_dir = SCSI_XFER_NONE; } task->expxferlen = xferlen; return task; } /* * READ12 */ struct scsi_task * scsi_cdb_read12(uint32_t lba, uint32_t xferlen, int blocksize, int rdprotect, int dpo, int fua, int fua_nv, int group_number) { struct scsi_task *task; task = malloc(sizeof(struct scsi_task)); if (task == NULL) { return NULL; } memset(task, 0, sizeof(struct scsi_task)); task->cdb[0] = SCSI_OPCODE_READ12; task->cdb[1] |= ((rdprotect & 0x07) << 5); if (dpo) { task->cdb[1] |= 0x10; } if (fua) { task->cdb[1] |= 0x08; } if (fua_nv) { task->cdb[1] |= 0x02; } scsi_set_uint32(&task->cdb[2], lba); scsi_set_uint32(&task->cdb[6], xferlen/blocksize); task->cdb[10] |= (group_number & 0x1f); task->cdb_size = 12; if (xferlen != 0) { task->xfer_dir = SCSI_XFER_READ; } else { task->xfer_dir = SCSI_XFER_NONE; } task->expxferlen = xferlen; return task; } /* * READ16 */ struct scsi_task * scsi_cdb_read16(uint64_t lba, uint32_t xferlen, int blocksize, int rdprotect, int dpo, int fua, int fua_nv, int group_number) { struct scsi_task *task; task = malloc(sizeof(struct scsi_task)); if (task == NULL) { return NULL; } memset(task, 0, sizeof(struct scsi_task)); task->cdb[0] = SCSI_OPCODE_READ16; task->cdb[1] |= ((rdprotect & 0x07) << 5); if (dpo) { task->cdb[1] |= 0x10; } if (fua) { task->cdb[1] |= 0x08; } if (fua_nv) { task->cdb[1] |= 0x02; } scsi_set_uint32(&task->cdb[2], lba >> 32); scsi_set_uint32(&task->cdb[6], lba & 0xffffffff); scsi_set_uint32(&task->cdb[10], xferlen/blocksize); task->cdb[14] |= (group_number & 0x1f); task->cdb_size = 16; if (xferlen != 0) { task->xfer_dir = SCSI_XFER_READ; } else { task->xfer_dir = SCSI_XFER_NONE; } task->expxferlen = xferlen; return task; } /* * WRITE10 */ struct scsi_task * scsi_cdb_write10(uint32_t lba, uint32_t xferlen, int blocksize, int wrprotect, int dpo, int fua, int fua_nv, int group_number) { struct scsi_task *task; task = malloc(sizeof(struct scsi_task)); if (task == NULL) { return NULL; } memset(task, 0, sizeof(struct scsi_task)); task->cdb[0] = SCSI_OPCODE_WRITE10; task->cdb[1] |= ((wrprotect & 0x07) << 5); if (dpo) { task->cdb[1] |= 0x10; } if (fua) { task->cdb[1] |= 0x08; } if (fua_nv) { task->cdb[1] |= 0x02; } scsi_set_uint32(&task->cdb[2], lba); scsi_set_uint16(&task->cdb[7], xferlen/blocksize); task->cdb[6] |= (group_number & 0x1f); task->cdb_size = 10; if (xferlen != 0) { task->xfer_dir = SCSI_XFER_WRITE; } else { task->xfer_dir = SCSI_XFER_NONE; } task->expxferlen = xferlen; return task; } /* * WRITE12 */ struct scsi_task * scsi_cdb_write12(uint32_t lba, uint32_t xferlen, int blocksize, int wrprotect, int dpo, int fua, int fua_nv, int group_number) { struct scsi_task *task; task = malloc(sizeof(struct scsi_task)); if (task == NULL) { return NULL; } memset(task, 0, sizeof(struct scsi_task)); task->cdb[0] = SCSI_OPCODE_WRITE12; task->cdb[1] |= ((wrprotect & 0x07) << 5); if (dpo) { task->cdb[1] |= 0x10; } if (fua) { task->cdb[1] |= 0x08; } if (fua_nv) { task->cdb[1] |= 0x02; } scsi_set_uint32(&task->cdb[2], lba); scsi_set_uint32(&task->cdb[6], xferlen/blocksize); task->cdb[10] |= (group_number & 0x1f); task->cdb_size = 12; if (xferlen != 0) { task->xfer_dir = SCSI_XFER_WRITE; } else { task->xfer_dir = SCSI_XFER_NONE; } task->expxferlen = xferlen; return task; } /* * WRITE16 */ struct scsi_task * scsi_cdb_write16(uint64_t lba, uint32_t xferlen, int blocksize, int wrprotect, int dpo, int fua, int fua_nv, int group_number) { struct scsi_task *task; task = malloc(sizeof(struct scsi_task)); if (task == NULL) { return NULL; } memset(task, 0, sizeof(struct scsi_task)); task->cdb[0] = SCSI_OPCODE_WRITE16; task->cdb[1] |= ((wrprotect & 0x07) << 5); if (dpo) { task->cdb[1] |= 0x10; } if (fua) { task->cdb[1] |= 0x08; } if (fua_nv) { task->cdb[1] |= 0x02; } scsi_set_uint32(&task->cdb[2], lba >> 32); scsi_set_uint32(&task->cdb[6], lba & 0xffffffff); scsi_set_uint32(&task->cdb[10], xferlen / blocksize); task->cdb[14] |= (group_number & 0x1f); task->cdb_size = 16; if (xferlen != 0) { task->xfer_dir = SCSI_XFER_WRITE; } else { task->xfer_dir = SCSI_XFER_NONE; } task->expxferlen = xferlen; return task; } /* * WRITEATOMIC16 */ struct scsi_task * scsi_cdb_writeatomic16(uint64_t lba, uint32_t xferlen, int blocksize, int wrprotect, int dpo, int fua, int group_number) { struct scsi_task *task; task = malloc(sizeof(struct scsi_task)); if (task == NULL) { return NULL; } memset(task, 0, sizeof(struct scsi_task)); task->cdb[0] = SCSI_OPCODE_WRITE_ATOMIC16; task->cdb[1] |= ((wrprotect & 0x07) << 5); if (dpo) { task->cdb[1] |= 0x10; } if (fua) { task->cdb[1] |= 0x08; } scsi_set_uint32(&task->cdb[2], lba >> 32); scsi_set_uint32(&task->cdb[6], lba & 0xffffffff); scsi_set_uint16(&task->cdb[12], xferlen / blocksize); task->cdb[14] |= (group_number & 0x1f); task->cdb_size = 16; if (xferlen != 0) { task->xfer_dir = SCSI_XFER_WRITE; } else { task->xfer_dir = SCSI_XFER_NONE; } task->expxferlen = xferlen; return task; } /* * ORWRITE */ struct scsi_task * scsi_cdb_orwrite(uint64_t lba, uint32_t xferlen, int blocksize, int wrprotect, int dpo, int fua, int fua_nv, int group_number) { struct scsi_task *task; task = malloc(sizeof(struct scsi_task)); if (task == NULL) { return NULL; } memset(task, 0, sizeof(struct scsi_task)); task->cdb[0] = SCSI_OPCODE_ORWRITE; task->cdb[1] |= ((wrprotect & 0x07) << 5); if (dpo) { task->cdb[1] |= 0x10; } if (fua) { task->cdb[1] |= 0x08; } if (fua_nv) { task->cdb[1] |= 0x02; } scsi_set_uint32(&task->cdb[2], lba >> 32); scsi_set_uint32(&task->cdb[6], lba & 0xffffffff); scsi_set_uint32(&task->cdb[10], xferlen/blocksize); task->cdb[14] |= (group_number & 0x1f); task->cdb_size = 16; if (xferlen != 0) { task->xfer_dir = SCSI_XFER_WRITE; } else { task->xfer_dir = SCSI_XFER_NONE; } task->expxferlen = xferlen; return task; } /* * COMPAREANDWRITE */ struct scsi_task * scsi_cdb_compareandwrite(uint64_t lba, uint32_t xferlen, int blocksize, int wrprotect, int dpo, int fua, int fua_nv, int group_number) { struct scsi_task *task; task = malloc(sizeof(struct scsi_task)); if (task == NULL) { return NULL; } memset(task, 0, sizeof(struct scsi_task)); task->cdb[0] = SCSI_OPCODE_COMPARE_AND_WRITE; task->cdb[1] |= ((wrprotect & 0x07) << 5); if (dpo) { task->cdb[1] |= 0x10; } if (fua) { task->cdb[1] |= 0x08; } if (fua_nv) { task->cdb[1] |= 0x02; } scsi_set_uint32(&task->cdb[2], lba >> 32); scsi_set_uint32(&task->cdb[6], lba & 0xffffffff); task->cdb[13] = xferlen / blocksize / 2; task->cdb[14] |= (group_number & 0x1f); task->cdb_size = 16; if (xferlen != 0) { task->xfer_dir = SCSI_XFER_WRITE; } else { task->xfer_dir = SCSI_XFER_NONE; } task->expxferlen = xferlen; return task; } /* * VERIFY10 */ struct scsi_task * scsi_cdb_verify10(uint32_t lba, uint32_t xferlen, int vprotect, int dpo, int bytchk, int blocksize) { struct scsi_task *task; task = malloc(sizeof(struct scsi_task)); if (task == NULL) { return NULL; } memset(task, 0, sizeof(struct scsi_task)); task->cdb[0] = SCSI_OPCODE_VERIFY10; if (vprotect) { task->cdb[1] |= ((vprotect << 5) & 0xe0); } if (dpo) { task->cdb[1] |= 0x10; } if (bytchk) { task->cdb[1] |= 0x02; } scsi_set_uint32(&task->cdb[2], lba); scsi_set_uint16(&task->cdb[7], xferlen/blocksize); task->cdb_size = 10; if (xferlen != 0 && bytchk) { task->xfer_dir = SCSI_XFER_WRITE; task->expxferlen = xferlen; } else { task->xfer_dir = SCSI_XFER_NONE; task->expxferlen = 0; } return task; } /* * VERIFY12 */ struct scsi_task * scsi_cdb_verify12(uint32_t lba, uint32_t xferlen, int vprotect, int dpo, int bytchk, int blocksize) { struct scsi_task *task; task = malloc(sizeof(struct scsi_task)); if (task == NULL) { return NULL; } memset(task, 0, sizeof(struct scsi_task)); task->cdb[0] = SCSI_OPCODE_VERIFY12; if (vprotect) { task->cdb[1] |= ((vprotect << 5) & 0xe0); } if (dpo) { task->cdb[1] |= 0x10; } if (bytchk) { task->cdb[1] |= 0x02; } scsi_set_uint32(&task->cdb[2], lba); scsi_set_uint32(&task->cdb[6], xferlen/blocksize); task->cdb_size = 12; if (xferlen != 0 && bytchk) { task->xfer_dir = SCSI_XFER_WRITE; task->expxferlen = xferlen; } else { task->xfer_dir = SCSI_XFER_NONE; task->expxferlen = 0; } return task; } /* * VERIFY16 */ struct scsi_task * scsi_cdb_verify16(uint64_t lba, uint32_t xferlen, int vprotect, int dpo, int bytchk, int blocksize) { struct scsi_task *task; task = malloc(sizeof(struct scsi_task)); if (task == NULL) { return NULL; } memset(task, 0, sizeof(struct scsi_task)); task->cdb[0] = SCSI_OPCODE_VERIFY16; if (vprotect) { task->cdb[1] |= ((vprotect << 5) & 0xe0); } if (dpo) { task->cdb[1] |= 0x10; } if (bytchk) { task->cdb[1] |= 0x02; } scsi_set_uint32(&task->cdb[2], lba >> 32); scsi_set_uint32(&task->cdb[6], lba & 0xffffffff); scsi_set_uint32(&task->cdb[10], xferlen/blocksize); task->cdb_size = 16; if (xferlen != 0 && bytchk) { task->xfer_dir = SCSI_XFER_WRITE; task->expxferlen = xferlen; } else { task->xfer_dir = SCSI_XFER_NONE; task->expxferlen = 0; } return task; } /* * UNMAP */ struct scsi_task * scsi_cdb_unmap(int anchor, int group, uint16_t xferlen) { struct scsi_task *task; task = malloc(sizeof(struct scsi_task)); if (task == NULL) { return NULL; } memset(task, 0, sizeof(struct scsi_task)); task->cdb[0] = SCSI_OPCODE_UNMAP; if (anchor) { task->cdb[1] |= 0x01; } task->cdb[6] |= group & 0x1f; scsi_set_uint16(&task->cdb[7], xferlen); task->cdb_size = 10; if (xferlen != 0) { task->xfer_dir = SCSI_XFER_WRITE; } else { task->xfer_dir = SCSI_XFER_NONE; } task->expxferlen = xferlen; return task; } /* * PERSISTENT_RESEERVE_IN */ struct scsi_task * scsi_cdb_persistent_reserve_in(enum scsi_persistent_in_sa sa, uint16_t xferlen) { struct scsi_task *task; task = malloc(sizeof(struct scsi_task)); if (task == NULL) { return NULL; } memset(task, 0, sizeof(struct scsi_task)); task->cdb[0] = SCSI_OPCODE_PERSISTENT_RESERVE_IN; task->cdb[1] |= sa & 0x1f; scsi_set_uint16(&task->cdb[7], xferlen); task->cdb_size = 10; if (xferlen != 0) { task->xfer_dir = SCSI_XFER_READ; } else { task->xfer_dir = SCSI_XFER_NONE; } task->expxferlen = xferlen; return task; } /* * PERSISTENT_RESERVE_OUT */ struct scsi_task * scsi_cdb_persistent_reserve_out(enum scsi_persistent_out_sa sa, enum scsi_persistent_out_scope scope, enum scsi_persistent_out_type type, void *param) { struct scsi_task *task; struct scsi_persistent_reserve_out_basic *basic; struct scsi_iovec *iov; unsigned char *buf; int xferlen; task = malloc(sizeof(struct scsi_task)); if (task == NULL) goto err; memset(task, 0, sizeof(struct scsi_task)); iov = scsi_malloc(task, sizeof(struct scsi_iovec)); if (iov == NULL) goto err; switch(sa) { case SCSI_PERSISTENT_RESERVE_REGISTER: case SCSI_PERSISTENT_RESERVE_RESERVE: case SCSI_PERSISTENT_RESERVE_RELEASE: case SCSI_PERSISTENT_RESERVE_CLEAR: case SCSI_PERSISTENT_RESERVE_PREEMPT: case SCSI_PERSISTENT_RESERVE_PREEMPT_AND_ABORT: case SCSI_PERSISTENT_RESERVE_REGISTER_AND_IGNORE_EXISTING_KEY: basic = param; xferlen = 24; buf = scsi_malloc(task, xferlen); if (buf == NULL) goto err; memset(buf, 0, xferlen); scsi_set_uint64(&buf[0], basic->reservation_key); scsi_set_uint64(&buf[8], basic->service_action_reservation_key); if (basic->spec_i_pt) { buf[20] |= 0x08; } if (basic->all_tg_pt) { buf[20] |= 0x04; } if (basic->aptpl) { buf[20] |= 0x01; } break; case SCSI_PERSISTENT_RESERVE_REGISTER_AND_MOVE: /* XXX FIXME */ goto err; default: goto err; } task->cdb[0] = SCSI_OPCODE_PERSISTENT_RESERVE_OUT; task->cdb[1] |= sa & 0x1f; task->cdb[2] = ((scope << 4) & 0xf0) | (type & 0x0f); scsi_set_uint32(&task->cdb[5], xferlen); task->cdb_size = 10; task->xfer_dir = SCSI_XFER_WRITE; task->expxferlen = xferlen; iov->iov_base = buf; iov->iov_len = xferlen; scsi_task_set_iov_out(task, iov, 1); return task; err: scsi_free_scsi_task(task); return NULL; } /* * WRITE_SAME10 */ struct scsi_task * scsi_cdb_writesame10(int wrprotect, int anchor, int unmap, uint32_t lba, int group, uint16_t num_blocks, uint32_t datalen) { struct scsi_task *task; task = malloc(sizeof(struct scsi_task)); if (task == NULL) { return NULL; } memset(task, 0, sizeof(struct scsi_task)); task->cdb[0] = SCSI_OPCODE_WRITE_SAME10; if (wrprotect) { task->cdb[1] |= ((wrprotect & 0x7) << 5); } if (anchor) { task->cdb[1] |= 0x10; } if (unmap) { task->cdb[1] |= 0x08; } scsi_set_uint32(&task->cdb[2], lba); if (group) { task->cdb[6] |= (group & 0x1f); } scsi_set_uint16(&task->cdb[7], num_blocks); task->cdb_size = 10; task->xfer_dir = SCSI_XFER_WRITE; task->expxferlen = datalen; return task; } /* * WRITE_SAME16 */ struct scsi_task * scsi_cdb_writesame16(int wrprotect, int anchor, int unmap, uint64_t lba, int group, uint32_t num_blocks, uint32_t datalen) { struct scsi_task *task; task = malloc(sizeof(struct scsi_task)); if (task == NULL) { return NULL; } memset(task, 0, sizeof(struct scsi_task)); task->cdb[0] = SCSI_OPCODE_WRITE_SAME16; if (wrprotect) { task->cdb[1] |= ((wrprotect & 0x7) << 5); } if (anchor) { task->cdb[1] |= 0x10; } if (unmap) { task->cdb[1] |= 0x08; } if (datalen == 0) { task->cdb[1] |= 0x01; } scsi_set_uint32(&task->cdb[2], lba >> 32); scsi_set_uint32(&task->cdb[6], lba & 0xffffffff); scsi_set_uint32(&task->cdb[10], num_blocks); if (group) { task->cdb[14] |= (group & 0x1f); } task->cdb_size = 16; task->xfer_dir = SCSI_XFER_WRITE; task->expxferlen = datalen; return task; } /* * MODESENSE6 */ struct scsi_task * scsi_cdb_modesense6(int dbd, enum scsi_modesense_page_control pc, enum scsi_modesense_page_code page_code, int sub_page_code, unsigned char alloc_len) { struct scsi_task *task; task = malloc(sizeof(struct scsi_task)); if (task == NULL) { return NULL; } memset(task, 0, sizeof(struct scsi_task)); task->cdb[0] = SCSI_OPCODE_MODESENSE6; if (dbd) { task->cdb[1] |= 0x08; } task->cdb[2] = pc<<6 | page_code; task->cdb[3] = sub_page_code; task->cdb[4] = alloc_len; task->cdb_size = 6; if (alloc_len != 0) { task->xfer_dir = SCSI_XFER_READ; } else { task->xfer_dir = SCSI_XFER_NONE; } task->expxferlen = alloc_len; return task; } /* * MODESENSE10 */ struct scsi_task * scsi_cdb_modesense10(int llbaa, int dbd, enum scsi_modesense_page_control pc, enum scsi_modesense_page_code page_code, int sub_page_code, unsigned char alloc_len) { struct scsi_task *task; task = malloc(sizeof(struct scsi_task)); if (task == NULL) { return NULL; } memset(task, 0, sizeof(struct scsi_task)); task->cdb[0] = SCSI_OPCODE_MODESENSE10; if (llbaa) { task->cdb[1] |= 0x10; } if (dbd) { task->cdb[1] |= 0x08; } task->cdb[2] = pc<<6 | page_code; task->cdb[3] = sub_page_code; scsi_set_uint16(&task->cdb[7], alloc_len); task->cdb_size = 10; if (alloc_len != 0) { task->xfer_dir = SCSI_XFER_READ; } else { task->xfer_dir = SCSI_XFER_NONE; } task->expxferlen = alloc_len; return task; } /* * MODESELECT6 */ struct scsi_task * scsi_cdb_modeselect6(int pf, int sp, int param_len) { struct scsi_task *task; task = malloc(sizeof(struct scsi_task)); if (task == NULL) { return NULL; } memset(task, 0, sizeof(struct scsi_task)); task->cdb[0] = SCSI_OPCODE_MODESELECT6; if (pf) { task->cdb[1] |= 0x10; } if (sp) { task->cdb[1] |= 0x01; } task->cdb[4] = param_len; task->cdb_size = 6; if (param_len != 0) { task->xfer_dir = SCSI_XFER_WRITE; } else { task->xfer_dir = SCSI_XFER_NONE; } task->expxferlen = param_len; return task; } /* * MODESELECT10 */ struct scsi_task * scsi_cdb_modeselect10(int pf, int sp, int param_len) { struct scsi_task *task; task = malloc(sizeof(struct scsi_task)); if (task == NULL) { return NULL; } memset(task, 0, sizeof(struct scsi_task)); task->cdb[0] = SCSI_OPCODE_MODESELECT10; if (pf) { task->cdb[1] |= 0x10; } if (sp) { task->cdb[1] |= 0x01; } scsi_set_uint16(&task->cdb[7], param_len); task->cdb_size = 10; if (param_len != 0) { task->xfer_dir = SCSI_XFER_WRITE; } else { task->xfer_dir = SCSI_XFER_NONE; } task->expxferlen = param_len; return task; } struct scsi_mode_page * scsi_modesense_get_page(struct scsi_mode_sense *ms, enum scsi_modesense_page_code page_code, int subpage_code) { struct scsi_mode_page *mp; for (mp = ms->pages; mp; mp = mp->next) { if (mp->page_code == page_code && mp->subpage_code == subpage_code) { return mp; } } return NULL; } /* * parse the data in blob and calculate the size of a full * modesense6 datain structure */ static int scsi_modesense_datain_getfullsize(struct scsi_task *task, int is_modesense6) { int len; if (is_modesense6) { len = task_get_uint8(task, 0) + 1; } else { len = task_get_uint16(task, 0) + 2; } return len; } static void scsi_parse_mode_caching(struct scsi_task *task, int pos, struct scsi_mode_page *mp) { mp->caching.ic = !!(task_get_uint8(task, pos) & 0x80); mp->caching.abpf = !!(task_get_uint8(task, pos) & 0x40); mp->caching.cap = !!(task_get_uint8(task, pos) & 0x20); mp->caching.disc = !!(task_get_uint8(task, pos) & 0x10); mp->caching.size = !!(task_get_uint8(task, pos) & 0x08); mp->caching.wce = !!(task_get_uint8(task, pos) & 0x04); mp->caching.mf = !!(task_get_uint8(task, pos) & 0x02); mp->caching.rcd = !!(task_get_uint8(task, pos) & 0x01); mp->caching.demand_read_retention_priority = (task_get_uint8(task, pos + 1) >> 4) & 0x0f; mp->caching.write_retention_priority = task_get_uint8(task, pos + 1) & 0x0f; mp->caching.disable_prefetch_transfer_length = task_get_uint16(task, pos + 2); mp->caching.minimum_prefetch = task_get_uint16(task, pos + 4); mp->caching.maximum_prefetch = task_get_uint16(task, pos + 6); mp->caching.maximum_prefetch_ceiling = task_get_uint16(task, pos + 8); mp->caching.fsw = !!(task_get_uint8(task, pos + 10) & 0x80); mp->caching.lbcss = !!(task_get_uint8(task, pos + 10) & 0x40); mp->caching.dra = !!(task_get_uint8(task, pos + 10) & 0x20); mp->caching.nv_dis = !!(task_get_uint8(task, pos + 10) & 0x01); mp->caching.number_of_cache_segments = task_get_uint8(task, pos + 11); mp->caching.cache_segment_size = task_get_uint16(task, pos + 12); } static void scsi_parse_mode_control(struct scsi_task *task, int pos, struct scsi_mode_page *mp) { mp->control.tst = (task_get_uint8(task, pos) >> 5) & 0x07; mp->control.tmf_only = !!(task_get_uint8(task, pos) & 0x10); mp->control.dpicz = !!(task_get_uint8(task, pos) & 0x08); mp->control.d_sense = !!(task_get_uint8(task, pos) & 0x04); mp->control.gltsd = !!(task_get_uint8(task, pos) & 0x02); mp->control.rlec = !!(task_get_uint8(task, pos) & 0x01); mp->control.queue_algorithm_modifier = (task_get_uint8(task, pos + 1) >> 4) & 0x0f; mp->control.nuar = task_get_uint8(task, pos + 1) & 0x08; mp->control.qerr = (task_get_uint8(task, pos + 1) >> 1) & 0x03; mp->control.vs = !!(task_get_uint8(task, pos + 2) & 0x80); mp->control.rac = !!(task_get_uint8(task, pos + 2) & 0x40); mp->control.ua_intlck_ctrl = (task_get_uint8(task, pos + 2) >> 4) & 0x0f; mp->control.swp = !!(task_get_uint8(task, pos + 2) & 0x08); mp->control.ato = !!(task_get_uint8(task, pos + 3) & 0x80); mp->control.tas = !!(task_get_uint8(task, pos + 3) & 0x40); mp->control.atmpe = !!(task_get_uint8(task, pos + 3) & 0x20); mp->control.rwwp = !!(task_get_uint8(task, pos + 3) & 0x10); mp->control.autoload_mode = !!(task_get_uint8(task, pos + 3) & 0x07); mp->control.busy_timeout_period = task_get_uint16(task, pos + 6); mp->control.extended_selftest_completion_time = task_get_uint16(task, pos + 8); } static void scsi_parse_mode_power_condition(struct scsi_task *task, int pos, struct scsi_mode_page *mp) { mp->power_condition.pm_bg_precedence = (task_get_uint8(task, pos) >> 6) & 0x03; mp->power_condition.standby_y = !!(task_get_uint8(task, pos) & 0x01); mp->power_condition.idle_c = !!(task_get_uint8(task, pos + 1) & 0x08); mp->power_condition.idle_b = !!(task_get_uint8(task, pos + 1) & 0x04); mp->power_condition.idle_a = !!(task_get_uint8(task, pos + 1) & 0x02); mp->power_condition.standby_z = !!(task_get_uint8(task, pos + 1) & 0x01); mp->power_condition.idle_a_condition_timer = task_get_uint32(task, pos + 2); mp->power_condition.standby_z_condition_timer = task_get_uint32(task, pos + 6); mp->power_condition.idle_b_condition_timer = task_get_uint32(task, pos + 10); mp->power_condition.idle_c_condition_timer = task_get_uint32(task, pos + 14); mp->power_condition.standby_y_condition_timer = task_get_uint32(task, pos + 18); mp->power_condition.ccf_idle = (task_get_uint8(task, pos + 37) >> 6) & 0x03; mp->power_condition.ccf_standby = (task_get_uint8(task, pos + 37) >> 4) & 0x03; mp->power_condition.ccf_stopped = (task_get_uint8(task, pos + 37) >> 2) & 0x03; } static void scsi_parse_mode_disconnect_reconnect(struct scsi_task *task, int pos, struct scsi_mode_page *mp) { mp->disconnect_reconnect.buffer_full_ratio = task_get_uint8(task, pos); mp->disconnect_reconnect.buffer_empty_ratio = task_get_uint8(task, pos + 1); mp->disconnect_reconnect.bus_inactivity_limit = task_get_uint16(task, pos + 2); mp->disconnect_reconnect.disconnect_time_limit = task_get_uint16(task, pos + 4); mp->disconnect_reconnect.connect_time_limit = task_get_uint16(task, pos + 6); mp->disconnect_reconnect.maximum_burst_size = task_get_uint16(task, pos + 8); mp->disconnect_reconnect.emdp = !!(task_get_uint8(task, pos + 10) & 0x80); mp->disconnect_reconnect.fair_arbitration = (task_get_uint8(task, pos + 10) >> 4) & 0x0f; mp->disconnect_reconnect.dimm = !!(task_get_uint8(task, pos + 10) & 0x08); mp->disconnect_reconnect.dtdc = task_get_uint8(task, pos + 10) & 0x07; mp->disconnect_reconnect.first_burst_size = task_get_uint16(task, pos + 12); } static void scsi_parse_mode_informational_exceptions_control(struct scsi_task *task, int pos, struct scsi_mode_page *mp) { mp->iec.perf = !!(task_get_uint8(task, pos) & 0x80); mp->iec.ebf = !!(task_get_uint8(task, pos) & 0x20); mp->iec.ewasc = !!(task_get_uint8(task, pos) & 0x10); mp->iec.dexcpt = !!(task_get_uint8(task, pos) & 0x08); mp->iec.test = !!(task_get_uint8(task, pos) & 0x04); mp->iec.ebackerr = !!(task_get_uint8(task, pos) & 0x02); mp->iec.logerr = !!(task_get_uint8(task, pos) & 0x01); mp->iec.mrie = task_get_uint8(task, pos + 1) & 0x0f; mp->iec.interval_timer = task_get_uint32(task, pos + 2); mp->iec.report_count = task_get_uint32(task, pos + 6); } /* * parse and unmarshall the mode sense data in buffer */ static struct scsi_mode_sense * scsi_modesense_datain_unmarshall(struct scsi_task *task, int is_modesense6) { struct scsi_mode_sense *ms; int hdr_len; int pos; if (is_modesense6) { hdr_len = 4; } else { hdr_len = 8; } if (task->datain.size < hdr_len) { return NULL; } ms = scsi_malloc(task, sizeof(struct scsi_mode_sense)); if (ms == NULL) { return NULL; } if (is_modesense6) { ms->mode_data_length = task_get_uint8(task, 0); ms->medium_type = task_get_uint8(task, 1); ms->device_specific_parameter = task_get_uint8(task, 2); ms->block_descriptor_length = task_get_uint8(task, 3); ms->pages = NULL; } else { ms->mode_data_length = task_get_uint16(task, 0); ms->medium_type = task_get_uint8(task, 2); ms->device_specific_parameter = task_get_uint8(task, 3); ms->longlba = task_get_uint8(task, 4) & 0x01; ms->block_descriptor_length = task_get_uint16(task, 6); ms->pages = NULL; } if (ms->mode_data_length + 1 > task->datain.size) { return NULL; } pos = hdr_len + ms->block_descriptor_length; while (pos < task->datain.size) { struct scsi_mode_page *mp; mp = scsi_malloc(task, sizeof(struct scsi_mode_page)); if (mp == NULL) { return ms; } mp->ps = task_get_uint8(task, pos) & 0x80; mp->spf = task_get_uint8(task, pos) & 0x40; mp->page_code = task_get_uint8(task, pos) & 0x3f; pos++; if (mp->spf) { mp->subpage_code = task_get_uint8(task, pos); mp->len = task_get_uint16(task, pos + 1); pos += 3; } else { mp->subpage_code = 0; mp->len = task_get_uint8(task, pos); pos++; } switch (mp->page_code) { case SCSI_MODEPAGE_CACHING: scsi_parse_mode_caching(task, pos, mp); break; case SCSI_MODEPAGE_CONTROL: scsi_parse_mode_control(task, pos, mp); break; case SCSI_MODEPAGE_DISCONNECT_RECONNECT: scsi_parse_mode_disconnect_reconnect(task, pos, mp); break; case SCSI_MODEPAGE_INFORMATIONAL_EXCEPTIONS_CONTROL: scsi_parse_mode_informational_exceptions_control(task, pos, mp); break; case SCSI_MODEPAGE_POWER_CONDITION: scsi_parse_mode_power_condition(task, pos, mp); break; default: /* TODO: process other pages, or add raw data to struct * scsi_mode_page. */ break; } mp->next = ms->pages; ms->pages = mp; pos += mp->len; } return ms; } static struct scsi_data * scsi_modesense_marshall_caching(struct scsi_task *task, struct scsi_mode_page *mp, int hdr_size) { struct scsi_data *data; data = scsi_malloc(task, sizeof(struct scsi_data)); data->size = 20 + hdr_size; data->data = scsi_malloc(task, data->size); if (mp->caching.ic) data->data[hdr_size + 2] |= 0x80; if (mp->caching.abpf) data->data[hdr_size + 2] |= 0x40; if (mp->caching.cap) data->data[hdr_size + 2] |= 0x20; if (mp->caching.disc) data->data[hdr_size + 2] |= 0x10; if (mp->caching.size) data->data[hdr_size + 2] |= 0x08; if (mp->caching.wce) data->data[hdr_size + 2] |= 0x04; if (mp->caching.mf) data->data[hdr_size + 2] |= 0x02; if (mp->caching.rcd) data->data[hdr_size + 2] |= 0x01; data->data[hdr_size + 3] |= (mp->caching.demand_read_retention_priority << 4) & 0xf0; data->data[hdr_size + 3] |= mp->caching.write_retention_priority & 0x0f; scsi_set_uint16(&data->data[hdr_size + 4], mp->caching.disable_prefetch_transfer_length); scsi_set_uint16(&data->data[hdr_size + 6], mp->caching.minimum_prefetch); scsi_set_uint16(&data->data[hdr_size + 8], mp->caching.maximum_prefetch); scsi_set_uint16(&data->data[hdr_size + 10], mp->caching.maximum_prefetch_ceiling); if (mp->caching.fsw) data->data[hdr_size + 12] |= 0x80; if (mp->caching.lbcss) data->data[hdr_size + 12] |= 0x40; if (mp->caching.dra) data->data[hdr_size + 12] |= 0x20; if (mp->caching.nv_dis) data->data[hdr_size + 12] |= 0x01; data->data[hdr_size + 13] = mp->caching.number_of_cache_segments; scsi_set_uint16(&data->data[hdr_size + 14], mp->caching.cache_segment_size); return data; } static struct scsi_data * scsi_modesense_marshall_control(struct scsi_task *task, struct scsi_mode_page *mp, int hdr_size) { struct scsi_data *data; data = scsi_malloc(task, sizeof(struct scsi_data)); data->size = 12 + hdr_size; data->data = scsi_malloc(task, data->size); data->data[hdr_size + 2] |= (mp->control.tst << 5) & 0xe0; if (mp->control.tmf_only) data->data[hdr_size + 2] |= 0x10; if (mp->control.dpicz) data->data[hdr_size + 2] |= 0x08; if (mp->control.d_sense) data->data[hdr_size + 2] |= 0x04; if (mp->control.gltsd) data->data[hdr_size + 2] |= 0x02; if (mp->control.rlec) data->data[hdr_size + 2] |= 0x01; data->data[hdr_size + 3] |= (mp->control.queue_algorithm_modifier << 4) & 0xf0; if (mp->control.nuar) data->data[hdr_size + 3] |= 0x08; data->data[hdr_size + 3] |= (mp->control.qerr << 1) & 0x06; if (mp->control.vs) data->data[hdr_size + 4] |= 0x80; if (mp->control.rac) data->data[hdr_size + 4] |= 0x40; data->data[hdr_size + 4] |= (mp->control.ua_intlck_ctrl << 4) & 0x30; if (mp->control.swp) data->data[hdr_size + 4] |= 0x08; if (mp->control.ato) data->data[hdr_size + 5] |= 0x80; if (mp->control.tas) data->data[hdr_size + 5] |= 0x40; if (mp->control.atmpe) data->data[hdr_size + 5] |= 0x20; if (mp->control.rwwp) data->data[hdr_size + 5] |= 0x10; data->data[hdr_size + 5] |= mp->control.autoload_mode & 0x07; scsi_set_uint16(&data->data[hdr_size + 8], mp->control.busy_timeout_period); scsi_set_uint16(&data->data[hdr_size + 10], mp->control.extended_selftest_completion_time); return data; } static struct scsi_data * scsi_modesense_marshall_power_condition(struct scsi_task *task, struct scsi_mode_page *mp, int hdr_size) { struct scsi_data *data; data = scsi_malloc(task, sizeof(struct scsi_data)); data->size = 40 + hdr_size; data->data = scsi_malloc(task, data->size); data->data[hdr_size + 2] |= (mp->power_condition.pm_bg_precedence << 6) & 0xc0; if (mp->power_condition.standby_y) data->data[hdr_size + 2] |= 0x01; if (mp->power_condition.idle_c) data->data[hdr_size + 3] |= 0x08; if (mp->power_condition.idle_b) data->data[hdr_size + 3] |= 0x04; if (mp->power_condition.idle_a) data->data[hdr_size + 3] |= 0x02; if (mp->power_condition.standby_z) data->data[hdr_size + 3] |= 0x01; scsi_set_uint32(&data->data[hdr_size + 4], mp->power_condition.idle_a_condition_timer); scsi_set_uint32(&data->data[hdr_size + 8], mp->power_condition.standby_z_condition_timer); scsi_set_uint32(&data->data[hdr_size + 12], mp->power_condition.idle_b_condition_timer); scsi_set_uint32(&data->data[hdr_size + 16], mp->power_condition.idle_c_condition_timer); scsi_set_uint32(&data->data[hdr_size + 20], mp->power_condition.standby_y_condition_timer); data->data[hdr_size + 39] |= (mp->power_condition.ccf_idle << 6) & 0xc0; data->data[hdr_size + 39] |= (mp->power_condition.ccf_standby << 4) & 0x30; data->data[hdr_size + 39] |= (mp->power_condition.ccf_stopped << 2) & 0x0c; return data; } static struct scsi_data * scsi_modesense_marshall_disconnect_reconnect(struct scsi_task *task, struct scsi_mode_page *mp, int hdr_size) { struct scsi_data *data; data = scsi_malloc(task, sizeof(struct scsi_data)); data->size = 16 + hdr_size; data->data = scsi_malloc(task, data->size); data->data[hdr_size + 2] = mp->disconnect_reconnect.buffer_full_ratio; data->data[hdr_size + 3] = mp->disconnect_reconnect.buffer_empty_ratio; scsi_set_uint16(&data->data[hdr_size + 4], mp->disconnect_reconnect.bus_inactivity_limit); scsi_set_uint16(&data->data[hdr_size + 6], mp->disconnect_reconnect.disconnect_time_limit); scsi_set_uint16(&data->data[hdr_size + 8], mp->disconnect_reconnect.connect_time_limit); scsi_set_uint16(&data->data[hdr_size + 10], mp->disconnect_reconnect.maximum_burst_size); if (mp->disconnect_reconnect.emdp) data->data[hdr_size + 12] |= 0x80; data->data[hdr_size + 12] |= (mp->disconnect_reconnect.fair_arbitration << 4) & 0x70; if (mp->disconnect_reconnect.dimm) data->data[hdr_size + 12] |= 0x08; data->data[hdr_size + 12] |= mp->disconnect_reconnect.dtdc & 0x07; scsi_set_uint16(&data->data[hdr_size + 14], mp->disconnect_reconnect.first_burst_size); return data; } static struct scsi_data * scsi_modesense_marshall_informational_exceptions_control(struct scsi_task *task, struct scsi_mode_page *mp, int hdr_size) { struct scsi_data *data; data = scsi_malloc(task, sizeof(struct scsi_data)); data->size = 12 + hdr_size; data->data = scsi_malloc(task, data->size); if (mp->iec.perf) data->data[hdr_size + 2] |= 0x80; if (mp->iec.ebf) data->data[hdr_size + 2] |= 0x20; if (mp->iec.ewasc) data->data[hdr_size + 2] |= 0x10; if (mp->iec.dexcpt) data->data[hdr_size + 2] |= 0x08; if (mp->iec.test) data->data[hdr_size + 2] |= 0x04; if (mp->iec.ebackerr) data->data[hdr_size + 2] |= 0x02; if (mp->iec.logerr) data->data[hdr_size + 2] |= 0x01; data->data[hdr_size + 3] |= mp->iec.mrie & 0x0f; scsi_set_uint32(&data->data[hdr_size + 4], mp->iec.interval_timer); scsi_set_uint32(&data->data[hdr_size + 8], mp->iec.report_count); return data; } /* * marshall the mode sense data out buffer */ struct scsi_data * scsi_modesense_dataout_marshall(struct scsi_task *task, struct scsi_mode_page *mp, int is_modeselect6) { struct scsi_data *data; int hdr_size = is_modeselect6 ? 4 : 8; switch (mp->page_code) { case SCSI_MODEPAGE_CACHING: data = scsi_modesense_marshall_caching(task, mp, hdr_size); break; case SCSI_MODEPAGE_CONTROL: data = scsi_modesense_marshall_control(task, mp, hdr_size); break; case SCSI_MODEPAGE_DISCONNECT_RECONNECT: data = scsi_modesense_marshall_disconnect_reconnect(task, mp, hdr_size); break; case SCSI_MODEPAGE_INFORMATIONAL_EXCEPTIONS_CONTROL: data = scsi_modesense_marshall_informational_exceptions_control(task, mp, hdr_size); break; case SCSI_MODEPAGE_POWER_CONDITION: data = scsi_modesense_marshall_power_condition(task, mp, hdr_size); break; default: /* TODO error reporting ? */ return NULL; } if (data == NULL) { return NULL; } data->data[hdr_size + 0] = mp->page_code & 0x3f; if (mp->ps) { data->data[hdr_size + 0] |= 0x80; } if (mp->spf) { data->data[hdr_size + 0] |= 0x40; data->data[hdr_size + 1] = mp->subpage_code; scsi_set_uint16(&data->data[hdr_size + 2], data->size -hdr_size - 4); } else { data->data[hdr_size + 1] = (data->size - hdr_size - 2) & 0xff; } return data; } /* * STARTSTOPUNIT */ struct scsi_task * scsi_cdb_startstopunit(int immed, int pcm, int pc, int no_flush, int loej, int start) { struct scsi_task *task; task = malloc(sizeof(struct scsi_task)); if (task == NULL) { return NULL; } memset(task, 0, sizeof(struct scsi_task)); task->cdb[0] = SCSI_OPCODE_STARTSTOPUNIT; if (immed) { task->cdb[1] |= 0x01; } task->cdb[3] |= pcm & 0x0f; task->cdb[4] |= (pc << 4) & 0xf0; if (no_flush) { task->cdb[4] |= 0x04; } if (loej) { task->cdb[4] |= 0x02; } if (start) { task->cdb[4] |= 0x01; } task->cdb_size = 6; task->xfer_dir = SCSI_XFER_NONE; task->expxferlen = 0; return task; } /* * PREVENTALLOWMEDIUMREMOVAL */ struct scsi_task * scsi_cdb_preventallow(int prevent) { struct scsi_task *task; task = malloc(sizeof(struct scsi_task)); if (task == NULL) { return NULL; } memset(task, 0, sizeof(struct scsi_task)); task->cdb[0] = SCSI_OPCODE_PREVENTALLOW; task->cdb[4] = prevent & 0x03; task->cdb_size = 6; task->xfer_dir = SCSI_XFER_NONE; task->expxferlen = 0; return task; } /* * SYNCHRONIZECACHE10 */ struct scsi_task * scsi_cdb_synchronizecache10(int lba, int num_blocks, int syncnv, int immed) { struct scsi_task *task; task = malloc(sizeof(struct scsi_task)); if (task == NULL) { return NULL; } memset(task, 0, sizeof(struct scsi_task)); task->cdb[0] = SCSI_OPCODE_SYNCHRONIZECACHE10; if (syncnv) { task->cdb[1] |= 0x04; } if (immed) { task->cdb[1] |= 0x02; } scsi_set_uint32(&task->cdb[2], lba); scsi_set_uint16(&task->cdb[7], num_blocks); task->cdb_size = 10; task->xfer_dir = SCSI_XFER_NONE; task->expxferlen = 0; return task; } /* * SYNCHRONIZECACHE16 */ struct scsi_task * scsi_cdb_synchronizecache16(uint64_t lba, uint32_t num_blocks, int syncnv, int immed) { struct scsi_task *task; task = malloc(sizeof(struct scsi_task)); if (task == NULL) { return NULL; } memset(task, 0, sizeof(struct scsi_task)); task->cdb[0] = SCSI_OPCODE_SYNCHRONIZECACHE16; if (syncnv) { task->cdb[1] |= 0x04; } if (immed) { task->cdb[1] |= 0x02; } scsi_set_uint32(&task->cdb[2], lba >> 32); scsi_set_uint32(&task->cdb[6], lba & 0xffffffff); scsi_set_uint32(&task->cdb[10], num_blocks); task->cdb_size = 16; task->xfer_dir = SCSI_XFER_NONE; task->expxferlen = 0; return task; } /* * PREFETCH10 */ struct scsi_task * scsi_cdb_prefetch10(uint32_t lba, int num_blocks, int immed, int group) { struct scsi_task *task; task = malloc(sizeof(struct scsi_task)); if (task == NULL) { return NULL; } memset(task, 0, sizeof(struct scsi_task)); task->cdb[0] = SCSI_OPCODE_PREFETCH10; if (immed) { task->cdb[1] |= 0x02; } scsi_set_uint32(&task->cdb[2], lba); task->cdb[6] |= group & 0x1f; scsi_set_uint16(&task->cdb[7], num_blocks); task->cdb_size = 10; task->xfer_dir = SCSI_XFER_NONE; task->expxferlen = 0; return task; } /* * PREFETCH16 */ struct scsi_task * scsi_cdb_prefetch16(uint64_t lba, int num_blocks, int immed, int group) { struct scsi_task *task; task = malloc(sizeof(struct scsi_task)); if (task == NULL) { return NULL; } memset(task, 0, sizeof(struct scsi_task)); task->cdb[0] = SCSI_OPCODE_PREFETCH16; if (immed) { task->cdb[1] |= 0x02; } scsi_set_uint32(&task->cdb[2], lba >> 32); scsi_set_uint32(&task->cdb[6], lba & 0xffffffff); scsi_set_uint32(&task->cdb[10], num_blocks); task->cdb[14] |= group & 0x1f; task->cdb_size = 16; task->xfer_dir = SCSI_XFER_NONE; task->expxferlen = 0; return task; } /* * SERVICEACTIONIN16 */ struct scsi_task * scsi_cdb_serviceactionin16(enum scsi_service_action_in sa, uint32_t xferlen) { struct scsi_task *task; task = malloc(sizeof(struct scsi_task)); if (task == NULL) { return NULL; } memset(task, 0, sizeof(struct scsi_task)); task->cdb[0] = SCSI_OPCODE_SERVICE_ACTION_IN; task->cdb[1] = sa; scsi_set_uint32(&task->cdb[10], xferlen); task->cdb_size = 16; if (xferlen != 0) { task->xfer_dir = SCSI_XFER_READ; } else { task->xfer_dir = SCSI_XFER_NONE; } task->expxferlen = xferlen; return task; } /* * READCAPACITY16 */ struct scsi_task * scsi_cdb_readcapacity16(void) { return scsi_cdb_serviceactionin16(SCSI_READCAPACITY16, 32); } /* * GET_LBA_STATUS */ struct scsi_task * scsi_cdb_get_lba_status(uint64_t starting_lba, uint32_t alloc_len) { struct scsi_task *task; task = malloc(sizeof(struct scsi_task)); if (task == NULL) { return NULL; } memset(task, 0, sizeof(struct scsi_task)); task->cdb[0] = SCSI_OPCODE_SERVICE_ACTION_IN; task->cdb[1] = SCSI_GET_LBA_STATUS; scsi_set_uint32(&task->cdb[2], starting_lba >> 32); scsi_set_uint32(&task->cdb[6], starting_lba & 0xffffffff); scsi_set_uint32(&task->cdb[10], alloc_len); task->cdb_size = 16; if (alloc_len != 0) { task->xfer_dir = SCSI_XFER_READ; } else { task->xfer_dir = SCSI_XFER_NONE; } task->expxferlen = alloc_len; return task; } /* * WRITEVERIFY10 */ struct scsi_task * scsi_cdb_writeverify10(uint32_t lba, uint32_t xferlen, int blocksize, int wrprotect, int dpo, int bytchk, int group_number) { struct scsi_task *task; task = malloc(sizeof(struct scsi_task)); if (task == NULL) { return NULL; } memset(task, 0, sizeof(struct scsi_task)); task->cdb[0] = SCSI_OPCODE_WRITE_VERIFY10; task->cdb[1] |= ((wrprotect & 0x07) << 5); if (dpo) { task->cdb[1] |= 0x10; } if (bytchk) { task->cdb[1] |= 0x02; } scsi_set_uint32(&task->cdb[2], lba); scsi_set_uint16(&task->cdb[7], xferlen/blocksize); task->cdb[6] |= (group_number & 0x1f); task->cdb_size = 10; if (xferlen != 0) { task->xfer_dir = SCSI_XFER_WRITE; } else { task->xfer_dir = SCSI_XFER_NONE; } task->expxferlen = xferlen; return task; } /* * WRITEVERIFY12 */ struct scsi_task * scsi_cdb_writeverify12(uint32_t lba, uint32_t xferlen, int blocksize, int wrprotect, int dpo, int bytchk, int group_number) { struct scsi_task *task; task = malloc(sizeof(struct scsi_task)); if (task == NULL) { return NULL; } memset(task, 0, sizeof(struct scsi_task)); task->cdb[0] = SCSI_OPCODE_WRITE_VERIFY12; task->cdb[1] |= ((wrprotect & 0x07) << 5); if (dpo) { task->cdb[1] |= 0x10; } if (bytchk) { task->cdb[1] |= 0x02; } scsi_set_uint32(&task->cdb[2], lba); scsi_set_uint32(&task->cdb[6], xferlen/blocksize); task->cdb[10] |= (group_number & 0x1f); task->cdb_size = 12; if (xferlen != 0) { task->xfer_dir = SCSI_XFER_WRITE; } else { task->xfer_dir = SCSI_XFER_NONE; } task->expxferlen = xferlen; return task; } /* * WRITEVERIFY16 */ struct scsi_task * scsi_cdb_writeverify16(uint64_t lba, uint32_t xferlen, int blocksize, int wrprotect, int dpo, int bytchk, int group_number) { struct scsi_task *task; task = malloc(sizeof(struct scsi_task)); if (task == NULL) { return NULL; } memset(task, 0, sizeof(struct scsi_task)); task->cdb[0] = SCSI_OPCODE_WRITE_VERIFY16; task->cdb[1] |= ((wrprotect & 0x07) << 5); if (dpo) { task->cdb[1] |= 0x10; } if (bytchk) { task->cdb[1] |= 0x02; } scsi_set_uint32(&task->cdb[2], lba >> 32); scsi_set_uint32(&task->cdb[6], lba & 0xffffffff); scsi_set_uint32(&task->cdb[10], xferlen/blocksize); task->cdb[14] |= (group_number & 0x1f); task->cdb_size = 16; if (xferlen != 0) { task->xfer_dir = SCSI_XFER_WRITE; } else { task->xfer_dir = SCSI_XFER_NONE; } task->expxferlen = xferlen; return task; } /* * EXTENDED COPY */ struct scsi_task * scsi_cdb_extended_copy(int param_len) { struct scsi_task *task; task = malloc(sizeof(struct scsi_task)); if (task == NULL) return NULL; memset(task, 0, sizeof(struct scsi_task)); task->cdb[0] = SCSI_OPCODE_EXTENDED_COPY; task->cdb[10] = (param_len >> 24) & 0xFF; task->cdb[11] = (param_len >> 16) & 0xFF; task->cdb[12] = (param_len >> 8) & 0xFF; task->cdb[13] = param_len & 0xFF; /* Inititalize other fields in CDB */ task->cdb_size = 16; task->xfer_dir = SCSI_XFER_WRITE; task->expxferlen = param_len; return task; } /* * RECEIVE COPY RESULTS */ struct scsi_task * scsi_cdb_receive_copy_results(enum scsi_copy_results_sa sa, int list_id, int xferlen) { struct scsi_task *task; task = malloc(sizeof(struct scsi_task)); if (task == NULL) { return NULL; } memset(task, 0, sizeof(struct scsi_task)); task->cdb[0] = SCSI_OPCODE_RECEIVE_COPY_RESULTS; task->cdb[1] |= sa & 0x1f; task->cdb[2] = list_id & 0xFF; scsi_set_uint32(&task->cdb[10], xferlen); task->cdb_size = 16; if (xferlen != 0) { task->xfer_dir = SCSI_XFER_READ; } else { task->xfer_dir = SCSI_XFER_NONE; } task->expxferlen = xferlen; return task; } int scsi_datain_getfullsize(struct scsi_task *task) { switch (task->cdb[0]) { case SCSI_OPCODE_TESTUNITREADY: return 0; case SCSI_OPCODE_INQUIRY: return scsi_inquiry_datain_getfullsize(task); case SCSI_OPCODE_MODESENSE6: return scsi_modesense_datain_getfullsize(task, 1); case SCSI_OPCODE_READCAPACITY10: return scsi_readcapacity10_datain_getfullsize(task); case SCSI_OPCODE_SYNCHRONIZECACHE10: return 0; case SCSI_OPCODE_READTOC: return scsi_readtoc_datain_getfullsize(task); case SCSI_OPCODE_REPORTLUNS: return scsi_reportluns_datain_getfullsize(task); case SCSI_OPCODE_PERSISTENT_RESERVE_IN: return scsi_persistentreservein_datain_getfullsize(task); case SCSI_OPCODE_MAINTENANCE_IN: return scsi_maintenancein_datain_getfullsize(task); } return -1; } void * scsi_datain_unmarshall(struct scsi_task *task) { if (!task || !task->datain.size) return NULL; switch (task->cdb[0]) { case SCSI_OPCODE_INQUIRY: return scsi_inquiry_datain_unmarshall(task); case SCSI_OPCODE_MODESENSE6: return scsi_modesense_datain_unmarshall(task, 1); case SCSI_OPCODE_MODESENSE10: return scsi_modesense_datain_unmarshall(task, 0); case SCSI_OPCODE_READCAPACITY10: return scsi_readcapacity10_datain_unmarshall(task); case SCSI_OPCODE_READTOC: return scsi_readtoc_datain_unmarshall(task); case SCSI_OPCODE_REPORTLUNS: return scsi_reportluns_datain_unmarshall(task); case SCSI_OPCODE_SERVICE_ACTION_IN: return scsi_serviceactionin_datain_unmarshall(task); case SCSI_OPCODE_PERSISTENT_RESERVE_IN: return scsi_persistentreservein_datain_unmarshall(task); case SCSI_OPCODE_MAINTENANCE_IN: return scsi_maintenancein_datain_unmarshall(task); case SCSI_OPCODE_RECEIVE_COPY_RESULTS: return scsi_receivecopyresults_datain_unmarshall(task); } return NULL; } static struct scsi_read6_cdb * scsi_read6_cdb_unmarshall(struct scsi_task *task) { struct scsi_read6_cdb *read6; read6 = scsi_malloc(task, sizeof(struct scsi_read6_cdb)); if (read6 == NULL) { return NULL; } read6->opcode = SCSI_OPCODE_READ6; read6->lba = scsi_get_uint32(&task->cdb[0]) & 0x001fffff; read6->transfer_length = task->cdb[4]; read6->control = task->cdb[5]; return read6; } static struct scsi_read10_cdb * scsi_read10_cdb_unmarshall(struct scsi_task *task) { struct scsi_read10_cdb *read10; read10 = scsi_malloc(task, sizeof(struct scsi_read10_cdb)); if (read10 == NULL) { return NULL; } read10->opcode = SCSI_OPCODE_READ10; read10->rdprotect = (task->cdb[1] >> 5) & 0x7; read10->dpo = !!(task->cdb[1] & 0x10); read10->fua = !!(task->cdb[1] & 0x08); read10->fua_nv = !!(task->cdb[1] & 0x02); read10->lba = scsi_get_uint32(&task->cdb[2]); read10->group = task->cdb[6] & 0x1f; read10->transfer_length = scsi_get_uint16(&task->cdb[7]); read10->control = task->cdb[9]; return read10; } static struct scsi_read12_cdb * scsi_read12_cdb_unmarshall(struct scsi_task *task) { struct scsi_read12_cdb *read12; read12 = scsi_malloc(task, sizeof(struct scsi_read12_cdb)); if (read12 == NULL) { return NULL; } read12->opcode = SCSI_OPCODE_READ12; read12->rdprotect = (task->cdb[1] >> 5) & 0x7; read12->dpo = !!(task->cdb[1] & 0x10); read12->fua = !!(task->cdb[1] & 0x08); read12->rarc = !!(task->cdb[1] & 0x04); read12->fua_nv = !!(task->cdb[1] & 0x02); read12->lba = scsi_get_uint32(&task->cdb[2]); read12->transfer_length = scsi_get_uint32(&task->cdb[6]); read12->group = task->cdb[10] & 0x1f; read12->control = task->cdb[11]; return read12; } static struct scsi_read16_cdb * scsi_read16_cdb_unmarshall(struct scsi_task *task) { struct scsi_read16_cdb *read16; read16 = scsi_malloc(task, sizeof(struct scsi_read16_cdb)); if (read16 == NULL) { return NULL; } read16->opcode = SCSI_OPCODE_READ16; read16->rdprotect = (task->cdb[1] >> 5) & 0x7; read16->dpo = !!(task->cdb[1] & 0x10); read16->fua = !!(task->cdb[1] & 0x08); read16->rarc = !!(task->cdb[1] & 0x04); read16->fua_nv = !!(task->cdb[1] & 0x02); read16->lba = scsi_get_uint64(&task->cdb[2]); read16->transfer_length = scsi_get_uint32(&task->cdb[10]); read16->group = task->cdb[14] & 0x1f; read16->control = task->cdb[15]; return read16; } static struct scsi_verify10_cdb * scsi_verify10_cdb_unmarshall(struct scsi_task *task) { struct scsi_verify10_cdb *verify10; verify10 = scsi_malloc(task, sizeof(struct scsi_verify10_cdb)); if (verify10 == NULL) { return NULL; } verify10->opcode = SCSI_OPCODE_VERIFY10; verify10->vrprotect = (task->cdb[1] >> 5) & 0x7; verify10->dpo = !!(task->cdb[1] & 0x10); verify10->bytchk = !!(task->cdb[1] & 0x02); verify10->lba = scsi_get_uint32(&task->cdb[2]); verify10->group = task->cdb[6] & 0x1f; verify10->verification_length = scsi_get_uint16(&task->cdb[7]); verify10->control = task->cdb[9]; return verify10; } static struct scsi_verify12_cdb * scsi_verify12_cdb_unmarshall(struct scsi_task *task) { struct scsi_verify12_cdb *verify12; verify12 = scsi_malloc(task, sizeof(struct scsi_verify12_cdb)); if (verify12 == NULL) { return NULL; } verify12->opcode = SCSI_OPCODE_VERIFY12; verify12->vrprotect = (task->cdb[1] >> 5) & 0x7; verify12->dpo = !!(task->cdb[1] & 0x10); verify12->bytchk = !!(task->cdb[1] & 0x02); verify12->lba = scsi_get_uint32(&task->cdb[2]); verify12->verification_length = scsi_get_uint32(&task->cdb[6]); verify12->group = task->cdb[10] & 0x1f; verify12->control = task->cdb[11]; return verify12; } static struct scsi_verify16_cdb * scsi_verify16_cdb_unmarshall(struct scsi_task *task) { struct scsi_verify16_cdb *verify16; verify16 = scsi_malloc(task, sizeof(struct scsi_verify16_cdb)); if (verify16 == NULL) { return NULL; } verify16->opcode = SCSI_OPCODE_VERIFY16; verify16->vrprotect = (task->cdb[1] >> 5) & 0x7; verify16->dpo = !!(task->cdb[1] & 0x10); verify16->bytchk = !!(task->cdb[1] & 0x02); verify16->lba = scsi_get_uint64(&task->cdb[2]); verify16->verification_length = scsi_get_uint32(&task->cdb[10]); verify16->group = task->cdb[14] & 0x1f; verify16->control = task->cdb[15]; return verify16; } static struct scsi_write10_cdb * scsi_write10_cdb_unmarshall(struct scsi_task *task) { struct scsi_write10_cdb *write10; write10 = scsi_malloc(task, sizeof(struct scsi_write10_cdb)); if (write10 == NULL) { return NULL; } write10->opcode = SCSI_OPCODE_WRITE10; write10->wrprotect = (task->cdb[1] >> 5) & 0x7; write10->dpo = !!(task->cdb[1] & 0x10); write10->fua = !!(task->cdb[1] & 0x08); write10->fua_nv = !!(task->cdb[1] & 0x02); write10->lba = scsi_get_uint32(&task->cdb[2]); write10->group = task->cdb[6] & 0x1f; write10->transfer_length = scsi_get_uint16(&task->cdb[7]); write10->control = task->cdb[9]; return write10; } static struct scsi_write12_cdb * scsi_write12_cdb_unmarshall(struct scsi_task *task) { struct scsi_write12_cdb *write12; write12 = scsi_malloc(task, sizeof(struct scsi_write12_cdb)); if (write12 == NULL) { return NULL; } write12->opcode = SCSI_OPCODE_WRITE12; write12->wrprotect = (task->cdb[1] >> 5) & 0x7; write12->dpo = !!(task->cdb[1] & 0x10); write12->fua = !!(task->cdb[1] & 0x08); write12->fua_nv = !!(task->cdb[1] & 0x02); write12->lba = scsi_get_uint32(&task->cdb[2]); write12->transfer_length = scsi_get_uint32(&task->cdb[6]); write12->group = task->cdb[10] & 0x1f; write12->control = task->cdb[11]; return write12; } static struct scsi_write16_cdb * scsi_write16_cdb_unmarshall(struct scsi_task *task) { struct scsi_write16_cdb *write16; write16 = scsi_malloc(task, sizeof(struct scsi_write16_cdb)); if (write16 == NULL) { return NULL; } write16->opcode = SCSI_OPCODE_WRITE16; write16->wrprotect = (task->cdb[1] >> 5) & 0x7; write16->dpo = !!(task->cdb[1] & 0x10); write16->fua = !!(task->cdb[1] & 0x08); write16->fua_nv = !!(task->cdb[1] & 0x02); write16->lba = scsi_get_uint64(&task->cdb[2]); write16->transfer_length = scsi_get_uint32(&task->cdb[10]); write16->group = task->cdb[14] & 0x1f; write16->control = task->cdb[15]; return write16; } static struct scsi_writeatomic16_cdb * scsi_writeatomic16_cdb_unmarshall(struct scsi_task *task) { struct scsi_writeatomic16_cdb *writeatomic16; writeatomic16 = scsi_malloc(task, sizeof(struct scsi_writeatomic16_cdb)); if (writeatomic16 == NULL) { return NULL; } writeatomic16->opcode = SCSI_OPCODE_WRITE_ATOMIC16; writeatomic16->wrprotect = (task->cdb[1] >> 5) & 0x7; writeatomic16->dpo = !!(task->cdb[1] & 0x10); writeatomic16->fua = !!(task->cdb[1] & 0x08); writeatomic16->lba = scsi_get_uint64(&task->cdb[2]); writeatomic16->transfer_length = scsi_get_uint16(&task->cdb[12]); writeatomic16->group = task->cdb[14] & 0x1f; writeatomic16->control = task->cdb[15]; return writeatomic16; } void * scsi_cdb_unmarshall(struct scsi_task *task, enum scsi_opcode opcode) { if (task->cdb[0] != opcode) { return NULL; } switch (task->cdb[0]) { case SCSI_OPCODE_READ6: return scsi_read6_cdb_unmarshall(task); case SCSI_OPCODE_READ10: return scsi_read10_cdb_unmarshall(task); case SCSI_OPCODE_READ12: return scsi_read12_cdb_unmarshall(task); case SCSI_OPCODE_READ16: return scsi_read16_cdb_unmarshall(task); case SCSI_OPCODE_VERIFY10: return scsi_verify10_cdb_unmarshall(task); case SCSI_OPCODE_VERIFY12: return scsi_verify12_cdb_unmarshall(task); case SCSI_OPCODE_VERIFY16: return scsi_verify16_cdb_unmarshall(task); case SCSI_OPCODE_WRITE10: return scsi_write10_cdb_unmarshall(task); case SCSI_OPCODE_WRITE12: return scsi_write12_cdb_unmarshall(task); case SCSI_OPCODE_WRITE16: return scsi_write16_cdb_unmarshall(task); case SCSI_OPCODE_WRITE_ATOMIC16: return scsi_writeatomic16_cdb_unmarshall(task); } return NULL; } const char * scsi_devtype_to_str(enum scsi_inquiry_peripheral_device_type type) { switch (type) { case SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_DIRECT_ACCESS: return "DIRECT_ACCESS"; case SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_SEQUENTIAL_ACCESS: return "SEQUENTIAL_ACCESS"; case SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_PRINTER: return "PRINTER"; case SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_PROCESSOR: return "PROCESSOR"; case SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_WRITE_ONCE: return "WRITE_ONCE"; case SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_MMC: return "MMC"; case SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_SCANNER: return "SCANNER"; case SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_OPTICAL_MEMORY: return "OPTICAL_MEMORY"; case SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_MEDIA_CHANGER: return "MEDIA_CHANGER"; case SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_COMMUNICATIONS: return "COMMUNICATIONS"; case SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_STORAGE_ARRAY_CONTROLLER: return "STORAGE_ARRAY_CONTROLLER"; case SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_ENCLOSURE_SERVICES: return "ENCLOSURE_SERVICES"; case SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_SIMPLIFIED_DIRECT_ACCESS: return "SIMPLIFIED_DIRECT_ACCESS"; case SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_OPTICAL_CARD_READER: return "OPTICAL_CARD_READER"; case SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_BRIDGE_CONTROLLER: return "BRIDGE_CONTROLLER"; case SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_OSD: return "OSD"; case SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_AUTOMATION: return "AUTOMATION"; case SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_SEQURITY_MANAGER: return "SEQURITY_MANAGER"; case SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_WELL_KNOWN_LUN: return "WELL_KNOWN_LUN"; case SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_UNKNOWN: return "UNKNOWN"; } return "unknown"; } const char * scsi_devqualifier_to_str(enum scsi_inquiry_peripheral_qualifier qualifier) { switch (qualifier) { case SCSI_INQUIRY_PERIPHERAL_QUALIFIER_CONNECTED: return "CONNECTED"; case SCSI_INQUIRY_PERIPHERAL_QUALIFIER_DISCONNECTED: return "DISCONNECTED"; case SCSI_INQUIRY_PERIPHERAL_QUALIFIER_NOT_SUPPORTED: return "NOT_SUPPORTED"; } return "unknown"; } const char * scsi_version_to_str(enum scsi_version version) { switch (version) { case SCSI_VERSION_SPC: return "ANSI INCITS 301-1997 (SPC)"; case SCSI_VERSION_SPC2: return "ANSI INCITS 351-2001 (SPC-2)"; case SCSI_VERSION_SPC3: return "ANSI INCITS 408-2005 (SPC-3)"; } return "unknown"; } const char * scsi_version_descriptor_to_str(enum scsi_version_descriptor version_descriptor) { switch (version_descriptor) { case SCSI_VERSION_DESCRIPTOR_ISCSI: return "iSCSI"; case SCSI_VERSION_DESCRIPTOR_SBC: return "SBC"; case SCSI_VERSION_DESCRIPTOR_SBC_ANSI_INCITS_306_1998: return "SBC ANSI INCITS 306-1998"; case SCSI_VERSION_DESCRIPTOR_SBC_T10_0996_D_R08C: return "SBC T10/0996-D revision 08c"; case SCSI_VERSION_DESCRIPTOR_SBC_2: return "SBC-2"; case SCSI_VERSION_DESCRIPTOR_SBC_2_ISO_IEC_14776_322: return "SBC-2 ISO/IEC 14776-322"; case SCSI_VERSION_DESCRIPTOR_SBC_2_ANSI_INCITS_405_2005: return "SBC-2 ANSI INCITS 405-2005"; case SCSI_VERSION_DESCRIPTOR_SBC_2_T10_1417_D_R16: return "SBC-2 T10/1417-D revision 16"; case SCSI_VERSION_DESCRIPTOR_SBC_2_T10_1417_D_R5A: return "SBC-2 T10/1417-D revision 5A"; case SCSI_VERSION_DESCRIPTOR_SBC_2_T10_1417_D_R15: return "SBC-2 T10/1417-D revision 15"; case SCSI_VERSION_DESCRIPTOR_SBC_3: return "SBC-3"; case SCSI_VERSION_DESCRIPTOR_SPC: return "SPC"; case SCSI_VERSION_DESCRIPTOR_SPC_ANSI_INCITS_301_1997: return "SPC ANSI INCITS 301-1997"; case SCSI_VERSION_DESCRIPTOR_SPC_T10_0995_D_R11A: return "SPC T10/0995-D revision 11a"; case SCSI_VERSION_DESCRIPTOR_SPC_2: return "SPC-2"; case SCSI_VERSION_DESCRIPTOR_SPC_2_ISO_IEC_14776_452: return "SPC-2 ISO.IEC 14776-452"; case SCSI_VERSION_DESCRIPTOR_SPC_2_ANSI_INCITS_351_2001: return "SPC-2 ANSI INCITS 351-2001"; case SCSI_VERSION_DESCRIPTOR_SPC_2_T10_1236_D_R20: return "SPC-2 T10/1236-D revision 20"; case SCSI_VERSION_DESCRIPTOR_SPC_2_T10_1236_D_R12: return "SPC-2 T10/1236-D revision 12"; case SCSI_VERSION_DESCRIPTOR_SPC_2_T10_1236_D_R18: return "SPC-2 T10/1236-D revision 18"; case SCSI_VERSION_DESCRIPTOR_SPC_2_T10_1236_D_R19: return "SPC-2 T10/1236-D revision 19"; case SCSI_VERSION_DESCRIPTOR_SPC_3: return "SPC-3"; case SCSI_VERSION_DESCRIPTOR_SPC_3_ISO_IEC_14776_453: return "SPC-3 ISO/IEC 14776-453"; case SCSI_VERSION_DESCRIPTOR_SPC_3_ANSI_INCITS_408_2005: return "SPC-3 ANSI INCITS 408-2005"; case SCSI_VERSION_DESCRIPTOR_SPC_3_T10_1416_D_R7: return "SPC-3 T10/1416-D revision 7"; case SCSI_VERSION_DESCRIPTOR_SPC_3_T10_1416_D_R21: return "SPC-3 T10/1416-D revision 21"; case SCSI_VERSION_DESCRIPTOR_SPC_3_T10_1416_D_R22: return "SPC-3 T10/1416-D revision 22"; case SCSI_VERSION_DESCRIPTOR_SPC_3_T10_1416_D_R23: return "SPC-3 T10/1416-D revision 23"; case SCSI_VERSION_DESCRIPTOR_SPC_4: return "SPC-4"; case SCSI_VERSION_DESCRIPTOR_SPC_4_T10_1731_D_R16: return "SPC-4 T10/1731-D revision 16"; case SCSI_VERSION_DESCRIPTOR_SPC_4_T10_1731_D_R18: return "SPC-4 T10/1731-D revision 18"; case SCSI_VERSION_DESCRIPTOR_SPC_4_T10_1731_D_R23: return "SPC-4 T10/1731-D revision 23"; case SCSI_VERSION_DESCRIPTOR_SSC: return "SSC"; case SCSI_VERSION_DESCRIPTOR_UAS_T10_2095D_R04: return "UAS T10/2095-D revision 04"; } return "unknown"; } const char * scsi_inquiry_pagecode_to_str(int pagecode) { switch (pagecode) { case SCSI_INQUIRY_PAGECODE_SUPPORTED_VPD_PAGES: return "SUPPORTED_VPD_PAGES"; case SCSI_INQUIRY_PAGECODE_UNIT_SERIAL_NUMBER: return "UNIT_SERIAL_NUMBER"; case SCSI_INQUIRY_PAGECODE_DEVICE_IDENTIFICATION: return "DEVICE_IDENTIFICATION"; case SCSI_INQUIRY_PAGECODE_BLOCK_DEVICE_CHARACTERISTICS: return "BLOCK_DEVICE_CHARACTERISTICS"; } return "unknown"; } const char * scsi_protocol_identifier_to_str(int identifier) { switch (identifier) { case SCSI_PROTOCOL_IDENTIFIER_FIBRE_CHANNEL: return "FIBRE_CHANNEL"; case SCSI_PROTOCOL_IDENTIFIER_PARALLEL_SCSI: return "PARALLEL_SCSI"; case SCSI_PROTOCOL_IDENTIFIER_SSA: return "SSA"; case SCSI_PROTOCOL_IDENTIFIER_IEEE_1394: return "IEEE_1394"; case SCSI_PROTOCOL_IDENTIFIER_RDMA: return "RDMA"; case SCSI_PROTOCOL_IDENTIFIER_ISCSI: return "ISCSI"; case SCSI_PROTOCOL_IDENTIFIER_SAS: return "SAS"; case SCSI_PROTOCOL_IDENTIFIER_ADT: return "ADT"; case SCSI_PROTOCOL_IDENTIFIER_ATA: return "ATA"; } return "unknown"; } const char * scsi_codeset_to_str(int codeset) { switch (codeset) { case SCSI_CODESET_BINARY: return "BINARY"; case SCSI_CODESET_ASCII: return "ASCII"; case SCSI_CODESET_UTF8: return "UTF8"; } return "unknown"; } const char * scsi_association_to_str(int association) { switch (association) { case SCSI_ASSOCIATION_LOGICAL_UNIT: return "LOGICAL_UNIT"; case SCSI_ASSOCIATION_TARGET_PORT: return "TARGET_PORT"; case SCSI_ASSOCIATION_TARGET_DEVICE: return "TARGET_DEVICE"; } return "unknown"; } const char * scsi_designator_type_to_str(int type) { switch (type) { case SCSI_DESIGNATOR_TYPE_VENDOR_SPECIFIC: return "VENDOR_SPECIFIC"; case SCSI_DESIGNATOR_TYPE_T10_VENDORT_ID: return "T10_VENDORT_ID"; case SCSI_DESIGNATOR_TYPE_EUI_64: return "EUI_64"; case SCSI_DESIGNATOR_TYPE_NAA: return "NAA"; case SCSI_DESIGNATOR_TYPE_RELATIVE_TARGET_PORT: return "RELATIVE_TARGET_PORT"; case SCSI_DESIGNATOR_TYPE_TARGET_PORT_GROUP: return "TARGET_PORT_GROUP"; case SCSI_DESIGNATOR_TYPE_LOGICAL_UNIT_GROUP: return "LOGICAL_UNIT_GROUP"; case SCSI_DESIGNATOR_TYPE_MD5_LOGICAL_UNIT_IDENTIFIER: return "MD5_LOGICAL_UNIT_IDENTIFIER"; case SCSI_DESIGNATOR_TYPE_SCSI_NAME_STRING: return "SCSI_NAME_STRING"; } return "unknown"; } void scsi_set_task_private_ptr(struct scsi_task *task, void *ptr) { task->ptr = ptr; } void * scsi_get_task_private_ptr(struct scsi_task *task) { return task->ptr; } void scsi_task_set_iov_out(struct scsi_task *task, struct scsi_iovec *iov, int niov) { task->iovector_out.iov = iov; task->iovector_out.niov = niov; } void scsi_task_set_iov_in(struct scsi_task *task, struct scsi_iovec *iov, int niov) { task->iovector_in.iov = iov; task->iovector_in.niov = niov; } void scsi_task_reset_iov(struct scsi_iovector *iovector) { iovector->offset = 0; iovector->consumed = 0; } #define IOVECTOR_INITAL_ALLOC (16) static int scsi_iovector_add(struct scsi_task *task, struct scsi_iovector *iovector, int len, unsigned char *buf) { if (len < 0) { return -1; } if (iovector->iov == NULL) { iovector->iov = scsi_malloc(task, IOVECTOR_INITAL_ALLOC*sizeof(struct iovec)); if (iovector->iov == NULL) { return -1; } iovector->nalloc = IOVECTOR_INITAL_ALLOC; } /* iovec allocation is too small */ if (iovector->nalloc < iovector->niov + 1) { struct scsi_iovec *old_iov = iovector->iov; iovector->iov = scsi_malloc(task, 2 * iovector->nalloc * sizeof(struct iovec)); if (iovector->iov == NULL) { return -1; } memcpy(iovector->iov, old_iov, iovector->niov * sizeof(struct iovec)); iovector->nalloc <<= 1; } iovector->iov[iovector->niov].iov_len = len; iovector->iov[iovector->niov].iov_base = buf; iovector->niov++; return 0; } int scsi_task_add_data_in_buffer(struct scsi_task *task, int len, unsigned char *buf) { return scsi_iovector_add(task, &task->iovector_in, len, buf); } int scsi_task_add_data_out_buffer(struct scsi_task *task, int len, unsigned char *buf) { return scsi_iovector_add(task, &task->iovector_out, len, buf); } int scsi_task_get_status(struct scsi_task *task, struct scsi_sense *sense) { if (sense) { memcpy(sense, &task->sense, sizeof(struct scsi_sense)); } return task->status; } libiscsi-1.17.0/lib/socket.c000066400000000000000000000641221271371262700156200ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2010 by Ronnie Sahlberg This program 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 program is distributed in the hope that 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 program; if not, see . */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #ifdef HAVE_SYS_TYPES_H #include #endif #ifdef HAVE_ARPA_INET_H #include #endif #ifdef HAVE_UNISTD_H #include #endif #ifdef HAVE_POLL_H #include #endif #ifdef AROS #include "aros/aros_compat.h" #endif #if defined(WIN32) #include #include #include "win32/win32_compat.h" #define ioctl ioctlsocket #define close closesocket #else #include #include #include #include #include #include #endif #ifdef NEED_SYS_FILIO_H #include #endif #include #include #include #include #include #include #include #include #include "scsi-lowlevel.h" #include "iscsi.h" #include "iscsi-private.h" #include "slist.h" static uint32_t iface_rr = 0; void iscsi_add_to_outqueue(struct iscsi_context *iscsi, struct iscsi_pdu *pdu) { struct iscsi_pdu *current = iscsi->outqueue; struct iscsi_pdu *last = NULL; if (iscsi->scsi_timeout > 0) { pdu->scsi_timeout = time(NULL) + iscsi->scsi_timeout; } else { pdu->scsi_timeout = 0; } if (iscsi->outqueue == NULL) { iscsi->outqueue = pdu; pdu->next = NULL; return; } /* queue pdus in ascending order of CmdSN. * ensure that pakets with the same CmdSN are kept in FIFO order. * immediate PDUs are queued in front of queue with the CmdSN * of the first element in the outqueue. */ if (pdu->outdata.data[0] & ISCSI_PDU_IMMEDIATE) { iscsi_pdu_set_cmdsn(pdu, current->cmdsn); } do { if (iscsi_serial32_compare(pdu->cmdsn, current->cmdsn) < 0 || (pdu->outdata.data[0] & ISCSI_PDU_IMMEDIATE && !(current->outdata.data[0] & ISCSI_PDU_IMMEDIATE))) { /* insert PDU before the current */ if (last != NULL) { last->next=pdu; } else { iscsi->outqueue=pdu; } pdu->next = current; return; } last=current; current=current->next; } while (current != NULL); last->next = pdu; pdu->next = NULL; } void iscsi_decrement_iface_rr() { iface_rr--; } static void set_nonblocking(int fd) { #if defined(WIN32) unsigned long opt = 1; ioctlsocket(fd, FIONBIO, &opt); #else unsigned v; v = fcntl(fd, F_GETFL, 0); fcntl(fd, F_SETFL, v | O_NONBLOCK); #endif } static int set_tcp_sockopt(int sockfd, int optname, int value) { int level; #ifndef SOL_TCP struct protoent *buf; if ((buf = getprotobyname("tcp")) != NULL) level = buf->p_proto; else return -1; #else level = SOL_TCP; #endif return setsockopt(sockfd, level, optname, (char *)&value, sizeof(value)); } #ifndef TCP_USER_TIMEOUT #define TCP_USER_TIMEOUT 18 #endif static int set_tcp_user_timeout(struct iscsi_context *iscsi) { if (set_tcp_sockopt(iscsi->fd, TCP_USER_TIMEOUT, iscsi->tcp_user_timeout) != 0) { iscsi_set_error(iscsi, "TCP: Failed to set tcp user timeout. Error %s(%d)", strerror(errno), errno); return -1; } ISCSI_LOG(iscsi, 3, "TCP_USER_TIMEOUT set to %d",iscsi->tcp_user_timeout); return 0; } #ifndef TCP_SYNCNT #define TCP_SYNCNT 7 #endif static int set_tcp_syncnt(struct iscsi_context *iscsi) { if (set_tcp_sockopt(iscsi->fd, TCP_SYNCNT, iscsi->tcp_syncnt) != 0) { iscsi_set_error(iscsi, "TCP: Failed to set tcp syn retries. Error %s(%d)", strerror(errno), errno); return -1; } ISCSI_LOG(iscsi, 3, "TCP_SYNCNT set to %d",iscsi->tcp_syncnt); return 0; } union socket_address { struct sockaddr_in sin; struct sockaddr_in6 sin6; struct sockaddr sa; }; int iscsi_connect_async(struct iscsi_context *iscsi, const char *portal, iscsi_command_cb cb, void *private_data) { int port = 3260; char *str; char *addr, *host; struct addrinfo *ai = NULL; union socket_address sa; int socksize; ISCSI_LOG(iscsi, 2, "connecting to portal %s",portal); if (iscsi->fd != -1) { iscsi_set_error(iscsi, "Trying to connect but already connected."); return -1; } addr = iscsi_strdup(iscsi, portal); if (addr == NULL) { iscsi_set_error(iscsi, "Out-of-memory: " "Failed to strdup portal address."); return -1; } host = addr; /* check if we have a target portal group tag */ str = strrchr(host, ','); if (str != NULL) { str[0] = 0; } str = strrchr(host, ':'); if (str != NULL) { if (strchr(str, ']') == NULL) { if (str != NULL) { port = atoi(str+1); str[0] = 0; } } } /* ipv6 in [...] form ? */ if (host[0] == '[') { host ++; str = strchr(host, ']'); if (str == NULL) { iscsi_free(iscsi, addr); iscsi_set_error(iscsi, "Invalid target:%s " "Missing ']' in IPv6 address", portal); return -1; } *str = 0; } /* is it a hostname ? */ if (getaddrinfo(host, NULL, NULL, &ai) != 0) { iscsi_free(iscsi, addr); iscsi_set_error(iscsi, "Invalid target:%s " "Can not resolv into IPv4/v6.", portal); return -1; } iscsi_free(iscsi, addr); memset(&sa, 0, sizeof(sa)); switch (ai->ai_family) { case AF_INET: socksize = sizeof(struct sockaddr_in); memcpy(&sa.sin, ai->ai_addr, socksize); sa.sin.sin_port = htons(port); #ifdef HAVE_SOCK_SIN_LEN sa.sin.sin_len = socksize; #endif break; #ifdef HAVE_SOCKADDR_IN6 case AF_INET6: socksize = sizeof(struct sockaddr_in6); memcpy(&sa.sin6, ai->ai_addr, socksize); sa.sin6.sin6_port = htons(port); #ifdef HAVE_SOCK_SIN_LEN sa.sin6.sin6_len = socksize; #endif break; #endif default: iscsi_set_error(iscsi, "Unknown address family :%d. " "Only IPv4/IPv6 supported so far.", ai->ai_family); freeaddrinfo(ai); return -1; } iscsi->fd = socket(ai->ai_family, SOCK_STREAM, 0); if (iscsi->fd == -1) { freeaddrinfo(ai); iscsi_set_error(iscsi, "Failed to open iscsi socket. " "Errno:%s(%d).", strerror(errno), errno); return -1; } if (iscsi->old_iscsi && iscsi->fd != iscsi->old_iscsi->fd) { if (dup2(iscsi->fd, iscsi->old_iscsi->fd) == -1) { return -1; } close(iscsi->fd); iscsi->fd = iscsi->old_iscsi->fd; } iscsi->socket_status_cb = cb; iscsi->connect_data = private_data; set_nonblocking(iscsi->fd); iscsi_set_tcp_keepalive(iscsi, iscsi->tcp_keepidle, iscsi->tcp_keepcnt, iscsi->tcp_keepintvl); if (iscsi->tcp_user_timeout > 0) { set_tcp_user_timeout(iscsi); } if (iscsi->tcp_syncnt > 0) { set_tcp_syncnt(iscsi); } #if __linux if (iscsi->bind_interfaces[0]) { char *pchr = iscsi->bind_interfaces, *pchr2; int iface_n = iface_rr++%iscsi->bind_interfaces_cnt; int iface_c = 0; do { pchr2 = strchr(pchr,','); if (iface_c == iface_n) { if (pchr2) pchr2[0]=0x00; break; } if (pchr2) {pchr=pchr2+1;} iface_c++; } while (pchr2); int res = setsockopt(iscsi->fd, SOL_SOCKET, SO_BINDTODEVICE, pchr, strlen(pchr)); if (res < 0) { ISCSI_LOG(iscsi,1,"failed to bind to interface '%s': %s",pchr,strerror(errno)); } else { ISCSI_LOG(iscsi,3,"successfully bound to interface '%s'",pchr); } if (pchr2) pchr2[0]=','; } #endif if (set_tcp_sockopt(iscsi->fd, TCP_NODELAY, 1) != 0) { ISCSI_LOG(iscsi,1,"failed to set TCP_NODELAY sockopt: %s",strerror(errno)); } else { ISCSI_LOG(iscsi,3,"TCP_NODELAY set to 1"); } if (connect(iscsi->fd, &sa.sa, socksize) != 0 #if defined(WIN32) && WSAGetLastError() != WSAEWOULDBLOCK) { #else && errno != EINPROGRESS) { #endif iscsi_set_error(iscsi, "Connect failed with errno : " "%s(%d)", strerror(errno), errno); close(iscsi->fd); iscsi->fd = -1; freeaddrinfo(ai); return -1; } freeaddrinfo(ai); strncpy(iscsi->connected_portal,portal,MAX_STRING_SIZE); return 0; } int iscsi_disconnect(struct iscsi_context *iscsi) { if (iscsi->fd == -1) { iscsi_set_error(iscsi, "Trying to disconnect " "but not connected"); return -1; } close(iscsi->fd); if (!(iscsi->pending_reconnect && iscsi->old_iscsi) && iscsi->connected_portal[0]) { ISCSI_LOG(iscsi, 2, "disconnected from portal %s",iscsi->connected_portal); } iscsi->fd = -1; iscsi->is_connected = 0; iscsi->is_corked = 0; return 0; } int iscsi_get_fd(struct iscsi_context *iscsi) { if (iscsi->old_iscsi) { return iscsi->old_iscsi->fd; } return iscsi->fd; } int iscsi_which_events(struct iscsi_context *iscsi) { int events = iscsi->is_connected ? POLLIN : POLLOUT; if (iscsi->pending_reconnect && iscsi->old_iscsi && time(NULL) < iscsi->next_reconnect) { return 0; } if (iscsi->outqueue_current != NULL || (iscsi->outqueue != NULL && !iscsi->is_corked && (iscsi_serial32_compare(iscsi->outqueue->cmdsn, iscsi->maxcmdsn) <= 0 || iscsi->outqueue->outdata.data[0] & ISCSI_PDU_IMMEDIATE) ) ) { events |= POLLOUT; } return events; } int iscsi_queue_length(struct iscsi_context *iscsi) { int i = 0; struct iscsi_pdu *pdu; for (pdu = iscsi->outqueue; pdu; pdu = pdu->next) { i++; } for (pdu = iscsi->waitpdu; pdu; pdu = pdu->next) { i++; } if (iscsi->is_connected == 0) { i++; } return i; } int iscsi_out_queue_length(struct iscsi_context *iscsi) { int i = 0; struct iscsi_pdu *pdu; for (pdu = iscsi->outqueue; pdu; pdu = pdu->next) { i++; } return i; } ssize_t iscsi_iovector_readv_writev(struct iscsi_context *iscsi, struct scsi_iovector *iovector, uint32_t pos, ssize_t count, int do_write) { struct scsi_iovec *iov, *iov2; int niov; uint32_t len2; size_t _len2; ssize_t n; if (iovector->iov == NULL) { errno = EINVAL; return -1; } if (pos < iovector->offset) { iscsi_set_error(iscsi, "iovector reset. pos is smaller than" "current offset"); errno = EINVAL; return -1; } if (iovector->niov <= iovector->consumed) { /* someone issued a read/write but did not provide enough user buffers for all the data. * maybe someone tried to read just 512 bytes off a MMC device? */ errno = EINVAL; return -1; } /* iov is a pointer to the first iovec to pass */ iov = &iovector->iov[iovector->consumed]; pos -= iovector->offset; /* forward until iov points to the first iov to pass */ while (pos >= iov->iov_len) { iovector->offset += iov->iov_len; iovector->consumed++; pos -= iov->iov_len; if (iovector->niov <= iovector->consumed) { errno = EINVAL; return -1; } iov = &iovector->iov[iovector->consumed]; } iov2 = iov; /* iov2 is a pointer to the last iovec to pass */ niov = 1; /* number of iovectors to pass */ len2 = pos + count; /* adjust length of iov2 */ /* forward until iov2 points to the last iovec we pass later. it might happen that we have a lot of iovectors but are limited by count */ while (len2 > iov2->iov_len) { niov++; if (iovector->niov < iovector->consumed + niov) { errno = EINVAL; return -1; } len2 -= iov2->iov_len; iov2 = &iovector->iov[iovector->consumed + niov - 1]; } /* we might limit the length of the last iovec we pass to readv/writev store its orignal length to restore it later */ _len2 = iov2->iov_len; /* adjust base+len of start iovec and len of last iovec */ iov2->iov_len = len2; iov->iov_base = (void*) ((uintptr_t)iov->iov_base + pos); iov->iov_len -= pos; if (do_write) { n = writev(iscsi->fd, (struct iovec*) iov, niov); } else { n = readv(iscsi->fd, (struct iovec*) iov, niov); } /* restore original values */ iov->iov_base = (void*) ((uintptr_t)iov->iov_base - pos); iov->iov_len += pos; iov2->iov_len = _len2; if (n > count) { /* we read/write more bytes than expected, this MUST not happen */ errno = EINVAL; return -1; } return n; } static int iscsi_read_from_socket(struct iscsi_context *iscsi) { struct iscsi_in_pdu *in; ssize_t data_size, count, padding_size; if (iscsi->incoming == NULL) { iscsi->incoming = iscsi_szmalloc(iscsi, sizeof(struct iscsi_in_pdu)); if (iscsi->incoming == NULL) { iscsi_set_error(iscsi, "Out-of-memory: failed to malloc iscsi_in_pdu"); return -1; } } in = iscsi->incoming; /* first we must read the header, including any digests */ if (in->hdr_pos < ISCSI_HEADER_SIZE) { /* try to only read the header, the socket is nonblocking, so * no need to limit the read to what is available in the socket */ count = ISCSI_HEADER_SIZE - in->hdr_pos; count = recv(iscsi->fd, &in->hdr[in->hdr_pos], count, 0); if (count == 0) { return -1; } if (count < 0) { if (errno == EINTR || errno == EAGAIN) { return 0; } iscsi_set_error(iscsi, "read from socket failed, " "errno:%d", errno); return -1; } in->hdr_pos += count; } if (in->hdr_pos < ISCSI_HEADER_SIZE) { /* we don't have the full header yet, so return */ return 0; } padding_size = iscsi_get_pdu_padding_size(&in->hdr[0]); data_size = iscsi_get_pdu_data_size(&in->hdr[0]) + padding_size; if (data_size < 0 || data_size > (ssize_t)iscsi->initiator_max_recv_data_segment_length) { iscsi_set_error(iscsi, "Invalid data size received from target (%d)", (int)data_size); return -1; } if (data_size != 0) { unsigned char padding_buf[3]; unsigned char *buf = padding_buf; struct scsi_iovector * iovector_in; count = data_size - in->data_pos; /* first try to see if we already have a user buffer */ iovector_in = iscsi_get_scsi_task_iovector_in(iscsi, in); if (iovector_in != NULL && count > padding_size) { uint32_t offset = scsi_get_uint32(&in->hdr[40]); count = iscsi_iovector_readv_writev(iscsi, iovector_in, in->data_pos + offset, count - padding_size, 0); } else { if (iovector_in == NULL) { if (in->data == NULL) { in->data = iscsi_malloc(iscsi, data_size); if (in->data == NULL) { iscsi_set_error(iscsi, "Out-of-memory: failed to malloc iscsi_in_pdu->data(%d)", (int)data_size); return -1; } } buf = &in->data[in->data_pos]; } count = recv(iscsi->fd, buf, count, 0); } if (count == 0) { return -1; } if (count < 0) { if (errno == EINTR || errno == EAGAIN) { return 0; } iscsi_set_error(iscsi, "read from socket failed, " "errno:%d %s", errno, iscsi_get_error(iscsi)); return -1; } in->data_pos += count; } if (in->data_pos < data_size) { return 0; } ISCSI_LIST_ADD_END(&iscsi->inqueue, in); iscsi->incoming = NULL; while (iscsi->inqueue != NULL) { struct iscsi_in_pdu *current = iscsi->inqueue; if (iscsi_process_pdu(iscsi, current) != 0) { return -1; } ISCSI_LIST_REMOVE(&iscsi->inqueue, current); iscsi_free_iscsi_in_pdu(iscsi, current); } return 0; } static int iscsi_write_to_socket(struct iscsi_context *iscsi) { ssize_t count; size_t total; struct iscsi_pdu *pdu; static char padding_buf[3]; int socket_flags = 0; #ifdef MSG_NOSIGNAL socket_flags |= MSG_NOSIGNAL; #elif SO_NOSIGPIPE socket_flags |= SO_NOSIGPIPE; #endif if (iscsi->fd == -1) { iscsi_set_error(iscsi, "trying to write but not connected"); return -1; } while (iscsi->outqueue != NULL || iscsi->outqueue_current != NULL) { if (iscsi->outqueue_current == NULL) { if (iscsi->is_corked) { /* connection is corked we are not allowed to send * additional PDUs */ ISCSI_LOG(iscsi, 6, "iscsi_write_to_socket: socket is corked"); return 0; } if (iscsi_serial32_compare(iscsi->outqueue->cmdsn, iscsi->maxcmdsn) > 0 && !(iscsi->outqueue->outdata.data[0] & ISCSI_PDU_IMMEDIATE)) { /* stop sending for non-immediate PDUs. maxcmdsn is reached */ ISCSI_LOG(iscsi, 6, "iscsi_write_to_socket: maxcmdsn reached (outqueue[0]->cmdsnd %08x > maxcmdsn %08x)", iscsi->outqueue->cmdsn, iscsi->maxcmdsn); return 0; } /* pop first element of the outqueue */ if (iscsi_serial32_compare(iscsi->outqueue->cmdsn, iscsi->expcmdsn) < 0 && (iscsi->outqueue->outdata.data[0] & 0x3f) != ISCSI_PDU_DATA_OUT) { iscsi_set_error(iscsi, "iscsi_write_to_socket: outqueue[0]->cmdsn < expcmdsn (%08x < %08x) opcode %02x", iscsi->outqueue->cmdsn, iscsi->expcmdsn, iscsi->outqueue->outdata.data[0] & 0x3f); return -1; } iscsi->outqueue_current = iscsi->outqueue; /* set exp statsn */ iscsi_pdu_set_expstatsn(iscsi->outqueue_current, iscsi->statsn + 1); ISCSI_LIST_REMOVE(&iscsi->outqueue, iscsi->outqueue_current); if (!(iscsi->outqueue_current->flags & ISCSI_PDU_DELETE_WHEN_SENT)) { /* we have to add the pdu to the waitqueue already here since the storage might sent a R2T as soon as it has received the header. if we sent immediate data in a cmd PDU the R2T might get lost otherwise. */ ISCSI_LIST_ADD_END(&iscsi->waitpdu, iscsi->outqueue_current); } } pdu = iscsi->outqueue_current; pdu->outdata.size = (pdu->outdata.size + 3) & 0xfffffffc; /* Write header and any immediate data */ if (pdu->outdata_written < pdu->outdata.size) { count = send(iscsi->fd, pdu->outdata.data + pdu->outdata_written, pdu->outdata.size - pdu->outdata_written, socket_flags); if (count == -1) { if (errno == EAGAIN || errno == EWOULDBLOCK) { return 0; } iscsi_set_error(iscsi, "Error when writing to " "socket :%d", errno); return -1; } pdu->outdata_written += count; } /* if we havent written the full header yet. */ if (pdu->outdata_written != pdu->outdata.size) { return 0; } /* Write any iovectors that might have been passed to us */ while (pdu->payload_written < pdu->payload_len) { struct scsi_iovector* iovector_out; iovector_out = iscsi_get_scsi_task_iovector_out(iscsi, pdu); if (iovector_out == NULL) { iscsi_set_error(iscsi, "Can't find iovector data for DATA-OUT"); return -1; } count = iscsi_iovector_readv_writev(iscsi, iovector_out, pdu->payload_offset + pdu->payload_written, pdu->payload_len - pdu->payload_written, 1); if (count == -1) { if (errno == EAGAIN || errno == EWOULDBLOCK) { return 0; } iscsi_set_error(iscsi, "Error when writing to " "socket :%d %s", errno, iscsi_get_error(iscsi)); return -1; } pdu->payload_written += count; } total = pdu->payload_len; total = (total + 3) & 0xfffffffc; /* Write padding */ if (pdu->payload_written < total) { count = send(iscsi->fd, padding_buf, total - pdu->payload_written, socket_flags); if (count == -1) { if (errno == EAGAIN || errno == EWOULDBLOCK) { return 0; } iscsi_set_error(iscsi, "Error when writing to " "socket :%d", errno); return -1; } pdu->payload_written += count; } /* if we havent written the full padding yet. */ if (pdu->payload_written != total) { return 0; } if (pdu->flags & ISCSI_PDU_CORK_WHEN_SENT) { iscsi->is_corked = 1; } if (pdu->flags & ISCSI_PDU_DELETE_WHEN_SENT) { iscsi_free_pdu(iscsi, pdu); } iscsi->outqueue_current = NULL; } return 0; } static int iscsi_service_reconnect_if_loggedin(struct iscsi_context *iscsi) { if (iscsi->is_loggedin) { if (iscsi_reconnect(iscsi) == 0) { return 0; } } if (iscsi->old_iscsi) { if (!iscsi->pending_reconnect) { iscsi_reconnect_cb(iscsi, SCSI_STATUS_ERROR, NULL, NULL); } return 0; } iscsi_set_error(iscsi, "iscsi_service_reconnect_if_loggedin. Can not " "reconnect right now.\n"); return -1; } int iscsi_service(struct iscsi_context *iscsi, int revents) { if (iscsi->fd < 0) { return 0; } if (iscsi->pending_reconnect) { if (time(NULL) >= iscsi->next_reconnect) { return iscsi_reconnect(iscsi); } else { if (iscsi->old_iscsi) { return 0; } } } if (revents & POLLERR) { int err = 0; socklen_t err_size = sizeof(err); if (getsockopt(iscsi->fd, SOL_SOCKET, SO_ERROR, (char *)&err, &err_size) != 0 || err != 0) { if (err == 0) { err = errno; } iscsi_set_error(iscsi, "iscsi_service: socket error " "%s(%d).", strerror(err), err); } else { iscsi_set_error(iscsi, "iscsi_service: POLLERR, " "Unknown socket error."); } if (iscsi->socket_status_cb) { iscsi->socket_status_cb(iscsi, SCSI_STATUS_ERROR, NULL, iscsi->connect_data); iscsi->socket_status_cb = NULL; } return iscsi_service_reconnect_if_loggedin(iscsi); } if (revents & POLLHUP) { iscsi_set_error(iscsi, "iscsi_service: POLLHUP, " "socket error."); if (iscsi->socket_status_cb) { iscsi->socket_status_cb(iscsi, SCSI_STATUS_ERROR, NULL, iscsi->connect_data); iscsi->socket_status_cb = NULL; } return iscsi_service_reconnect_if_loggedin(iscsi); } if (iscsi->is_connected == 0 && revents&POLLOUT) { int err = 0; socklen_t err_size = sizeof(err); struct sockaddr_in local; socklen_t local_l = sizeof(local); if (getsockopt(iscsi->fd, SOL_SOCKET, SO_ERROR, (char *)&err, &err_size) != 0 || err != 0) { if (err == 0) { err = errno; } iscsi_set_error(iscsi, "iscsi_service: socket error " "%s(%d) while connecting.", strerror(err), err); if (iscsi->socket_status_cb) { iscsi->socket_status_cb(iscsi, SCSI_STATUS_ERROR, NULL, iscsi->connect_data); iscsi->socket_status_cb = NULL; } return iscsi_service_reconnect_if_loggedin(iscsi); } if (getsockname(iscsi->fd, (struct sockaddr *) &local, &local_l) == 0) { ISCSI_LOG(iscsi, 2, "connection established (%s:%u -> %s)", inet_ntoa(local.sin_addr), (unsigned)ntohs(local.sin_port),iscsi->connected_portal); } iscsi->is_connected = 1; if (iscsi->socket_status_cb) { iscsi->socket_status_cb(iscsi, SCSI_STATUS_GOOD, NULL, iscsi->connect_data); iscsi->socket_status_cb = NULL; } return 0; } if (revents & POLLIN) { if (iscsi_read_from_socket(iscsi) != 0) { return iscsi_service_reconnect_if_loggedin(iscsi); } } if (revents & POLLOUT) { if (iscsi_write_to_socket(iscsi) != 0) { return iscsi_service_reconnect_if_loggedin(iscsi); } } iscsi_timeout_scan(iscsi); return 0; } int iscsi_queue_pdu(struct iscsi_context *iscsi, struct iscsi_pdu *pdu) { if (pdu == NULL) { iscsi_set_error(iscsi, "trying to queue NULL pdu"); return -1; } if (iscsi->header_digest != ISCSI_HEADER_DIGEST_NONE) { unsigned long crc; if (pdu->outdata.size < ISCSI_RAW_HEADER_SIZE + 4) { iscsi_set_error(iscsi, "PDU too small (%u) to contain header digest", (unsigned int) pdu->outdata.size); return -1; } crc = crc32c((char *)pdu->outdata.data, ISCSI_RAW_HEADER_SIZE); pdu->outdata.data[ISCSI_RAW_HEADER_SIZE+3] = (crc >> 24)&0xff; pdu->outdata.data[ISCSI_RAW_HEADER_SIZE+2] = (crc >> 16)&0xff; pdu->outdata.data[ISCSI_RAW_HEADER_SIZE+1] = (crc >> 8)&0xff; pdu->outdata.data[ISCSI_RAW_HEADER_SIZE+0] = (crc) &0xff; } iscsi_add_to_outqueue(iscsi, pdu); return 0; } void iscsi_free_iscsi_in_pdu(struct iscsi_context *iscsi, struct iscsi_in_pdu *in) { iscsi_free(iscsi, in->data); in->data=NULL; iscsi_sfree(iscsi, in); in=NULL; } void iscsi_free_iscsi_inqueue(struct iscsi_context *iscsi, struct iscsi_in_pdu *inqueue) { while (inqueue != NULL) { struct iscsi_in_pdu *next = inqueue->next; iscsi_free_iscsi_in_pdu(iscsi, inqueue); inqueue = next; } } void iscsi_set_tcp_syncnt(struct iscsi_context *iscsi, int value) { iscsi->tcp_syncnt=value; ISCSI_LOG(iscsi, 2, "TCP_SYNCNT will be set to %d on next socket creation",value); } void iscsi_set_tcp_user_timeout(struct iscsi_context *iscsi, int value) { iscsi->tcp_user_timeout=value; ISCSI_LOG(iscsi, 2, "TCP_USER_TIMEOUT will be set to %dms on next socket creation",value); } void iscsi_set_tcp_keepidle(struct iscsi_context *iscsi, int value) { iscsi->tcp_keepidle=value; ISCSI_LOG(iscsi, 2, "TCP_KEEPIDLE will be set to %d on next socket creation",value); } void iscsi_set_tcp_keepcnt(struct iscsi_context *iscsi, int value) { iscsi->tcp_keepcnt=value; ISCSI_LOG(iscsi, 2, "TCP_KEEPCNT will be set to %d on next socket creation",value); } void iscsi_set_tcp_keepintvl(struct iscsi_context *iscsi, int value) { iscsi->tcp_keepintvl=value; ISCSI_LOG(iscsi, 2, "TCP_KEEPINTVL will be set to %d on next socket creation",value); } int iscsi_set_tcp_keepalive(struct iscsi_context *iscsi, int idle _U_, int count _U_, int interval _U_) { #ifdef SO_KEEPALIVE int value = 1; if (setsockopt(iscsi->fd, SOL_SOCKET, SO_KEEPALIVE, (char *)&value, sizeof(value)) != 0) { iscsi_set_error(iscsi, "TCP: Failed to set socket option SO_KEEPALIVE. Error %s(%d)", strerror(errno), errno); return -1; } ISCSI_LOG(iscsi, 3, "SO_KEEPALIVE set to %d",value); #ifdef TCP_KEEPCNT if (set_tcp_sockopt(iscsi->fd, TCP_KEEPCNT, count) != 0) { iscsi_set_error(iscsi, "TCP: Failed to set tcp keepalive count. Error %s(%d)", strerror(errno), errno); return -1; } ISCSI_LOG(iscsi, 3, "TCP_KEEPCNT set to %d",count); #endif #ifdef TCP_KEEPINTVL if (set_tcp_sockopt(iscsi->fd, TCP_KEEPINTVL, interval) != 0) { iscsi_set_error(iscsi, "TCP: Failed to set tcp keepalive interval. Error %s(%d)", strerror(errno), errno); return -1; } ISCSI_LOG(iscsi, 3, "TCP_KEEPINTVL set to %d",interval); #endif #ifdef TCP_KEEPIDLE if (set_tcp_sockopt(iscsi->fd, TCP_KEEPIDLE, idle) != 0) { iscsi_set_error(iscsi, "TCP: Failed to set tcp keepalive idle. Error %s(%d)", strerror(errno), errno); return -1; } ISCSI_LOG(iscsi, 3, "TCP_KEEPIDLE set to %d",idle); #endif #endif return 0; } void iscsi_set_bind_interfaces(struct iscsi_context *iscsi, char * interfaces _U_) { #if __linux strncpy(iscsi->bind_interfaces,interfaces,MAX_STRING_SIZE); iscsi->bind_interfaces_cnt=0; char * pchr = interfaces; char * pchr2 = NULL; do { pchr2 = strchr(pchr,','); if (pchr2) {pchr=pchr2+1;} iscsi->bind_interfaces_cnt++; } while (pchr2); ISCSI_LOG(iscsi,2,"will bind to one of the following %d interface(s) on next socket creation: %s",iscsi->bind_interfaces_cnt,interfaces); if (!iface_rr) iface_rr=rand()%iscsi->bind_interfaces_cnt+1; #else ISCSI_LOG(iscsi,1,"binding to an interface is not supported on your OS"); #endif } libiscsi-1.17.0/lib/sync.c000066400000000000000000000734511271371262700153110ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2010 by Ronnie Sahlberg This program 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 program is distributed in the hope that 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 program; if not, see . */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #ifdef HAVE_POLL_H #include #endif #ifdef AROS #include "aros/aros_compat.h" #endif #if defined(WIN32) #include #include "win32/win32_compat.h" #endif #include #include #include #include #include #include "iscsi.h" #include "iscsi-private.h" #include "scsi-lowlevel.h" struct iscsi_sync_state { int finished; int status; struct scsi_task *task; }; static void event_loop(struct iscsi_context *iscsi, struct iscsi_sync_state *state) { struct pollfd pfd; int ret; while (state->finished == 0) { short revents; pfd.fd = iscsi_get_fd(iscsi); pfd.events = iscsi_which_events(iscsi); if ((ret = poll(&pfd, 1, 1000)) < 0) { iscsi_set_error(iscsi, "Poll failed"); state->status = -1; return; } revents = (ret == 0) ? 0 : pfd.revents; if (iscsi_service(iscsi, revents) < 0) { iscsi_set_error(iscsi, "iscsi_service failed with : %s", iscsi_get_error(iscsi)); state->status = -1; return; } } } /* * Synchronous iSCSI commands */ static void iscsi_sync_cb(struct iscsi_context *iscsi _U_, int status, void *command_data _U_, void *private_data) { struct iscsi_sync_state *state = private_data; if (state != NULL) { state->status = status; state->finished = 1; } } int iscsi_connect_sync(struct iscsi_context *iscsi, const char *portal) { struct iscsi_sync_state state; memset(&state, 0, sizeof(state)); if (iscsi_connect_async(iscsi, portal, iscsi_sync_cb, &state) != 0) { iscsi_set_error(iscsi, "Failed to start connect() %s", iscsi_get_error(iscsi)); return -1; } event_loop(iscsi, &state); /* clear connect_data so it doesnt point to our stack */ iscsi->connect_data = NULL; return state.status; } int iscsi_full_connect_sync(struct iscsi_context *iscsi, const char *portal, int lun) { struct iscsi_sync_state state; memset(&state, 0, sizeof(state)); if (iscsi_full_connect_async(iscsi, portal, lun, iscsi_sync_cb, &state) != 0) { iscsi_set_error(iscsi, "Failed to start full connect %s", iscsi_get_error(iscsi)); return -1; } event_loop(iscsi, &state); return state.status; } int iscsi_login_sync(struct iscsi_context *iscsi) { struct iscsi_sync_state state; memset(&state, 0, sizeof(state)); if (iscsi_login_async(iscsi, iscsi_sync_cb, &state) != 0) { iscsi_set_error(iscsi, "Failed to login. %s", iscsi_get_error(iscsi)); return -1; } event_loop(iscsi, &state); return state.status; } int iscsi_logout_sync(struct iscsi_context *iscsi) { struct iscsi_sync_state state; memset(&state, 0, sizeof(state)); if (iscsi_logout_async(iscsi, iscsi_sync_cb, &state) != 0) { iscsi_set_error(iscsi, "Failed to start logout() %s", iscsi_get_error(iscsi)); return -1; } event_loop(iscsi, &state); return state.status; } static void reconnect_event_loop(struct iscsi_context *iscsi, struct iscsi_sync_state *state) { struct pollfd pfd; int ret; while (iscsi->old_iscsi) { pfd.fd = iscsi_get_fd(iscsi); pfd.events = iscsi_which_events(iscsi); if (!pfd.events) { sleep(1); continue; } if ((ret = poll(&pfd, 1, 1000)) < 0) { iscsi_set_error(iscsi, "Poll failed"); state->status = -1; return; } if (iscsi_service(iscsi, pfd.revents) < 0) { iscsi_set_error(iscsi, "iscsi_service failed with : %s", iscsi_get_error(iscsi)); state->status = -1; return; } } state->status = 0; } int iscsi_reconnect_sync(struct iscsi_context *iscsi) { struct iscsi_sync_state state; memset(&state, 0, sizeof(state)); if (iscsi_reconnect(iscsi) != 0) { iscsi_set_error(iscsi, "Failed to reconnect. %s", iscsi_get_error(iscsi)); return -1; } reconnect_event_loop(iscsi, &state); return state.status; } static void iscsi_task_mgmt_sync_cb(struct iscsi_context *iscsi, int status, void *command_data, void *private_data) { struct iscsi_sync_state *state = private_data; state->status = status; state->finished = 1; /* The task mgmt command might have completed successfully * but the target might have responded with * "command not implemented" or something. */ if (command_data && *(uint32_t *)command_data) { switch (*(uint32_t *)command_data) { case 1: iscsi_set_error(iscsi, "TASK MGMT responded Task Does Not Exist"); break; case 2: iscsi_set_error(iscsi, "TASK MGMT responded LUN Does Not Exist"); break; case 3: iscsi_set_error(iscsi, "TASK MGMT responded Task Still Allegiant"); break; case 4: iscsi_set_error(iscsi, "TASK MGMT responded Task Allegiance Reassignment Not Supported"); break; case 5: iscsi_set_error(iscsi, "TASK MGMT responded Task Mgmt Function Not Supported"); break; case 6: iscsi_set_error(iscsi, "TASK MGMT responded Function Authorization Failed"); break; case 255: iscsi_set_error(iscsi, "TASK MGMT responded Function Rejected"); break; } state->status = SCSI_STATUS_ERROR; } } int iscsi_task_mgmt_sync(struct iscsi_context *iscsi, int lun, enum iscsi_task_mgmt_funcs function, uint32_t ritt, uint32_t rcmdsn) { struct iscsi_sync_state state; memset(&state, 0, sizeof(state)); if (iscsi_task_mgmt_async(iscsi, lun, function, ritt, rcmdsn, iscsi_task_mgmt_sync_cb, &state) != 0) { iscsi_set_error(iscsi, "Failed to send TASK MGMT function: %s", iscsi_get_error(iscsi)); return -1; } event_loop(iscsi, &state); return state.status; } int iscsi_task_mgmt_abort_task_sync(struct iscsi_context *iscsi, struct scsi_task *task) { return iscsi_task_mgmt_sync(iscsi, task->lun, ISCSI_TM_ABORT_TASK, task->itt, task->cmdsn); } int iscsi_task_mgmt_abort_task_set_sync(struct iscsi_context *iscsi, uint32_t lun) { iscsi_scsi_cancel_all_tasks(iscsi); return iscsi_task_mgmt_sync(iscsi, lun, ISCSI_TM_ABORT_TASK_SET, 0xffffffff, 0); } int iscsi_task_mgmt_lun_reset_sync(struct iscsi_context *iscsi, uint32_t lun) { iscsi_scsi_cancel_all_tasks(iscsi); return iscsi_task_mgmt_sync(iscsi, lun, ISCSI_TM_LUN_RESET, 0xffffffff, 0); } int iscsi_task_mgmt_target_warm_reset_sync(struct iscsi_context *iscsi) { iscsi_scsi_cancel_all_tasks(iscsi); return iscsi_task_mgmt_sync(iscsi, 0, ISCSI_TM_TARGET_WARM_RESET, 0xffffffff, 0); } int iscsi_task_mgmt_target_cold_reset_sync(struct iscsi_context *iscsi) { iscsi_scsi_cancel_all_tasks(iscsi); return iscsi_task_mgmt_sync(iscsi, 0, ISCSI_TM_TARGET_COLD_RESET, 0xffffffff, 0); } /* * Synchronous SCSI commands */ static void scsi_sync_cb(struct iscsi_context *iscsi _U_, int status, void *command_data, void *private_data) { struct scsi_task *task = command_data; struct iscsi_sync_state *state = private_data; task->status = status; state->status = status; state->finished = 1; state->task = task; } struct scsi_task * iscsi_reportluns_sync(struct iscsi_context *iscsi, int report_type, int alloc_len) { struct iscsi_sync_state state; memset(&state, 0, sizeof(state)); if (iscsi_reportluns_task(iscsi, report_type, alloc_len, scsi_sync_cb, &state) == NULL) { iscsi_set_error(iscsi, "Failed to send ReportLuns command"); return NULL; } event_loop(iscsi, &state); return state.task; } struct scsi_task * iscsi_testunitready_sync(struct iscsi_context *iscsi, int lun) { struct iscsi_sync_state state; memset(&state, 0, sizeof(state)); if (iscsi_testunitready_task(iscsi, lun, scsi_sync_cb, &state) == NULL) { iscsi_set_error(iscsi, "Failed to send TestUnitReady command"); return NULL; } event_loop(iscsi, &state); return state.task; } struct scsi_task * iscsi_inquiry_sync(struct iscsi_context *iscsi, int lun, int evpd, int page_code, int maxsize) { struct iscsi_sync_state state; memset(&state, 0, sizeof(state)); if (iscsi_inquiry_task(iscsi, lun, evpd, page_code, maxsize, scsi_sync_cb, &state) == NULL) { iscsi_set_error(iscsi, "Failed to send Inquiry command"); return NULL; } event_loop(iscsi, &state); return state.task; } struct scsi_task * iscsi_read6_sync(struct iscsi_context *iscsi, int lun, uint32_t lba, uint32_t datalen, int blocksize) { struct iscsi_sync_state state; memset(&state, 0, sizeof(state)); if (iscsi_read6_task(iscsi, lun, lba, datalen, blocksize, scsi_sync_cb, &state) == NULL) { iscsi_set_error(iscsi, "Failed to send Read6 command"); return NULL; } event_loop(iscsi, &state); return state.task; } struct scsi_task * iscsi_read10_sync(struct iscsi_context *iscsi, int lun, uint32_t lba, uint32_t datalen, int blocksize, int rdprotect, int dpo, int fua, int fua_nv, int group_number) { struct iscsi_sync_state state; memset(&state, 0, sizeof(state)); if (iscsi_read10_task(iscsi, lun, lba, datalen, blocksize, rdprotect, dpo, fua, fua_nv, group_number, scsi_sync_cb, &state) == NULL) { iscsi_set_error(iscsi, "Failed to send Read10 command"); return NULL; } event_loop(iscsi, &state); return state.task; } struct scsi_task * iscsi_read12_sync(struct iscsi_context *iscsi, int lun, uint32_t lba, uint32_t datalen, int blocksize, int rdprotect, int dpo, int fua, int fua_nv, int group_number) { struct iscsi_sync_state state; memset(&state, 0, sizeof(state)); if (iscsi_read12_task(iscsi, lun, lba, datalen, blocksize, rdprotect, dpo, fua, fua_nv, group_number, scsi_sync_cb, &state) == NULL) { iscsi_set_error(iscsi, "Failed to send Read12 command"); return NULL; } event_loop(iscsi, &state); return state.task; } struct scsi_task * iscsi_read16_sync(struct iscsi_context *iscsi, int lun, uint64_t lba, uint32_t datalen, int blocksize, int rdprotect, int dpo, int fua, int fua_nv, int group_number) { struct iscsi_sync_state state; memset(&state, 0, sizeof(state)); if (iscsi_read16_task(iscsi, lun, lba, datalen, blocksize, rdprotect, dpo, fua, fua_nv, group_number, scsi_sync_cb, &state) == NULL) { iscsi_set_error(iscsi, "Failed to send Read16 command"); return NULL; } event_loop(iscsi, &state); return state.task; } struct scsi_task * iscsi_readcapacity10_sync(struct iscsi_context *iscsi, int lun, int lba, int pmi) { struct iscsi_sync_state state; memset(&state, 0, sizeof(state)); if (iscsi_readcapacity10_task(iscsi, lun, lba, pmi, scsi_sync_cb, &state) == NULL) { iscsi_set_error(iscsi, "Failed to send ReadCapacity10 command"); return NULL; } event_loop(iscsi, &state); return state.task; } struct scsi_task * iscsi_readcapacity16_sync(struct iscsi_context *iscsi, int lun) { struct iscsi_sync_state state; memset(&state, 0, sizeof(state)); if (iscsi_readcapacity16_task(iscsi, lun, scsi_sync_cb, &state) == NULL) { iscsi_set_error(iscsi, "Failed to send ReadCapacity16 command"); return NULL; } event_loop(iscsi, &state); return state.task; } struct scsi_task * iscsi_sanitize_sync(struct iscsi_context *iscsi, int lun, int immed, int ause, int sa, int param_len, struct iscsi_data *data) { struct iscsi_sync_state state; memset(&state, 0, sizeof(state)); if (iscsi_sanitize_task(iscsi, lun, immed, ause, sa, param_len, data, scsi_sync_cb, &state) == NULL) { iscsi_set_error(iscsi, "Failed to send Sanitize command"); return NULL; } event_loop(iscsi, &state); return state.task; } struct scsi_task * iscsi_sanitize_block_erase_sync(struct iscsi_context *iscsi, int lun, int immed, int ause) { struct iscsi_sync_state state; memset(&state, 0, sizeof(state)); if (iscsi_sanitize_block_erase_task(iscsi, lun, immed, ause, scsi_sync_cb, &state) == NULL) { iscsi_set_error(iscsi, "Failed to send Sanitize command"); return NULL; } event_loop(iscsi, &state); return state.task; } struct scsi_task * iscsi_sanitize_crypto_erase_sync(struct iscsi_context *iscsi, int lun, int immed, int ause) { struct iscsi_sync_state state; memset(&state, 0, sizeof(state)); if (iscsi_sanitize_crypto_erase_task(iscsi, lun, immed, ause, scsi_sync_cb, &state) == NULL) { iscsi_set_error(iscsi, "Failed to send Sanitize command"); return NULL; } event_loop(iscsi, &state); return state.task; } struct scsi_task * iscsi_sanitize_exit_failure_mode_sync(struct iscsi_context *iscsi, int lun, int immed, int ause) { struct iscsi_sync_state state; memset(&state, 0, sizeof(state)); if (iscsi_sanitize_exit_failure_mode_task(iscsi, lun, immed, ause, scsi_sync_cb, &state) == NULL) { iscsi_set_error(iscsi, "Failed to send Sanitize command"); return NULL; } event_loop(iscsi, &state); return state.task; } struct scsi_task * iscsi_get_lba_status_sync(struct iscsi_context *iscsi, int lun, uint64_t starting_lba, uint32_t alloc_len) { struct iscsi_sync_state state; memset(&state, 0, sizeof(state)); if (iscsi_get_lba_status_task(iscsi, lun, starting_lba, alloc_len, scsi_sync_cb, &state) == NULL) { iscsi_set_error(iscsi, "Failed to send GetLbaStatus command"); return NULL; } event_loop(iscsi, &state); return state.task; } struct scsi_task * iscsi_synchronizecache10_sync(struct iscsi_context *iscsi, int lun, int lba, int num_blocks, int syncnv, int immed) { struct iscsi_sync_state state; memset(&state, 0, sizeof(state)); if (iscsi_synchronizecache10_task(iscsi, lun, lba, num_blocks, syncnv, immed, scsi_sync_cb, &state) == NULL) { iscsi_set_error(iscsi, "Failed to send SynchronizeCache10 command"); return NULL; } event_loop(iscsi, &state); return state.task; } struct scsi_task * iscsi_startstopunit_sync(struct iscsi_context *iscsi, int lun, int immed, int pcm, int pc, int no_flush, int loej, int start) { struct iscsi_sync_state state; memset(&state, 0, sizeof(state)); if (iscsi_startstopunit_task(iscsi, lun, immed, pcm, pc, no_flush, loej, start, scsi_sync_cb, &state) == NULL) { iscsi_set_error(iscsi, "Failed to send StartStopUnit command"); return NULL; } event_loop(iscsi, &state); return state.task; } struct scsi_task * iscsi_preventallow_sync(struct iscsi_context *iscsi, int lun, int prevent) { struct iscsi_sync_state state; memset(&state, 0, sizeof(state)); if (iscsi_preventallow_task(iscsi, lun, prevent, scsi_sync_cb, &state) == NULL) { iscsi_set_error(iscsi, "Failed to send PreventAllowMediumRemoval command"); return NULL; } event_loop(iscsi, &state); return state.task; } struct scsi_task * iscsi_synchronizecache16_sync(struct iscsi_context *iscsi, int lun, uint64_t lba, uint32_t num_blocks, int syncnv, int immed) { struct iscsi_sync_state state; memset(&state, 0, sizeof(state)); if (iscsi_synchronizecache16_task(iscsi, lun, lba, num_blocks, syncnv, immed, scsi_sync_cb, &state) == NULL) { iscsi_set_error(iscsi, "Failed to send SynchronizeCache16 command"); return NULL; } event_loop(iscsi, &state); return state.task; } struct scsi_task * iscsi_prefetch10_sync(struct iscsi_context *iscsi, int lun, uint32_t lba, int num_blocks, int immed, int group) { struct iscsi_sync_state state; memset(&state, 0, sizeof(state)); if (iscsi_prefetch10_task(iscsi, lun, lba, num_blocks, immed, group, scsi_sync_cb, &state) == NULL) { iscsi_set_error(iscsi, "Failed to send PreFetch10 command"); return NULL; } event_loop(iscsi, &state); return state.task; } struct scsi_task * iscsi_prefetch16_sync(struct iscsi_context *iscsi, int lun, uint64_t lba, int num_blocks, int immed, int group) { struct iscsi_sync_state state; memset(&state, 0, sizeof(state)); if (iscsi_prefetch16_task(iscsi, lun, lba, num_blocks, immed, group, scsi_sync_cb, &state) == NULL) { iscsi_set_error(iscsi, "Failed to send PreFetch16 command"); return NULL; } event_loop(iscsi, &state); return state.task; } struct scsi_task * iscsi_write10_sync(struct iscsi_context *iscsi, int lun, uint32_t lba, unsigned char *data, uint32_t datalen, int blocksize, int wrprotect, int dpo, int fua, int fua_nv, int group_number) { struct iscsi_sync_state state; memset(&state, 0, sizeof(state)); if (iscsi_write10_task(iscsi, lun, lba, data, datalen, blocksize, wrprotect, dpo, fua, fua_nv, group_number, scsi_sync_cb, &state) == NULL) { iscsi_set_error(iscsi, "Failed to send Write10 command"); return NULL; } event_loop(iscsi, &state); return state.task; } struct scsi_task * iscsi_write12_sync(struct iscsi_context *iscsi, int lun, uint32_t lba, unsigned char *data, uint32_t datalen, int blocksize, int wrprotect, int dpo, int fua, int fua_nv, int group_number) { struct iscsi_sync_state state; memset(&state, 0, sizeof(state)); if (iscsi_write12_task(iscsi, lun, lba, data, datalen, blocksize, wrprotect, dpo, fua, fua_nv, group_number, scsi_sync_cb, &state) == NULL) { iscsi_set_error(iscsi, "Failed to send Write12 command"); return NULL; } event_loop(iscsi, &state); return state.task; } struct scsi_task * iscsi_write16_sync(struct iscsi_context *iscsi, int lun, uint64_t lba, unsigned char *data, uint32_t datalen, int blocksize, int wrprotect, int dpo, int fua, int fua_nv, int group_number) { struct iscsi_sync_state state; memset(&state, 0, sizeof(state)); if (iscsi_write16_task(iscsi, lun, lba, data, datalen, blocksize, wrprotect, dpo, fua, fua_nv, group_number, scsi_sync_cb, &state) == NULL) { iscsi_set_error(iscsi, "Failed to send Write16 command"); return NULL; } event_loop(iscsi, &state); return state.task; } struct scsi_task * iscsi_writeatomic16_sync(struct iscsi_context *iscsi, int lun, uint64_t lba, unsigned char *data, uint32_t datalen, int blocksize, int wrprotect, int dpo, int fua, int group_number) { struct iscsi_sync_state state; memset(&state, 0, sizeof(state)); if (iscsi_writeatomic16_task(iscsi, lun, lba, data, datalen, blocksize, wrprotect, dpo, fua, group_number, scsi_sync_cb, &state) == NULL) { iscsi_set_error(iscsi, "Failed to send WriteAtomic16 command"); return NULL; } event_loop(iscsi, &state); return state.task; } struct scsi_task * iscsi_orwrite_sync(struct iscsi_context *iscsi, int lun, uint64_t lba, unsigned char *data, uint32_t datalen, int blocksize, int wrprotect, int dpo, int fua, int fua_nv, int group_number) { struct iscsi_sync_state state; memset(&state, 0, sizeof(state)); if (iscsi_orwrite_task(iscsi, lun, lba, data, datalen, blocksize, wrprotect, dpo, fua, fua_nv, group_number, scsi_sync_cb, &state) == NULL) { iscsi_set_error(iscsi, "Failed to send Orwrite command"); return NULL; } event_loop(iscsi, &state); return state.task; } struct scsi_task * iscsi_compareandwrite_sync(struct iscsi_context *iscsi, int lun, uint64_t lba, unsigned char *data, uint32_t datalen, int blocksize, int wrprotect, int dpo, int fua, int fua_nv, int group_number) { struct iscsi_sync_state state; memset(&state, 0, sizeof(state)); if (iscsi_compareandwrite_task(iscsi, lun, lba, data, datalen, blocksize, wrprotect, dpo, fua, fua_nv, group_number, scsi_sync_cb, &state) == NULL) { iscsi_set_error(iscsi, "Failed to send CompareAndWrite command"); return NULL; } event_loop(iscsi, &state); return state.task; } struct scsi_task * iscsi_writeverify10_sync(struct iscsi_context *iscsi, int lun, uint32_t lba, unsigned char *data, uint32_t datalen, int blocksize, int wrprotect, int dpo, int bytchk, int group_number) { struct iscsi_sync_state state; memset(&state, 0, sizeof(state)); if (iscsi_writeverify10_task(iscsi, lun, lba, data, datalen, blocksize, wrprotect, dpo, bytchk, group_number, scsi_sync_cb, &state) == NULL) { iscsi_set_error(iscsi, "Failed to send Writeverify10 command"); return NULL; } event_loop(iscsi, &state); return state.task; } struct scsi_task * iscsi_writeverify12_sync(struct iscsi_context *iscsi, int lun, uint32_t lba, unsigned char *data, uint32_t datalen, int blocksize, int wrprotect, int dpo, int bytchk, int group_number) { struct iscsi_sync_state state; memset(&state, 0, sizeof(state)); if (iscsi_writeverify12_task(iscsi, lun, lba, data, datalen, blocksize, wrprotect, dpo, bytchk, group_number, scsi_sync_cb, &state) == NULL) { iscsi_set_error(iscsi, "Failed to send Writeverify12 command"); return NULL; } event_loop(iscsi, &state); return state.task; } struct scsi_task * iscsi_writeverify16_sync(struct iscsi_context *iscsi, int lun, uint64_t lba, unsigned char *data, uint32_t datalen, int blocksize, int wrprotect, int dpo, int bytchk, int group_number) { struct iscsi_sync_state state; memset(&state, 0, sizeof(state)); if (iscsi_writeverify16_task(iscsi, lun, lba, data, datalen, blocksize, wrprotect, dpo, bytchk, group_number, scsi_sync_cb, &state) == NULL) { iscsi_set_error(iscsi, "Failed to send Writeverify16 command"); return NULL; } event_loop(iscsi, &state); return state.task; } struct scsi_task * iscsi_verify10_sync(struct iscsi_context *iscsi, int lun, unsigned char *data, uint32_t datalen, uint32_t lba, int vprotect, int dpo, int bytchk, int blocksize) { struct iscsi_sync_state state; memset(&state, 0, sizeof(state)); if (iscsi_verify10_task(iscsi, lun, data, datalen, lba, vprotect, dpo, bytchk, blocksize, scsi_sync_cb, &state) == NULL) { iscsi_set_error(iscsi, "Failed to send Verify10 command"); return NULL; } event_loop(iscsi, &state); return state.task; } struct scsi_task * iscsi_verify12_sync(struct iscsi_context *iscsi, int lun, unsigned char *data, uint32_t datalen, uint32_t lba, int vprotect, int dpo, int bytchk, int blocksize) { struct iscsi_sync_state state; memset(&state, 0, sizeof(state)); if (iscsi_verify12_task(iscsi, lun, data, datalen, lba, vprotect, dpo, bytchk, blocksize, scsi_sync_cb, &state) == NULL) { iscsi_set_error(iscsi, "Failed to send Verify12 command"); return NULL; } event_loop(iscsi, &state); return state.task; } struct scsi_task * iscsi_verify16_sync(struct iscsi_context *iscsi, int lun, unsigned char *data, uint32_t datalen, uint64_t lba, int vprotect, int dpo, int bytchk, int blocksize) { struct iscsi_sync_state state; memset(&state, 0, sizeof(state)); if (iscsi_verify16_task(iscsi, lun, data, datalen, lba, vprotect, dpo, bytchk, blocksize, scsi_sync_cb, &state) == NULL) { iscsi_set_error(iscsi, "Failed to send Verify16 command"); return NULL; } event_loop(iscsi, &state); return state.task; } struct scsi_task * iscsi_writesame10_sync(struct iscsi_context *iscsi, int lun, uint32_t lba, unsigned char *data, uint32_t datalen, uint16_t num_blocks, int anchor, int unmap, int wrprotect, int group) { struct iscsi_sync_state state; memset(&state, 0, sizeof(state)); if (iscsi_writesame10_task(iscsi, lun, lba, data, datalen, num_blocks, anchor, unmap, wrprotect, group, scsi_sync_cb, &state) == NULL) { iscsi_set_error(iscsi, "Failed to send WRITESAME10 command"); return NULL; } event_loop(iscsi, &state); return state.task; } struct scsi_task * iscsi_writesame16_sync(struct iscsi_context *iscsi, int lun, uint64_t lba, unsigned char *data, uint32_t datalen, uint32_t num_blocks, int anchor, int unmap, int wrprotect, int group) { struct iscsi_sync_state state; memset(&state, 0, sizeof(state)); if (iscsi_writesame16_task(iscsi, lun, lba, data, datalen, num_blocks, anchor, unmap, wrprotect, group, scsi_sync_cb, &state) == NULL) { iscsi_set_error(iscsi, "Failed to send WRITESAME16 command"); return NULL; } event_loop(iscsi, &state); return state.task; } struct scsi_task * iscsi_persistent_reserve_in_sync(struct iscsi_context *iscsi, int lun, int sa, uint16_t xferlen) { struct iscsi_sync_state state; memset(&state, 0, sizeof(state)); if (iscsi_persistent_reserve_in_task(iscsi, lun, sa, xferlen, scsi_sync_cb, &state) == NULL) { iscsi_set_error(iscsi, "Failed to send PERSISTENT_RESERVE_IN command"); return NULL; } event_loop(iscsi, &state); return state.task; } struct scsi_task * iscsi_persistent_reserve_out_sync(struct iscsi_context *iscsi, int lun, int sa, int scope, int type, void *param) { struct iscsi_sync_state state; memset(&state, 0, sizeof(state)); if (iscsi_persistent_reserve_out_task(iscsi, lun, sa, scope, type, param, scsi_sync_cb, &state) == NULL) { iscsi_set_error(iscsi, "Failed to send PERSISTENT_RESERVE_OUT command"); return NULL; } event_loop(iscsi, &state); return state.task; } struct scsi_task * iscsi_unmap_sync(struct iscsi_context *iscsi, int lun, int anchor, int group, struct unmap_list *list, int list_len) { struct iscsi_sync_state state; memset(&state, 0, sizeof(state)); if (iscsi_unmap_task(iscsi, lun, anchor, group, list, list_len, scsi_sync_cb, &state) == NULL) { iscsi_set_error(iscsi, "Failed to send UNMAP command"); return NULL; } event_loop(iscsi, &state); return state.task; } struct scsi_task * iscsi_readtoc_sync(struct iscsi_context *iscsi, int lun, int msf, int format, int track_session, int maxsize) { struct iscsi_sync_state state; memset(&state, 0, sizeof(state)); if (iscsi_readtoc_task(iscsi, lun, msf, format, track_session, maxsize, scsi_sync_cb, &state) == NULL) { iscsi_set_error(iscsi, "Failed to send Read TOC command"); return NULL; } event_loop(iscsi, &state); return state.task; } struct scsi_task * iscsi_reserve6_sync(struct iscsi_context *iscsi, int lun) { struct iscsi_sync_state state; memset(&state, 0, sizeof(state)); if (iscsi_reserve6_task(iscsi, lun, scsi_sync_cb, &state) == NULL) { iscsi_set_error(iscsi, "Failed to send RESERVE6 command"); return NULL; } event_loop(iscsi, &state); return state.task; } struct scsi_task * iscsi_release6_sync(struct iscsi_context *iscsi, int lun) { struct iscsi_sync_state state; memset(&state, 0, sizeof(state)); if (iscsi_release6_task(iscsi, lun, scsi_sync_cb, &state) == NULL) { iscsi_set_error(iscsi, "Failed to send RELEASE6 command"); return NULL; } event_loop(iscsi, &state); return state.task; } struct scsi_task * iscsi_report_supported_opcodes_sync(struct iscsi_context *iscsi, int lun, int rctd, int options, int opcode, int sa, uint32_t alloc_len) { struct iscsi_sync_state state; memset(&state, 0, sizeof(state)); if (iscsi_report_supported_opcodes_task(iscsi, lun, rctd, options, opcode, sa, alloc_len, scsi_sync_cb, &state) == NULL) { iscsi_set_error(iscsi, "Failed to send MaintenanceIn:" "Report Supported Opcodes command"); return NULL; } event_loop(iscsi, &state); return state.task; } struct scsi_task * iscsi_scsi_command_sync(struct iscsi_context *iscsi, int lun, struct scsi_task *task, struct iscsi_data *data) { struct iscsi_sync_state state; memset(&state, 0, sizeof(state)); if (iscsi_scsi_command_async(iscsi, lun, task, scsi_sync_cb, data, &state) != 0) { iscsi_set_error(iscsi, "Failed to send SCSI command"); return NULL; } event_loop(iscsi, &state); return state.task; } struct scsi_task * iscsi_modeselect6_sync(struct iscsi_context *iscsi, int lun, int pf, int sp, struct scsi_mode_page *mp) { struct iscsi_sync_state state; memset(&state, 0, sizeof(state)); if (iscsi_modeselect6_task(iscsi, lun, pf, sp, mp, scsi_sync_cb, &state) == NULL) { iscsi_set_error(iscsi, "Failed to send MODE_SELECT6 command"); return NULL; } event_loop(iscsi, &state); return state.task; } struct scsi_task * iscsi_modeselect10_sync(struct iscsi_context *iscsi, int lun, int pf, int sp, struct scsi_mode_page *mp) { struct iscsi_sync_state state; memset(&state, 0, sizeof(state)); if (iscsi_modeselect10_task(iscsi, lun, pf, sp, mp, scsi_sync_cb, &state) == NULL) { iscsi_set_error(iscsi, "Failed to send MODE_SELECT10 command"); return NULL; } event_loop(iscsi, &state); return state.task; } struct scsi_task * iscsi_modesense6_sync(struct iscsi_context *iscsi, int lun, int dbd, int pc, int page_code, int sub_page_code, unsigned char alloc_len) { struct iscsi_sync_state state; memset(&state, 0, sizeof(state)); if (iscsi_modesense6_task(iscsi, lun, dbd, pc, page_code, sub_page_code, alloc_len, scsi_sync_cb, &state) == NULL) { iscsi_set_error(iscsi, "Failed to send MODE_SENSE6 command"); return NULL; } event_loop(iscsi, &state); return state.task; } struct scsi_task * iscsi_modesense10_sync(struct iscsi_context *iscsi, int lun, int llbaa, int dbd, int pc, int page_code, int sub_page_code, unsigned char alloc_len) { struct iscsi_sync_state state; memset(&state, 0, sizeof(state)); if (iscsi_modesense10_task(iscsi, lun, llbaa, dbd, pc, page_code, sub_page_code, alloc_len, scsi_sync_cb, &state) == NULL) { iscsi_set_error(iscsi, "Failed to send MODE_SENSE10 command"); return NULL; } event_loop(iscsi, &state); return state.task; } libiscsi-1.17.0/lib/task_mgmt.c000066400000000000000000000077671271371262700163320ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2011 by Ronnie Sahlberg This program 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 program is distributed in the hope that 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 program; if not, see . */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #ifdef HAVE_SYS_TYPES_H #include #endif #ifdef HAVE_ARPA_INET_H #include #endif #ifdef HAVE_UNISTD_H #include #endif #include #include "iscsi.h" #include "iscsi-private.h" #include "scsi-lowlevel.h" int iscsi_task_mgmt_async(struct iscsi_context *iscsi, int lun, enum iscsi_task_mgmt_funcs function, uint32_t ritt, uint32_t rcmdsn, iscsi_command_cb cb, void *private_data) { struct iscsi_pdu *pdu; if (iscsi->is_loggedin == 0) { iscsi_set_error(iscsi, "trying to send task-mgmt while not " "logged in"); return -1; } pdu = iscsi_allocate_pdu(iscsi, ISCSI_PDU_SCSI_TASK_MANAGEMENT_REQUEST, ISCSI_PDU_SCSI_TASK_MANAGEMENT_RESPONSE, iscsi_itt_post_increment(iscsi), ISCSI_PDU_DROP_ON_RECONNECT); if (pdu == NULL) { iscsi_set_error(iscsi, "Failed to allocate task mgmt pdu"); return -1; } /* immediate flag */ iscsi_pdu_set_immediate(pdu); /* flags */ iscsi_pdu_set_pduflags(pdu, 0x80 | function); /* lun */ iscsi_pdu_set_lun(pdu, lun); /* ritt */ iscsi_pdu_set_ritt(pdu, ritt); /* cmdsn is not increased if Immediate delivery*/ iscsi_pdu_set_cmdsn(pdu, iscsi->cmdsn); /* rcmdsn */ iscsi_pdu_set_rcmdsn(pdu, rcmdsn); pdu->callback = cb; pdu->private_data = private_data; if (iscsi_queue_pdu(iscsi, pdu) != 0) { iscsi_set_error(iscsi, "failed to queue iscsi taskmgmt pdu"); iscsi_free_pdu(iscsi, pdu); return -1; } return 0; } int iscsi_process_task_mgmt_reply(struct iscsi_context *iscsi, struct iscsi_pdu *pdu, struct iscsi_in_pdu *in) { uint32_t response = in->hdr[2]; if (pdu->callback) { pdu->callback(iscsi, SCSI_STATUS_GOOD, &response, pdu->private_data); } return 0; } int iscsi_task_mgmt_abort_task_async(struct iscsi_context *iscsi, struct scsi_task *task, iscsi_command_cb cb, void *private_data) { return iscsi_task_mgmt_async(iscsi, task->lun, ISCSI_TM_ABORT_TASK, task->itt, task->cmdsn, cb, private_data); } int iscsi_task_mgmt_abort_task_set_async(struct iscsi_context *iscsi, uint32_t lun, iscsi_command_cb cb, void *private_data) { iscsi_scsi_cancel_all_tasks(iscsi); return iscsi_task_mgmt_async(iscsi, lun, ISCSI_TM_ABORT_TASK_SET, 0xffffffff, 0, cb, private_data); } int iscsi_task_mgmt_lun_reset_async(struct iscsi_context *iscsi, uint32_t lun, iscsi_command_cb cb, void *private_data) { iscsi_scsi_cancel_all_tasks(iscsi); return iscsi_task_mgmt_async(iscsi, lun, ISCSI_TM_LUN_RESET, 0xffffffff, 0, cb, private_data); } int iscsi_task_mgmt_target_warm_reset_async(struct iscsi_context *iscsi, iscsi_command_cb cb, void *private_data) { iscsi_scsi_cancel_all_tasks(iscsi); return iscsi_task_mgmt_async(iscsi, 0, ISCSI_TM_TARGET_WARM_RESET, 0xffffffff, 0, cb, private_data); } int iscsi_task_mgmt_target_cold_reset_async(struct iscsi_context *iscsi, iscsi_command_cb cb, void *private_data) { iscsi_scsi_cancel_all_tasks(iscsi); return iscsi_task_mgmt_async(iscsi, 0, ISCSI_TM_TARGET_COLD_RESET, 0xffffffff, 0, cb, private_data); } libiscsi-1.17.0/libiscsi.pc.in000066400000000000000000000003271271371262700161450ustar00rootroot00000000000000prefix=@prefix@ exec_prefix=${prefix} libdir=@libdir@ includedir=@includedir@ Name: libiscsi Description: iSCSI initiator library Version: @VERSION@ Libs: -L${libdir} -liscsi Libs.private: Cflags: -I${includedir} libiscsi-1.17.0/packaging/000077500000000000000000000000001271371262700153355ustar00rootroot00000000000000libiscsi-1.17.0/packaging/RPM/000077500000000000000000000000001271371262700157735ustar00rootroot00000000000000libiscsi-1.17.0/packaging/RPM/.gitignore000066400000000000000000000000101271371262700177520ustar00rootroot00000000000000/*.spec libiscsi-1.17.0/packaging/RPM/libiscsi.spec.in000066400000000000000000000200711271371262700210550ustar00rootroot00000000000000Name: libiscsi Summary: iSCSI client library Version: 1.17.0 Release: 1GITHASH%{?dist} License: LGPLv2+ Group: System Environment/Libraries URL: https://github.com/sahlberg/libiscsi Source: libiscsi-%{version}.tar.gz BuildRoot: %{_tmppath}/%{name}-%{version}-root BuildRequires: autoconf BuildRequires: automake BuildRequires: libtool %description libiscsi is a library for attaching to iSCSI resources across a network. ####################################################################### %prep %setup -q # setup the init script and sysconfig file %setup -T -D -n libiscsi-%{version} -q %build CC="gcc" export CC ## always run autogen.sh ./autogen.sh %configure make %{?_smp_mflags} %install # Clean up in case there is trash left from a previous build rm -rf $RPM_BUILD_ROOT make DESTDIR=$RPM_BUILD_ROOT install %{?_smp_mflags} rm $RPM_BUILD_ROOT/%{_libdir}/libiscsi.a rm $RPM_BUILD_ROOT/%{_libdir}/libiscsi.la # Remove "*.old" files find $RPM_BUILD_ROOT -name "*.old" -exec rm -f {} \; %clean rm -rf $RPM_BUILD_ROOT %post -p /sbin/ldconfig %postun -p /sbin/ldconfig %files %defattr(-,root,root) %doc COPYING LICENCE-LGPL-2.1.txt README TODO %{_libdir}/libiscsi.so.* %package utils Summary: iSCSI Client Utilities Group: Applications/System %description utils The libiscsi-utils package provides a set of assorted utilities to connect to iSCSI servers without having to set up the Linux iSCSI initiator. %files utils %doc COPYING LICENCE-GPL-2.txt LICENCE-LGPL-2.1.txt README TODO %{_bindir}/ld_iscsi.so %{_bindir}/iscsi-ls %{_bindir}/iscsi-inq %{_bindir}/iscsi-perf %{_bindir}/iscsi-readcapacity16 %{_bindir}/iscsi-swp %{_mandir}/man1/iscsi-inq.1.gz %{_mandir}/man1/iscsi-ls.1.gz %{_mandir}/man1/iscsi-swp.1.gz %package devel Summary: iSCSI client development libraries Group: Development/Libraries Requires: libiscsi = %{version}-%{release} %description devel The libiscsi-devel package includes the header files for libiscsi. %files devel %defattr(-,root,root) %doc COPYING LICENCE-LGPL-2.1.txt README TODO %{_includedir}/iscsi/iscsi.h %{_includedir}/iscsi/scsi-lowlevel.h %{_libdir}/libiscsi.so %{_libdir}/pkgconfig/libiscsi.pc %package testsuite Summary: iSCSI test suite Group: Development/Tools %description testsuite Test tool for iSCSI/SCSI targets %files testsuite %defattr(-,root,root) %{_bindir}/iscsi-test-cu %{_mandir}/man1/iscsi-test-cu.1.gz %changelog * Sun May 8 2016 : 1.0.17 - Fix a rare bug with DATA-OUT callbacks causing a crash on reconnect/timeout. * Sun May 1 2016 : 1.0.16 - Various test updates. - Add tests for ExtendedCopy and ReceiveCopyResults - Add support for WRITE_ATOMIC_16 and tests - Multipath tests added - Persistent Reservation test updates. - Make sure to process target NOPs in the tests - Fix broken CHAP has handling. * Sun Jun 21 2015 : 1.0.15 - Replace some u_int32_t with uint32_t - Portability fixes to iscsi-perf - Improved documentation for the tests in README - Add/fix support for setting task/pdu timeouts and add unit tests for it. - Add multipath helpers for the test suite and add a simple multipath test - Skip sending TUR during reconnect, since it can cause the connection to hang. * Sun May 10 2015 : 1.0.14 - Add support to use /dev/* nodes instead of just iscsi devices. - Create a dedicated test for the FUA/DPO flags - Allow reusing the context after disconnect - Fix non-randomness in rand_key() - Add iscsi-perf tool - Fix length bug when sending unsolicited data in iscsi_command - Reqrite the reconnect logic to begome fully async - Fix wrong checks for username in CHAP - Support Bidirectional CHAP - Improve handling of IMMEDIATE bit - Cmdsn, statsn fixes - iscsi_which_events can return 0, which means that there are no events right now but try again in a second or so. - Ignore any ASYNC EVENTS we receive since we can not yet pass them back to the application. - Add initial make test support - Various minor fixes to libiscsi and the test suite * Sun Dec 21 2014 : 1.13.0 - Fix UNMAP tests - Build updates - Check residuals also when status != GOOD - Add WSNZ bit to the tests - Various test updates - Fix bug in scsi_create_task so it sets cdb_len correctly - Add a getter function for scsi task status and sense - Fixes and workarounds for Dell Equallogic issues - Be more flexible when allowing for unit attentions during connect time * Mon Jul 21 2014 : 1.12.0 - Fix statsn when wrapping - Add Async event handling * Sat Apr 19 2014 : 1.11.0 - Fix memory leaks in persistent reserve out. - Fix various leaks in the testsuite. - Add additional READ16 tests - Better logging when reconnect fails. - Discovery and Login fixes. - Various fixes found by coverity. - WRITESAME test updates. - Add XML oputput support for the testsuite. * Sun Oct 20 2013 : 1.10.0 - Add manpages for iscsi-ls/iscsi-inq and iscsi-swp - Many new tests and updates to the testsuite. - Fix memory leak in iscsi_reconnect - Fixes to the persistent reservation tests - AROS support - Fix/restore windows support - Switch to libgcrypt for the MD5 support. - Fix buffer overflow in the persistent reserver unmarshalling code. - And many more minor fixes. * Sun Feb 24 2013 : 1.9.0 - Add new CUnit based test tool: iscsi-test-cu to replace iscsi-test - Add tests for persistent reservation - Autotools updates - Add pkgconfig * Sat Jan 5 2013 : 1.8.0 - Add new debugging/logging subsystem. - Collapse related structures into one and reduce amount of calls to malloc()/free() - Add more functions to ld-iscsi so that we can write to luns too. - Start adding unmarshalling of CDBs - Add support for using iovectors for both reading and writing. When used this eliminates any data copy in libiscsi. - Fix a deadlock between CMDSN and DATA-OUT PDUs. - Redo how we handle FirstBurstLength/MaxRecvDataSegmentLength/ImmediateData/InitialR2T. - Add a new nice API for handling sending NOPs from the application. - Add tests for PersistentReservations. * Tue Nov 6 2012 : 1.7.0 - Lots of additional tests. - ld_iscsi updates and bugfixes. - Fix a protocol bug where we might send > FirstBurstLength amount of data as unsolicited data. - Tcp keepalive improvements. - Debugging framework. - Add support for redirection. - Fix reconnect bug where we would incorrectly re-queue DATA-OUT PDUs after reconnecting. - Add a new iscsi-readcapacity16 command. - Squelch a huge number of compiler warnings. * Mon Sep 3 2012 : 1.6.0 - Lots of test updates. - Fix for iscsi-ls for removable media/medium not present - Allow login to LUNs with no medium loaded - Update SG3-UTILS patch - Support > 255 LUNs - Fix DATASN handling - Add proper MAXCMDSN handling - Various fixes to be able to interoperate with LIO - Fix SEGV bug in the CONNECT code. - Add libiscsi.syms to only export public symbols from the library - Change all default iqn names so they are valid iqn names. * Mon Jul 9 2012 : 1.5.0 - Make sure we can handle racy eventsystems which might call us for POLLIN eventhough there is no longer any data to read from the socket. - Only set up tcp keepalives on systems that support them. - Only export symbols we really want to make public - FreeBSD and Illumos does not define SOL_TCP - Lots of updates to the test tool - Fix a bug related to header-digest that could make the login fail on targets that require header digest. * Thu May 3 2012 : 1.4.0 - Add WRITESAME10/16 and tests for them - Add READ/WRITE12/16 and tests for them - Add PREFETCH10/16 and tests for them - Add automatic reconnect. If being logged in to a target and the tcp session is torn down, then try to reconnect and re-drive the i/o in flight. * Mon Apr 23 2012 : 1.3.0 - Add READCAPACITY16 support - Add VPD pages for thin-provisioning - Add support for UNMAP command for thin provisioning - Add tests for RC16 and UNMAP * Wed Mar 7 2012 : 1.2.0 - rename library back to libiscsi and release it as 1.2.0 * Sun Dec 25 2011 : 1.1.0 - Fix TaskManagement AbortTask/AbortTaskSet to send to correct LUN * Fri Oct 28 2011 Paolo Bonzini - 1.0.0-2 - Fixed rpmlint problems * Sat Dec 4 2010 Ronnie Sahlberg - 1.0.0-1 - Initial version libiscsi-1.17.0/packaging/RPM/makerpms.sh000077500000000000000000000047701271371262700201610ustar00rootroot00000000000000#!/bin/sh # # makerpms.sh - build RPM packages from the git sources # # Copyright (C) John H Terpstra 1998-2002 # Copyright (C) Gerald (Jerry) Carter 2003 # Copyright (C) Jim McDonough 2007 # Copyright (C) Andrew Tridgell 2007 # Copyright (C) Michael Adam 2008-2009 # # 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 3 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, see . # # # The following allows environment variables to override the target directories # the alternative is to have a file in your home directory calles .rpmmacros # containing the following: # %_topdir /home/mylogin/redhat # # Note: Under this directory rpm expects to find the same directories that are under the # /usr/src/redhat directory # EXTRA_OPTIONS="$1" DIRNAME=$(dirname $0) TOPDIR=${DIRNAME}/../.. SPECDIR=`rpm --eval %_specdir` SRCDIR=`rpm --eval %_sourcedir` SPECFILE="libiscsi.spec" SPECFILE_IN="libiscsi.spec.in" RPMBUILD="rpmbuild" GITHASH=".$(git log --pretty=format:%h -1)" if test "x$USE_GITHASH" = "xno" ; then GITHASH="" fi sed -e s/GITHASH/${GITHASH}/g \ < ${DIRNAME}/${SPECFILE_IN} \ > ${DIRNAME}/${SPECFILE} VERSION=$(grep ^Version ${DIRNAME}/${SPECFILE} | sed -e 's/^Version:\ \+//') RELEASE=$(grep ^Release ${DIRNAME}/${SPECFILE} | sed -e 's/^Release:\ \+//') if echo | gzip -c --rsyncable - > /dev/null 2>&1 ; then GZIP_ENV="-9 --rsyncable" else GZIP_ENV="-9" fi pushd ${TOPDIR} echo -n "Creating libiscsi-${VERSION}.tar.gz ... " make dist GZIP_ENV="\"$GZIP_ENV\"" RC=$? popd echo "Done." if [ $RC -ne 0 ]; then echo "Build failed!" exit 1 fi # At this point the SPECDIR and SRCDIR vaiables must have a value! ## ## copy additional source files ## mkdir -p ${SRCDIR} cp -p ${TOPDIR}/libiscsi-${VERSION}.tar.gz ${SRCDIR} mkdir -p ${SPECDIR} cp -p ${DIRNAME}/${SPECFILE} ${SPECDIR} ## ## Build ## echo "$(basename $0): Getting Ready to build release package" ${RPMBUILD} -ba --clean --rmsource ${EXTRA_OPTIONS} ${SPECDIR}/${SPECFILE} || exit 1 echo "$(basename $0): Done." exit 0 libiscsi-1.17.0/patches/000077500000000000000000000000001271371262700150405ustar00rootroot00000000000000libiscsi-1.17.0/patches/README000066400000000000000000000017321271371262700157230ustar00rootroot00000000000000This directory contains patches to other packages to make them iSCSI aware. sg3_utils-1.32.patch ==================== This patch adds iscsi support to the SG3 package. This is only added for the Linux platform, but adding to other platforms supported by SG3 should be trivial. $ ./src/sg_inq iscsi://127.0.0.1/iqn.ronnie.test/1 standard INQUIRY: PQual=0 Device_type=0 RMB=0 version=0x05 [SPC-3] [AERC=0] [TrmTsk=1] NormACA=0 HiSUP=0 Resp_data_format=2 SCCS=0 ACC=0 TPGS=0 3PC=0 Protect=0 BQue=0 EncServ=0 MultiP=0 [MChngr=0] [ACKREQQ=0] Addr16=0 [RelAdr=0] WBus16=0 Sync=0 Linked=0 [TranDis=0] CmdQue=1 [SPI: Clocking=0x0 QAS=0 IUS=0] length=66 (0x42) Peripheral device type: disk Vendor identification: IET Product identification: VIRTUAL-DISK Product revision level: 0001 Unit serial number: beaf11 mtx-iscsi.diff ============== Adds iscsi support to the MTX package to manage media changer devices. libiscsi-1.17.0/patches/mtx-iscsi.diff000066400000000000000000000176241271371262700176240ustar00rootroot00000000000000commit 2b859765244332e95fe960e7ec94b744e02c7c83 Author: Ronnie Sahlberg Date: Thu Aug 9 20:44:45 2012 +1000 ISCSI support Add support for specifying devices using '-f iscsi://// syntax Signed-off-by: Ronnie Sahlberg diff --git a/Makefile.in b/Makefile.in index 6b967cf..fe81280 100644 --- a/Makefile.in +++ b/Makefile.in @@ -26,7 +26,7 @@ INSTALL = @INSTALL@ CFLAGS = @CFLAGS@ CPPFLAGS = @CPPFLAGS@ -DVERSION="\"$(VERSION)\"" -I$(srcdir) -I. LDFLAGS = @LDFLAGS@ -LIBS = @LIBS@ +LIBS = @LIBS@ @libiscsi@ USE_OBJCOPY = @USE_OBJCOPY@ INSTALL_DOC = $(INSTALL) -m 644 diff --git a/config.h.in b/config.h.in index 7282e46..81ce3d4 100644 --- a/config.h.in +++ b/config.h.in @@ -30,8 +30,8 @@ #define HAVE_SYS_IOCTL_H 0 #define HAVE_SYS_MTIO_H 0 #define HAVE_DDK_NTDDSCSI_H 0 +#define HAVE_LIBISCSI 0 #define WORDS_BIGENDIAN 0 #endif - diff --git a/configure.in b/configure.in index 10aa09d..b8b79e7 100755 --- a/configure.in +++ b/configure.in @@ -110,4 +110,50 @@ AC_FUNC_VPRINTF dnl Check for files +dnl Check for libiscsi +use_libiscsi="" +AC_ARG_ENABLE([libiscsi], + AC_HELP_STRING([--enable-libiscsi], [enable iscsi support]), + use_libiscsi="$enableval" +) + +AC_MSG_CHECKING(if libiscsi is available) +if test "$use_libiscsi" = "yes" ; then + AC_MSG_RESULT(enabled) + AC_SUBST([libiscsi], ['-liscsi']) + AC_DEFINE_UNQUOTED(HAVE_LIBISCSI, 1, [we have libiscsi support], ) +elif test "$use_libiscsi" = "no" ; then + AC_MSG_RESULT(disabled) + AC_SUBST([libiscsi], ['']) +else +ac_save_CFLAGS="$CFLAGS" +ac_save_LIBS="$LIBS" +CFLAGS="" +LIBS="-liscsi" +AC_TRY_RUN([ +/* + * Just see if we can compile/link with libiscsi + */ +#include +int main(int argc, const char *argv[]) +{ + iscsi_create_context(""); + return 0; +} +], ac_cv_have_libiscsi=yes, ac_cv_have_libiscsi=no, + [echo $ac_n "compile with LIBISCSI. Assuming OK... $ac_c" + ac_cv_have_libiscsi=yes]) +CFLAGS="$ac_save_CFLAGS" +LIBS="$ac_save_LIBS" +if test "$ac_cv_have_libiscsi" = yes ; then + AC_MSG_RESULT(yes) + AC_SUBST([libiscsi], ['-liscsi']) + AC_DEFINE_UNQUOTED(HAVE_LIBISCSI, 1, [we have libiscsi support], ) +else + AC_MSG_RESULT(no) + AC_SUBST([libiscsi], ['']) +fi +fi + + AC_OUTPUT(Makefile) diff --git a/mtxl.h b/mtxl.h index 073f4fa..84510cc 100644 --- a/mtxl.h +++ b/mtxl.h @@ -26,6 +26,10 @@ #ifndef MTXL_H #define MTXL_H 1 +#if HAVE_LIBISCSI +#define ISCSI_FD 0x7fffffff +#endif + #include "mtx.h" #undef min diff --git a/scsi_linux.c b/scsi_linux.c index cc14ebf..8a7becb 100644 --- a/scsi_linux.c +++ b/scsi_linux.c @@ -58,14 +58,133 @@ $Revision: 193 $ static int pack_id; static int sg_timeout; + +#if HAVE_LIBISCSI +#include +#include +#include +struct iscsi_lun { + struct iscsi_context *context; + int lun; +}; +static struct iscsi_lun iscsi_lun; + +static int +do_iscsi_io(Direction_T Direction, + CDB_T *CDB, + int CDB_Length, + void *DataBuffer, + int DataBufferLength, + RequestSense_T *RequestSense) +{ + struct scsi_task *task; + struct iscsi_data outdata, *data = NULL; + + task = malloc(sizeof(struct scsi_task)); + if (task == NULL) { + FatalError("Out-of-memory: Failed to allocate iscsi task structure\n"); + } + memset(task, 0, sizeof(struct scsi_task)); + + task->cdb_size = CDB_Length; + memcpy(&task->cdb[0], CDB, task->cdb_size); + + if (DataBufferLength > 0) { + switch (Direction) { + case Input: + task->xfer_dir = SCSI_XFER_READ; + task->expxferlen = DataBufferLength; + break; + case Output: + task->xfer_dir = SCSI_XFER_WRITE; + task->expxferlen = DataBufferLength; + outdata.size = DataBufferLength; + outdata.data = DataBuffer; + data = &outdata; + break; + } + } else { + task->xfer_dir = SCSI_XFER_NONE; + task->expxferlen = 0; + } + + if (iscsi_scsi_command_sync(iscsi_lun.context, iscsi_lun.lun, task, data) == NULL) { + FatalError("Failed to do iscsi i/o : %s\n", iscsi_get_error(iscsi_lun.context)); + scsi_free_scsi_task(task); + return -1; + } + + if (task->status == SCSI_STATUS_GOOD) { + if (task->xfer_dir == SCSI_XFER_READ) { + memcpy(DataBuffer, task->datain.data, DataBufferLength); + } + } + if (task->status == SCSI_STATUS_CHECK_CONDITION) { + memcpy(RequestSense, task->datain.data+2, task->datain.size-2); + } + scsi_free_scsi_task(task); + + return 0; + } + +static DEVICE_TYPE OpenISCSI(char *DeviceName) +{ + char *portal = NULL; + char *target = NULL; + char *lun = NULL; + + portal = strdup(DeviceName + 8); + if (portal == NULL) { + FatalError("Out-of-memory. Failed to strdup %s\n", DeviceName); + } + target = index(portal, '/'); + if (target == NULL) { + FatalError("Invalid iSCSI URL : %s.\nURL must be specified as \"iscsi://[:]//\"\n", DeviceName); + } + *target++ = 0; + + lun = index(target, '/'); + if (lun == NULL) { + FatalError("Invalid iSCSI URL : %s.\nURL must be specified as \"iscsi://[:]//\"\n", DeviceName); + } + *lun++ = 0; + + iscsi_lun.context = iscsi_create_context("iqn.1999-08.net.sourceforge.mtx"); + if (iscsi_lun.context == NULL) { + FatalError("Failed to create iSCSI context for %s. (%s)\n", DeviceName, + iscsi_get_error(iscsi_lun.context)); + } + iscsi_set_targetname(iscsi_lun.context, target); + iscsi_set_session_type(iscsi_lun.context, ISCSI_SESSION_NORMAL); + iscsi_set_header_digest(iscsi_lun.context, ISCSI_HEADER_DIGEST_NONE_CRC32C); + + iscsi_lun.lun = atoi(lun); + if (iscsi_full_connect_sync(iscsi_lun.context, portal, iscsi_lun.lun) != 0) { + FatalError("Failed to connect to %s. (%s)\n", DeviceName, + iscsi_get_error(iscsi_lun.context)); + iscsi_destroy_context(iscsi_lun.context); + iscsi_lun.context = NULL; + } + + return ISCSI_FD; +} +#endif + DEVICE_TYPE SCSI_OpenDevice(char *DeviceName) { int timeout = SG_SCSI_DEFAULT_TIMEOUT; #ifdef SG_IO int k; /* version */ #endif - int DeviceFD = open(DeviceName, O_RDWR); + int DeviceFD; + +#if HAVE_LIBISCSI + if (!strncmp(DeviceName, "iscsi://", 8)) { + return OpenISCSI(DeviceName); + } +#endif + DeviceFD = open(DeviceName, O_RDWR); if (DeviceFD < 0) FatalError("cannot open SCSI device '%s' - %m\n", DeviceName); @@ -98,6 +217,14 @@ void SCSI_Default_Timeout(void) void SCSI_CloseDevice(char *DeviceName, DEVICE_TYPE DeviceFD) { +#if HAVE_LIBISCSI + if (iscsi_lun.context != NULL && DeviceFD == ISCSI_FD) { + iscsi_logout_sync(iscsi_lun.context); + iscsi_destroy_context(iscsi_lun.context); + iscsi_lun.context = NULL; + return; + } +#endif if (close(DeviceFD) < 0) FatalError("cannot close SCSI device '%s' - %m\n", DeviceName); } @@ -118,6 +245,16 @@ scsi_id_t *SCSI_GetIDLun(DEVICE_TYPE fd) int word2; } idlun; +#if HAVE_LIBISCSI + if (fd == ISCSI_FD) { + /* we only need the lun */ + retval = (scsi_id_t *)xmalloc(sizeof(scsi_id_t)); + retval->id = 0; + retval->lun = iscsi_lun.lun; + return retval; + } +#endif + status = ioctl(fd, SCSI_IOCTL_GET_IDLUN, &idlun); if (status) { @@ -157,6 +294,13 @@ int SCSI_ExecuteCommand(DEVICE_TYPE DeviceFD, unsigned int status; sg_io_hdr_t io_hdr; +#if HAVE_LIBISCSI + if (DeviceFD == ISCSI_FD) { + return do_iscsi_io(Direction, CDB, CDB_Length, + DataBuffer, DataBufferLength, + RequestSense); + } +#endif memset(&io_hdr, 0, sizeof(sg_io_hdr_t)); memset(RequestSense, 0, sizeof(RequestSense_T)); @@ -261,6 +405,13 @@ int SCSI_ExecuteCommand(DEVICE_TYPE DeviceFD, struct sg_header *Header; /* we actually point this into Command... */ struct sg_header *ResultHeader; /* we point this into ResultBuf... */ +#if HAVE_LIBISCSI + if (DeviceFD == ISCSI_FD) { + return do_iscsi_io(Direction, CDB, CDB_Length, + DataBuffer, DataBufferLength, + RequestSense); + } +#endif /* First, see if we need to set our SCSI timeout to something different */ if (sg_timeout != SG_SCSI_DEFAULT_TIMEOUT) { libiscsi-1.17.0/patches/sg3_utils-1.32.patch000066400000000000000000000751161271371262700203700ustar00rootroot00000000000000commit 2648e61716b76bd7deeb2218a5ff7ae0493930ed Author: Ronnie Sahlberg Date: Wed Jul 11 15:41:59 2012 +1000 iscsi support diff --git a/README b/README index 46d8f94..7b8736a 100644 --- a/README +++ b/README @@ -7,6 +7,8 @@ command set. Apart from SCSI parallel interface (SPI) devices, the SCSI command set is used by ATAPI devices (CD/DVDs and tapes), USB mass storage devices, Fibre Channel disks, IEEE 1394 storage devices (that use the "SBP" protocol), SAS, iSCSI and FCoE devices (amongst others). +On some platforms the package can also access iSCSI devices directly +using a built-in iscsi client. See the iSCSI section below for more info. This package originally targeted the Linux SCSI subsystem. Since most operating systems contain a SCSI command pass-through mechanism, many @@ -335,6 +337,44 @@ The more recent utilities that use "getopt_long" only are: sg_wr_mode +iSCSI support +============= +On some platforms (Linux) sg3_utils can be built with iSCSI support. +When built with iSCSI support, the utilities will be able to access iSCSI +devices directly using a built-in iSCSI client, without having to first make +the devices visible to the host. +This is very useful for cases where you have very many iSCSI targets you want +to manage and very many LUNs, in which case it may be impractical to make all +those devices visible to the local host. + +In order to get access to the iSCSI functionality, the sg3_utils package must +be built and linked against the iscsi client library from : + https://github.com/sahlberg/libiscsi +When configuring and building sg3_utils, the build process will detect if +this library is available and if it is build a version of sg3_utils with +iSCSI support. + +With iSCSI support, you can specify a iSCSI URL instead of the /dev/sg* +device that most of the utilities take. + +Example: + sudo sg_inq iscsi://karelin/iqn.ronnie.test/1 +Which means : Use LUN 1 on the target with the name "iqn.ronnie.test" +that is available on network host "karelin". + +The full form of the iSCSI URL is : + iscsi://[[%]@][:]// + +iSCSI Authentication: +The package supports normal iSCSI CHAP authentication to those targets that +require it. +This is done by specifying hte CHAP username and password as part of the iSCSI +URL. + +Example: + sudo sg_inq iscsi://ronnie%secret@karelin/iqn.ronnie.test/1 + + Dangerous code ============== This code: diff --git a/configure.ac b/configure.ac index 355ca84..1916603 100644 --- a/configure.ac +++ b/configure.ac @@ -76,4 +76,34 @@ AC_ARG_ENABLE([win32-spt-direct], AC_DEFINE_UNQUOTED(WIN32_SPT_DIRECT, 1, [enable Win32 SPT Direct], ) ) + +AC_MSG_CHECKING(if libiscsi is available) +ac_save_CFLAGS="$CFLAGS" +ac_save_LIBS="$LIBS" +CFLAGS="" +LIBS="-liscsi" +AC_TRY_RUN([ +/* + * Just see if we can compile/link with libiscsi + */ +#include +int main(int argc, const char *argv[]) +{ + iscsi_create_context(""); + return 0; +} +], ac_cv_have_libiscsi=yes, ac_cv_have_libiscsi=no, + [echo $ac_n "compile with LIBISCSI. Assuming OK... $ac_c" + ac_cv_have_libiscsi=yes]) +CFLAGS="$ac_save_CFLAGS" +LIBS="$ac_save_LIBS" +if test "$ac_cv_have_libiscsi" = yes ; then + AC_MSG_RESULT(yes) + AC_SUBST([libiscsi], ['-liscsi']) + AC_DEFINE_UNQUOTED(CONFIG_LIBISCSI, 1, [we have libiscsi support], ) +else + AC_MSG_RESULT(no) + AC_SUBST([libiscsi], ['']) +fi + AC_OUTPUT(Makefile include/Makefile lib/Makefile src/Makefile doc/Makefile) diff --git a/include/sg_pt_iscsi.h b/include/sg_pt_iscsi.h new file mode 100644 index 0000000..f849eb0 --- /dev/null +++ b/include/sg_pt_iscsi.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2010 Ronnie Sahlberg + * All rights reserved. + * Use of this source code is governed by a BSD-style + * license that can be found in the BSD_LICENSE file. + */ + +extern struct sg_context *iscsi; + +#define ISCSI_FAKE_FD 0x7fffffff + +int do_iscsi_pt(struct sg_pt_base * vp, int fd, int time_secs, int verbose); +struct sg_pt_base *construct_iscsi_pt_obj(void); +void clear_iscsi_pt_obj(struct sg_pt_base * vp); +void destruct_iscsi_pt_obj(struct sg_pt_base * vp); +void set_iscsi_pt_cdb(struct sg_pt_base * vp, const unsigned char * cdb, int cdb_len); +void set_iscsi_pt_sense(struct sg_pt_base * vp, unsigned char * sense, int max_sense_len); +void set_iscsi_pt_data_in(struct sg_pt_base * vp, unsigned char * dxferp, int dxfer_len); +void set_iscsi_pt_data_out(struct sg_pt_base * vp, const unsigned char * dxferp, int dxfer_len); +int get_iscsi_pt_resid(const struct sg_pt_base * vp); +int get_iscsi_pt_result_category(const struct sg_pt_base * vp); +int get_iscsi_pt_os_err(const struct sg_pt_base * vp); +int get_iscsi_pt_sense_len(const struct sg_pt_base * vp); +void set_iscsi_pt_packet_id(struct sg_pt_base * vp, int pack_id); +int iscsi_pt_close_device(int device_fd); +char *get_iscsi_pt_os_err_str(const struct sg_pt_base * vp, int max_b_len, char * b); +int get_iscsi_pt_transport_err(const struct sg_pt_base * vp); +char *get_iscsi_pt_transport_err_str(const struct sg_pt_base * vp, int max_b_len, char * b); +void set_iscsi_pt_tag(struct sg_pt_base * vp, uint64_t tag); +void set_iscsi_pt_task_management(struct sg_pt_base * vp, int tmf_code); +void set_iscsi_pt_task_attr(struct sg_pt_base * vp, int attribute, int priority); +int get_iscsi_pt_duration_ms(const struct sg_pt_base * vp); +int get_iscsi_pt_status_response(const struct sg_pt_base * vp); +int iscsi_pt_open_device(const char *device_name, int read_only, int verbose); diff --git a/lib/Makefile.am b/lib/Makefile.am index 3d5213e..6990f12 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -8,6 +8,7 @@ libsgutils2_la_SOURCES = \ sg_cmds_extra.c \ sg_cmds_mmc.c \ sg_pt_common.c \ + sg_pt_iscsi.c \ sg_pt_linux.c \ sg_io_linux.c @@ -33,6 +34,7 @@ libsgutils2_la_SOURCES = \ sg_pt_win32.c EXTRA_libsgutils2_la_SOURCES = \ + sg_pt_iscsi.c \ sg_pt_linux.c \ sg_io_linux.c \ sg_pt_freebsd.c \ @@ -55,6 +57,7 @@ libsgutils2_la_SOURCES = \ sg_pt_win32.c EXTRA_libsgutils2_la_SOURCES = \ + sg_pt_iscsi.c \ sg_pt_linux.c \ sg_io_linux.c \ sg_pt_freebsd.c \ @@ -77,6 +80,7 @@ libsgutils2_la_SOURCES = \ sg_pt_freebsd.c EXTRA_libsgutils2_la_SOURCES = \ + sg_pt_iscsi.c \ sg_pt_linux.c \ sg_io_linux.c \ sg_linux_inc.h \ @@ -100,6 +104,7 @@ libsgutils2_la_SOURCES = \ sg_pt_solaris.c EXTRA_libsgutils2_la_SOURCES = \ + sg_pt_iscsi.c \ sg_pt_linux.c \ sg_io_linux.c \ sg_linux_inc.h \ @@ -123,6 +128,7 @@ libsgutils2_la_SOURCES = \ sg_pt_osf1.c EXTRA_libsgutils2_la_SOURCES = \ + sg_pt_iscsi.c \ sg_pt_linux.c \ sg_io_linux.c \ sg_pt_freebsd.c \ @@ -140,7 +146,7 @@ lib_LTLIBRARIES = libsgutils2.la libsgutils2_la_LDFLAGS = -version-info 2:0:0 -libsgutils2_la_LIBADD = @GETOPT_O_FILES@ @os_libs@ +libsgutils2_la_LIBADD = @GETOPT_O_FILES@ @os_libs@ @libiscsi@ libsgutils2_la_DEPENDENCIES = @GETOPT_O_FILES@ diff --git a/lib/sg_pt_iscsi.c b/lib/sg_pt_iscsi.c new file mode 100644 index 0000000..8f09baf --- /dev/null +++ b/lib/sg_pt_iscsi.c @@ -0,0 +1,392 @@ +/* + * Copyright (c) 2010 Ronnie Sahlberg + * 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 HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef CONFIG_LIBISCSI + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include "sg_lib.h" +#include "sg_pt.h" +#include "sg_linux_inc.h" +#include "sg_pt_iscsi.h" + +struct sg_context { + struct iscsi_context *context; + int lun; +}; +struct sg_context *iscsi; + +struct sg_pt_iscsi { + int os_err; + + int xferdir; + const unsigned char *cdb; + int cdb_len; + unsigned char *sense; + int sense_len; + int max_sense_len; + struct iscsi_data datain; + struct iscsi_data dataout; + int device_status; +}; + +struct sg_pt_base { + struct sg_pt_iscsi impl; +}; + +int +do_iscsi_pt(struct sg_pt_base * vp, int fd, int time_secs, int verbose) +{ + struct sg_pt_iscsi * ptp = &vp->impl; + struct scsi_task *task; + struct iscsi_data *data = NULL; + + /* keep compiler happy */ + time_secs = time_secs; + verbose = verbose; + fd = fd; + + task = malloc(sizeof(struct scsi_task)); + bzero(task, sizeof(struct scsi_task)); + + ptp->os_err = 0; + + task->cdb_size = ptp->cdb_len; + memcpy(&task->cdb[0], ptp->cdb, task->cdb_size); + + task->xfer_dir = ptp->xferdir; + switch (task->xfer_dir) { + case SCSI_XFER_NONE: + task->expxferlen = 0; + break; + case SCSI_XFER_READ: + task->expxferlen = ptp->datain.size; + break; + case SCSI_XFER_WRITE: + task->expxferlen = ptp->dataout.size; + data = &ptp->dataout; + break; + } + + if (iscsi_scsi_command_sync(iscsi->context, iscsi->lun, task, data) == NULL) { + printf("error\n"); + scsi_free_scsi_task(task); + ptp->os_err = -1; + return -1; + } + + ptp->device_status = task->status; + if (task->status == SCSI_STATUS_GOOD) { + memcpy(ptp->datain.data, task->datain.data, + task->datain.sizedatain.size? + task->datain.size:ptp->datain.size); + scsi_free_scsi_task(task); + return 0; + } + + if (task->status == SCSI_STATUS_CHECK_CONDITION) { + /* +2 is to strip off the initial "length" and just copy the sense blob itself */ + ptp->sense_len = (ptp->max_sense_len<(task->datain.size-2))?ptp->max_sense_len:(task->datain.size-2); + memcpy(ptp->sense, task->datain.data+2, ptp->sense_len); + scsi_free_scsi_task(task); + return 0; + } + return -1; +} + + +struct sg_pt_base * +construct_iscsi_pt_obj(void) +{ + struct sg_pt_iscsi * ptp; + + ptp = (struct sg_pt_iscsi *) + calloc(1, sizeof(struct sg_pt_iscsi)); + ptp->xferdir = SCSI_XFER_NONE; + return (struct sg_pt_base *)ptp; +} + +void +clear_iscsi_pt_obj(struct sg_pt_base * vp) +{ + struct sg_pt_iscsi * ptp = &vp->impl; + + if (ptp) { + memset(ptp, 0, sizeof(struct sg_pt_iscsi)); + ptp->xferdir = SCSI_XFER_READ; + } +} + +void +destruct_iscsi_pt_obj(struct sg_pt_base * vp) +{ + struct sg_pt_iscsi * ptp = &vp->impl; + + if (ptp) + free(ptp); +} + +void +set_iscsi_pt_cdb(struct sg_pt_base * vp, const unsigned char * cdb, + int cdb_len) +{ + struct sg_pt_iscsi * ptp = &vp->impl; + + ptp->cdb = cdb; + ptp->cdb_len = cdb_len; +} + +void +set_iscsi_pt_sense(struct sg_pt_base * vp, unsigned char * sense, + int max_sense_len) +{ + struct sg_pt_iscsi * ptp = &vp->impl; + + memset(sense, 0, max_sense_len); + ptp->sense = sense; + ptp->max_sense_len = max_sense_len; +} + +void +set_iscsi_pt_data_in(struct sg_pt_base * vp, unsigned char * dxferp, + int dxfer_len) +{ + struct sg_pt_iscsi * ptp = &vp->impl; + + if (dxfer_len > 0) { + ptp->datain.data = dxferp; + ptp->datain.size = dxfer_len; + ptp->xferdir = SCSI_XFER_READ; + } +} + +void +set_iscsi_pt_data_out(struct sg_pt_base * vp, const unsigned char * dxferp, + int dxfer_len) +{ + struct sg_pt_iscsi * ptp = &vp->impl; + + if (dxfer_len > 0) { + ptp->dataout.data = (unsigned char *)dxferp; + ptp->dataout.size = dxfer_len; + ptp->xferdir = SCSI_XFER_WRITE; + } +} + +int +get_iscsi_pt_resid(const struct sg_pt_base * vp) +{ + /* keep compiler happy */ + vp = vp; + + return 0; +} + +int +get_iscsi_pt_result_category(const struct sg_pt_base * vp) +{ + const struct sg_pt_iscsi * ptp = &vp->impl; + + if (ptp->os_err) + return SCSI_PT_RESULT_OS_ERR; + + if (ptp->device_status) + return SCSI_PT_RESULT_SENSE; + else + return SCSI_PT_RESULT_GOOD; +} + +int +get_iscsi_pt_os_err(const struct sg_pt_base * vp) +{ + const struct sg_pt_iscsi * ptp = &vp->impl; + + return ptp->os_err; +} + +int +get_iscsi_pt_sense_len(const struct sg_pt_base * vp) +{ + const struct sg_pt_iscsi * ptp = &vp->impl; + + return ptp->sense_len; +} + +void +set_iscsi_pt_packet_id(struct sg_pt_base * vp, int pack_id) +{ + /* keep compiler happy */ + vp = vp; + pack_id = pack_id; +} + +int +iscsi_pt_close_device(int device_fd) +{ + /* keep compiler happy */ + device_fd = device_fd; + + iscsi_logout_sync(iscsi->context); + iscsi_destroy_context(iscsi->context); + free(iscsi); + iscsi = NULL; + return 0; +} + +char * +get_iscsi_pt_os_err_str(const struct sg_pt_base * vp, int max_b_len, char * b) +{ + const struct sg_pt_iscsi * 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; +} + +int +get_iscsi_pt_transport_err(const struct sg_pt_base * vp) +{ + /* keep compiler happy */ + vp = vp; + + return 0; +} + +char * +get_iscsi_pt_transport_err_str(const struct sg_pt_base * vp, int max_b_len, + char * b) +{ + /* keep compiler happy */ + vp = vp; + max_b_len = max_b_len; + b = b; + + return ""; +} + +void +set_iscsi_pt_tag(struct sg_pt_base * vp, uint64_t tag) +{ + /* keep compiler happy */ + vp = vp; + tag = tag; +} + +void +set_iscsi_pt_task_management(struct sg_pt_base * vp, int tmf_code) +{ + /* keep compiler happy */ + vp = vp; + tmf_code = tmf_code; +} + +void +set_iscsi_pt_task_attr(struct sg_pt_base * vp, int attribute, int priority) +{ + /* keep compiler happy */ + vp = vp; + attribute = attribute; + priority = priority; +} + +int +get_iscsi_pt_duration_ms(const struct sg_pt_base * vp) +{ + /* keep compiler happy */ + vp = vp; + + return 0; +} + + +int +get_iscsi_pt_status_response(const struct sg_pt_base * vp) +{ + /* keep compiler happy */ + vp = vp; + + return 0; +} + +int +iscsi_pt_open_device(const char *device_name, int read_only, int verbose) +{ + char *tmpstr = NULL, *portal, *target, *lun; + + /* keep compiler happy */ + read_only = read_only; + verbose = verbose; + + if (strncmp(device_name, "iscsi://", 8)) { + return -1; + } + + tmpstr = strdup(device_name); + portal = tmpstr+8; + target = index(portal, '/'); + if (!target) { + fprintf(stderr, "Invalid iscsi url : %s\n", device_name); + free(tmpstr); + return -1; + } + *target++ = 0; + lun = index(target, '/'); + if (!lun) { + fprintf(stderr, "Invalid iscsi url : %s\n", device_name); + free(tmpstr); + return -1; + } + *lun++ = 0; + + iscsi = malloc(sizeof(struct sg_context)); + iscsi->context = iscsi_create_context("iqn.2010-12.org.sg3utils"); + if (!iscsi->context) { + fprintf(stderr, "Failed to create iscsi context for url %s\n", device_name); + free(tmpstr); + free(iscsi); + iscsi = NULL; + return -1; + } + + iscsi->lun = atoi(lun); + iscsi_set_targetname(iscsi->context, target); + iscsi_set_session_type(iscsi->context, ISCSI_SESSION_NORMAL); + iscsi_set_header_digest(iscsi->context, ISCSI_HEADER_DIGEST_NONE_CRC32C); + + if (iscsi_full_connect_sync(iscsi->context, portal, iscsi->lun) != 0) { + fprintf(stderr, "iSCSI login failed: %s\n", iscsi_get_error(iscsi->context)); + free(tmpstr); + iscsi_destroy_context(iscsi->context); + free(iscsi); + iscsi = NULL; + return -1; + } + + return ISCSI_FAKE_FD; +} + +#endif diff --git a/lib/sg_pt_linux.c b/lib/sg_pt_linux.c index 0a5216e..6be755e 100644 --- a/lib/sg_pt_linux.c +++ b/lib/sg_pt_linux.c @@ -27,6 +27,10 @@ #include "sg_lib.h" #include "sg_linux_inc.h" +#ifdef CONFIG_LIBISCSI +#include "sg_pt_iscsi.h" +#endif + #define DEF_TIMEOUT 60000 /* 60,000 millisecs (60 seconds) */ static const char * linux_host_bytes[] = { @@ -117,6 +121,12 @@ scsi_pt_open_flags(const char * device_name, int flags, int verbose) { int fd; +#ifdef CONFIG_LIBISCSI + if (!strncmp(device_name, "iscsi://", 8)) { + return iscsi_pt_open_device(device_name, flags, verbose); + } +#endif + if (verbose > 1) { if (NULL == sg_warnings_strm) sg_warnings_strm = stderr; @@ -135,6 +145,12 @@ scsi_pt_close_device(int device_fd) { int res; +#ifdef CONFIG_LIBISCSI + if (iscsi) { + return iscsi_pt_close_device(device_fd); + } +#endif + res = close(device_fd); if (res < 0) res = -errno; @@ -147,6 +163,12 @@ construct_scsi_pt_obj() { struct sg_pt_linux_scsi * ptp; +#ifdef CONFIG_LIBISCSI + if (iscsi) { + return construct_iscsi_pt_obj(); + } +#endif + ptp = (struct sg_pt_linux_scsi *) calloc(1, sizeof(struct sg_pt_linux_scsi)); if (ptp) { @@ -161,6 +183,12 @@ destruct_scsi_pt_obj(struct sg_pt_base * vp) { struct sg_pt_linux_scsi * ptp = &vp->impl; +#ifdef CONFIG_LIBISCSI + if (iscsi) { + return destruct_iscsi_pt_obj(vp); + } +#endif + if (ptp) free(ptp); } @@ -170,6 +198,12 @@ clear_scsi_pt_obj(struct sg_pt_base * vp) { struct sg_pt_linux_scsi * ptp = &vp->impl; +#ifdef CONFIG_LIBISCSI + if (iscsi) { + return clear_iscsi_pt_obj(vp); + } +#endif + if (ptp) { memset(ptp, 0, sizeof(struct sg_pt_linux_scsi)); ptp->io_hdr.interface_id = 'S'; @@ -183,6 +217,12 @@ set_scsi_pt_cdb(struct sg_pt_base * vp, const unsigned char * cdb, { struct sg_pt_linux_scsi * ptp = &vp->impl; +#ifdef CONFIG_LIBISCSI + if (iscsi) { + return set_iscsi_pt_cdb(vp, cdb, cdb_len); + } +#endif + if (ptp->io_hdr.cmdp) ++ptp->in_err; ptp->io_hdr.cmdp = (unsigned char *)cdb; @@ -195,6 +235,12 @@ set_scsi_pt_sense(struct sg_pt_base * vp, unsigned char * sense, { struct sg_pt_linux_scsi * ptp = &vp->impl; +#ifdef CONFIG_LIBISCSI + if (iscsi) { + return set_iscsi_pt_sense(vp, sense, max_sense_len); + } +#endif + if (ptp->io_hdr.sbp) ++ptp->in_err; memset(sense, 0, max_sense_len); @@ -209,6 +255,12 @@ set_scsi_pt_data_in(struct sg_pt_base * vp, unsigned char * dxferp, { struct sg_pt_linux_scsi * ptp = &vp->impl; +#ifdef CONFIG_LIBISCSI + if (iscsi) { + return set_iscsi_pt_data_in(vp, dxferp, dxfer_len); + } +#endif + if (ptp->io_hdr.dxferp) ++ptp->in_err; if (dxfer_len > 0) { @@ -225,6 +277,12 @@ set_scsi_pt_data_out(struct sg_pt_base * vp, const unsigned char * dxferp, { struct sg_pt_linux_scsi * ptp = &vp->impl; +#ifdef CONFIG_LIBISCSI + if (iscsi) { + return set_iscsi_pt_data_out(vp, dxferp, dxfer_len); + } +#endif + if (ptp->io_hdr.dxferp) ++ptp->in_err; if (dxfer_len > 0) { @@ -239,6 +297,13 @@ set_scsi_pt_packet_id(struct sg_pt_base * vp, int pack_id) { struct sg_pt_linux_scsi * ptp = &vp->impl; +#ifdef CONFIG_LIBISCSI + if (iscsi) { + set_iscsi_pt_packet_id(vp, pack_id); + return; + } +#endif + ptp->io_hdr.pack_id = pack_id; } @@ -247,6 +312,13 @@ set_scsi_pt_tag(struct sg_pt_base * vp, uint64_t tag) { struct sg_pt_linux_scsi * ptp = &vp->impl; +#ifdef CONFIG_LIBISCSI + if (iscsi) { + set_iscsi_pt_tag(vp, tag); + return; + } +#endif + ++ptp->in_err; tag = tag; /* dummy to silence compiler */ } @@ -257,6 +329,13 @@ set_scsi_pt_task_management(struct sg_pt_base * vp, int tmf_code) { struct sg_pt_linux_scsi * ptp = &vp->impl; +#ifdef CONFIG_LIBISCSI + if (iscsi) { + set_iscsi_pt_task_management(vp, tmf_code); + return; + } +#endif + ++ptp->in_err; tmf_code = tmf_code; /* dummy to silence compiler */ } @@ -266,6 +345,13 @@ set_scsi_pt_task_attr(struct sg_pt_base * vp, int attribute, int priority) { struct sg_pt_linux_scsi * ptp = &vp->impl; +#ifdef CONFIG_LIBISCSI + if (iscsi) { + set_iscsi_pt_task_attr(vp, attribute, priority); + return; + } +#endif + ++ptp->in_err; attribute = attribute; /* dummy to silence compiler */ priority = priority; /* dummy to silence compiler */ @@ -303,6 +389,12 @@ do_scsi_pt(struct sg_pt_base * vp, int fd, int time_secs, int verbose) { struct sg_pt_linux_scsi * ptp = &vp->impl; +#ifdef CONFIG_LIBISCSI + if (iscsi) { + return do_iscsi_pt(vp, fd, time_secs, verbose); + } +#endif + if (NULL == sg_warnings_strm) sg_warnings_strm = stderr; ptp->os_err = 0; @@ -339,6 +431,12 @@ get_scsi_pt_result_category(const struct sg_pt_base * vp) int dr_st = ptp->io_hdr.driver_status & SG_LIB_DRIVER_MASK; int scsi_st = ptp->io_hdr.status & 0x7e; +#ifdef CONFIG_LIBISCSI + if (iscsi) { + return get_iscsi_pt_result_category(vp); + } +#endif + if (ptp->os_err) return SCSI_PT_RESULT_OS_ERR; else if (ptp->io_hdr.host_status) @@ -360,6 +458,12 @@ get_scsi_pt_resid(const struct sg_pt_base * vp) { const struct sg_pt_linux_scsi * ptp = &vp->impl; +#ifdef CONFIG_LIBISCSI + if (iscsi) { + return get_iscsi_pt_resid(vp); + } +#endif + return ptp->io_hdr.resid; } @@ -368,6 +472,12 @@ get_scsi_pt_status_response(const struct sg_pt_base * vp) { const struct sg_pt_linux_scsi * ptp = &vp->impl; +#ifdef CONFIG_LIBISCSI + if (iscsi) { + return get_iscsi_pt_status_response(vp); + } +#endif + return ptp->io_hdr.status; } @@ -376,6 +486,12 @@ get_scsi_pt_sense_len(const struct sg_pt_base * vp) { const struct sg_pt_linux_scsi * ptp = &vp->impl; +#ifdef CONFIG_LIBISCSI + if (iscsi) { + return get_iscsi_pt_sense_len(vp); + } +#endif + return ptp->io_hdr.sb_len_wr; } @@ -384,6 +500,12 @@ get_scsi_pt_duration_ms(const struct sg_pt_base * vp) { const struct sg_pt_linux_scsi * ptp = &vp->impl; +#ifdef CONFIG_LIBISCSI + if (iscsi) { + return get_iscsi_pt_duration_ms(vp); + } +#endif + return ptp->io_hdr.duration; } @@ -392,6 +514,12 @@ get_scsi_pt_transport_err(const struct sg_pt_base * vp) { const struct sg_pt_linux_scsi * ptp = &vp->impl; +#ifdef CONFIG_LIBISCSI + if (iscsi) { + return get_iscsi_pt_transport_err(vp); + } +#endif + return (ptp->io_hdr.host_status << 8) + ptp->io_hdr.driver_status; } @@ -400,6 +528,12 @@ get_scsi_pt_os_err(const struct sg_pt_base * vp) { const struct sg_pt_linux_scsi * ptp = &vp->impl; +#ifdef CONFIG_LIBISCSI + if (iscsi) { + return get_iscsi_pt_os_err(vp); + } +#endif + return ptp->os_err; } @@ -416,6 +550,12 @@ get_scsi_pt_transport_err_str(const struct sg_pt_base * vp, int max_b_len, const char * driv_cp = "invalid"; const char * sugg_cp = "invalid"; +#ifdef CONFIG_LIBISCSI + if (iscsi) { + return get_iscsi_pt_transport_err_str(vp, max_b_len, b); + } +#endif + m = max_b_len; n = 0; if (hs) { @@ -451,6 +591,12 @@ 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; +#ifdef CONFIG_LIBISCSI + if (iscsi) { + return get_iscsi_pt_os_err_str(vp, max_b_len, b); + } +#endif + cp = safe_strerror(ptp->os_err); strncpy(b, cp, max_b_len); if ((int)strlen(cp) >= max_b_len) @@ -567,6 +713,12 @@ scsi_pt_open_flags(const char * device_name, int flags, int verbose) { int fd; +#ifdef CONFIG_LIBISCSI + if (!strncmp(device_name, "iscsi://", 8)) { + return iscsi_pt_open_device(device_name, flags, verbose); + } +#endif + if (! bsg_major_checked) { bsg_major_checked = 1; find_bsg_major(verbose); @@ -589,6 +741,12 @@ scsi_pt_close_device(int device_fd) { int res; +#ifdef CONFIG_LIBISCSI + if (iscsi) { + return iscsi_pt_close_device(device_fd); + } +#endif + res = close(device_fd); if (res < 0) res = -errno; @@ -601,6 +759,12 @@ construct_scsi_pt_obj() { struct sg_pt_linux_scsi * ptp; +#ifdef CONFIG_LIBISCSI + if (iscsi) { + return construct_iscsi_pt_obj(); + } +#endif + ptp = (struct sg_pt_linux_scsi *) calloc(1, sizeof(struct sg_pt_linux_scsi)); if (ptp) { @@ -620,6 +784,12 @@ destruct_scsi_pt_obj(struct sg_pt_base * vp) { struct sg_pt_linux_scsi * ptp = &vp->impl; +#ifdef CONFIG_LIBISCSI + if (iscsi) { + return destruct_iscsi_pt_obj(vp); + } +#endif + if (ptp) free(ptp); } @@ -629,6 +799,12 @@ clear_scsi_pt_obj(struct sg_pt_base * vp) { struct sg_pt_linux_scsi * ptp = &vp->impl; +#ifdef CONFIG_LIBISCSI + if (iscsi) { + return clear_iscsi_pt_obj(vp); + } +#endif + if (ptp) { memset(ptp, 0, sizeof(struct sg_pt_linux_scsi)); ptp->io_hdr.guard = 'Q'; @@ -647,6 +823,12 @@ set_scsi_pt_cdb(struct sg_pt_base * vp, const unsigned char * cdb, { struct sg_pt_linux_scsi * ptp = &vp->impl; +#ifdef CONFIG_LIBISCSI + if (iscsi) { + return set_iscsi_pt_cdb(vp, cdb, cdb_len); + } +#endif + if (ptp->io_hdr.request) ++ptp->in_err; /* C99 has intptr_t instead of long */ @@ -660,6 +842,12 @@ set_scsi_pt_sense(struct sg_pt_base * vp, unsigned char * sense, { struct sg_pt_linux_scsi * ptp = &vp->impl; +#ifdef CONFIG_LIBISCSI + if (iscsi) { + return set_iscsi_pt_sense(vp, sense, max_sense_len); + } +#endif + if (ptp->io_hdr.response) ++ptp->in_err; memset(sense, 0, max_sense_len); @@ -674,6 +862,12 @@ set_scsi_pt_data_in(struct sg_pt_base * vp, unsigned char * dxferp, { struct sg_pt_linux_scsi * ptp = &vp->impl; +#ifdef CONFIG_LIBISCSI + if (iscsi) { + return set_iscsi_pt_data_in(vp, dxferp, dxfer_len); + } +#endif + if (ptp->io_hdr.din_xferp) ++ptp->in_err; if (dxfer_len > 0) { @@ -689,6 +883,12 @@ set_scsi_pt_data_out(struct sg_pt_base * vp, const unsigned char * dxferp, { struct sg_pt_linux_scsi * ptp = &vp->impl; +#ifdef CONFIG_LIBISCSI + if (iscsi) { + return set_iscsi_pt_data_out(vp, dxferp, dxfer_len); + } +#endif + if (ptp->io_hdr.dout_xferp) ++ptp->in_err; if (dxfer_len > 0) { @@ -702,6 +902,13 @@ set_scsi_pt_packet_id(struct sg_pt_base * vp, int pack_id) { struct sg_pt_linux_scsi * ptp = &vp->impl; +#ifdef CONFIG_LIBISCSI + if (iscsi) { + set_iscsi_pt_packet_id(vp, pack_id); + return; + } +#endif + ptp->io_hdr.spare_in = pack_id; } @@ -710,6 +917,13 @@ set_scsi_pt_tag(struct sg_pt_base * vp, uint64_t tag) { struct sg_pt_linux_scsi * ptp = &vp->impl; +#ifdef CONFIG_LIBISCSI + if (iscsi) { + set_iscsi_pt_tag(vp, tag); + return; + } +#endif + ptp->io_hdr.request_tag = tag; } @@ -719,6 +933,13 @@ set_scsi_pt_task_management(struct sg_pt_base * vp, int tmf_code) { struct sg_pt_linux_scsi * ptp = &vp->impl; +#ifdef CONFIG_LIBISCSI + if (iscsi) { + set_iscsi_pt_task_management(vp, tmf_code); + return; + } +#endif + 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)(long)(&(ptp->tmf_request[0])); @@ -730,6 +951,12 @@ set_scsi_pt_task_attr(struct sg_pt_base * vp, int attribute, int priority) { struct sg_pt_linux_scsi * ptp = &vp->impl; +#ifdef CONFIG_LIBISCSI + if (iscsi) { + set_iscsi_pt_task_attr(vp, attribute, priority); + return; + } +#endif ptp->io_hdr.request_attr = attribute; ptp->io_hdr.request_priority = priority; } @@ -756,6 +983,12 @@ get_scsi_pt_resid(const struct sg_pt_base * vp) { const struct sg_pt_linux_scsi * ptp = &vp->impl; +#ifdef CONFIG_LIBISCSI + if (iscsi) { + return get_iscsi_pt_resid(vp); + } +#endif + return ptp->io_hdr.din_resid; } @@ -764,6 +997,12 @@ get_scsi_pt_status_response(const struct sg_pt_base * vp) { const struct sg_pt_linux_scsi * ptp = &vp->impl; +#ifdef CONFIG_LIBISCSI + if (iscsi) { + return get_iscsi_pt_status_response(vp); + } +#endif + return ptp->io_hdr.device_status; } @@ -772,6 +1011,12 @@ get_scsi_pt_sense_len(const struct sg_pt_base * vp) { const struct sg_pt_linux_scsi * ptp = &vp->impl; +#ifdef CONFIG_LIBISCSI + if (iscsi) { + return get_iscsi_pt_sense_len(vp); + } +#endif + return ptp->io_hdr.response_len; } @@ -780,6 +1025,12 @@ get_scsi_pt_duration_ms(const struct sg_pt_base * vp) { const struct sg_pt_linux_scsi * ptp = &vp->impl; +#ifdef CONFIG_LIBISCSI + if (iscsi) { + return get_iscsi_pt_duration_ms(vp); + } +#endif + return ptp->io_hdr.duration; } @@ -788,6 +1039,12 @@ get_scsi_pt_transport_err(const struct sg_pt_base * vp) { const struct sg_pt_linux_scsi * ptp = &vp->impl; +#ifdef CONFIG_LIBISCSI + if (iscsi) { + return get_iscsi_pt_transport_err(vp); + } +#endif + return ptp->io_hdr.transport_status; } @@ -805,6 +1062,12 @@ get_scsi_pt_transport_err_str(const struct sg_pt_base * vp, int max_b_len, const char * driv_cp = "invalid"; const char * sugg_cp = "invalid"; +#ifdef CONFIG_LIBISCSI + if (iscsi) { + return get_iscsi_pt_transport_err_str(vp, max_b_len, b); + } +#endif + m = max_b_len; n = 0; if (hs) { @@ -841,6 +1104,12 @@ get_scsi_pt_result_category(const struct sg_pt_base * vp) int dr_st = ptp->io_hdr.driver_status & SG_LIB_DRIVER_MASK; int scsi_st = ptp->io_hdr.device_status & 0x7e; +#ifdef CONFIG_LIBISCSI + if (iscsi) { + return get_iscsi_pt_result_category(vp); + } +#endif + if (ptp->os_err) return SCSI_PT_RESULT_OS_ERR; else if (ptp->io_hdr.transport_status) @@ -862,6 +1131,12 @@ get_scsi_pt_os_err(const struct sg_pt_base * vp) { const struct sg_pt_linux_scsi * ptp = &vp->impl; +#ifdef CONFIG_LIBISCSI + if (iscsi) { + return get_iscsi_pt_os_err(vp); + } +#endif + return ptp->os_err; } @@ -871,6 +1146,12 @@ 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; +#ifdef CONFIG_LIBISCSI + if (iscsi) { + return get_iscsi_pt_os_err_str(vp, max_b_len, b); + } +#endif + cp = safe_strerror(ptp->os_err); strncpy(b, cp, max_b_len); if ((int)strlen(cp) >= max_b_len) @@ -944,6 +1225,12 @@ do_scsi_pt(struct sg_pt_base * vp, int fd, int time_secs, int verbose) { struct sg_pt_linux_scsi * ptp = &vp->impl; +#ifdef CONFIG_LIBISCSI + if (iscsi) { + return do_iscsi_pt(vp, fd, time_secs, verbose); + } +#endif + if (! bsg_major_checked) { bsg_major_checked = 1; find_bsg_major(verbose); libiscsi-1.17.0/test-tool/000077500000000000000000000000001271371262700153435ustar00rootroot00000000000000libiscsi-1.17.0/test-tool/Makefile.am000066400000000000000000000153051271371262700174030ustar00rootroot00000000000000AM_CPPFLAGS=-I. -I${srcdir}/../include \ "-D_U_=__attribute__((unused)) " \ "-D_R_(A,B)=__attribute__((format(printf,A,B)))" AM_CFLAGS=$(WARN_CFLAGS) LDADD = ../lib/libiscsi.la EXTRA_DIST = README dist_noinst_HEADERS = iscsi-support.h \ iscsi-test-cu.h iscsi-multipath.h # libiscsi test tool using cunit if ISCSITEST bin_PROGRAMS = iscsi-test-cu iscsi_test_cu_LDFLAGS = -ldl -lcunit iscsi_test_cu_SOURCES = iscsi-test-cu.c \ iscsi-support.c \ iscsi-multipath.c \ test_compareandwrite_simple.c \ test_compareandwrite_dpofua.c \ test_compareandwrite_miscompare.c \ test_extendedcopy_simple.c \ test_extendedcopy_param.c \ test_extendedcopy_descr_limits.c \ test_extendedcopy_descr_type.c \ test_extendedcopy_validate_tgt_descr.c \ test_extendedcopy_validate_seg_descr.c \ test_get_lba_status_simple.c \ test_get_lba_status_beyond_eol.c \ test_get_lba_status_unmap_single.c \ test_inquiry_alloc_length.c \ test_inquiry_block_limits.c \ test_inquiry_evpd.c \ test_inquiry_mandatory_vpd_sbc.c \ test_inquiry_standard.c \ test_inquiry_supported_vpd.c \ test_inquiry_version_descriptors.c \ test_iscsi_cmdsn_toohigh.c \ test_iscsi_cmdsn_toolow.c \ test_iscsi_datasn_invalid.c \ test_mandatory_sbc.c \ test_modesense6_all_pages.c \ test_modesense6_control.c \ test_modesense6_control_d_sense.c \ test_modesense6_control_swp.c \ test_modesense6_residuals.c \ test_nomedia_sbc.c \ test_orwrite_simple.c \ test_orwrite_beyond_eol.c \ test_orwrite_0blocks.c \ test_orwrite_wrprotect.c \ test_orwrite_dpofua.c \ test_orwrite_verify.c \ test_prefetch10_simple.c \ test_prefetch10_beyond_eol.c \ test_prefetch10_0blocks.c \ test_prefetch10_flags.c \ test_prefetch16_simple.c \ test_prefetch16_beyond_eol.c \ test_prefetch16_0blocks.c \ test_prefetch16_flags.c \ test_preventallow_simple.c \ test_preventallow_eject.c \ test_preventallow_itnexus_loss.c \ test_preventallow_logout.c \ test_preventallow_warm_reset.c \ test_preventallow_cold_reset.c \ test_preventallow_lun_reset.c \ test_preventallow_2_itnexuses.c \ test_prin_read_keys_simple.c \ test_prin_serviceaction_range.c \ test_prin_report_caps.c \ test_prout_register_simple.c \ test_prout_reserve_simple.c \ test_prout_reserve_access.c \ test_prout_reserve_ownership.c \ test_prout_clear_simple.c \ test_prout_preempt.c \ test_read6_simple.c \ test_read6_beyond_eol.c \ test_read10_simple.c \ test_read10_beyond_eol.c \ test_read10_0blocks.c \ test_read10_rdprotect.c \ test_read10_dpofua.c \ test_read10_residuals.c \ test_read10_invalid.c \ test_read12_simple.c \ test_read12_beyond_eol.c \ test_read12_0blocks.c \ test_read12_rdprotect.c \ test_read12_dpofua.c \ test_read12_residuals.c \ test_read16_simple.c \ test_read16_beyond_eol.c \ test_read16_0blocks.c \ test_read16_rdprotect.c \ test_read16_dpofua.c \ test_read16_residuals.c \ test_readcapacity10_simple.c \ test_readcapacity16_alloclen.c \ test_readcapacity16_protection.c \ test_readcapacity16_simple.c \ test_readcapacity16_support.c \ test_readonly_sbc.c \ test_receive_copy_results_copy_status.c \ test_receive_copy_results_op_params.c \ test_report_supported_opcodes_one_command.c \ test_report_supported_opcodes_rctd.c \ test_report_supported_opcodes_servactv.c \ test_report_supported_opcodes_simple.c \ test_reserve6_simple.c \ test_reserve6_2initiators.c \ test_reserve6_logout.c \ test_reserve6_itnexus_loss.c \ test_reserve6_target_warm_reset.c \ test_reserve6_target_cold_reset.c \ test_reserve6_lun_reset.c \ test_sanitize_block_erase.c \ test_sanitize_block_erase_reserved.c \ test_sanitize_crypto_erase.c \ test_sanitize_crypto_erase_reserved.c \ test_sanitize_exit_failure_mode.c \ test_sanitize_invalid_serviceaction.c \ test_sanitize_overwrite.c \ test_sanitize_overwrite_reserved.c \ test_sanitize_readonly.c \ test_sanitize_reservations.c \ test_sanitize_reset.c \ test_startstopunit_simple.c \ test_startstopunit_pwrcnd.c \ test_startstopunit_noloej.c \ test_testunitready_simple.c \ test_unmap_simple.c \ test_unmap_0blocks.c \ test_unmap_vpd.c \ test_verify10_simple.c \ test_verify10_beyond_eol.c \ test_verify10_0blocks.c \ test_verify10_vrprotect.c \ test_verify10_flags.c \ test_verify10_dpo.c \ test_verify10_mismatch.c \ test_verify10_mismatch_no_cmp.c \ test_verify12_simple.c \ test_verify12_beyond_eol.c \ test_verify12_0blocks.c \ test_verify12_vrprotect.c \ test_verify12_flags.c \ test_verify12_dpo.c \ test_verify12_mismatch.c \ test_verify12_mismatch_no_cmp.c \ test_verify16_simple.c \ test_verify16_beyond_eol.c \ test_verify16_0blocks.c \ test_verify16_vrprotect.c \ test_verify16_flags.c \ test_verify16_dpo.c \ test_verify16_mismatch.c \ test_verify16_mismatch_no_cmp.c \ test_write10_simple.c \ test_write10_beyond_eol.c \ test_write10_0blocks.c \ test_write10_wrprotect.c \ test_write10_dpofua.c \ test_write10_residuals.c \ test_write12_simple.c \ test_write12_beyond_eol.c \ test_write12_0blocks.c \ test_write12_wrprotect.c \ test_write12_dpofua.c \ test_write12_residuals.c \ test_write16_simple.c \ test_write16_beyond_eol.c \ test_write16_0blocks.c \ test_write16_wrprotect.c \ test_write16_dpofua.c \ test_write16_residuals.c \ test_writeatomic16_simple.c \ test_writeatomic16_beyond_eol.c \ test_writeatomic16_0blocks.c \ test_writeatomic16_wrprotect.c \ test_writeatomic16_dpofua.c \ test_writeatomic16_vpd.c \ test_writesame10_simple.c \ test_writesame10_beyond_eol.c \ test_writesame10_0blocks.c \ test_writesame10_wrprotect.c \ test_writesame10_unmap.c \ test_writesame10_unmap_unaligned.c \ test_writesame10_unmap_until_end.c \ test_writesame10_unmap_vpd.c \ test_writesame16_simple.c \ test_writesame16_beyond_eol.c \ test_writesame16_0blocks.c \ test_writesame16_wrprotect.c \ test_writesame16_unmap.c \ test_writesame16_unmap_unaligned.c \ test_writesame16_unmap_until_end.c \ test_writesame16_unmap_vpd.c \ test_writeverify10_simple.c \ test_writeverify10_beyond_eol.c \ test_writeverify10_0blocks.c \ test_writeverify10_wrprotect.c \ test_writeverify10_flags.c \ test_writeverify10_dpo.c \ test_writeverify10_residuals.c \ test_writeverify12_simple.c \ test_writeverify12_beyond_eol.c \ test_writeverify12_0blocks.c \ test_writeverify12_wrprotect.c \ test_writeverify12_flags.c \ test_writeverify12_dpo.c \ test_writeverify12_residuals.c \ test_writeverify16_simple.c \ test_writeverify16_beyond_eol.c \ test_writeverify16_0blocks.c \ test_writeverify16_wrprotect.c \ test_writeverify16_flags.c \ test_writeverify16_dpo.c \ test_writeverify16_residuals.c \ test_multipathio_simple.c \ test_multipathio_reset.c \ test_multipathio_compareandwrite.c \ test_multipathio_async_caw.c \ test_async_read.c \ test_async_write.c \ test_async_abort_simple.c endif libiscsi-1.17.0/test-tool/README000066400000000000000000000030741271371262700162270ustar00rootroot00000000000000This directory contains a libiscsi based test tool. The purpose of this test tool is to validate iscsi and scsi protocol compliance of a target. Initiatornames and LUN-masking ============================== If the target uses lun-masking or ACLs you need to set the target up to allow iscsi-test access to the LUNs you want to test. By default iscsi-test uses the following two initiator names : iqn.2007-10.com.github:sahlberg:libiscsi:iscsi-test iqn.2007-10.com.github:sahlberg:libiscsi:iscsi-test-2 Most tests only use the first name but some tests, such as the RESERVE/RELEASE tests, will use both names. Optionally you can use different initiatir names for your testing using the command line flags : --initiator-name=iqn. --initiator-name-2=iqn. Listing all tests and test details: =================================== The --list argument is used to show a lost of all major tests that are available iscsi-test --list To list all tests and a description of each test and subtests, use iscsi-test --list --info Running tests: ============== Running tests you need to specify which test to run using the --test argument. This can either be the name of one specific test or a set of tests using * as the wildcard character. Example to run one specific test : iscsi-test --test="T1020_bufferoffset_invalid" iscsi://127.0.0.1/iqn.ronnie.test/1 Example to run all READ10 tests : iscsi-test --test="*read10*" iscsi://127.0.0.1/iqn.ronnie.test/1 Or to run every test : iscsi-test --test="*" iscsi://127.0.0.1/iqn.ronnie.test/1 libiscsi-1.17.0/test-tool/iscsi-multipath.c000066400000000000000000000352471271371262700206410ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* iscsi test-tool multipath support Copyright (C) 2015 David Disseldorp 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, see . */ #include "config.h" #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_SG_IO #include #include #include #endif #include "slist.h" #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-private.h" #include "iscsi-support.h" #include "iscsi-multipath.h" int mp_num_sds = 0; struct scsi_device *mp_sds[MPATH_MAX_DEVS]; static void mpath_des_free(struct scsi_inquiry_device_designator *des) { if (!des) { return; } free(des->designator); free(des); } static int mpath_des_copy(struct scsi_inquiry_device_designator *des, struct scsi_inquiry_device_designator **_des_cp) { struct scsi_inquiry_device_designator *des_cp; if (!_des_cp) { return -1; } des_cp = malloc(sizeof(*des_cp)); if (des_cp == NULL) { return -1; } des_cp->protocol_identifier = des->protocol_identifier; des_cp->code_set = des->code_set; des_cp->piv = des->piv; des_cp->association = des->association; des_cp->designator_type = des->designator_type; des_cp->designator_length = des->designator_length; des_cp->designator = malloc(des->designator_length); if (des_cp->designator == NULL) { free(des_cp); return -1; } memcpy(des_cp->designator, des->designator, des->designator_length); *_des_cp = des_cp; return 0; } static int mpath_des_cmp(struct scsi_inquiry_device_designator *des1, struct scsi_inquiry_device_designator *des2) { if (des1->protocol_identifier != des2->protocol_identifier) { return -1; } if (des1->code_set != des2->code_set) { return -1; } if (des1->piv != des2->piv) { return -1; } if (des1->association != des2->association) { return -1; } if (des1->designator_type != des2->designator_type) { return -1; } if (des1->designator_length != des2->designator_length) { return -1; } return memcmp(des1->designator, des2->designator, des1->designator_length); } static int mpath_check_matching_ids_devid_vpd(int num_sds, struct scsi_device **sds) { int i; int num_sds_with_valid_id = 0; struct scsi_task *inq_task = NULL; struct scsi_inquiry_device_designator *des_saved = NULL; for (i = 0; i < num_sds; i++) { int ret; int full_size; struct scsi_inquiry_device_identification *inq_id_data; struct scsi_inquiry_device_designator *des; /* * dev ID inquiry to confirm that all multipath devices carry * an identical logical unit identifier. */ inquiry(sds[i], &inq_task, 1, SCSI_INQUIRY_PAGECODE_DEVICE_IDENTIFICATION, 64, EXPECT_STATUS_GOOD); if (inq_task == NULL || inq_task->status != SCSI_STATUS_GOOD) { printf("Inquiry command failed : %s\n", sds[i]->error_str); goto err_cleanup; } full_size = scsi_datain_getfullsize(inq_task); if (full_size > inq_task->datain.size) { /* we need more data */ scsi_free_scsi_task(inq_task); inq_task = NULL; inquiry(sds[i], &inq_task, 1, SCSI_INQUIRY_PAGECODE_DEVICE_IDENTIFICATION, full_size, EXPECT_STATUS_GOOD); if (inq_task == NULL) { printf("Inquiry command failed : %s\n", sds[i]->error_str); goto err_cleanup; } } inq_id_data = scsi_datain_unmarshall(inq_task); if (inq_id_data == NULL) { printf("failed to unmarshall inquiry ID datain blob\n"); goto err_cleanup; } if (inq_id_data->qualifier != SCSI_INQUIRY_PERIPHERAL_QUALIFIER_CONNECTED) { printf("error: multipath device not connected\n"); goto err_cleanup; } if (inq_id_data->device_type != SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_DIRECT_ACCESS) { printf("error: multipath devices must be SBC\n"); goto err_cleanup; } /* walk the list of IDs, and find a suitable LU candidate */ for (des = inq_id_data->designators; des != NULL; des = des->next) { if (des->association != SCSI_ASSOCIATION_LOGICAL_UNIT) { printf("skipping non-LU designator: %d\n", des->association); continue; } if ((des->designator_type != SCSI_DESIGNATOR_TYPE_EUI_64) && (des->designator_type != SCSI_DESIGNATOR_TYPE_NAA) && (des->designator_type != SCSI_DESIGNATOR_TYPE_MD5_LOGICAL_UNIT_IDENTIFIER) && (des->designator_type != SCSI_DESIGNATOR_TYPE_SCSI_NAME_STRING)) { printf("skipping unsupported des type: %d\n", des->designator_type); continue; } if (des->designator_length <= 0) { printf("skipping designator with bad len: %d\n", des->designator_length); continue; } if (des_saved == NULL) { ret = mpath_des_copy(des, &des_saved); if (ret < 0) { goto err_cleanup; } /* * we now have a reference to look for in all * subsequent paths. */ num_sds_with_valid_id++; break; } else if (mpath_des_cmp(des, des_saved) == 0) { /* found match for previous path designator */ num_sds_with_valid_id++; break; } /* no match yet, keep checking other designators */ } scsi_free_scsi_task(inq_task); inq_task = NULL; } mpath_des_free(des_saved); if (num_sds_with_valid_id != num_sds) { printf("failed to find matching LU device ID for all paths\n"); return -1; } printf("found matching LU device identifier for all (%d) paths\n", num_sds); return 0; err_cleanup: mpath_des_free(des_saved); scsi_free_scsi_task(inq_task); return -1; } static int mpath_check_matching_ids_serial_vpd(int num_sds, struct scsi_device **sds) { int i; int num_sds_with_valid_id = 0; struct scsi_task *inq_task = NULL; char *usn_saved = NULL; for (i = 0; i < num_sds; i++) { int full_size; struct scsi_inquiry_unit_serial_number *inq_serial; /* * inquiry to confirm that all multipath devices carry an * identical unit serial number. */ inq_task = NULL; inquiry(sds[i], &inq_task, 1, SCSI_INQUIRY_PAGECODE_UNIT_SERIAL_NUMBER, 64, EXPECT_STATUS_GOOD); if (inq_task == NULL || inq_task->status != SCSI_STATUS_GOOD) { printf("Inquiry command failed : %s\n", sds[i]->error_str); goto err_cleanup; } full_size = scsi_datain_getfullsize(inq_task); if (full_size > inq_task->datain.size) { scsi_free_scsi_task(inq_task); /* we need more data */ inq_task = NULL; inquiry(sds[i], &inq_task, 1, SCSI_INQUIRY_PAGECODE_UNIT_SERIAL_NUMBER, full_size, EXPECT_STATUS_GOOD); if (inq_task == NULL) { printf("Inquiry command failed : %s\n", sds[i]->error_str); goto err_cleanup; } } inq_serial = scsi_datain_unmarshall(inq_task); if (inq_serial == NULL) { printf("failed to unmarshall inquiry datain blob\n"); goto err_cleanup; } if (inq_serial->qualifier != SCSI_INQUIRY_PERIPHERAL_QUALIFIER_CONNECTED) { printf("error: multipath device not connected\n"); goto err_cleanup; } if (inq_serial->device_type != SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_DIRECT_ACCESS) { printf("error: multipath devices must be SBC\n"); goto err_cleanup; } if (inq_serial->usn == NULL) { printf("error: empty usn for multipath device\n"); goto err_cleanup; } if (usn_saved == NULL) { usn_saved = strdup(inq_serial->usn); if (usn_saved == NULL) { goto err_cleanup; } num_sds_with_valid_id++; } else if (strcmp(usn_saved, inq_serial->usn) == 0) { num_sds_with_valid_id++; } else { printf("multipath unit serial mismatch: %s != %s\n", usn_saved, inq_serial->usn); } scsi_free_scsi_task(inq_task); inq_task = NULL; } if (num_sds_with_valid_id != num_sds) { printf("failed to find matching serial number for all paths\n"); goto err_cleanup; } printf("found matching serial number for all (%d) paths: %s\n", num_sds, usn_saved); free(usn_saved); return 0; err_cleanup: free(usn_saved); scsi_free_scsi_task(inq_task); return -1; } int mpath_check_matching_ids(int num_sds, struct scsi_device **sds) { int ret; /* * first check all devices for a matching LU identifier in the device * identification INQUIRY VPD page. */ ret = mpath_check_matching_ids_devid_vpd(num_sds, sds); if (ret == 0) { return 0; /* found matching */ } /* fall back to a unit serial number check */ ret = mpath_check_matching_ids_serial_vpd(num_sds, sds); return ret; } int mpath_count_iscsi(int num_sds, struct scsi_device **sds) { int i; int found = 0; for (i = 0; i < num_sds; i++) { if (sds[i]->iscsi_ctx != NULL) { found++; } } return found; } /* * use an existing multi-path connection, or clone iscsi sd1. */ int mpath_sd2_get_or_clone(struct scsi_device *sd1, struct scsi_device **_sd2) { struct scsi_device *sd2; if (mp_num_sds > 1) { logging(LOG_VERBOSE, "using multipath dev for second session"); *_sd2 = mp_sds[1]; return 0; } if (sd1->iscsi_ctx == NULL) { logging(LOG_NORMAL, "can't clone non-iscsi device"); return -EINVAL; } logging(LOG_VERBOSE, "cloning sd1 for second session"); sd2 = malloc(sizeof(*sd2)); if (sd2 == NULL) { return -ENOMEM; } memset(sd2, 0, sizeof(*sd2)); sd2->iscsi_url = sd1->iscsi_url; sd2->iscsi_lun = sd1->iscsi_lun; sd2->iscsi_ctx = iscsi_context_login(initiatorname2, sd2->iscsi_url, &sd2->iscsi_lun); if (sd2->iscsi_ctx == NULL) { logging(LOG_VERBOSE, "Failed to login to target"); free(sd2); return -ENOMEM; } *_sd2 = sd2; return 0; } void mpath_sd2_put(struct scsi_device *sd2) { if (mp_num_sds > 1) { if (sd2 != mp_sds[1]) { logging(LOG_NORMAL, "Invalid sd2!"); } return; } /* sd2 was allocated by mp_get - cleanup */ iscsi_logout_sync(sd2->iscsi_ctx); iscsi_destroy_context(sd2->iscsi_ctx); free(sd2); } libiscsi-1.17.0/test-tool/iscsi-multipath.h000066400000000000000000000051411271371262700206340ustar00rootroot00000000000000/* iscsi test-tool multipath support Copyright (C) 2015 David Disseldorp 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, see . */ #ifndef _ISCSI_MULTIPATH_H_ #define _ISCSI_MULTIPATH_H_ #define MPATH_MAX_DEVS 2 extern int mp_num_sds; extern struct scsi_device *mp_sds[MPATH_MAX_DEVS]; int mpath_check_matching_ids(int num_sds, struct scsi_device **sds); int mpath_count_iscsi(int num_sds, struct scsi_device **sds); int mpath_sd2_get_or_clone(struct scsi_device *sd1, struct scsi_device **_sd2); void mpath_sd2_put(struct scsi_device *sd2); #define MPATH_SKIP_IF_UNAVAILABLE(_sds, _num_sds) \ do { \ if (_num_sds <= 1) { \ logging(LOG_NORMAL, "[SKIPPED] Multipath unavailable." \ " Skipping test"); \ CU_PASS("[SKIPPED] Multipath unavailable." \ " Skipping test"); \ return; \ } \ } while (0); #define MPATH_SKIP_UNLESS_ISCSI(_sds, _num_sds) \ do { \ if (mpath_count_iscsi(_num_sds, _sds) != _num_sds) { \ logging(LOG_NORMAL, "[SKIPPED] Non-iSCSI multipath." \ " Skipping test"); \ CU_PASS("[SKIPPED] Non-iSCSI multipath." \ " Skipping test"); \ return; \ } \ } while (0); #endif /* _ISCSI_MULTIPATH_H_ */ libiscsi-1.17.0/test-tool/iscsi-support.c000066400000000000000000003110721271371262700203370ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* iscsi-test tool support Copyright (C) 2012 by Lee Duncan Copyright (C) 2014 Ronnie Sahlberg 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, see . */ #include "config.h" #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_SG_IO #include #include #include #endif #include "slist.h" #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-private.h" #include "iscsi-support.h" #include "iscsi-multipath.h" /***************************************************************** * globals *****************************************************************/ const char *initiatorname1 = "iqn.2007-10.com.github:sahlberg:libiscsi:iscsi-test"; const char *initiatorname2 = "iqn.2007-10.com.github:sahlberg:libiscsi:iscsi-test-2"; int no_medium_ascqs[3] = { SCSI_SENSE_ASCQ_MEDIUM_NOT_PRESENT, SCSI_SENSE_ASCQ_MEDIUM_NOT_PRESENT_TRAY_OPEN, SCSI_SENSE_ASCQ_MEDIUM_NOT_PRESENT_TRAY_CLOSED }; int lba_oob_ascqs[1] = { SCSI_SENSE_ASCQ_LBA_OUT_OF_RANGE }; int invalid_cdb_ascqs[2] = { SCSI_SENSE_ASCQ_INVALID_FIELD_IN_CDB, SCSI_SENSE_ASCQ_INVALID_FIELD_IN_PARAMETER_LIST }; int param_list_len_err_ascqs[1] = { SCSI_SENSE_ASCQ_PARAMETER_LIST_LENGTH_ERROR }; int write_protect_ascqs[3] = { SCSI_SENSE_ASCQ_WRITE_PROTECTED, SCSI_SENSE_ASCQ_HARDWARE_WRITE_PROTECTED, SCSI_SENSE_ASCQ_SOFTWARE_WRITE_PROTECTED }; int sanitize_ascqs[1] = { SCSI_SENSE_ASCQ_SANITIZE_IN_PROGRESS }; int removal_ascqs[1] = { SCSI_SENSE_ASCQ_MEDIUM_REMOVAL_PREVENTED }; int miscompare_ascqs[1] = { SCSI_SENSE_ASCQ_MISCOMPARE_DURING_VERIFY }; int too_many_desc_ascqs[2] = { SCSI_SENSE_ASCQ_TOO_MANY_TARGET_DESCRIPTORS, SCSI_SENSE_ASCQ_TOO_MANY_SEGMENT_DESCRIPTORS, }; int unsupp_desc_code_ascqs[2] = { SCSI_SENSE_ASCQ_UNSUPPORTED_TARGET_DESCRIPTOR_TYPE_CODE, SCSI_SENSE_ASCQ_UNSUPPORTED_SEGMENT_DESCRIPTOR_TYPE_CODE }; int copy_aborted_ascqs[3] = { SCSI_SENSE_ASCQ_NO_ADDL_SENSE, SCSI_SENSE_ASCQ_UNREACHABLE_COPY_TARGET, SCSI_SENSE_ASCQ_COPY_TARGET_DEVICE_NOT_REACHABLE }; struct scsi_inquiry_standard *inq; struct scsi_inquiry_logical_block_provisioning *inq_lbp; struct scsi_inquiry_block_device_characteristics *inq_bdc; struct scsi_inquiry_block_limits *inq_bl; struct scsi_readcapacity16 *rc16; struct scsi_report_supported_op_codes *rsop; unsigned char *scratch; size_t block_size; uint64_t num_blocks; int lbppb; enum scsi_inquiry_peripheral_device_type device_type; int data_loss; int allow_sanitize; int readonly; int sbc3_support; int maximum_transfer_length; int (*real_iscsi_queue_pdu)(struct iscsi_context *iscsi, struct iscsi_pdu *pdu); static const unsigned char zeroBlock[4096]; /** * Returns 1 if and only if buf[0..size-1] is zero. */ int all_zero(const unsigned char *buf, unsigned size) { unsigned j, e; for (j = 0; j < size; j += e) { e = size - j; if (e > sizeof(zeroBlock)) e = sizeof(zeroBlock); if (memcmp(buf + j, zeroBlock, e) != 0) return 0; } return 1; } static const char *scsi_status_str(int status) { switch(status) { case SCSI_STATUS_GOOD: return "SUCCESS"; case SCSI_STATUS_CHECK_CONDITION: return "CHECK_CONDITION"; case SCSI_STATUS_CONDITION_MET: return "CONDITIONS_MET"; case SCSI_STATUS_BUSY: return "BUSY"; case SCSI_STATUS_RESERVATION_CONFLICT: return "RESERVATION_CONFLICT"; case SCSI_STATUS_TASK_SET_FULL: return "TASK_SET_FULL"; case SCSI_STATUS_ACA_ACTIVE: return "ACA_ACTIVE"; case SCSI_STATUS_TASK_ABORTED: return "TASK_ABORTED"; } return "UNKNOWN"; } /* * There is no agreement among the T10 committee whether a SCSI target should * report "invalid opcode", "invalid field in CDB" or "invalid field in * parameter list" if the opcode consists of two bytes. Hence accept all three * sense codes for two-byte opcodes. For more information see also Frederick * Knight, RE: INVALID COMMAND OPERATION CODE, T10 Reflector, 16 May 2008 * (http://t10.org/ftp/t10/t10r/2008/r0805167.htm). */ static int status_is_invalid_opcode(struct scsi_task *task) { if (task->status == SCSI_STATUS_CHECK_CONDITION && task->sense.key == SCSI_SENSE_ILLEGAL_REQUEST) { if (task->sense.ascq == SCSI_SENSE_ASCQ_INVALID_OPERATION_CODE) return 1; switch (task->cdb[0]) { case SCSI_OPCODE_MAINTENANCE_IN: case SCSI_OPCODE_SERVICE_ACTION_IN: switch (task->sense.ascq) { case SCSI_SENSE_ASCQ_INVALID_FIELD_IN_CDB: case SCSI_SENSE_ASCQ_INVALID_FIELD_IN_PARAMETER_LIST: return !task->sense.sense_specific || task->sense.field_pointer == 1; } } } return 0; } static int check_result(const char *opcode, struct scsi_device *sdev, struct scsi_task *task, int status, enum scsi_sense_key key, int *ascq, int num_ascq) { int ascq_ok = 0; if (task == NULL) { logging(LOG_NORMAL, "[FAILED] Failed to send %s command: " "%s", opcode, sdev->error_str); return -1; } if (status_is_invalid_opcode(task)) { logging(LOG_NORMAL, "[SKIPPED] %s is not implemented.", opcode); return -2; } if (status == SCSI_STATUS_GOOD && task->status != SCSI_STATUS_GOOD) { logging(LOG_NORMAL, "[FAILED] %s command failed with " "sense. %s(0x%02x)/%s(0x%04x)", opcode, scsi_sense_key_str(task->sense.key), task->sense.key, scsi_sense_ascq_str(task->sense.ascq), task->sense.ascq); return -1; } if (status != SCSI_STATUS_GOOD && task->status == SCSI_STATUS_GOOD) { logging(LOG_NORMAL, "[FAILED] %s successful but should " "have failed with %s(0x%02x)/%s(0x%04x)", opcode, scsi_sense_key_str(key), key, num_ascq ? scsi_sense_ascq_str(ascq[0]) : "NO ASCQ", num_ascq ? ascq[0] : 0); return -1; } if (status == SCSI_STATUS_RESERVATION_CONFLICT && task->status != SCSI_STATUS_RESERVATION_CONFLICT) { logging(LOG_NORMAL, "[FAILED] %s command should have failed " "with RESERVATION_CONFLICT.", opcode); return -1; } /* did we get any of the expected ASCQs ?*/ if (status == SCSI_STATUS_CHECK_CONDITION) { int i; for (i = 0; i < num_ascq; i++) { if (ascq[i] == task->sense.ascq) { ascq_ok = 1; } } if (num_ascq == 0) { ascq_ok = 1; } } if (status == SCSI_STATUS_CHECK_CONDITION && (task->status != status || task->sense.key != key || !ascq_ok)) { logging(LOG_NORMAL, "[FAILED] %s failed with wrong sense. " "Should have failed with %s(0x%02x)/%s(0x%04x) " "but failed with Sense: %s(0x%02x)/(0x%04x)\n", opcode, scsi_sense_key_str(key), key, num_ascq ? scsi_sense_ascq_str(ascq[0]) : "NO ASCQ", num_ascq ? ascq[0] : 0, sdev->error_str, task->sense.key, task->sense.ascq); return -1; } logging(LOG_VERBOSE, "[OK] %s returned %s %s(0x%02x) %s(0x%04x)", opcode, scsi_status_str(status), scsi_sense_key_str(task->sense.key), task->sense.key, scsi_sense_ascq_str(task->sense.ascq), task->sense.ascq); return 0; } #ifdef HAVE_SG_IO static size_t iov_tot_len(struct scsi_iovec *iov, int niov) { size_t len = 0; int i; for (i = 0; i < niov; i++) len += iov[i].iov_len; return len; } #endif static struct scsi_task *send_scsi_command(struct scsi_device *sdev, struct scsi_task *task, struct iscsi_data *d) { static time_t last_time = 0; if (sdev->iscsi_url) { time_t current_time = time(NULL); if (sdev->error_str != NULL) { free(discard_const(sdev->error_str)); sdev->error_str = NULL; } task = iscsi_scsi_command_sync(sdev->iscsi_ctx, sdev->iscsi_lun, task, d); if (task == NULL) { sdev->error_str = strdup(iscsi_get_error(sdev->iscsi_ctx)); } if (current_time > last_time + 1) { int i; /* Device [0] is where we are doing all the I/O * so it will always work the socket and respond * to NOPs from the target. * But we need to trigger a service event every * now and then on all the other devices to ensure that * we detect and respond to any NOPs. */ for (i = 1; i < mp_num_sds; i++) { iscsi_service(mp_sds[i]->iscsi_ctx, POLLIN|POLLOUT); } last_time = current_time; } return task; } /* We got an actual buffer from the application. Convert it to * a data-out iovector. */ if (d != NULL && d->data != NULL) { struct scsi_iovec *iov; iov = scsi_malloc(task, sizeof(struct scsi_iovec)); iov->iov_base = d->data; iov->iov_len = d->size; scsi_task_set_iov_out(task, iov, 1); } #ifdef HAVE_SG_IO if (sdev->sgio_dev) { sg_io_hdr_t io_hdr; unsigned int sense_len=32; unsigned char sense[sense_len]; char buf[1024]; memset(sense, 0, sizeof(sense)); memset(&io_hdr, 0, sizeof(sg_io_hdr_t)); io_hdr.interface_id = 'S'; /* CDB */ io_hdr.cmdp = task->cdb; io_hdr.cmd_len = task->cdb_size; /* Where to store the sense_data, if there was an error */ io_hdr.sbp = sense; io_hdr.mx_sb_len = sense_len; /* Transfer direction, either in or out. Linux does not yet support bidirectional SCSI transfers ? */ switch (task->xfer_dir) { case SCSI_XFER_WRITE: io_hdr.dxfer_direction = SG_DXFER_TO_DEV; io_hdr.iovec_count = task->iovector_out.niov; io_hdr.dxferp = task->iovector_out.iov; io_hdr.dxfer_len = iov_tot_len(task->iovector_out.iov, task->iovector_out.niov); break; case SCSI_XFER_READ: io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; task->datain.size = task->expxferlen; task->datain.data = malloc(task->datain.size); memset(task->datain.data, 0, task->datain.size); io_hdr.dxferp = task->datain.data; io_hdr.dxfer_len = task->datain.size; break; } /* SCSI timeout in ms */ io_hdr.timeout = 5000; if(ioctl(sdev->sgio_fd, SG_IO, &io_hdr) < 0){ if (sdev->error_str != NULL) { free(discard_const(sdev->error_str)); } sdev->error_str = strdup("SG_IO ioctl failed"); return NULL; } task->residual_status = SCSI_RESIDUAL_NO_RESIDUAL; task->residual = 0; if (io_hdr.resid) { task->residual_status = SCSI_RESIDUAL_UNDERFLOW; task->residual = io_hdr.resid; } if (task->xfer_dir == SCSI_XFER_READ) task->datain.size -= task->residual; /* now for the error processing */ if(io_hdr.sb_len_wr > 0){ task->status = SCSI_STATUS_CHECK_CONDITION; scsi_parse_sense_data(&task->sense, sense); snprintf(buf, sizeof(buf), "SENSE KEY:%s(%d) ASCQ:%s(0x%04x)", scsi_sense_key_str(task->sense.key), task->sense.key, scsi_sense_ascq_str(task->sense.ascq), task->sense.ascq); if (sdev->error_str != NULL) { free(discard_const(sdev->error_str)); } sdev->error_str = strdup(buf); return task; } if(io_hdr.masked_status){ task->status = SCSI_STATUS_ERROR; task->sense.key = 0x0f; task->sense.ascq = 0xffff; if (sdev->error_str != NULL) { free(discard_const(sdev->error_str)); } sdev->error_str = strdup("SCSI masked error"); return NULL; } if(io_hdr.host_status){ task->status = SCSI_STATUS_ERROR; task->sense.key = 0x0f; task->sense.ascq = 0xffff; snprintf(buf, sizeof(buf), "SCSI host error. Status=0x%x", io_hdr.host_status); if (sdev->error_str != NULL) { free(discard_const(sdev->error_str)); } sdev->error_str = strdup(buf); return task; } if(io_hdr.driver_status){ task->status = SCSI_STATUS_ERROR; task->sense.key = 0x0f; task->sense.ascq = 0xffff; if (sdev->error_str != NULL) { free(discard_const(sdev->error_str)); } sdev->error_str = strdup("SCSI driver error"); return NULL; } return task; } #endif return NULL; } void logging(int level, const char *format, ...) { va_list ap; static char message[1024]; int ret; if (loglevel < level) { return; } if (strncmp(LOG_BLANK_LINE, format, LOG_BLANK_LINE_CMP_LEN)==0) { printf("\n"); return; } va_start(ap, format); ret = vsnprintf(message, 1024, format, ap); va_end(ap); if (ret < 0) { return; } printf(" %s\n", message); } struct iscsi_context * iscsi_context_login(const char *initiatorname, const char *url, int *lun) { struct iscsi_context *iscsi; struct iscsi_url *iscsi_url; iscsi = iscsi_create_context(initiatorname); if (iscsi == NULL) { fprintf(stderr, "Failed to create context\n"); return NULL; } iscsi_url = iscsi_parse_full_url(iscsi, url); if (iscsi_url == NULL) { fprintf(stderr, "Failed to parse URL: %s\n", iscsi_get_error(iscsi)); iscsi_destroy_context(iscsi); return NULL; } iscsi_set_targetname(iscsi, iscsi_url->target); iscsi_set_session_type(iscsi, ISCSI_SESSION_NORMAL); iscsi_set_header_digest(iscsi, ISCSI_HEADER_DIGEST_NONE_CRC32C); if (iscsi_url->user[0] != '\0') { if (iscsi_set_initiator_username_pwd(iscsi, iscsi_url->user, iscsi_url->passwd) != 0) { fprintf(stderr, "Failed to set initiator username and password\n"); iscsi_destroy_url(iscsi_url); iscsi_destroy_context(iscsi); return NULL; } } if (iscsi_full_connect_sync(iscsi, iscsi_url->portal, iscsi_url->lun) != 0) { fprintf(stderr, "Login Failed. %s\n", iscsi_get_error(iscsi)); iscsi_destroy_url(iscsi_url); iscsi_destroy_context(iscsi); return NULL; } if (lun != NULL) { *lun = iscsi_url->lun; } iscsi_destroy_url(iscsi_url); return iscsi; } void wait_until_test_finished(struct iscsi_context *iscsi, struct iscsi_async_state *state) { struct pollfd pfd; int count = 0; int ret; while (state->finished == 0) { pfd.fd = iscsi_get_fd(iscsi); pfd.events = iscsi_which_events(iscsi); ret = poll(&pfd, 1, 1000); if (ret < 0) { printf("Poll failed"); exit(10); } if (ret == 0) { if (count++ > 5) { struct iscsi_pdu *pdu; state->finished = 1; state->status = SCSI_STATUS_CANCELLED; state->task->status = SCSI_STATUS_CANCELLED; /* this may leak memory since we don't free the pdu */ while ((pdu = iscsi->outqueue)) { ISCSI_LIST_REMOVE(&iscsi->outqueue, pdu); } while ((pdu = iscsi->waitpdu)) { ISCSI_LIST_REMOVE(&iscsi->waitpdu, pdu); } return; } continue; } if (iscsi_service(iscsi, pfd.revents) < 0) { printf("iscsi_service failed with : %s\n", iscsi_get_error(iscsi)); break; } } } int iscsi_queue_pdu(struct iscsi_context *iscsi, struct iscsi_pdu *pdu) { if (local_iscsi_queue_pdu != NULL) { local_iscsi_queue_pdu(iscsi, pdu); } return real_iscsi_queue_pdu(iscsi, pdu); } int orwrite(struct scsi_device *sdev, uint64_t lba, uint32_t datalen, int blocksize, int wrprotect, int dpo, int fua, int fua_nv, int group, unsigned char *data, int status, enum scsi_sense_key key, int *ascq, int num_ascq) { struct scsi_task *task; struct iscsi_data d; int ret; logging(LOG_VERBOSE, "Send ORWRITE (Expecting %s) LBA:%" PRIu64 " blocks:%d wrprotect:%d dpo:%d fua:%d fua_nv:%d group:%d", scsi_status_str(status), lba, datalen / blocksize, wrprotect, dpo, fua, fua_nv, group); if (!data_loss) { printf("--dataloss flag is not set in. Skipping write\n"); return -1; } task = scsi_cdb_orwrite(lba, datalen, blocksize, wrprotect, dpo, fua, fua_nv, group); assert(task != NULL); d.data = data; d.size = datalen; task = send_scsi_command(sdev, task, &d); ret = check_result("ORWRITE", sdev, task, status, key, ascq, num_ascq); if (task) { scsi_free_scsi_task(task); } return ret; } int prin_task(struct scsi_device *sdev, int service_action, int success_expected) { const int buf_sz = 16384; struct scsi_task *task; int ret = 0; logging(LOG_VERBOSE, "Send PRIN/SA=0x%02x, expect %s", service_action, success_expected ? "success" : "failure"); task = scsi_cdb_persistent_reserve_in(service_action, buf_sz); assert(task != NULL); task = send_scsi_command(sdev, task, NULL); if (task == NULL) { logging(LOG_NORMAL, "[FAILED] Failed to send PRIN command: %s", iscsi_get_error(sdev->iscsi_ctx)); return -1; } if (status_is_invalid_opcode(task)) { scsi_free_scsi_task(task); logging(LOG_NORMAL, "[SKIPPED] PERSISTENT RESERVE IN is not implemented."); return -2; } if (success_expected) { if (task->status != SCSI_STATUS_GOOD) { logging(LOG_NORMAL, "[FAILED] PRIN/SA=0x%x failed: %s", service_action, iscsi_get_error(sdev->iscsi_ctx)); ret = -1; } } else { if (task->status == SCSI_STATUS_GOOD) { logging(LOG_NORMAL, "[FAILED] PRIN/SA=0x%x succeeded with invalid serviceaction", service_action); ret = -1; } } scsi_free_scsi_task(task); task = NULL; return ret; } int prin_read_keys(struct scsi_device *sdev, struct scsi_task **tp, struct scsi_persistent_reserve_in_read_keys **rkp) { const int buf_sz = 16384; struct scsi_persistent_reserve_in_read_keys *rk = NULL; logging(LOG_VERBOSE, "Send PRIN/READ_KEYS"); *tp = scsi_cdb_persistent_reserve_in(SCSI_PERSISTENT_RESERVE_READ_KEYS, buf_sz); assert(*tp != NULL); *tp = send_scsi_command(sdev, *tp, NULL); if (*tp == NULL) { logging(LOG_NORMAL, "[FAILED] Failed to send PRIN command: %s", iscsi_get_error(sdev->iscsi_ctx)); return -1; } if (status_is_invalid_opcode(*tp)) { logging(LOG_NORMAL, "[SKIPPED] PERSISTENT RESERVE IN is not implemented."); return -2; } if ((*tp)->status != SCSI_STATUS_GOOD) { logging(LOG_NORMAL, "[FAILED] PRIN command: failed with sense. %s", iscsi_get_error(sdev->iscsi_ctx)); return -1; } rk = scsi_datain_unmarshall(*tp); if (rk == NULL) { logging(LOG_NORMAL, "[FAIL] failed to unmarshall PRIN/READ_KEYS data. %s", iscsi_get_error(sdev->iscsi_ctx)); return -1; } if (rkp != NULL) *rkp = rk; return 0; } int prout_register_and_ignore(struct scsi_device *sdev, unsigned long long sark) { struct scsi_persistent_reserve_out_basic poc; struct scsi_task *task; int ret = 0; /* register our reservation key with the target */ logging(LOG_VERBOSE, "Send PROUT/REGISTER_AND_IGNORE to register init=%s", sdev->iscsi_ctx->initiator_name); if (!data_loss) { printf("--dataloss flag is not set in. Skipping PROUT\n"); return -1; } memset(&poc, 0, sizeof (poc)); poc.service_action_reservation_key = sark; task = scsi_cdb_persistent_reserve_out( SCSI_PERSISTENT_RESERVE_REGISTER_AND_IGNORE_EXISTING_KEY, SCSI_PERSISTENT_RESERVE_SCOPE_LU, 0, &poc); assert(task != NULL); task = send_scsi_command(sdev, task, NULL); if (task == NULL) { logging(LOG_NORMAL, "[FAILED] Failed to send PROUT command: %s", iscsi_get_error(sdev->iscsi_ctx)); return -1; } if (status_is_invalid_opcode(task)) { logging(LOG_NORMAL, "[SKIPPED] PROUT Not Supported"); ret = -2; goto dun; } if (task->status != SCSI_STATUS_GOOD) { logging(LOG_NORMAL, "[FAILED] PROUT command: failed with sense. %s", iscsi_get_error(sdev->iscsi_ctx)); ret = -1; } dun: scsi_free_scsi_task(task); return ret; } int prout_register_key(struct scsi_device *sdev, unsigned long long sark, unsigned long long rk) { struct scsi_persistent_reserve_out_basic poc; struct scsi_task *task; int ret = 0; /* register/unregister our reservation key with the target */ logging(LOG_VERBOSE, "Send PROUT/REGISTER to %s init=%s", sark != 0 ? "register" : "unregister", sdev->iscsi_ctx->initiator_name); if (!data_loss) { printf("--dataloss flag is not set in. Skipping PROUT\n"); return -1; } memset(&poc, 0, sizeof (poc)); poc.service_action_reservation_key = sark; poc.reservation_key = rk; task = scsi_cdb_persistent_reserve_out( SCSI_PERSISTENT_RESERVE_REGISTER, SCSI_PERSISTENT_RESERVE_SCOPE_LU, 0, &poc); assert(task != NULL); task = send_scsi_command(sdev, task, NULL); if (task == NULL) { logging(LOG_NORMAL, "[FAILED] Failed to send PROUT command: %s", iscsi_get_error(sdev->iscsi_ctx)); return -1; } if (status_is_invalid_opcode(task)) { scsi_free_scsi_task(task); logging(LOG_NORMAL, "[SKIPPED] PERSISTENT RESERVE OUT is not implemented."); return -2; } if (task->status != SCSI_STATUS_GOOD) { logging(LOG_NORMAL, "[FAILED] PROUT command: failed with sense: %s", iscsi_get_error(sdev->iscsi_ctx)); ret = -1; } scsi_free_scsi_task(task); return ret; } int prin_verify_key_presence(struct scsi_device *sdev, unsigned long long key, int present) { struct scsi_task *task; const int buf_sz = 16384; int i; int key_found; struct scsi_persistent_reserve_in_read_keys *rk = NULL; int ret = 0; logging(LOG_VERBOSE, "Send PRIN/READ_KEYS to verify key %s init=%s... ", present ? "present" : "absent", sdev->iscsi_ctx->initiator_name); task = scsi_cdb_persistent_reserve_in(SCSI_PERSISTENT_RESERVE_READ_KEYS, buf_sz); assert(task != NULL); task = send_scsi_command(sdev, task, NULL); if (task == NULL) { logging(LOG_NORMAL, "[FAILED] Failed to send PRIN command: %s", iscsi_get_error(sdev->iscsi_ctx)); return -1; } if (status_is_invalid_opcode(task)) { scsi_free_scsi_task(task); logging(LOG_NORMAL, "[SKIPPED] PERSISTENT RESERVE IN is not implemented."); return -2; } if (task->status != SCSI_STATUS_GOOD) { logging(LOG_NORMAL, "[FAILED] PRIN command: failed with sense. %s", iscsi_get_error(sdev->iscsi_ctx)); ret = -1; goto dun; } rk = scsi_datain_unmarshall(task); if (rk == NULL) { logging(LOG_NORMAL, "[FAILED] failed to unmarshall PRIN/READ_KEYS data. %s", iscsi_get_error(sdev->iscsi_ctx)); ret = -1; goto dun; } key_found = 0; for (i = 0; i < rk->num_keys; i++) { if (rk->keys[i] == key) key_found = 1; } if ((present && !key_found) || (!present && key_found)) { if (present) logging(LOG_NORMAL, "[FAILED] Key found when none expected"); else logging(LOG_NORMAL, "[FAILED] Key not found when expected"); ret = -1; } dun: scsi_free_scsi_task(task); return ret; } int prout_reregister_key_fails(struct scsi_device *sdev, unsigned long long sark) { struct scsi_persistent_reserve_out_basic poc; struct scsi_task *task; int ret = 0; logging(LOG_VERBOSE, "Send PROUT/REGISTER to ensure reregister fails init=%s", sdev->iscsi_ctx->initiator_name); if (!data_loss) { printf("--dataloss flag is not set in. Skipping PROUT\n"); return -1; } memset(&poc, 0, sizeof (poc)); poc.service_action_reservation_key = sark; task = scsi_cdb_persistent_reserve_out( SCSI_PERSISTENT_RESERVE_REGISTER, SCSI_PERSISTENT_RESERVE_SCOPE_LU, 0, &poc); assert(task != NULL); task = send_scsi_command(sdev, task, NULL); if (task == NULL) { logging(LOG_NORMAL, "[FAILED] Failed to send PROUT command: %s", iscsi_get_error(sdev->iscsi_ctx)); return -1; } if (status_is_invalid_opcode(task)) { scsi_free_scsi_task(task); logging(LOG_NORMAL, "[SKIPPED] PERSISTENT RESERVE OUT is not implemented."); return -2; } if (task->status != SCSI_STATUS_RESERVATION_CONFLICT) { logging(LOG_NORMAL, "[FAILED] Expected RESERVATION CONFLICT"); ret = -1; } scsi_free_scsi_task(task); return ret; } int prout_reserve(struct scsi_device *sdev, unsigned long long key, enum scsi_persistent_out_type pr_type) { struct scsi_persistent_reserve_out_basic poc; struct scsi_task *task; int ret = 0; /* reserve the target using specified reservation type */ logging(LOG_VERBOSE, "Send PROUT/RESERVE to reserve, type=%d (%s) init=%s", pr_type, scsi_pr_type_str(pr_type), sdev->iscsi_ctx->initiator_name); if (!data_loss) { printf("--dataloss flag is not set in. Skipping PROUT\n"); return -1; } memset(&poc, 0, sizeof (poc)); poc.reservation_key = key; task = scsi_cdb_persistent_reserve_out( SCSI_PERSISTENT_RESERVE_RESERVE, SCSI_PERSISTENT_RESERVE_SCOPE_LU, pr_type, &poc); assert(task != NULL); task = send_scsi_command(sdev, task, NULL); if (task == NULL) { logging(LOG_NORMAL, "[FAILED] Failed to send PROUT command: %s", iscsi_get_error(sdev->iscsi_ctx)); return -1; } if (status_is_invalid_opcode(task)) { scsi_free_scsi_task(task); logging(LOG_NORMAL, "[SKIPPED] PERSISTENT RESERVE OUT is not implemented."); return -2; } if (task->status != SCSI_STATUS_GOOD) { logging(LOG_NORMAL, "[FAILED] PROUT command: failed with sense. %s", iscsi_get_error(sdev->iscsi_ctx)); ret = -1; } scsi_free_scsi_task(task); return ret; } int prout_release(struct scsi_device *sdev, unsigned long long key, enum scsi_persistent_out_type pr_type) { struct scsi_persistent_reserve_out_basic poc; struct scsi_task *task; int ret = 0; logging(LOG_VERBOSE, "Send PROUT/RELEASE to release reservation, type=%d init=%s", pr_type, sdev->iscsi_ctx->initiator_name); if (!data_loss) { printf("--dataloss flag is not set in. Skipping PROUT\n"); return -1; } memset(&poc, 0, sizeof (poc)); poc.reservation_key = key; task = scsi_cdb_persistent_reserve_out( SCSI_PERSISTENT_RESERVE_RELEASE, SCSI_PERSISTENT_RESERVE_SCOPE_LU, pr_type, &poc); assert(task != NULL); task = send_scsi_command(sdev, task, NULL); if (task == NULL) { logging(LOG_NORMAL, "[FAILED] Failed to send PROUT command: %s", iscsi_get_error(sdev->iscsi_ctx)); return -1; } if (status_is_invalid_opcode(task)) { scsi_free_scsi_task(task); logging(LOG_NORMAL, "[SKIPPED] PERSISTENT RESERVE OUT is not implemented."); return -2; } if (task->status != SCSI_STATUS_GOOD) { logging(LOG_NORMAL, "[FAILED] PROUT command: failed with sense. %s", iscsi_get_error(sdev->iscsi_ctx)); ret = -1; } scsi_free_scsi_task(task); return ret; } int prout_clear(struct scsi_device *sdev, unsigned long long key) { struct scsi_persistent_reserve_out_basic poc; struct scsi_task *task; int ret = 0; /* reserve the target using specified reservation type */ logging(LOG_VERBOSE, "Send PROUT/CLEAR to clear all registrations and any PR " "reservation"); if (!data_loss) { printf("--dataloss flag is not set in. Skipping PROUT\n"); return -1; } memset(&poc, 0, sizeof (poc)); poc.reservation_key = key; task = scsi_cdb_persistent_reserve_out( SCSI_PERSISTENT_RESERVE_CLEAR, SCSI_PERSISTENT_RESERVE_SCOPE_LU, 0, &poc); assert(task != NULL); task = send_scsi_command(sdev, task, NULL); if (task == NULL) { logging(LOG_NORMAL, "[FAILED] Failed to send PROUT command: %s", iscsi_get_error(sdev->iscsi_ctx)); return -1; } if (status_is_invalid_opcode(task)) { scsi_free_scsi_task(task); logging(LOG_NORMAL, "[SKIPPED] PERSISTENT RESERVE OUT is not implemented."); return -2; } if (task->status != SCSI_STATUS_GOOD) { logging(LOG_NORMAL, "[FAILED] PROUT command: failed with sense. %s", iscsi_get_error(sdev->iscsi_ctx)); ret = -1; } scsi_free_scsi_task(task); return ret; } int prout_preempt(struct scsi_device *sdev, unsigned long long sark, unsigned long long rk, enum scsi_persistent_out_type pr_type) { struct scsi_persistent_reserve_out_basic poc; struct scsi_task *task; int ret = 0; /* reserve the target using specified reservation type */ logging(LOG_VERBOSE, "Send PROUT/PREEMPT to preempt reservation and/or " "registration"); if (!data_loss) { printf("--dataloss flag is not set in. Skipping PROUT\n"); return -1; } memset(&poc, 0, sizeof (poc)); poc.reservation_key = rk; poc.service_action_reservation_key = sark; task = scsi_cdb_persistent_reserve_out( SCSI_PERSISTENT_RESERVE_PREEMPT, SCSI_PERSISTENT_RESERVE_SCOPE_LU, pr_type, &poc); assert(task != NULL); task = send_scsi_command(sdev, task, NULL); if (task == NULL) { logging(LOG_NORMAL, "[FAILED] Failed to send PROUT command: %s", iscsi_get_error(sdev->iscsi_ctx)); return -1; } if (status_is_invalid_opcode(task)) { scsi_free_scsi_task(task); logging(LOG_NORMAL, "[SKIPPED] PERSISTENT RESERVE OUT is not implemented."); return -2; } if (task->status != SCSI_STATUS_GOOD) { logging(LOG_NORMAL, "[FAILED] PROUT command: failed with sense. %s", iscsi_get_error(sdev->iscsi_ctx)); ret = -1; } scsi_free_scsi_task(task); return ret; } int prin_verify_reserved_as(struct scsi_device *sdev, unsigned long long key, enum scsi_persistent_out_type pr_type) { struct scsi_task *task; const int buf_sz = 16384; struct scsi_persistent_reserve_in_read_reservation *rr = NULL; int ret = 0; logging(LOG_VERBOSE, "Send PRIN/READ_RESERVATION to verify type=%d init=%s... ", pr_type, sdev->iscsi_ctx->initiator_name); task = scsi_cdb_persistent_reserve_in( SCSI_PERSISTENT_RESERVE_READ_RESERVATION, buf_sz); assert(task != NULL); task = send_scsi_command(sdev, task, NULL); if (task == NULL) { logging(LOG_NORMAL, "[FAILED] Failed to send PRIN command: %s", iscsi_get_error(sdev->iscsi_ctx)); return -1; } if (status_is_invalid_opcode(task)) { scsi_free_scsi_task(task); logging(LOG_NORMAL, "[SKIPPED] PERSISTENT RESERVE IN is not implemented."); return -2; } if (task->status != SCSI_STATUS_GOOD) { logging(LOG_NORMAL, "[FAILED] PRIN command: failed with sense: %s", iscsi_get_error(sdev->iscsi_ctx)); ret = -1; goto dun; } rr = scsi_datain_unmarshall(task); if (rr == NULL) { logging(LOG_NORMAL, "[FAILED] Failed to unmarshall PRIN/READ_RESERVATION data. %s", iscsi_get_error(sdev->iscsi_ctx)); ret = -1; goto dun; } if (!rr->reserved) { logging(LOG_NORMAL, "[FAILED] Failed to find Target reserved as expected."); ret = -1; goto dun; } if (rr->reservation_key != key) { logging(LOG_NORMAL, "[FAILED] Failed to find reservation key 0x%llx: found 0x%" PRIu64 ".", key, rr->reservation_key); ret = -1; goto dun; } if (rr->pr_type != pr_type) { logging(LOG_NORMAL, "Failed to find reservation type %d: found %d.", pr_type, rr->pr_type); return -1; ret = -1; goto dun; } dun: /* ??? free rr? */ scsi_free_scsi_task(task); return ret; } int prin_verify_not_reserved(struct scsi_device *sdev) { struct scsi_task *task; const int buf_sz = 16384; struct scsi_persistent_reserve_in_read_reservation *rr = NULL; int ret = 0; logging(LOG_VERBOSE, "Send PRIN/READ_RESERVATION to verify not reserved init=%s", sdev->iscsi_ctx->initiator_name); task = scsi_cdb_persistent_reserve_in( SCSI_PERSISTENT_RESERVE_READ_RESERVATION, buf_sz); assert(task != NULL); task = send_scsi_command(sdev, task, NULL); if (task == NULL) { logging(LOG_NORMAL, "[FAILED] Failed to send PRIN command: %s", iscsi_get_error(sdev->iscsi_ctx)); return -1; } if (status_is_invalid_opcode(task)) { scsi_free_scsi_task(task); logging(LOG_NORMAL, "[SKIPPED] PERSISTENT RESERVE IN is not implemented."); return -2; } if (task->status != SCSI_STATUS_GOOD) { logging(LOG_NORMAL, "[FAILED] PRIN command: failed with sense: %s", iscsi_get_error(sdev->iscsi_ctx)); ret = -1; goto dun; } rr = scsi_datain_unmarshall(task); if (rr == NULL) { logging(LOG_NORMAL, "[FAILED] Failed to unmarshall PRIN/READ_RESERVATION data: %s", iscsi_get_error(sdev->iscsi_ctx)); ret = -1; goto dun; } if (rr->reserved) { logging(LOG_NORMAL, "[FAILED] Failed to find Target not reserved as expected."); ret = -1; goto dun; } dun: /* ??? free rr? */ scsi_free_scsi_task(task); return ret; } int prin_report_caps(struct scsi_device *sdev, struct scsi_task **tp, struct scsi_persistent_reserve_in_report_capabilities **_rcaps) { const int buf_sz = 16384; struct scsi_persistent_reserve_in_report_capabilities *rcaps = NULL; logging(LOG_VERBOSE, "Send PRIN/REPORT_CAPABILITIES"); *tp = scsi_cdb_persistent_reserve_in( SCSI_PERSISTENT_RESERVE_REPORT_CAPABILITIES, buf_sz); assert(*tp != NULL); *tp = send_scsi_command(sdev, *tp, NULL); if (*tp == NULL) { logging(LOG_NORMAL, "[FAILED] Failed to send PRIN command: %s", iscsi_get_error(sdev->iscsi_ctx)); return -1; } if (status_is_invalid_opcode(*tp)) { logging(LOG_NORMAL, "[SKIPPED] PERSISTENT RESERVE IN is not implemented."); return -2; } if ((*tp)->status != SCSI_STATUS_GOOD) { logging(LOG_NORMAL, "[FAILED] PRIN command: failed with sense. %s", iscsi_get_error(sdev->iscsi_ctx)); return -1; } rcaps = scsi_datain_unmarshall(*tp); if (rcaps == NULL) { logging(LOG_NORMAL, "[FAIL] failed to unmarshall PRIN/REPORT_CAPABILITIES " "data. %s", iscsi_get_error(sdev->iscsi_ctx)); return -1; } if (_rcaps != NULL) *_rcaps = rcaps; return 0; } int verify_read_works(struct scsi_device *sdev, unsigned char *buf) { struct scsi_task *task; const uint32_t lba = 1; const int blksize = 512; const uint32_t datalen = 1 * blksize; int ret = 0; /* * try to read the second 512-byte block */ logging(LOG_VERBOSE, "Send READ10 to verify READ works init=%s", sdev->iscsi_ctx->initiator_name); task = scsi_cdb_read10(lba, datalen, blksize, 0, 0, 0, 0, 0); assert(task != NULL); task = send_scsi_command(sdev, task, NULL); if (task == NULL) { logging(LOG_NORMAL, "[FAILED] Failed to send READ10 command: %s", iscsi_get_error(sdev->iscsi_ctx)); return -1; } if (task->status != SCSI_STATUS_GOOD) { logging(LOG_NORMAL, "[FAILED] READ10 command: failed with sense: %s", iscsi_get_error(sdev->iscsi_ctx)); ret = -1; goto dun; } memcpy(buf, task->datain.data, task->datain.size); dun: scsi_free_scsi_task(task); return ret; } int verify_write_works(struct scsi_device *sdev, unsigned char *buf) { struct scsi_task *task; struct iscsi_data d; const uint32_t lba = 1; const int blksize = 512; const uint32_t datalen = 1 * blksize; int ret = 0; /* * try to write the second 512-byte block */ logging(LOG_VERBOSE, "Send WRITE10 to verify WRITE works init=%s", sdev->iscsi_ctx->initiator_name); task = scsi_cdb_write10(lba, datalen, blksize, 0, 0, 0, 0, 0); assert(task != NULL); d.data = buf; d.size = datalen; task = send_scsi_command(sdev, task, &d); if (task == NULL) { logging(LOG_NORMAL, "[FAILED] Failed to send WRITE10 command: %s", iscsi_get_error(sdev->iscsi_ctx)); return -1; } if (task->status != SCSI_STATUS_GOOD) { logging(LOG_NORMAL, "[FAILED] WRITE10 command: failed with sense: %s", iscsi_get_error(sdev->iscsi_ctx)); ret = -1; } scsi_free_scsi_task(task); return ret; } int verify_read_fails(struct scsi_device *sdev, unsigned char *buf) { struct scsi_task *task; const uint32_t lba = 1; const int blksize = 512; const uint32_t datalen = 1 * blksize; int ret = 0; /* * try to read the second 512-byte block -- should fail */ logging(LOG_VERBOSE, "Send READ10 to verify READ does not work init=%s", sdev->iscsi_ctx->initiator_name); task = scsi_cdb_read10(lba, datalen, blksize, 0, 0, 0, 0, 0); assert(task != NULL); task = send_scsi_command(sdev, task, NULL); if (task == NULL) { logging(LOG_NORMAL, "[FAILED] Failed to send READ10 command: %s", iscsi_get_error(sdev->iscsi_ctx)); return -1; } if (task->status == SCSI_STATUS_GOOD) { memcpy(buf, task->datain.data, task->datain.size); logging(LOG_NORMAL, "[FAILED] READ10 command succeeded when expected to fail"); ret = -1; goto dun; } /* * XXX should we verify sense data? */ dun: scsi_free_scsi_task(task); return ret; } int verify_write_fails(struct scsi_device *sdev, unsigned char *buf) { struct scsi_task *task; struct iscsi_data d; const uint32_t lba = 1; const int blksize = 512; const uint32_t datalen = 1 * blksize; int ret = 0; /* * try to write the second 512-byte block */ logging(LOG_VERBOSE, "Send WRITE10 to verify WRITE does not work init=%s", sdev->iscsi_ctx->initiator_name); task = scsi_cdb_write10(lba, datalen, blksize, 0, 0, 0, 0, 0); assert(task != NULL); d.data = buf; d.size = datalen; task = send_scsi_command(sdev, task, &d); if (task == NULL) { logging(LOG_NORMAL, "[FAILED] Failed to send WRITE10 command: %s", iscsi_get_error(sdev->iscsi_ctx)); return -1; } if (task->status == SCSI_STATUS_GOOD) { logging(LOG_NORMAL, "[FAILED] WRITE10 command: succeeded when exptec to fail"); ret = -1; goto dun; } /* * XXX should we verify sense data? */ dun: scsi_free_scsi_task(task); return ret; } int synchronizecache10(struct scsi_device *sdev, uint32_t lba, int num, int sync_nv, int immed, int status, enum scsi_sense_key key, int *ascq, int num_ascq) { struct scsi_task *task; int ret; logging(LOG_VERBOSE, "Send SYNCHRONIZECACHE10 (Expecting %s) LBA:%d" " blocks:%d sync_nv:%d immed:%d", scsi_status_str(status), lba, num, sync_nv, immed); task = scsi_cdb_synchronizecache10(lba, num_blocks, sync_nv, immed); assert(task != NULL); task = send_scsi_command(sdev, task, NULL); ret = check_result("SYNCHRONIZECACHE10", sdev, task, status, key, ascq, num_ascq); if (task) { scsi_free_scsi_task(task); } return ret; } int synchronizecache16(struct scsi_device *sdev, uint64_t lba, int num, int sync_nv, int immed, int status, enum scsi_sense_key key, int *ascq, int num_ascq) { struct scsi_task *task; int ret; logging(LOG_VERBOSE, "Send SYNCHRONIZECACHE16 (Expecting %s) LBA:%" PRIu64 " blocks:%d sync_nv:%d immed:%d", scsi_status_str(status), lba, num, sync_nv, immed); task = scsi_cdb_synchronizecache16(lba, num_blocks, sync_nv, immed); assert(task != NULL); task = send_scsi_command(sdev, task, NULL); ret = check_result("SYNCHRONIZECACHE16", sdev, task, status, key, ascq, num_ascq); if (task) { scsi_free_scsi_task(task); } return ret; } int sanitize(struct scsi_device *sdev, int immed, int ause, int sa, int param_len, struct iscsi_data *data, int status, enum scsi_sense_key key, int *ascq, int num_ascq) { struct scsi_task *task; int ret; logging(LOG_VERBOSE, "Send SANITIZE (Expecting %s) IMMED:%d AUSE:%d " "SA:%d PARAM_LEN:%d", scsi_status_str(status), immed, ause, sa, param_len); if (!data_loss) { printf("--dataloss flag is not set in. Skipping sanitize\n"); return -1; } task = scsi_cdb_sanitize(immed, ause, sa, param_len); assert(task != NULL); task = send_scsi_command(sdev, task, data); ret = check_result("SANITIZE", sdev, task, status, key, ascq, num_ascq); if (task) { scsi_free_scsi_task(task); } return ret; } int startstopunit(struct scsi_device *sdev, int immed, int pcm, int pc, int no_flush, int loej, int start, int status, enum scsi_sense_key key, int *ascq, int num_ascq) { struct scsi_task *task; int ret; logging(LOG_VERBOSE, "Send STARTSTOPUNIT (Expecting %s) IMMED:%d " "PCM:%d PC:%d NO_FLUSH:%d LOEJ:%d START:%d", scsi_status_str(status), immed, pcm, pc, no_flush, loej, start); task = scsi_cdb_startstopunit(immed, pcm, pc, no_flush, loej, start); assert(task != NULL); task = send_scsi_command(sdev, task, NULL); ret = check_result("STARTSTOPUNIT", sdev, task, status, key, ascq, num_ascq); if (task) { scsi_free_scsi_task(task); } return ret; } int testunitready(struct scsi_device *sdev, int status, enum scsi_sense_key key, int *ascq, int num_ascq) { struct scsi_task *task; int ret; logging(LOG_VERBOSE, "Send TESTUNITREADY (Expecting %s)", scsi_status_str(status)); task = scsi_cdb_testunitready(); assert(task != NULL); task = send_scsi_command(sdev, task, NULL); ret = check_result("TESTUNITREADY", sdev, task, status, key, ascq, num_ascq); if (task) { scsi_free_scsi_task(task); } return ret; } int testunitready_clear_ua(struct scsi_device *sdev) { struct scsi_task *task; int ret = -1; logging(LOG_VERBOSE, "Send TESTUNITREADY (To Clear Possible UA) init=%s", sdev->iscsi_ctx->initiator_name); task = scsi_cdb_testunitready(); assert(task != NULL); task = send_scsi_command(sdev, task, NULL); if (task == NULL) { logging(LOG_NORMAL, "[FAILED] Failed to send TESTUNITREADY command: %s", iscsi_get_error(sdev->iscsi_ctx)); goto out; } if (task->status != SCSI_STATUS_GOOD) { logging(LOG_NORMAL, "[INFO] TESTUNITREADY command: failed with sense. %s", iscsi_get_error(sdev->iscsi_ctx)); goto out; } logging(LOG_VERBOSE, "[OK] TESTUNITREADY does not return unit " "attention."); ret = 0; out: scsi_free_scsi_task(task); return ret; } /* * Returns -1 if allocating a SCSI task failed or if a communication error * occurred and a SCSI status if a SCSI response has been received. */ int modesense6(struct scsi_device *sdev, struct scsi_task **out_task, int dbd, enum scsi_modesense_page_control pc, enum scsi_modesense_page_code page_code, int sub_page_code, unsigned char alloc_len, int status, enum scsi_sense_key key, int *ascq, int num_ascq) { struct scsi_task *task; int ret; logging(LOG_VERBOSE, "Send MODESENSE6 (Expecting %s) ", scsi_status_str(status)); task = scsi_cdb_modesense6(dbd, pc, page_code, sub_page_code, alloc_len); assert(task != NULL); task = send_scsi_command(sdev, task, NULL); ret = check_result("MODESENSE6", sdev, task, status, key, ascq, num_ascq); if (out_task) { *out_task = task; } else if (task) { scsi_free_scsi_task(task); } return ret; } int modeselect6(struct scsi_device *sdev, int pf, int sp, struct scsi_mode_page *mp, int status, enum scsi_sense_key key, int *ascq, int num_ascq) { struct scsi_task *task; int ret; struct scsi_data *data; struct iscsi_data d; logging(LOG_VERBOSE, "Send MODESELECT6 (Expecting %s) ", scsi_status_str(status)); task = scsi_cdb_modeselect6(pf, sp, 255); assert(task != NULL); data = scsi_modesense_dataout_marshall(task, mp, 1); if (data == NULL) { logging(LOG_VERBOSE, "Failed to marshall MODESELECT6 data"); scsi_free_scsi_task(task); return -1; } d.data = data->data; d.size = data->size; task->cdb[4] = data->size; task->expxferlen = data->size; task = send_scsi_command(sdev, task, &d); ret = check_result("MODESELECT6", sdev, task, status, key, ascq, num_ascq); if (task) { scsi_free_scsi_task(task); } return ret; } int compareandwrite(struct scsi_device *sdev, uint64_t lba, unsigned char *data, uint32_t datalen, int blocksize, int wrprotect, int dpo, int fua, int group_number, int status, enum scsi_sense_key key, int *ascq, int num_ascq) { struct scsi_task *task; struct iscsi_data d; int ret; logging(LOG_VERBOSE, "Send COMPAREANDWRITE (Expecting %s) LBA:%" PRIu64 " LEN:%d WRPROTECT:%d", scsi_status_str(status), lba, datalen, wrprotect); if (!data_loss) { printf("--dataloss flag is not set in. Skipping write\n"); return -1; } task = scsi_cdb_compareandwrite(lba, datalen, blocksize, wrprotect, dpo, fua, 0, group_number); assert(task != NULL); d.data = data; d.size = datalen; task = send_scsi_command(sdev, task, &d); ret = check_result("COMPAREANDWRITE", sdev, task, status, key, ascq, num_ascq); if (task) { scsi_free_scsi_task(task); } return ret; } int get_lba_status(struct scsi_device *sdev, struct scsi_task **out_task, uint64_t lba, uint32_t len, int status, enum scsi_sense_key key, int *ascq, int num_ascq) { struct scsi_task *task; int ret; logging(LOG_VERBOSE, "Send GET_LBA_STATUS (Expecting %s) LBA:%" PRIu64 " alloc_len:%d", scsi_status_str(status), lba, len); task = scsi_cdb_get_lba_status(lba, len); assert(task != NULL); task = send_scsi_command(sdev, task, NULL); ret = check_result("GET_LBA_STATUS", sdev, task, status, key, ascq, num_ascq); if (out_task) { *out_task = task; } else if (task) { scsi_free_scsi_task(task); } return ret; } int prefetch10(struct scsi_device *sdev, uint32_t lba, int num, int immed, int group, int status, enum scsi_sense_key key, int *ascq, int num_ascq) { struct scsi_task *task; int ret; logging(LOG_VERBOSE, "Send PREFETCH10 (Expecting %s) LBA:%d blocks:%d" " immed:%d group:%d", scsi_status_str(status), lba, num, immed, group); task = scsi_cdb_prefetch10(lba, num, immed, group); assert(task != NULL); task = send_scsi_command(sdev, task, NULL); ret = check_result("PREFETCH10", sdev, task, status, key, ascq, num_ascq); if (task) { scsi_free_scsi_task(task); } return ret; } int prefetch16(struct scsi_device *sdev, uint64_t lba, int num, int immed, int group, int status, enum scsi_sense_key key, int *ascq, int num_ascq) { struct scsi_task *task; int ret; logging(LOG_VERBOSE, "Send PREFETCH16 (Expecting %s) LBA:%" PRIu64 " blocks:%d immed:%d group:%d", scsi_status_str(status), lba, num, immed, group); task = scsi_cdb_prefetch16(lba, num, immed, group); assert(task != NULL); task = send_scsi_command(sdev, task, NULL); ret = check_result("PREFETCH16", sdev, task, status, key, ascq, num_ascq); if (task) { scsi_free_scsi_task(task); } return ret; } int preventallow(struct scsi_device *sdev, int prevent) { struct scsi_task *task; logging(LOG_VERBOSE, "Send PREVENTALLOW prevent:%d", prevent); task = scsi_cdb_preventallow(prevent); assert(task != NULL); task = send_scsi_command(sdev, task, NULL); if (task == NULL) { logging(LOG_NORMAL, "[FAILED] Failed to send PREVENTALLOW " "command: %s", iscsi_get_error(sdev->iscsi_ctx)); return -1; } if (status_is_invalid_opcode(task)) { logging(LOG_NORMAL, "[SKIPPED] PREVENTALLOW is not implemented on target"); scsi_free_scsi_task(task); return -2; } if (task->status != SCSI_STATUS_GOOD) { logging(LOG_NORMAL, "[FAILED] PREVENTALLOW command: " "failed with sense. %s", iscsi_get_error(sdev->iscsi_ctx)); scsi_free_scsi_task(task); return -1; } scsi_free_scsi_task(task); logging(LOG_VERBOSE, "[OK] PREVENTALLOW returned SUCCESS."); return 0; } int read6(struct scsi_device *sdev, struct scsi_task **out_task, uint32_t lba, uint32_t datalen, int blocksize, unsigned char *data, int status, enum scsi_sense_key key, int *ascq, int num_ascq) { struct scsi_task *task; int ret; logging(LOG_VERBOSE, "Send READ6 (Expecting %s) LBA:%d blocks:%d", scsi_status_str(status), lba, datalen / blocksize); task = scsi_cdb_read6(lba, datalen, blocksize); assert(task != NULL); task = send_scsi_command(sdev, task, NULL); ret = check_result("READ6", sdev, task, status, key, ascq, num_ascq); if (data && task) { memcpy(data, task->datain.data, task->datain.size); } if (out_task) { *out_task = task; } else if (task) { scsi_free_scsi_task(task); } return ret; } int read10(struct scsi_device *sdev, struct scsi_task **out_task, uint32_t lba, uint32_t datalen, int blocksize, int rdprotect, int dpo, int fua, int fua_nv, int group, unsigned char *data, int status, enum scsi_sense_key key, int *ascq, int num_ascq) { struct scsi_task *task; int ret; logging(LOG_VERBOSE, "Send READ10 (Expecting %s) LBA:%d" " blocks:%d rdprotect:%d dpo:%d fua:%d fua_nv:%d group:%d", scsi_status_str(status), lba, datalen / blocksize, rdprotect, dpo, fua, fua_nv, group); task = scsi_cdb_read10(lba, datalen, blocksize, rdprotect, dpo, fua, fua_nv, group); assert(task != NULL); task = send_scsi_command(sdev, task, NULL); ret = check_result("READ10", sdev, task, status, key, ascq, num_ascq); if (data && task) { memcpy(data, task->datain.data, task->datain.size); } if (out_task) { *out_task = task; } else if (task) { scsi_free_scsi_task(task); } return ret; } int read12(struct scsi_device *sdev, struct scsi_task **out_task, uint32_t lba, uint32_t datalen, int blocksize, int rdprotect, int dpo, int fua, int fua_nv, int group, unsigned char *data, int status, enum scsi_sense_key key, int *ascq, int num_ascq) { struct scsi_task *task; int ret; logging(LOG_VERBOSE, "Send READ12 (Expecting %s) LBA:%d" " blocks:%d rdprotect:%d dpo:%d fua:%d fua_nv:%d group:%d", scsi_status_str(status), lba, datalen / blocksize, rdprotect, dpo, fua, fua_nv, group); task = scsi_cdb_read12(lba, datalen, blocksize, rdprotect, dpo, fua, fua_nv, group); assert(task != NULL); task = send_scsi_command(sdev, task, NULL); ret = check_result("READ12", sdev, task, status, key, ascq, num_ascq); if (data && task) { memcpy(data, task->datain.data, task->datain.size); } if (out_task) { *out_task = task; } else if (task) { scsi_free_scsi_task(task); } return ret; } int read16(struct scsi_device *sdev, struct scsi_task **out_task, uint64_t lba, uint32_t datalen, int blocksize, int rdprotect, int dpo, int fua, int fua_nv, int group, unsigned char *data, int status, enum scsi_sense_key key, int *ascq, int num_ascq) { struct scsi_task *task; int ret; logging(LOG_VERBOSE, "Send READ16 (Expecting %s) LBA:%" PRIu64 " blocks:%d rdprotect:%d dpo:%d fua:%d fua_nv:%d group:%d", scsi_status_str(status), lba, datalen / blocksize, rdprotect, dpo, fua, fua_nv, group); task = scsi_cdb_read16(lba, datalen, blocksize, rdprotect, dpo, fua, fua_nv, group); assert(task != NULL); task = send_scsi_command(sdev, task, NULL); ret = check_result("READ16", sdev, task, status, key, ascq, num_ascq); if (data && task) { memcpy(data, task->datain.data, task->datain.size); } if (out_task) { *out_task = task; } else if (task) { scsi_free_scsi_task(task); } return ret; } int readcapacity10(struct scsi_device *sdev, struct scsi_task **out_task, uint32_t lba, int pmi, int status, enum scsi_sense_key key, int *ascq, int num_ascq) { struct scsi_task *task; int ret; logging(LOG_VERBOSE, "Send READCAPACITY10 (Expecting %s) LBA:%d" " pmi:%d", scsi_status_str(status), lba, pmi); task = scsi_cdb_readcapacity10(lba, pmi); assert(task != NULL); task = send_scsi_command(sdev, task, NULL); ret = check_result("READCAPACITY10", sdev, task, status, key, ascq, num_ascq); if (out_task) { *out_task = task; } else if (task) { scsi_free_scsi_task(task); } return ret; } int readcapacity16(struct scsi_device *sdev, struct scsi_task **out_task, int alloc_len, int status, enum scsi_sense_key key, int *ascq, int num_ascq) { struct scsi_task *task; int ret; logging(LOG_VERBOSE, "Send READCAPACITY16 (Expecting %s)", scsi_status_str(status)); task = scsi_cdb_serviceactionin16(SCSI_READCAPACITY16, alloc_len); assert(task != NULL); task = send_scsi_command(sdev, task, NULL); ret = check_result("READCAPACITY16", sdev, task, status, key, ascq, num_ascq); if (out_task) { *out_task = task; } else if (task) { scsi_free_scsi_task(task); } return ret; } int release6(struct scsi_device *sdev) { struct scsi_task *task; int i, res = -1; logging(LOG_VERBOSE, "Send RELEASE6"); for (i = 0; i < 3 && res != 0; ++i) { task = scsi_cdb_release6(); assert(task != NULL); task = send_scsi_command(sdev, task, NULL); if (task == NULL) { logging(LOG_NORMAL, "[FAILED] Failed to send RELEASE6 command: %s", iscsi_get_error(sdev->iscsi_ctx)); res = -1; break; } if (task->status != SCSI_STATUS_GOOD && !(task->status == SCSI_STATUS_CHECK_CONDITION && task->sense.key == SCSI_SENSE_UNIT_ATTENTION && task->sense.ascq == SCSI_SENSE_ASCQ_BUS_RESET)) { logging(LOG_NORMAL, "[FAILED] RELEASE6 command: " "failed with sense. %s", iscsi_get_error(sdev->iscsi_ctx)); res = -1; } else { res = 0; } scsi_free_scsi_task(task); } if (res == 0) logging(LOG_VERBOSE, "[OK] RELEASE6 returned SUCCESS."); return res; } int report_supported_opcodes(struct scsi_device *sdev, struct scsi_task **out_task, int rctd, int options, int opcode, int sa, int alloc_len, int status, enum scsi_sense_key key, int *ascq, int num_ascq) { struct scsi_task *task; int ret; logging(LOG_VERBOSE, "Send REPORT_SUPPORTED_OPCODE (Expecting %s) " "RCTD:%d OPTIONS:%d OPCODE:0x%02x SA:%d ALLOC_LEN:%d", scsi_status_str(status), rctd, options, opcode, sa, alloc_len); task = scsi_cdb_report_supported_opcodes(rctd, options, opcode, sa, alloc_len); assert(task != NULL); task = send_scsi_command(sdev, task, NULL); ret = check_result("REPORT_SUPPORTED_OPCODES", sdev, task, status, key, ascq, num_ascq); if (out_task) { *out_task = task; } else if (task) { scsi_free_scsi_task(task); } return ret; } int reserve6(struct scsi_device *sdev) { struct scsi_task *task; int i, res = -1; logging(LOG_VERBOSE, "Send RESERVE6"); for (i = 0; i < 3 && res != 0; ++i) { task = scsi_cdb_reserve6(); assert(task != NULL); task = send_scsi_command(sdev, task, NULL); if (task == NULL) { logging(LOG_NORMAL, "[FAILED] Failed to send RESERVE6 command: %s", iscsi_get_error(sdev->iscsi_ctx)); res = -1; break; } if (status_is_invalid_opcode(task)) { logging(LOG_NORMAL, "[SKIPPED] RESERVE6 is not " "implemented on target"); res = -2; } else if (task->status != SCSI_STATUS_GOOD && !(task->status == SCSI_STATUS_CHECK_CONDITION && task->sense.key == SCSI_SENSE_UNIT_ATTENTION && task->sense.ascq == SCSI_SENSE_ASCQ_BUS_RESET)) { logging(LOG_NORMAL, "[FAILED] RESERVE6 command: " "failed with sense. %s", iscsi_get_error(sdev->iscsi_ctx)); res = -1; } else { res = 0; } scsi_free_scsi_task(task); } if (res == 0) logging(LOG_VERBOSE, "[OK] RESERVE6 returned SUCCESS."); return res; } int reserve6_conflict(struct scsi_device *sdev) { struct scsi_task *task; int i, res = -1; logging(LOG_VERBOSE, "Send RESERVE6 (Expecting RESERVATION_CONFLICT)"); for (i = 0; i < 3 && res != 0; ++i) { task = scsi_cdb_reserve6(); assert(task != NULL); task = send_scsi_command(sdev, task, NULL); if (task == NULL) { logging(LOG_NORMAL, "[FAILED] Failed to send RESERVE6 command: %s", iscsi_get_error(sdev->iscsi_ctx)); res = -1; break; } if (status_is_invalid_opcode(task)) { logging(LOG_NORMAL, "[SKIPPED] RESERVE6 is not" " implemented on target"); res = -2; } else if (task->status != SCSI_STATUS_RESERVATION_CONFLICT && !(task->status == SCSI_STATUS_CHECK_CONDITION && task->sense.key == SCSI_SENSE_UNIT_ATTENTION && task->sense.ascq == SCSI_SENSE_ASCQ_BUS_RESET)) { logging(LOG_NORMAL, "[FAILED] RESERVE6 command: " "should have failed with RESERVATION_CONFLICT"); res = -1; } else { res = 0; } scsi_free_scsi_task(task); } if (res == 0) logging(LOG_VERBOSE, "[OK] RESERVE6 returned RESERVATION_CONFLICT."); return res; } int unmap(struct scsi_device *sdev, int anchor, struct unmap_list *list, int list_len, int status, enum scsi_sense_key key, int *ascq, int num_ascq) { struct scsi_task *task; unsigned char *data; struct iscsi_data d; int xferlen; int i; int ret; logging(LOG_VERBOSE, "Send UNMAP (Expecting %s) list_len:%d anchor:%d", scsi_status_str(status), list_len, anchor); if (!data_loss) { printf("--dataloss flag is not set in. Skipping unmap\n"); return -1; } xferlen = 8 + list_len * 16; task = scsi_cdb_unmap(anchor, 0, xferlen); assert(task != NULL); data = scsi_malloc(task, xferlen); if (data == NULL) { logging(LOG_NORMAL, "Out-of-memory: Failed to create " "unmap parameters."); scsi_free_scsi_task(task); return -1; } scsi_set_uint16(&data[0], xferlen - 2); scsi_set_uint16(&data[2], xferlen - 8); for (i = 0; i < list_len; i++) { scsi_set_uint32(&data[8 + 16 * i], list[i].lba >> 32); scsi_set_uint32(&data[8 + 16 * i + 4], list[i].lba & 0xffffffff); scsi_set_uint32(&data[8 + 16 * i + 8], list[i].num); } d.data = data; d.size = xferlen; task = send_scsi_command(sdev, task, &d); ret = check_result("UNMAP", sdev, task, status, key, ascq, num_ascq); if (task) { scsi_free_scsi_task(task); } return ret; } int verify10(struct scsi_device *sdev, uint32_t lba, uint32_t datalen, int blocksize, int vprotect, int dpo, int bytchk, unsigned char *data, int status, enum scsi_sense_key key, int *ascq, int num_ascq) { struct scsi_task *task; struct iscsi_data d; int ret; logging(LOG_VERBOSE, "Send VERIFY10 (Expecting %s) LBA:%d " "blocks:%d vprotect:%d dpo:%d bytchk:%d", scsi_status_str(status), lba, datalen / blocksize, vprotect, dpo, bytchk); task = scsi_cdb_verify10(lba, datalen, vprotect, dpo, bytchk, blocksize); assert(task != NULL); d.data = data; d.size = datalen; task = send_scsi_command(sdev, task, &d); ret = check_result("VERIFY10", sdev, task, status, key, ascq, num_ascq); if (task) { scsi_free_scsi_task(task); } return ret; } int verify12(struct scsi_device *sdev, uint32_t lba, uint32_t datalen, int blocksize, int vprotect, int dpo, int bytchk, unsigned char *data, int status, enum scsi_sense_key key, int *ascq, int num_ascq) { struct scsi_task *task; struct iscsi_data d; int ret; logging(LOG_VERBOSE, "Send VERIFY12 (Expecting %s) LBA:%d " "blocks:%d vprotect:%d dpo:%d bytchk:%d", scsi_status_str(status), lba, datalen / blocksize, vprotect, dpo, bytchk); task = scsi_cdb_verify12(lba, datalen, vprotect, dpo, bytchk, blocksize); assert(task != NULL); d.data = data; d.size = datalen; task = send_scsi_command(sdev, task, &d); ret = check_result("VERIFY12", sdev, task, status, key, ascq, num_ascq); if (task) { scsi_free_scsi_task(task); } return ret; } int verify16(struct scsi_device *sdev, uint64_t lba, uint32_t datalen, int blocksize, int vprotect, int dpo, int bytchk, unsigned char *data, int status, enum scsi_sense_key key, int *ascq, int num_ascq) { struct scsi_task *task; struct iscsi_data d; int ret; logging(LOG_VERBOSE, "Send VERIFY16 (Expecting %s) LBA:%" PRIu64 " blocks:%d vprotect:%d dpo:%d bytchk:%d", scsi_status_str(status), lba, datalen / blocksize, vprotect, dpo, bytchk); task = scsi_cdb_verify16(lba, datalen, vprotect, dpo, bytchk, blocksize); assert(task != NULL); d.data = data; d.size = datalen; task = send_scsi_command(sdev, task, &d); ret = check_result("VERIFY16", sdev, task, status, key, ascq, num_ascq); if (task) { scsi_free_scsi_task(task); } return ret; } int write10(struct scsi_device *sdev, uint32_t lba, uint32_t datalen, int blocksize, int wrprotect, int dpo, int fua, int fua_nv, int group, unsigned char *data, int status, enum scsi_sense_key key, int *ascq, int num_ascq) { struct scsi_task *task; struct iscsi_data d; int ret; logging(LOG_VERBOSE, "Send WRITE10 (Expecting %s) LBA:%d blocks:%d " "wrprotect:%d dpo:%d fua:%d fua_nv:%d group:%d", scsi_status_str(status), lba, datalen / blocksize, wrprotect, dpo, fua, fua_nv, group); if (!data_loss) { printf("--dataloss flag is not set in. Skipping write\n"); return -1; } task = scsi_cdb_write10(lba, datalen, blocksize, wrprotect, dpo, fua, fua_nv, group); assert(task != NULL); d.data = data; d.size = datalen; task = send_scsi_command(sdev, task, &d); ret = check_result("WRITE10", sdev, task, status, key, ascq, num_ascq); if (task) { scsi_free_scsi_task(task); } return ret; } int write12(struct scsi_device *sdev, uint32_t lba, uint32_t datalen, int blocksize, int wrprotect, int dpo, int fua, int fua_nv, int group, unsigned char *data, int status, enum scsi_sense_key key, int *ascq, int num_ascq) { struct scsi_task *task; struct iscsi_data d; int ret; logging(LOG_VERBOSE, "Send WRITE12 (Expecting %s) LBA:%d blocks:%d " "wrprotect:%d dpo:%d fua:%d fua_nv:%d group:%d", scsi_status_str(status), lba, datalen / blocksize, wrprotect, dpo, fua, fua_nv, group); if (!data_loss) { printf("--dataloss flag is not set in. Skipping write\n"); return -1; } task = scsi_cdb_write12(lba, datalen, blocksize, wrprotect, dpo, fua, fua_nv, group); assert(task != NULL); d.data = data; d.size = datalen; task = send_scsi_command(sdev, task, &d); ret = check_result("WRITE12", sdev, task, status, key, ascq, num_ascq); if (task) { scsi_free_scsi_task(task); } return ret; } int write16(struct scsi_device *sdev, uint64_t lba, uint32_t datalen, int blocksize, int wrprotect, int dpo, int fua, int fua_nv, int group, unsigned char *data, int status, enum scsi_sense_key key, int *ascq, int num_ascq) { struct scsi_task *task; struct iscsi_data d; int ret; logging(LOG_VERBOSE, "Send WRITE16 (Expecting %s) LBA:%" PRIu64 " blocks:%d wrprotect:%d dpo:%d fua:%d fua_nv:%d group:%d", scsi_status_str(status), lba, datalen / blocksize, wrprotect, dpo, fua, fua_nv, group); if (!data_loss) { printf("--dataloss flag is not set in. Skipping write\n"); return -1; } task = scsi_cdb_write16(lba, datalen, blocksize, wrprotect, dpo, fua, fua_nv, group); assert(task != NULL); d.data = data; d.size = datalen; send_scsi_command(sdev, task, &d); ret = check_result("WRITE16", sdev, task, status, key, ascq, num_ascq); scsi_free_scsi_task(task); return ret; } int writeatomic16(struct scsi_device *sdev, uint64_t lba, uint32_t datalen, int blocksize, int wrprotect, int dpo, int fua, int group, unsigned char *data, int status, enum scsi_sense_key key, int *ascq, int num_ascq) { struct scsi_task *task; struct iscsi_data d; int ret; logging(LOG_VERBOSE, "Send WRITEATOMIC16 (Expecting %s) LBA:%" PRIu64 " blocks:%d wrprotect:%d dpo:%d fua:%d group:%d", scsi_status_str(status), lba, datalen / blocksize, wrprotect, dpo, fua, group); if (!data_loss) { printf("--dataloss flag is not set in. Skipping write\n"); return -1; } task = scsi_cdb_writeatomic16(lba, datalen, blocksize, wrprotect, dpo, fua, group); assert(task != NULL); d.data = data; d.size = datalen; send_scsi_command(sdev, task, &d); ret = check_result("WRITEATOMIC16", sdev, task, status, key, ascq, num_ascq); scsi_free_scsi_task(task); return ret; } int writesame10(struct scsi_device *sdev, uint32_t lba, uint32_t datalen, int num, int anchor, int unmap_flag, int wrprotect, int group, unsigned char *data, int status, enum scsi_sense_key key, int *ascq, int num_ascq) { struct scsi_task *task; struct iscsi_data d; int ret; logging(LOG_VERBOSE, "Send WRITESAME10 (Expecting %s) LBA:%d blocks:%d " "wrprotect:%d anchor:%d unmap:%d group:%d", scsi_status_str(status), lba, num, wrprotect, anchor, unmap_flag, group); if (!data_loss) { printf("--dataloss flag is not set in. Skipping write\n"); return -1; } task = scsi_cdb_writesame10(wrprotect, anchor, unmap_flag, lba, group, num, datalen); assert(task != NULL); if (data != NULL) { task->expxferlen = datalen; } else { task->expxferlen = 0; task->xfer_dir = SCSI_XFER_NONE; } d.data = data; d.size = datalen; task = send_scsi_command(sdev, task, &d); ret = check_result("WRITESAME10", sdev, task, status, key, ascq, num_ascq); if (task) { scsi_free_scsi_task(task); } return ret; } int writesame16(struct scsi_device *sdev, uint64_t lba, uint32_t datalen, int num, int anchor, int unmap_flag, int wrprotect, int group, unsigned char *data, int status, enum scsi_sense_key key, int *ascq, int num_ascq) { struct scsi_task *task; struct iscsi_data d; int ret; logging(LOG_VERBOSE, "Send WRITESAME16 (Expecting %s) LBA:%" PRIu64 " blocks:%d wrprotect:%d anchor:%d unmap:%d group:%d", scsi_status_str(status), lba, num, wrprotect, anchor, unmap_flag, group); if (!data_loss) { printf("--dataloss flag is not set in. Skipping write\n"); return -1; } task = scsi_cdb_writesame16(wrprotect, anchor, unmap_flag, lba, group, num, datalen); assert(task != NULL); if (data != NULL) { task->expxferlen = datalen; } else { task->expxferlen = 0; task->xfer_dir = SCSI_XFER_NONE; } d.data = data; d.size = datalen; task = send_scsi_command(sdev, task, &d); ret = check_result("WRITESAME16", sdev, task, status, key, ascq, num_ascq); if (task) { scsi_free_scsi_task(task); } return ret; } int writeverify10(struct scsi_device *sdev, uint32_t lba, uint32_t datalen, int blocksize, int wrprotect, int dpo, int bytchk, int group, unsigned char *data, int status, enum scsi_sense_key key, int *ascq, int num_ascq) { struct scsi_task *task; struct iscsi_data d; int ret; logging(LOG_VERBOSE, "Send WRITEVERIFY10 (Expecting %s) LBA:%d " "blocks:%d wrprotect:%d dpo:%d bytchk:%d group:%d", scsi_status_str(status), lba, datalen / blocksize, wrprotect, dpo, bytchk, group); if (!data_loss) { printf("--dataloss flag is not set in. Skipping write\n"); return -1; } task = scsi_cdb_writeverify10(lba, datalen, blocksize, wrprotect, dpo, bytchk, group); assert(task != NULL); d.data = data; d.size = datalen; task = send_scsi_command(sdev, task, &d); ret = check_result("WRITEVERIFY10", sdev, task, status, key, ascq, num_ascq); if (task) { scsi_free_scsi_task(task); } return ret; } int writeverify12(struct scsi_device *sdev, uint32_t lba, uint32_t datalen, int blocksize, int wrprotect, int dpo, int bytchk, int group, unsigned char *data, int status, enum scsi_sense_key key, int *ascq, int num_ascq) { struct scsi_task *task; struct iscsi_data d; int ret; logging(LOG_VERBOSE, "Send WRITEVERIFY12 (Expecting %s) LBA:%d " "blocks:%d wrprotect:%d dpo:%d bytchk:%d group:%d", scsi_status_str(status), lba, datalen / blocksize, wrprotect, dpo, bytchk, group); if (!data_loss) { printf("--dataloss flag is not set in. Skipping write\n"); return -1; } task = scsi_cdb_writeverify12(lba, datalen, blocksize, wrprotect, dpo, bytchk, group); assert(task != NULL); d.data = data; d.size = datalen; task = send_scsi_command(sdev, task, &d); ret = check_result("WRITEVERIFY12", sdev, task, status, key, ascq, num_ascq); if (task) { scsi_free_scsi_task(task); } return ret; } int writeverify16(struct scsi_device *sdev, uint64_t lba, uint32_t datalen, int blocksize, int wrprotect, int dpo, int bytchk, int group, unsigned char *data, int status, enum scsi_sense_key key, int *ascq, int num_ascq) { struct scsi_task *task; struct iscsi_data d; int ret; logging(LOG_VERBOSE, "Send WRITEVERIFY16 (Expecting %s) LBA:%" PRIu64 " blocks:%d wrprotect:%d dpo:%d bytchk:%d group:%d", scsi_status_str(status), lba, datalen / blocksize, wrprotect, dpo, bytchk, group); if (!data_loss) { printf("--dataloss flag is not set in. Skipping write\n"); return -1; } task = scsi_cdb_writeverify16(lba, datalen, blocksize, wrprotect, dpo, bytchk, group); assert(task != NULL); d.data = data; d.size = datalen; task = send_scsi_command(sdev, task, &d); ret = check_result("WRITEVERIFY16", sdev, task, status, key, ascq, num_ascq); if (task) { scsi_free_scsi_task(task); } return ret; } int inquiry(struct scsi_device *sdev, struct scsi_task **out_task, int evpd, int page_code, int maxsize, int status, enum scsi_sense_key key, int *ascq, int num_ascq) { struct scsi_task *task; int ret; logging(LOG_VERBOSE, "Send INQUIRY (Expecting %s) evpd:%d " "page_code:%02x alloc_len:%d", scsi_status_str(status), evpd, page_code, maxsize); task = scsi_cdb_inquiry(evpd, page_code, maxsize); assert(task != NULL); task = send_scsi_command(sdev, task, NULL); ret = check_result("INQUIRY", sdev, task, status, key, ascq, num_ascq); if (out_task) { *out_task = task; } else if (task) { scsi_free_scsi_task(task); } return ret; } struct scsi_command_descriptor * get_command_descriptor(int opcode, int sa) { int i; if (rsop == NULL) { return NULL; } for (i = 0; i < rsop->num_descriptors; i++) { if (rsop->descriptors[i].opcode == opcode && rsop->descriptors[i].sa == sa) { return &rsop->descriptors[i]; } } return NULL; } int set_swp(struct scsi_device *sdev) { int ret; struct scsi_task *sense_task = NULL; struct scsi_mode_sense *ms; struct scsi_mode_page *mp; logging(LOG_VERBOSE, "Read CONTROL page"); /* see if we can even change swp */ ret = modesense6(sdev, &sense_task, 1, SCSI_MODESENSE_PC_CHANGEABLE, SCSI_MODEPAGE_CONTROL, 0, 255, EXPECT_STATUS_GOOD); if (ret) { logging(LOG_NORMAL, "Failed to read CONTROL mode page."); goto finished; } logging(LOG_VERBOSE, "[SUCCESS] CONTROL page fetched."); ms = scsi_datain_unmarshall(sense_task); if (ms == NULL) { logging(LOG_NORMAL, "failed to unmarshall mode sense datain " "blob"); ret = -1; goto finished; } /* if we cannot change swp, we are done here */ if (ms->pages->control.swp == 0) { logging(LOG_NORMAL, "SWP is not changeable"); ret = -2; goto finished; } /* get the current control page */ ret = modesense6(sdev, &sense_task, 1, SCSI_MODESENSE_PC_CURRENT, SCSI_MODEPAGE_CONTROL, 0, 255, EXPECT_STATUS_GOOD); if (ret) { logging(LOG_NORMAL, "Failed to read CONTROL mode page."); goto finished; } logging(LOG_VERBOSE, "[SUCCESS] CONTROL page fetched."); ms = scsi_datain_unmarshall(sense_task); if (ms == NULL) { logging(LOG_NORMAL, "failed to unmarshall mode sense datain " "blob"); ret = -1; goto finished; } mp = scsi_modesense_get_page(ms, SCSI_MODEPAGE_CONTROL, 0); if (mp == NULL) { logging(LOG_NORMAL, "failed to read control mode page"); ret = -1; goto finished; } /* For MODE SELECT PS is reserved and hence must be cleared */ mp->ps = 0; logging(LOG_VERBOSE, "Turn SWP ON"); mp->control.swp = 1; ret = modeselect6(sdev, 1, 0, mp, EXPECT_STATUS_GOOD); if (ret) { logging(LOG_NORMAL, "Failed to write CONTROL mode page."); goto finished; } logging(LOG_VERBOSE, "[SUCCESS] CONTROL page written."); finished: if (sense_task != NULL) { scsi_free_scsi_task(sense_task); } return ret; } int clear_swp(struct scsi_device *sdev) { int ret; struct scsi_task *sense_task = NULL; struct scsi_mode_sense *ms; struct scsi_mode_page *mp; logging(LOG_VERBOSE, "Read CONTROL page"); ret = modesense6(sdev, &sense_task, 1, SCSI_MODESENSE_PC_CURRENT, SCSI_MODEPAGE_CONTROL, 0, 255, EXPECT_STATUS_GOOD); if (ret) { logging(LOG_NORMAL, "Failed to read CONTROL mode page."); goto finished; } logging(LOG_VERBOSE, "[SUCCESS] CONTROL page fetched."); ms = scsi_datain_unmarshall(sense_task); if (ms == NULL) { logging(LOG_NORMAL, "failed to unmarshall mode sense datain " "blob"); ret = -1; goto finished; } mp = scsi_modesense_get_page(ms, SCSI_MODEPAGE_CONTROL, 0); if (mp == NULL) { logging(LOG_NORMAL, "failed to read control mode page"); ret = -1; goto finished; } /* For MODE SELECT PS is reserved and hence must be cleared */ mp->ps = 0; logging(LOG_VERBOSE, "Turn SWP OFF"); mp->control.swp = 0; ret = modeselect6(sdev, 1, 0, mp, EXPECT_STATUS_GOOD); if (ret) { logging(LOG_NORMAL, "Failed to write CONTROL mode page."); goto finished; } logging(LOG_VERBOSE, "[SUCCESS] CONTROL page written."); finished: if (sense_task != NULL) { scsi_free_scsi_task(sense_task); } return ret; } /* Extended Copy */ int extendedcopy(struct scsi_device *sdev, struct iscsi_data *data, int status, enum scsi_sense_key key, int *ascq, int num_ascq) { struct scsi_task *task; int ret; logging(LOG_VERBOSE, "Send EXTENDED COPY (Expecting %s)", scsi_status_str(status)); if (!data_loss) { logging(LOG_NORMAL, "--dataloss flag is not set in. Skipping extendedcopy\n"); return -1; } task = scsi_cdb_extended_copy(data->size); assert(task != NULL); send_scsi_command(sdev, task, data); ret = check_result("EXTENDEDCOPY", sdev, task, status, key, ascq, num_ascq); scsi_free_scsi_task(task); return ret; } int get_desc_len(enum ec_descr_type_code desc_type) { int desc_len = 0; switch (desc_type) { /* Segment Descriptors */ case BLK_TO_STRM_SEG_DESCR: case STRM_TO_BLK_SEG_DESCR: desc_len = 0x18; break; case BLK_TO_BLK_SEG_DESCR: desc_len = 0x1C; break; case STRM_TO_STRM_SEG_DESCR: desc_len = 0x14; break; /* Target Descriptors */ case IPV6_TGT_DESCR: case IP_COPY_SVC_TGT_DESCR: desc_len = 64; break; case IDENT_DESCR_TGT_DESCR: default: if (desc_type >= 0xE0 && desc_type <= 0xE9) desc_len = 32; } return desc_len; } void populate_ident_tgt_desc(unsigned char *buf, struct scsi_device *dev) { int ret; struct scsi_task *inq_di_task = NULL; struct scsi_inquiry_device_identification *inq_di = NULL; struct scsi_inquiry_device_designator *desig, *tgt_desig = NULL; enum scsi_designator_type prev_type = 0; ret = inquiry(dev, &inq_di_task, 1, SCSI_INQUIRY_PAGECODE_DEVICE_IDENTIFICATION, 255, EXPECT_STATUS_GOOD); if (ret < 0 || inq_di_task == NULL) { logging(LOG_NORMAL, "Failed to read Device Identification page"); goto finished; } else { inq_di = scsi_datain_unmarshall(inq_di_task); if (inq_di == NULL) { logging(LOG_NORMAL, "Failed to unmarshall inquiry datain blob"); goto finished; } } for (desig = inq_di->designators; desig; desig = desig->next) { switch (desig->designator_type) { case SCSI_DESIGNATOR_TYPE_VENDOR_SPECIFIC: case SCSI_DESIGNATOR_TYPE_T10_VENDORT_ID: case SCSI_DESIGNATOR_TYPE_EUI_64: case SCSI_DESIGNATOR_TYPE_NAA: if (prev_type <= desig->designator_type) { tgt_desig = desig; prev_type = desig->designator_type; } default: continue; } } if (tgt_desig == NULL) { logging(LOG_NORMAL, "No suitalble target descriptor format found"); goto finished; } buf[0] = tgt_desig->code_set; buf[1] = (tgt_desig->designator_type & 0xF) | ((tgt_desig->association & 3) << 4); buf[3] = tgt_desig->designator_length; memcpy(buf + 4, tgt_desig->designator, tgt_desig->designator_length); finished: scsi_free_scsi_task(inq_di_task); } int populate_tgt_desc(unsigned char *desc, enum ec_descr_type_code desc_type, int luid_type, int nul, int peripheral_type, int rel_init_port_id, int pad, struct scsi_device *dev) { desc[0] = desc_type; desc[1] = (luid_type << 6) | (nul << 5) | peripheral_type; desc[2] = (rel_init_port_id >> 8) & 0xFF; desc[3] = rel_init_port_id & 0xFF; if (desc_type == IDENT_DESCR_TGT_DESCR) populate_ident_tgt_desc(desc+4, dev); if (peripheral_type == 0) { // Issue readcapacity for each sd if testing with different LUs // If single LU, use block_size from prior readcapacity involcation desc[28] = pad << 2; desc[29] = (block_size >> 16) & 0xFF; desc[30] = (block_size >> 8) & 0xFF; desc[31] = block_size & 0xFF; } return get_desc_len(desc_type); } int populate_seg_desc_hdr(unsigned char *hdr, enum ec_descr_type_code desc_type, int dc, int cat, int src_index, int dst_index) { int desc_len = get_desc_len(desc_type); hdr[0] = desc_type; hdr[1] = ((dc << 1) | cat) & 0xFF; hdr[2] = (desc_len >> 8) & 0xFF; hdr[3] = (desc_len - SEG_DESC_SRC_INDEX_OFFSET) & 0xFF; /* don't account for the first 4 bytes in descriptor header*/ hdr[4] = (src_index >> 8) & 0xFF; hdr[5] = src_index & 0xFF; hdr[6] = (dst_index >> 8) & 0xFF; hdr[7] = dst_index & 0xFF; return desc_len; } int populate_seg_desc_b2b(unsigned char *desc, int dc, int cat, int src_index, int dst_index, int num_blks, uint64_t src_lba, uint64_t dst_lba) { int desc_len = populate_seg_desc_hdr(desc, BLK_TO_BLK_SEG_DESCR, dc, cat, src_index, dst_index); desc[10] = (num_blks >> 8) & 0xFF; desc[11] = num_blks & 0xFF; desc[12] = (src_lba >> 56) & 0xFF; desc[13] = (src_lba >> 48) & 0xFF; desc[14] = (src_lba >> 40) & 0xFF; desc[15] = (src_lba >> 32) & 0xFF; desc[16] = (src_lba >> 24) & 0xFF; desc[17] = (src_lba >> 16) & 0xFF; desc[18] = (src_lba >> 8) & 0xFF; desc[19] = src_lba & 0xFF; desc[20] = (dst_lba >> 56) & 0xFF; desc[21] = (dst_lba >> 48) & 0xFF; desc[22] = (dst_lba >> 40) & 0xFF; desc[23] = (dst_lba >> 32) & 0xFF; desc[24] = (dst_lba >> 24) & 0xFF; desc[25] = (dst_lba >> 16) & 0xFF; desc[26] = (dst_lba >> 8) & 0xFF; desc[27] = dst_lba & 0xFF; return desc_len; } void populate_param_header(unsigned char *buf, int list_id, int str, int list_id_usage, int prio, int tgt_desc_len, int seg_desc_len, int inline_data_len) { buf[0] = list_id; buf[1] = ((str & 1) << 5) | ((list_id_usage & 3) << 3) | (prio & 7); buf[2] = (tgt_desc_len >> 8) & 0xFF; buf[3] = tgt_desc_len & 0xFF; buf[8] = (seg_desc_len >> 24) & 0xFF; buf[9] = (seg_desc_len >> 16) & 0xFF; buf[10] = (seg_desc_len >> 8) & 0xFF; buf[11] = seg_desc_len & 0xFF; buf[12] = (inline_data_len >> 24) & 0xFF; buf[13] = (inline_data_len >> 16) & 0xFF; buf[14] = (inline_data_len >> 8) & 0xFF; buf[15] = inline_data_len & 0xFF; } int receive_copy_results(struct scsi_task **task, struct scsi_device *sdev, enum scsi_copy_results_sa sa, int list_id, void **datap, int status, enum scsi_sense_key key, int *ascq, int num_ascq) { int ret; logging(LOG_VERBOSE, "Send RECEIVE COPY RESULTS"); *task = scsi_cdb_receive_copy_results(sa, list_id, 1024); assert(task != NULL); *task = send_scsi_command(sdev, *task, NULL); ret = check_result("RECEIVECOPYRESULT", sdev, *task, status, key, ascq, num_ascq); if (ret < 0) return ret; if ((*task)->status == SCSI_STATUS_GOOD && datap != NULL) { *datap = scsi_datain_unmarshall(*task); if (*datap == NULL) { logging(LOG_NORMAL, "[FAIL] failed to unmarshall RECEIVE COPY RESULTS data. %s", iscsi_get_error(sdev->iscsi_ctx)); return -1; } } return check_result("RECEIVECOPYRESULT", sdev, *task, status, key, ascq, num_ascq); } #define TEST_ISCSI_TUR_MAX_RETRIES 5 int test_iscsi_tur_until_good(struct scsi_device *iscsi_sd, int *num_uas) { int num_turs; if (iscsi_sd->iscsi_ctx == NULL) { logging(LOG_NORMAL, "invalid sd for tur_until_good"); return -EINVAL; } *num_uas = 0; for (num_turs = 0; num_turs < TEST_ISCSI_TUR_MAX_RETRIES; num_turs++) { struct scsi_task *tsk; tsk = iscsi_testunitready_sync(iscsi_sd->iscsi_ctx, iscsi_sd->iscsi_lun); if (tsk->status == SCSI_STATUS_GOOD) { logging(LOG_VERBOSE, "TUR good after %d retries", num_turs); return 0; } else if ((tsk->status == SCSI_STATUS_CHECK_CONDITION) && (tsk->sense.key == SCSI_SENSE_UNIT_ATTENTION)) { logging(LOG_VERBOSE, "Got UA for TUR"); (*num_uas)++; } else { logging(LOG_NORMAL, "unexpected non-UA failure: %d,%d", tsk->status, tsk->sense.key); } } return -ETIMEDOUT; } uint64_t test_get_clock_sec(void) { uint64_t secs; int res; #ifdef HAVE_CLOCK_GETTIME struct timespec ts; res = clock_gettime(CLOCK_MONOTONIC, &ts); secs = ts.tv_sec; #else struct timeval tv; res = gettimeofday(&tv, NULL); secs = tv.tv_sec; #endif assert(res == 0); return secs; } libiscsi-1.17.0/test-tool/iscsi-support.h000066400000000000000000001542021271371262700203440ustar00rootroot00000000000000/* iscsi-test tool support Copyright (C) 2012 by Lee Duncan Copyright (C) 2014 by Ronnie sahlberg 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, see . */ #ifndef _ISCSI_SUPPORT_H_ #define _ISCSI_SUPPORT_H_ #include #include #include #include #include #ifndef discard_const #define discard_const(ptr) ((void *)((intptr_t)(ptr))) #endif extern const char *initiatorname1; extern const char *initiatorname2; #define EXPECT_STATUS_GOOD SCSI_STATUS_GOOD, SCSI_SENSE_NO_SENSE, NULL, 0 #define EXPECT_NO_MEDIUM SCSI_STATUS_CHECK_CONDITION, SCSI_SENSE_NOT_READY, no_medium_ascqs, 3 #define EXPECT_LBA_OOB SCSI_STATUS_CHECK_CONDITION, SCSI_SENSE_ILLEGAL_REQUEST, lba_oob_ascqs, 1 #define EXPECT_INVALID_FIELD_IN_CDB SCSI_STATUS_CHECK_CONDITION, SCSI_SENSE_ILLEGAL_REQUEST, invalid_cdb_ascqs,2 #define EXPECT_PARAM_LIST_LEN_ERR SCSI_STATUS_CHECK_CONDITION, SCSI_SENSE_ILLEGAL_REQUEST, param_list_len_err_ascqs, 1 #define EXPECT_TOO_MANY_DESCR SCSI_STATUS_CHECK_CONDITION, SCSI_SENSE_ILLEGAL_REQUEST, too_many_desc_ascqs, 2 #define EXPECT_UNSUPP_DESCR_CODE SCSI_STATUS_CHECK_CONDITION, SCSI_SENSE_ILLEGAL_REQUEST, unsupp_desc_code_ascqs, 2 #define EXPECT_MISCOMPARE SCSI_STATUS_CHECK_CONDITION, SCSI_SENSE_MISCOMPARE, miscompare_ascqs, 1 #define EXPECT_WRITE_PROTECTED SCSI_STATUS_CHECK_CONDITION, SCSI_SENSE_DATA_PROTECTION, write_protect_ascqs, 3 #define EXPECT_SANITIZE SCSI_STATUS_CHECK_CONDITION, SCSI_SENSE_NOT_READY, sanitize_ascqs, 1 #define EXPECT_REMOVAL_PREVENTED SCSI_STATUS_CHECK_CONDITION, SCSI_SENSE_ILLEGAL_REQUEST, removal_ascqs, 1 #define EXPECT_RESERVATION_CONFLICT SCSI_STATUS_RESERVATION_CONFLICT, 0, NULL, 0 #define EXPECT_COPY_ABORTED SCSI_STATUS_CHECK_CONDITION, SCSI_SENSE_COPY_ABORTED, copy_aborted_ascqs, 3 int no_medium_ascqs[3]; int lba_oob_ascqs[1]; int invalid_cdb_ascqs[2]; int param_list_len_err_ascqs[1]; int too_many_desc_ascqs[2]; int unsupp_desc_code_ascqs[2]; int write_protect_ascqs[3]; int sanitize_ascqs[1]; int removal_ascqs[1]; int miscompare_ascqs[1]; int copy_aborted_ascqs[3]; extern int loglevel; #define LOG_SILENT 0 #define LOG_NORMAL 1 #define LOG_VERBOSE 2 void logging(int level, const char *format, ...) _R_(2,3); /* * define special flags for logging a blank line, so compiler * does not commplain when logging a "" */ #define LOG_BLANK_LINE " " #define LOG_BLANK_LINE_CMP_LEN 2 #define CHECK_FOR_DATALOSS \ do { \ if (!data_loss) { \ logging(LOG_NORMAL, "[SKIPPED] --dataloss flag is not " \ "set. Skipping test."); \ CU_PASS("[SKIPPED] --dataloss flag is not set." \ " Skipping test"); \ return; \ } \ } while (0); #define CHECK_FOR_SANITIZE \ do { \ if (!allow_sanitize) { \ logging(LOG_NORMAL, "[SKIPPED] --allow-sanitize flag " \ "is not set. Skipping test."); \ CU_PASS("[SKIPPED] --allow-sanitize flag is not set." \ " Skipping test"); \ return; \ } \ } while (0); #define CHECK_FOR_READONLY \ do { \ if (!readonly) { \ logging(LOG_NORMAL, "[SKIPPED] Logical unit is not " \ "write-protected. Skipping test."); \ CU_PASS("[SKIPPED] Logical unit is not write-" \ "protected. Skipping test"); \ return; \ } \ } while (0); #define CHECK_FOR_REMOVABLE \ do { \ if (!inq->rmb) { \ logging(LOG_NORMAL, "[SKIPPED] Logical unit is not " \ "removable. Skipping test."); \ CU_PASS("[SKIPPED] Logical unit is not removable" \ " Skipping test"); \ return; \ } \ } while (0); #define CHECK_FOR_THIN_PROVISIONING \ do { \ if (rc16 == NULL || rc16->lbpme == 0) { \ logging(LOG_NORMAL, "[SKIPPED] Logical unit is fully" \ " provisioned. Skipping test"); \ CU_PASS("[SKIPPED] Logical unit is fully provisioned." \ " Skipping test"); \ return; \ } \ } while (0); #define CHECK_FOR_LBPWS10 \ do { \ if (inq_lbp->lbpws10 == 0) { \ logging(LOG_NORMAL, "[SKIPPED] Logical unit does not" \ " have LBPWS10. Skipping test"); \ CU_PASS("[SKIPPED] Logical unit does not have LBPWS10." \ " Skipping test"); \ return; \ } \ } while (0); #define CHECK_FOR_LBPWS \ do { \ if (inq_lbp->lbpws == 0) { \ logging(LOG_NORMAL, "[SKIPPED] Logical unit does not" \ " have LBPWS. Skipping test"); \ CU_PASS("[SKIPPED] Logical unit does not have LBPWS." \ " Skipping test"); \ return; \ } \ } while (0); #define CHECK_FOR_LBPU \ do { \ if (inq_lbp->lbpu == 0) { \ logging(LOG_NORMAL, "[SKIPPED] Logical unit does not" \ " have LBPU. Skipping test"); \ CU_PASS("[SKIPPED] Logical unit does not have LBPU." \ " Skipping test"); \ return; \ } \ } while (0); #define CHECK_FOR_LBPPB_GT_1 \ do { \ if (lbppb < 2) { \ logging(LOG_NORMAL, "[SKIPPED] LBPPB < 2. Skipping test"); \ CU_PASS("[SKIPPED] LBPPB < 2. Skipping test"); \ return; \ } \ } while (0); #define CHECK_FOR_SBC \ do { \ if (inq->device_type != SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_DIRECT_ACCESS) {\ logging(LOG_NORMAL, "[SKIPPED] Not SBC device." \ " Skipping test"); \ CU_PASS("[SKIPPED] Not SBC device." \ " Skipping test"); \ return; \ } \ } while (0); #define COMPAREANDWRITE(...) \ do { \ int _r; \ _r = compareandwrite(__VA_ARGS__); \ if (_r == -2) { \ logging(LOG_NORMAL, "[SKIPPED] COMPAREANDWRITE " \ "is not implemented."); \ CU_PASS("[SKIPPED] Target does not support " \ "COMPAREANDWRITE. Skipping test"); \ return; \ } \ CU_ASSERT_EQUAL(_r, 0); \ } while (0); #define EXTENDEDCOPY(...) \ do { \ int _r; \ _r = extendedcopy(__VA_ARGS__); \ if (_r == -2) { \ logging(LOG_NORMAL, "[SKIPPED] EXTENDEDCOPY " \ "is not implemented."); \ CU_PASS("[SKIPPED] Target does not support " \ "EXTENDEDCOPY. Skipping test"); \ return; \ } \ CU_ASSERT_EQUAL(_r, 0); \ } while (0); #define GETLBASTATUS(...) \ do { \ int _r; \ _r = get_lba_status(__VA_ARGS__); \ if (_r == -2) { \ logging(LOG_NORMAL, "[SKIPPED] GETLBASTATUS " \ "is not implemented."); \ CU_PASS("[SKIPPED] Target does not support " \ "GETLBASTATUS. Skipping test"); \ return; \ } \ CU_ASSERT_EQUAL(_r, 0); \ } while (0); #define MODESENSE6(...) \ do { \ int _r; \ _r = modesense6(__VA_ARGS__); \ if (_r == -2) { \ logging(LOG_NORMAL, "[SKIPPED] MODESENSE6 " \ "is not implemented."); \ CU_PASS("[SKIPPED] Target does not support " \ "MODESENSE6. Skipping test"); \ return; \ } \ CU_ASSERT_EQUAL(_r, 0); \ } while (0); #define ORWRITE(...) \ do { \ int _r; \ _r = orwrite(__VA_ARGS__); \ if (_r == -2) { \ logging(LOG_NORMAL, "[SKIPPED] ORWRITE " \ "is not implemented."); \ CU_PASS("[SKIPPED] Target does not support " \ "ORWRITE. Skipping test"); \ return; \ } \ CU_ASSERT_EQUAL(_r, 0); \ } while (0); #define PREFETCH10(...) \ do { \ int _r; \ _r = prefetch10(__VA_ARGS__); \ if (_r == -2) { \ logging(LOG_NORMAL, "[SKIPPED] PREFETCH10 " \ "is not implemented."); \ CU_PASS("[SKIPPED] Target does not support " \ "PREFETCH10. Skipping test"); \ return; \ } \ CU_ASSERT_EQUAL(_r, 0); \ } while (0); #define PREFETCH16(...) \ do { \ int _r; \ _r = prefetch16(__VA_ARGS__); \ if (_r == -2) { \ logging(LOG_NORMAL, "[SKIPPED] PREFETCH16 " \ "is not implemented."); \ CU_PASS("[SKIPPED] Target does not support " \ "PREFETCH16. Skipping test"); \ return; \ } \ CU_ASSERT_EQUAL(_r, 0); \ } while (0); #define PREVENTALLOW(...) \ do { \ int _r; \ _r = preventallow(__VA_ARGS__); \ if (_r == -2) { \ logging(LOG_NORMAL, "[SKIPPED] PREVENTALLOW " \ "is not implemented."); \ CU_PASS("[SKIPPED] Target does not support " \ "PREVENTALLOW. Skipping test"); \ return; \ } \ CU_ASSERT_EQUAL(_r, 0); \ } while (0); #define READ6(...) \ do { \ int _r; \ _r = read6(__VA_ARGS__); \ if (_r == -2) { \ logging(LOG_NORMAL, "[SKIPPED] READ6 " \ "is not implemented."); \ CU_PASS("[SKIPPED] Target does not support " \ "READ6. Skipping test"); \ return; \ } \ CU_ASSERT_EQUAL(_r, 0); \ } while (0); #define READ10(...) \ do { \ int _r; \ _r = read10(__VA_ARGS__); \ if (_r == -2) { \ logging(LOG_NORMAL, "[SKIPPED] READ10 " \ "is not implemented."); \ CU_PASS("[SKIPPED] Target does not support " \ "READ10. Skipping test"); \ return; \ } \ CU_ASSERT_EQUAL(_r, 0); \ } while (0); #define READ12(...) \ do { \ int _r; \ _r = read12(__VA_ARGS__); \ if (_r == -2) { \ logging(LOG_NORMAL, "[SKIPPED] READ12 " \ "is not implemented."); \ CU_PASS("[SKIPPED] Target does not support " \ "READ12. Skipping test"); \ return; \ } \ CU_ASSERT_EQUAL(_r, 0); \ } while (0); #define READ16(...) \ do { \ int _r; \ _r = read16(__VA_ARGS__); \ if (_r == -2) { \ logging(LOG_NORMAL, "[SKIPPED] READ16 " \ "is not implemented."); \ CU_PASS("[SKIPPED] Target does not support " \ "READ16. Skipping test"); \ return; \ } \ CU_ASSERT_EQUAL(_r, 0); \ } while (0); #define READCAPACITY10(...) \ do { \ int _r; \ _r = readcapacity10(__VA_ARGS__); \ if (_r == -2) { \ logging(LOG_NORMAL, "[SKIPPED] READCAPACITY10 " \ "is not implemented."); \ CU_PASS("[SKIPPED] Target does not support " \ "READCAPACITY10. Skipping test"); \ return; \ } \ CU_ASSERT_EQUAL(_r, 0); \ } while (0); #define READCAPACITY16(...) \ do { \ int _r; \ _r = readcapacity16(__VA_ARGS__); \ if (_r == -2) { \ logging(LOG_NORMAL, "[SKIPPED] READCAPACITY16 " \ "is not implemented."); \ CU_PASS("[SKIPPED] Target does not support " \ "READCAPACITY16. Skipping test"); \ return; \ } \ CU_ASSERT_EQUAL(_r, 0); \ } while (0); #define RECEIVE_COPY_RESULTS(...) \ do { \ int _r; \ _r = receive_copy_results(__VA_ARGS__); \ if (_r == -2) { \ logging(LOG_NORMAL, "[SKIPPED] RECEIVE_COPY" \ "_RESULTS is not implemented."); \ CU_PASS("[SKIPPED] Target does not support " \ "RECEIVE_COPY_RESULTS. Skipping test"); \ return; \ } \ CU_ASSERT_EQUAL(_r, 0); \ } while (0); #define RELEASE6(...) \ do { \ int _r; \ _r = release6(__VA_ARGS__); \ if (_r == -2) { \ logging(LOG_NORMAL, "[SKIPPED] RELEASE6 " \ "is not implemented."); \ CU_PASS("[SKIPPED] Target does not support " \ "RELEASE6. Skipping test"); \ return; \ } \ CU_ASSERT_EQUAL(_r, 0); \ } while (0); #define REPORT_SUPPORTED_OPCODES(...) \ do { \ int _r; \ _r = report_supported_opcodes(__VA_ARGS__); \ if (_r == -2) { \ logging(LOG_NORMAL, "[SKIPPED] REPORT_SUPPORTED" \ "_OPCODES is not implemented."); \ CU_PASS("[SKIPPED] Target does not support " \ "REPORT_SUPPORTED_OPCODES. Skipping " \ "test"); \ return; \ } \ CU_ASSERT_EQUAL(_r, 0); \ } while (0); #define RESERVE6(...) \ do { \ int _r; \ _r = reserve6(__VA_ARGS__); \ if (_r == -2) { \ logging(LOG_NORMAL, "[SKIPPED] RESERVE6 " \ "is not implemented."); \ CU_PASS("[SKIPPED] Target does not support " \ "RESERVE6. Skipping test"); \ return; \ } \ CU_ASSERT_EQUAL(_r, 0); \ } while (0); #define SANITIZE(...) \ do { \ int _r; \ _r = sanitize(__VA_ARGS__); \ if (_r == -2) { \ logging(LOG_NORMAL, "[SKIPPED] SANITIZE " \ "is not implemented."); \ CU_PASS("[SKIPPED] Target does not support " \ "SANITIZE. Skipping test"); \ return; \ } \ CU_ASSERT_EQUAL(_r, 0); \ } while (0); #define STARTSTOPUNIT(...) \ do { \ int _r; \ _r = startstopunit(__VA_ARGS__); \ if (_r == -2) { \ logging(LOG_NORMAL, "[SKIPPED] STARTSTOPUNIT " \ "is not implemented."); \ CU_PASS("[SKIPPED] Target does not support " \ "STARTSTOPUNIT. Skipping test"); \ return; \ } \ CU_ASSERT_EQUAL(_r, 0); \ } while (0); #define SYNCHRONIZECACHE10(...) \ do { \ int _r; \ _r = synchronizecache10(__VA_ARGS__); \ if (_r == -2) { \ logging(LOG_NORMAL, "[SKIPPED] SYNCHRONIZECACHE10 " \ "is not implemented."); \ CU_PASS("[SKIPPED] Target does not support " \ "SYNCHRONIZECACHE10. Skipping test"); \ return; \ } \ CU_ASSERT_EQUAL(_r, 0); \ } while (0); #define SYNCHRONIZECACHE16(...) \ do { \ int _r; \ _r = synchronizecache16(__VA_ARGS__); \ if (_r == -2) { \ logging(LOG_NORMAL, "[SKIPPED] SYNCHRONIZECACHE16 " \ "is not implemented."); \ CU_PASS("[SKIPPED] Target does not support " \ "SYNCHRONIZECACHE16. Skipping test"); \ return; \ } \ CU_ASSERT_EQUAL(_r, 0); \ } while (0); #define TESTUNITREADY(...) \ do { \ int _r; \ _r = testunitready(__VA_ARGS__); \ if (_r == -2) { \ logging(LOG_NORMAL, "[SKIPPED] TESTUNITREADY " \ "is not implemented."); \ CU_PASS("[SKIPPED] Target does not support " \ "TESTUNITREADY. Skipping test"); \ return; \ } \ CU_ASSERT_EQUAL(_r, 0); \ } while (0); #define UNMAP(...) \ do { \ int _r; \ _r = unmap(__VA_ARGS__); \ if (_r == -2) { \ logging(LOG_NORMAL, "[SKIPPED] UNMAP " \ "is not implemented."); \ CU_PASS("[SKIPPED] Target does not support " \ "UNMAP. Skipping test"); \ return; \ } \ CU_ASSERT_EQUAL(_r, 0); \ } while (0); #define VERIFY10(...) \ do { \ int _r; \ _r = verify10(__VA_ARGS__); \ if (_r == -2) { \ logging(LOG_NORMAL, "[SKIPPED] VERIFY10 " \ "is not implemented."); \ CU_PASS("[SKIPPED] Target does not support " \ "VERIFY10. Skipping test"); \ return; \ } \ CU_ASSERT_EQUAL(_r, 0); \ } while (0); #define VERIFY12(...) \ do { \ int _r; \ _r = verify12(__VA_ARGS__); \ if (_r == -2) { \ logging(LOG_NORMAL, "[SKIPPED] VERIFY12 " \ "is not implemented."); \ CU_PASS("[SKIPPED] Target does not support " \ "VERIFY12. Skipping test"); \ return; \ } \ CU_ASSERT_EQUAL(_r, 0); \ } while (0); #define VERIFY16(...) \ do { \ int _r; \ _r = verify16(__VA_ARGS__); \ if (_r == -2) { \ logging(LOG_NORMAL, "[SKIPPED] VERIFY16 " \ "is not implemented."); \ CU_PASS("[SKIPPED] Target does not support " \ "VERIFY16. Skipping test"); \ return; \ } \ CU_ASSERT_EQUAL(_r, 0); \ } while (0); #define WRITE10(...) \ do { \ int _r; \ _r = write10(__VA_ARGS__); \ if (_r == -2) { \ logging(LOG_NORMAL, "[SKIPPED] WRITE10 " \ "is not implemented."); \ CU_PASS("[SKIPPED] Target does not support " \ "WRITE10. Skipping test"); \ return; \ } \ CU_ASSERT_EQUAL(_r, 0); \ } while (0); #define WRITE12(...) \ do { \ int _r; \ _r = write12(__VA_ARGS__); \ if (_r == -2) { \ logging(LOG_NORMAL, "[SKIPPED] WRITE12 " \ "is not implemented."); \ CU_PASS("[SKIPPED] Target does not support " \ "WRITE12. Skipping test"); \ return; \ } \ CU_ASSERT_EQUAL(_r, 0); \ } while (0); #define WRITE16(...) \ do { \ int _r; \ _r = write16(__VA_ARGS__); \ if (_r == -2) { \ logging(LOG_NORMAL, "[SKIPPED] WRITE16 " \ "is not implemented."); \ CU_PASS("[SKIPPED] Target does not support " \ "WRITE16. Skipping test"); \ return; \ } \ CU_ASSERT_EQUAL(_r, 0); \ } while (0); #define WRITEATOMIC16(...) \ do { \ int _r; \ _r = writeatomic16(__VA_ARGS__); \ if (_r == -2) { \ logging(LOG_NORMAL, "[SKIPPED] WRITEATOMIC16 " \ "is not implemented."); \ CU_PASS("[SKIPPED] Target does not support " \ "WRITEATOMIC16. Skipping test"); \ return; \ } \ CU_ASSERT_EQUAL(_r, 0); \ } while (0); #define WRITESAME10(...) \ do { \ int _r; \ _r = writesame10(__VA_ARGS__); \ if (_r == -2) { \ logging(LOG_NORMAL, "[SKIPPED] WRITESAME10 " \ "is not implemented."); \ CU_PASS("[SKIPPED] Target does not support " \ "WRITESAME10. Skipping test"); \ return; \ } \ CU_ASSERT_EQUAL(_r, 0); \ } while (0); #define WRITESAME16(...) \ do { \ int _r; \ _r = writesame16(__VA_ARGS__); \ if (_r == -2) { \ logging(LOG_NORMAL, "[SKIPPED] WRITESAME16 " \ "is not implemented."); \ CU_PASS("[SKIPPED] Target does not support " \ "WRITESAME16. Skipping test"); \ return; \ } \ CU_ASSERT_EQUAL(_r, 0); \ } while (0); #define WRITEVERIFY10(...) \ do { \ int _r; \ _r = writeverify10(__VA_ARGS__); \ if (_r == -2) { \ logging(LOG_NORMAL, "[SKIPPED] WRITEVERIFY10 " \ "is not implemented."); \ CU_PASS("[SKIPPED] Target does not support " \ "WRITEVERIFY10. Skipping test"); \ return; \ } \ CU_ASSERT_EQUAL(_r, 0); \ } while (0); #define WRITEVERIFY12(...) \ do { \ int _r; \ _r = writeverify12(__VA_ARGS__); \ if (_r == -2) { \ logging(LOG_NORMAL, "[SKIPPED] WRITEVERIFY12 " \ "is not implemented."); \ CU_PASS("[SKIPPED] Target does not support " \ "WRITEVERIFY12. Skipping test"); \ return; \ } \ CU_ASSERT_EQUAL(_r, 0); \ } while (0); #define WRITEVERIFY16(...) \ do { \ int _r; \ _r = writeverify16(__VA_ARGS__); \ if (_r == -2) { \ logging(LOG_NORMAL, "[SKIPPED] WRITEVERIFY16 " \ "is not implemented."); \ CU_PASS("[SKIPPED] Target does not support " \ "WRITEVERIFY16. Skipping test"); \ return; \ } \ CU_ASSERT_EQUAL(_r, 0); \ } while (0); #define ALL_ZERO(...) \ do { \ if (all_zero(__VA_ARGS__) == 0) { \ logging(LOG_NORMAL, "[FAILED] Blocks did not " \ "read back as zero"); \ CU_FAIL("[FAILED] Blocks did not read back " \ "as zero"); \ return; \ } \ } while (0); extern struct scsi_inquiry_standard *inq; extern struct scsi_inquiry_logical_block_provisioning *inq_lbp; extern struct scsi_inquiry_block_device_characteristics *inq_bdc; extern struct scsi_inquiry_block_limits *inq_bl; extern struct scsi_readcapacity16 *rc16; extern struct scsi_report_supported_op_codes *rsop; extern unsigned char *scratch; extern size_t block_size; extern uint64_t num_blocks; extern int lbppb; extern int data_loss; extern int allow_sanitize; extern int readonly; extern int sbc3_support; extern int maximum_transfer_length; struct scsi_device { char *error_str; struct iscsi_context *iscsi_ctx; int iscsi_lun; char *iscsi_url; char *sgio_dev; int sgio_fd; }; extern struct scsi_device *sd; struct iscsi_context *iscsi_context_login(const char *initiatorname, const char *url, int *lun); struct iscsi_async_state { struct scsi_task *task; int status; int finished; }; void wait_until_test_finished(struct iscsi_context *iscsi, struct iscsi_async_state *test_state); struct iscsi_pdu; int (*local_iscsi_queue_pdu)(struct iscsi_context *iscsi, struct iscsi_pdu *pdu); struct scsi_command_descriptor *get_command_descriptor(int opcode, int sa); /* * PGR support */ static inline long rand_key(void) { static int seed = 0; if (!seed) { struct timeval tv; pid_t p; unsigned int s; gettimeofday(&tv, NULL); p = getpid(); s = p ^ tv.tv_sec ^ tv.tv_usec; srandom(s); } seed = 1; return random(); } static inline int pr_type_is_all_registrants( enum scsi_persistent_out_type pr_type) { switch (pr_type) { case SCSI_PERSISTENT_RESERVE_TYPE_WRITE_EXCLUSIVE_ALL_REGISTRANTS: case SCSI_PERSISTENT_RESERVE_TYPE_EXCLUSIVE_ACCESS_ALL_REGISTRANTS: return 1; default: return 0; } } int all_zero(const unsigned char *buf, unsigned size); int prin_task(struct scsi_device *sdev, int service_action, int success_expected); int prin_read_keys(struct scsi_device *sdev, struct scsi_task **tp, struct scsi_persistent_reserve_in_read_keys **rkp); int prout_register_and_ignore(struct scsi_device *sdev, unsigned long long key); int prout_register_key(struct scsi_device *sdev, unsigned long long sark, unsigned long long rk); int prin_verify_key_presence(struct scsi_device *sdev, unsigned long long key, int present); int prout_reregister_key_fails(struct scsi_device *sdev, unsigned long long sark); int prout_reserve(struct scsi_device *sdev, unsigned long long key, enum scsi_persistent_out_type pr_type); int prout_release(struct scsi_device *sdev, unsigned long long key, enum scsi_persistent_out_type pr_type); int prout_clear(struct scsi_device *sdev, unsigned long long key); int prout_preempt(struct scsi_device *sdev, unsigned long long sark, unsigned long long rk, enum scsi_persistent_out_type pr_type); int prin_verify_not_reserved(struct scsi_device *sdev); int prin_verify_reserved_as(struct scsi_device *sdev, unsigned long long key, enum scsi_persistent_out_type pr_type); int prin_report_caps(struct scsi_device *sdev, struct scsi_task **tp, struct scsi_persistent_reserve_in_report_capabilities **_rcaps); int verify_read_works(struct scsi_device *sdev, unsigned char *buf); int verify_write_works(struct scsi_device *sdev, unsigned char *buf); int verify_read_fails(struct scsi_device *sdev, unsigned char *buf); int verify_write_fails(struct scsi_device *sdev, unsigned char *buf); int compareandwrite(struct scsi_device *sdev, uint64_t lba, unsigned char *data, uint32_t len, int blocksize, int wrprotect, int dpo, int fua, int group_number, int status, enum scsi_sense_key key, int *ascq, int num_ascq); int get_lba_status(struct scsi_device *sdev, struct scsi_task **task, uint64_t lba, uint32_t len, int status, enum scsi_sense_key key, int *ascq, int num_ascq); int inquiry(struct scsi_device *sdev, struct scsi_task **task, int evpd, int page_code, int maxsize, int status, enum scsi_sense_key key, int *ascq, int num_ascq); int modesense6(struct scsi_device *sdev, struct scsi_task **task, int dbd, enum scsi_modesense_page_control pc, enum scsi_modesense_page_code page_code, int sub_page_code, unsigned char alloc_len, int status, enum scsi_sense_key key, int *ascq, int num_ascq); int modeselect6(struct scsi_device *sdev, int pf, int sp, struct scsi_mode_page *mp, int status, enum scsi_sense_key key, int *ascq, int num_ascq); int orwrite(struct scsi_device *sdev, uint64_t lba, uint32_t datalen, int blocksize, int wrprotect, int dpo, int fua, int fua_nv, int group, unsigned char *data, int status, enum scsi_sense_key key, int *ascq, int num_ascq); int prefetch10(struct scsi_device *sdev, uint32_t lba, int num_blocks, int immed, int group, int status, enum scsi_sense_key key, int *ascq, int num_ascq); int prefetch16(struct scsi_device *sdev, uint64_t lba, int num_blocks, int immed, int group, int status, enum scsi_sense_key key, int *ascq, int num_ascq); int preventallow(struct scsi_device *sdev, int prevent); int read6(struct scsi_device *sdev, struct scsi_task **task, uint32_t lba, uint32_t datalen, int blocksize, unsigned char *data, int status, enum scsi_sense_key key, int *ascq, int num_ascq); int read10(struct scsi_device *sdev, struct scsi_task **task, uint32_t lba, uint32_t datalen, int blocksize, int rdprotect, int dpo, int fua, int fua_nv, int group, unsigned char *data, int status, enum scsi_sense_key key, int *ascq, int num_ascq); int read12(struct scsi_device *sdev, struct scsi_task **task, uint32_t lba, uint32_t datalen, int blocksize, int rdprotect, int dpo, int fua, int fua_nv, int group, unsigned char *data, int status, enum scsi_sense_key key, int *ascq, int num_ascq); int read16(struct scsi_device *sdev, struct scsi_task **task, uint64_t lba, uint32_t datalen, int blocksize, int rdprotect, int dpo, int fua, int fua_nv, int group, unsigned char *data, int status, enum scsi_sense_key key, int *ascq, int num_ascq); int readcapacity10(struct scsi_device *sdev, struct scsi_task **task, uint32_t lba, int pmi, int status, enum scsi_sense_key key, int *ascq, int num_ascq); int readcapacity16(struct scsi_device *sdev, struct scsi_task **task, int alloc_len, int status, enum scsi_sense_key key, int *ascq, int num_ascq); int report_supported_opcodes(struct scsi_device *sdev, struct scsi_task **save_task, int rctd, int options, int opcode, int sa, int alloc_len, int status, enum scsi_sense_key key, int *ascq, int num_ascq); int release6(struct scsi_device *sdev); int reserve6(struct scsi_device *sdev); int reserve6_conflict(struct scsi_device *sdev); int sanitize(struct scsi_device *sdev, int immed, int ause, int sa, int param_len, struct iscsi_data *data, int status, enum scsi_sense_key key, int *ascq, int num_ascq); int startstopunit(struct scsi_device *sdev, int immed, int pcm, int pc, int no_flush, int loej, int start, int status, enum scsi_sense_key key, int *ascq, int num_ascq); int synchronizecache10(struct scsi_device *sdev, uint32_t lba, int num_blocks, int sync_nv, int immed, int status, enum scsi_sense_key key, int *ascq, int num_ascq); int synchronizecache16(struct scsi_device *sdev, uint64_t lba, int num_blocks, int sync_nv, int immed, int status, enum scsi_sense_key key, int *ascq, int num_ascq); int testunitready_clear_ua(struct scsi_device *sdev); int testunitready(struct scsi_device *sdev, int status, enum scsi_sense_key key, int *ascq, int num_ascq); int unmap(struct scsi_device *sdev, int anchor, struct unmap_list *list, int list_len, int status, enum scsi_sense_key key, int *ascq, int num_ascq); int verify10(struct scsi_device *sdev, uint32_t lba, uint32_t datalen, int blocksize, int vprotect, int dpo, int bytchk, unsigned char *data, int status, enum scsi_sense_key key, int *ascq, int num_ascq); int verify12(struct scsi_device *sdev, uint32_t lba, uint32_t datalen, int blocksize, int vprotect, int dpo, int bytchk, unsigned char *data, int status, enum scsi_sense_key key, int *ascq, int num_ascq); int verify16(struct scsi_device *sdev, uint64_t lba, uint32_t datalen, int blocksize, int vprotect, int dpo, int bytchk, unsigned char *data, int status, enum scsi_sense_key key, int *ascq, int num_ascq); int write10(struct scsi_device *sdev, uint32_t lba, uint32_t datalen, int blocksize, int wrprotect, int dpo, int fua, int fua_nv, int group, unsigned char *data, int status, enum scsi_sense_key key, int *ascq, int num_ascq); int write12(struct scsi_device *sdev, uint32_t lba, uint32_t datalen, int blocksize, int wrprotect, int dpo, int fua, int fua_nv, int group, unsigned char *data, int status, enum scsi_sense_key key, int *ascq, int num_ascq); int write16(struct scsi_device *sdev, uint64_t lba, uint32_t datalen, int blocksize, int wrprotect, int dpo, int fua, int fua_nv, int group, unsigned char *data, int status, enum scsi_sense_key key, int *ascq, int num_ascq); int writeatomic16(struct scsi_device *sdev, uint64_t lba, uint32_t datalen, int blocksize, int wrprotect, int dpo, int fua, int group, unsigned char *data, int status, enum scsi_sense_key key, int *ascq, int num_ascq); int writesame10(struct scsi_device *sdev, uint32_t lba, uint32_t datalen, int num_blocks, int anchor, int unmap, int wrprotect, int group, unsigned char *data, int status, enum scsi_sense_key key, int *ascq, int num_ascq); int writesame16(struct scsi_device *sdev, uint64_t lba, uint32_t datalen, int num_blocks, int anchor, int unmap, int wrprotect, int group, unsigned char *data, int status, enum scsi_sense_key key, int *ascq, int num_ascq); int writeverify10(struct scsi_device *sdev, uint32_t lba, uint32_t datalen, int blocksize, int wrprotect, int dpo, int bytchk, int group, unsigned char *data, int status, enum scsi_sense_key key, int *ascq, int num_ascq); int writeverify12(struct scsi_device *sdev, uint32_t lba, uint32_t datalen, int blocksize, int wrprotect, int dpo, int bytchk, int group, unsigned char *data, int status, enum scsi_sense_key key, int *ascq, int num_ascq); int writeverify16(struct scsi_device *sdev, uint64_t lba, uint32_t datalen, int blocksize, int wrprotect, int dpo, int bytchk, int group, unsigned char *data, int status, enum scsi_sense_key key, int *ascq, int num_ascq); int set_swp(struct scsi_device *sdev); int clear_swp(struct scsi_device *sdev); int extendedcopy(struct scsi_device *sdev, struct iscsi_data *data, int status, enum scsi_sense_key key, int *ascq, int num_ascq); int get_desc_len(enum ec_descr_type_code desc_type); int populate_tgt_desc(unsigned char *desc, enum ec_descr_type_code desc_type, int luid_type, int nul, int peripheral_type, int rel_init_port_id, int pad, struct scsi_device *dev); int populate_seg_desc_hdr(unsigned char *hdr, enum ec_descr_type_code desc_type, int dc, int cat, int src_index, int dst_index); int populate_seg_desc_b2b(unsigned char *desc, int dc, int cat, int src_index, int dst_index, int num_blks, uint64_t src_lba, uint64_t dst_lba); void populate_param_header(unsigned char *buf, int list_id, int str, int list_id_usage, int prio, int tgt_desc_len, int seg_desc_len, int inline_data_len); int receive_copy_results(struct scsi_task **task, struct scsi_device *sdev, enum scsi_copy_results_sa sa, int list_id, void **datap, int status, enum scsi_sense_key key, int *ascq, int num_ascq); int test_iscsi_tur_until_good(struct scsi_device *iscsi_sd, int *num_uas); uint64_t test_get_clock_sec(void); #endif /* _ISCSI_SUPPORT_H_ */ libiscsi-1.17.0/test-tool/iscsi-test-cu.c000066400000000000000000001574321271371262700202170ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* iscsi-test tool Copyright (C) 2012 by Lee Duncan 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, see . */ #include "config.h" #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_SG_IO #include #include #include #include #endif #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-private.h" #include "iscsi-support.h" #include "iscsi-test-cu.h" #include "iscsi-support.h" #include "iscsi-multipath.h" #define PROG "iscsi-test-cu" int loglevel = LOG_NORMAL; struct scsi_device *sd = NULL; /* mp_sds[0] alias */ static unsigned int maxsectors; /* * this allows us to redefine how PDU are queued, at times, for * testing purposes */ int (*real_iscsi_queue_pdu)(struct iscsi_context *iscsi, struct iscsi_pdu *pdu); /***************************************************************** * * list of tests and test suites * *****************************************************************/ static CU_TestInfo tests_compareandwrite[] = { { (char *)"Simple", test_compareandwrite_simple }, { (char *)"DpoFua", test_compareandwrite_dpofua }, { (char *)"Miscompare", test_compareandwrite_miscompare }, CU_TEST_INFO_NULL }; static CU_TestInfo tests_get_lba_status[] = { { (char *)"Simple", test_get_lba_status_simple }, { (char *)"BeyondEol", test_get_lba_status_beyond_eol }, { (char *)"UnmapSingle", test_get_lba_status_unmap_single }, CU_TEST_INFO_NULL }; static CU_TestInfo tests_inquiry[] = { { (char *)"Standard", test_inquiry_standard }, { (char *)"AllocLength", test_inquiry_alloc_length}, { (char *)"EVPD", test_inquiry_evpd}, { (char *)"BlockLimits", test_inquiry_block_limits}, { (char *)"MandatoryVPDSBC", test_inquiry_mandatory_vpd_sbc}, { (char *)"SupportedVPD", test_inquiry_supported_vpd}, { (char *)"VersionDescriptors", test_inquiry_version_descriptors}, CU_TEST_INFO_NULL }; static CU_TestInfo tests_mandatory[] = { { (char *)"MandatorySBC", test_mandatory_sbc }, CU_TEST_INFO_NULL }; static CU_TestInfo tests_modesense6[] = { { (char *)"AllPages", test_modesense6_all_pages }, { (char *)"Control", test_modesense6_control }, { (char *)"Control-D_SENSE", test_modesense6_control_d_sense }, { (char *)"Control-SWP", test_modesense6_control_swp }, { (char *)"Residuals", test_modesense6_residuals }, CU_TEST_INFO_NULL }; static CU_TestInfo tests_nomedia[] = { { (char *)"NoMediaSBC", test_nomedia_sbc }, CU_TEST_INFO_NULL }; static CU_TestInfo tests_orwrite[] = { { (char *)"Simple", test_orwrite_simple }, { (char *)"BeyondEol", test_orwrite_beyond_eol }, { (char *)"ZeroBlocks", test_orwrite_0blocks }, { (char *)"Protect", test_orwrite_wrprotect }, { (char *)"DpoFua", test_orwrite_dpofua }, { (char *)"Verify", test_orwrite_verify }, CU_TEST_INFO_NULL }; static CU_TestInfo tests_prefetch10[] = { { (char *)"Simple", test_prefetch10_simple }, { (char *)"BeyondEol", test_prefetch10_beyond_eol }, { (char *)"ZeroBlocks", test_prefetch10_0blocks }, { (char *)"Flags", test_prefetch10_flags }, CU_TEST_INFO_NULL }; static CU_TestInfo tests_prefetch16[] = { { (char *)"Simple", test_prefetch16_simple }, { (char *)"BeyondEol", test_prefetch16_beyond_eol }, { (char *)"ZeroBlocks", test_prefetch16_0blocks }, { (char *)"Flags", test_prefetch16_flags }, CU_TEST_INFO_NULL }; static CU_TestInfo tests_preventallow[] = { { (char *)"Simple", test_preventallow_simple }, { (char *)"Eject", test_preventallow_eject }, { (char *)"ITNexusLoss", test_preventallow_itnexus_loss }, { (char *)"Logout", test_preventallow_logout }, { (char *)"WarmReset", test_preventallow_warm_reset }, { (char *)"ColdReset", test_preventallow_cold_reset }, { (char *)"LUNReset", test_preventallow_lun_reset }, { (char *)"2ITNexuses", test_preventallow_2_itnexuses }, CU_TEST_INFO_NULL }; static CU_TestInfo tests_prin_read_keys[] = { { (char *)"Simple", test_prin_read_keys_simple }, CU_TEST_INFO_NULL }; static CU_TestInfo tests_prin_report_caps[] = { { (char *)"Simple", test_prin_report_caps_simple }, CU_TEST_INFO_NULL }; static CU_TestInfo tests_prout_register[] = { { (char *)"Simple", test_prout_register_simple }, CU_TEST_INFO_NULL }; static CU_TestInfo tests_prout_reserve[] = { { (char *)"Simple", test_prout_reserve_simple }, { (char *)"AccessEA", test_prout_reserve_access_ea }, { (char *)"AccessWE", test_prout_reserve_access_we }, { (char *)"AccessEARO", test_prout_reserve_access_earo }, { (char *)"AccessWERO", test_prout_reserve_access_wero }, { (char *)"AccessEAAR", test_prout_reserve_access_eaar }, { (char *)"AccessWEAR", test_prout_reserve_access_wear }, { (char *)"OwnershipEA", test_prout_reserve_ownership_ea }, { (char *)"OwnershipWE", test_prout_reserve_ownership_we }, { (char *)"OwnershipEARO", test_prout_reserve_ownership_earo }, { (char *)"OwnershipWERO", test_prout_reserve_ownership_wero }, { (char *)"OwnershipEAAR", test_prout_reserve_ownership_eaar }, { (char *)"OwnershipWEAR", test_prout_reserve_ownership_wear }, CU_TEST_INFO_NULL }; static CU_TestInfo tests_prout_clear[] = { { (char *)"Simple", test_prout_clear_simple }, CU_TEST_INFO_NULL }; static CU_TestInfo tests_prout_preempt[] = { { (char *)"RemoveRegistration", test_prout_preempt_rm_reg }, CU_TEST_INFO_NULL }; static CU_TestInfo tests_prin_serviceaction_range[] = { { (char *)"Range", test_prin_serviceaction_range }, CU_TEST_INFO_NULL }; static CU_TestInfo tests_read6[] = { { (char *)"Simple", test_read6_simple }, { (char *)"BeyondEol", test_read6_beyond_eol }, CU_TEST_INFO_NULL }; static CU_TestInfo tests_read10[] = { { (char *)"Simple", test_read10_simple }, { (char *)"BeyondEol", test_read10_beyond_eol }, { (char *)"ZeroBlocks", test_read10_0blocks }, { (char *)"ReadProtect", test_read10_rdprotect }, { (char *)"DpoFua", test_read10_dpofua }, { (char *)"Async", test_async_read }, CU_TEST_INFO_NULL }; static CU_TestInfo tests_read12[] = { { (char *)"Simple", test_read12_simple }, { (char *)"BeyondEol", test_read12_beyond_eol }, { (char *)"ZeroBlocks", test_read12_0blocks }, { (char *)"ReadProtect", test_read12_rdprotect }, { (char *)"DpoFua", test_read12_dpofua }, CU_TEST_INFO_NULL }; static CU_TestInfo tests_read16[] = { { (char *)"Simple", test_read16_simple }, { (char *)"BeyondEol", test_read16_beyond_eol }, { (char *)"ZeroBlocks", test_read16_0blocks }, { (char *)"ReadProtect", test_read16_rdprotect }, { (char *)"DpoFua", test_read16_dpofua }, CU_TEST_INFO_NULL }; static CU_TestInfo tests_readcapacity10[] = { { (char *)"Simple", test_readcapacity10_simple }, CU_TEST_INFO_NULL }; static CU_TestInfo tests_readcapacity16[] = { { (char *)"Simple", test_readcapacity16_simple }, { (char *)"Alloclen", test_readcapacity16_alloclen }, { (char *)"PI", test_readcapacity16_protection }, { (char *)"Support", test_readcapacity16_support }, CU_TEST_INFO_NULL }; static CU_TestInfo tests_readonly[] = { { (char *)"ReadOnlySBC", test_readonly_sbc }, CU_TEST_INFO_NULL }; static CU_TestInfo tests_sanitize[] = { { (char *)"BlockErase", test_sanitize_block_erase }, { (char *)"BlockEraseReserved", test_sanitize_block_erase_reserved }, { (char *)"CryptoErase", test_sanitize_crypto_erase }, { (char *)"CryptoEraseReserved", test_sanitize_crypto_erase_reserved }, { (char *)"ExitFailureMode", test_sanitize_exit_failure_mode }, { (char *)"InvalidServiceAction", test_sanitize_invalid_serviceaction }, { (char *)"Overwrite", test_sanitize_overwrite }, { (char *)"OverwriteReserved", test_sanitize_overwrite_reserved }, { (char *)"Readonly", test_sanitize_readonly }, { (char *)"Reservations", test_sanitize_reservations }, { (char *)"Reset", test_sanitize_reset }, CU_TEST_INFO_NULL }; static CU_TestInfo tests_extended_copy[] = { { (char *)"Simple", test_extendedcopy_simple }, { (char *)"ParamHdr", test_extendedcopy_param }, { (char *)"DescrLimits", test_extendedcopy_descr_limits }, { (char *)"DescrType", test_extendedcopy_descr_type }, { (char *)"ValidTgtDescr", test_extendedcopy_validate_tgt_descr }, { (char *)"ValidSegDescr", test_extendedcopy_validate_seg_descr }, CU_TEST_INFO_NULL }; static CU_TestInfo tests_receive_copy_results[] = { { (char *)"CopyStatus", test_receive_copy_results_copy_status }, { (char *)"OpParams", test_receive_copy_results_op_params }, CU_TEST_INFO_NULL }; static CU_TestInfo tests_report_supported_opcodes[] = { { (char *)"Simple", test_report_supported_opcodes_simple }, { (char *)"OneCommand", test_report_supported_opcodes_one_command }, { (char *)"RCTD", test_report_supported_opcodes_rctd }, { (char *)"SERVACTV", test_report_supported_opcodes_servactv }, CU_TEST_INFO_NULL }; static CU_TestInfo tests_reserve6[] = { { (char *)"Simple", test_reserve6_simple }, { (char *)"2Initiators", test_reserve6_2initiators }, { (char *)"Logout", test_reserve6_logout }, { (char *)"ITNexusLoss", test_reserve6_itnexus_loss }, { (char *)"TargetColdReset", test_reserve6_target_cold_reset }, { (char *)"TargetWarmReset", test_reserve6_target_warm_reset }, { (char *)"LUNReset", test_reserve6_lun_reset }, CU_TEST_INFO_NULL }; static CU_TestInfo tests_testunitready[] = { { (char *)"Simple", test_testunitready_simple }, CU_TEST_INFO_NULL }; static CU_TestInfo tests_startstopunit[] = { { (char *)"Simple", test_startstopunit_simple }, { (char *)"PwrCnd", test_startstopunit_pwrcnd }, { (char *)"NoLoej", test_startstopunit_noloej }, CU_TEST_INFO_NULL }; static CU_TestInfo tests_unmap[] = { { (char *)"Simple", test_unmap_simple }, { (char *)"VPD", test_unmap_vpd }, { (char *)"ZeroBlocks", test_unmap_0blocks }, CU_TEST_INFO_NULL }; static CU_TestInfo tests_verify10[] = { { (char *)"Simple", test_verify10_simple }, { (char *)"BeyondEol", test_verify10_beyond_eol }, { (char *)"ZeroBlocks", test_verify10_0blocks }, { (char *)"VerifyProtect", test_verify10_vrprotect }, { (char *)"Flags", test_verify10_flags }, { (char *)"Dpo", test_verify10_dpo }, { (char *)"Mismatch", test_verify10_mismatch }, { (char *)"MismatchNoCmp", test_verify10_mismatch_no_cmp }, CU_TEST_INFO_NULL }; static CU_TestInfo tests_verify12[] = { { (char *)"Simple", test_verify12_simple }, { (char *)"BeyondEol", test_verify12_beyond_eol }, { (char *)"ZeroBlocks", test_verify12_0blocks }, { (char *)"VerifyProtect", test_verify12_vrprotect }, { (char *)"Flags", test_verify12_flags }, { (char *)"Dpo", test_verify12_dpo }, { (char *)"Mismatch", test_verify12_mismatch }, { (char *)"MismatchNoCmp", test_verify12_mismatch_no_cmp }, CU_TEST_INFO_NULL }; static CU_TestInfo tests_verify16[] = { { (char *)"Simple", test_verify16_simple }, { (char *)"BeyondEol", test_verify16_beyond_eol }, { (char *)"ZeroBlocks", test_verify16_0blocks }, { (char *)"VerifyProtect", test_verify16_vrprotect }, { (char *)"Flags", test_verify16_flags }, { (char *)"Dpo", test_verify16_dpo }, { (char *)"Mismatch", test_verify16_mismatch }, { (char *)"MismatchNoCmp", test_verify16_mismatch_no_cmp }, CU_TEST_INFO_NULL }; static CU_TestInfo tests_write10[] = { { (char *)"Simple", test_write10_simple }, { (char *)"BeyondEol", test_write10_beyond_eol }, { (char *)"ZeroBlocks", test_write10_0blocks }, { (char *)"WriteProtect", test_write10_wrprotect }, { (char *)"DpoFua", test_write10_dpofua }, { (char *)"Async", test_async_write }, CU_TEST_INFO_NULL }; static CU_TestInfo tests_write12[] = { { (char *)"Simple", test_write12_simple }, { (char *)"BeyondEol", test_write12_beyond_eol }, { (char *)"ZeroBlocks", test_write12_0blocks }, { (char *)"WriteProtect", test_write12_wrprotect }, { (char *)"DpoFua", test_write12_dpofua }, CU_TEST_INFO_NULL }; static CU_TestInfo tests_write16[] = { { (char *)"Simple", test_write16_simple }, { (char *)"BeyondEol", test_write16_beyond_eol }, { (char *)"ZeroBlocks", test_write16_0blocks }, { (char *)"WriteProtect", test_write16_wrprotect }, { (char *)"DpoFua", test_write16_dpofua }, CU_TEST_INFO_NULL }; static CU_TestInfo tests_writeatomic16[] = { { (char *)"Simple", test_writeatomic16_simple }, { (char *)"BeyondEol", test_writeatomic16_beyond_eol }, { (char *)"ZeroBlocks", test_writeatomic16_0blocks }, { (char *)"WriteProtect", test_writeatomic16_wrprotect }, { (char *)"DpoFua", test_writeatomic16_dpofua }, { (char *)"VPD", test_writeatomic16_vpd }, CU_TEST_INFO_NULL }; static CU_TestInfo tests_writesame10[] = { { (char *)"Simple", test_writesame10_simple }, { (char *)"BeyondEol", test_writesame10_beyond_eol }, { (char *)"ZeroBlocks", test_writesame10_0blocks }, { (char *)"WriteProtect", test_writesame10_wrprotect }, { (char *)"Unmap", test_writesame10_unmap }, { (char *)"UnmapUnaligned", test_writesame10_unmap_unaligned }, { (char *)"UnmapUntilEnd", test_writesame10_unmap_until_end }, { (char *)"UnmapVPD", test_writesame10_unmap_vpd }, CU_TEST_INFO_NULL }; static CU_TestInfo tests_writesame16[] = { { (char *)"Simple", test_writesame16_simple }, { (char *)"BeyondEol", test_writesame16_beyond_eol }, { (char *)"ZeroBlocks", test_writesame16_0blocks }, { (char *)"WriteProtect", test_writesame16_wrprotect }, { (char *)"Unmap", test_writesame16_unmap }, { (char *)"UnmapUnaligned", test_writesame16_unmap_unaligned }, { (char *)"UnmapUntilEnd", test_writesame16_unmap_until_end }, { (char *)"UnmapVPD", test_writesame16_unmap_vpd }, CU_TEST_INFO_NULL }; static CU_TestInfo tests_writeverify10[] = { { (char *)"Simple", test_writeverify10_simple }, { (char *)"BeyondEol", test_writeverify10_beyond_eol }, { (char *)"ZeroBlocks", test_writeverify10_0blocks }, { (char *)"WriteProtect", test_writeverify10_wrprotect }, { (char *)"Flags", test_writeverify10_flags }, { (char *)"Dpo", test_writeverify10_dpo }, CU_TEST_INFO_NULL }; static CU_TestInfo tests_writeverify12[] = { { (char *)"Simple", test_writeverify12_simple }, { (char *)"BeyondEol", test_writeverify12_beyond_eol }, { (char *)"ZeroBlocks", test_writeverify12_0blocks }, { (char *)"WriteProtect", test_writeverify12_wrprotect }, { (char *)"Flags", test_writeverify12_flags }, { (char *)"Dpo", test_writeverify12_dpo }, CU_TEST_INFO_NULL }; static CU_TestInfo tests_writeverify16[] = { { (char *)"Simple", test_writeverify16_simple }, { (char *)"BeyondEol", test_writeverify16_beyond_eol }, { (char *)"ZeroBlocks", test_writeverify16_0blocks }, { (char *)"WriteProtect", test_writeverify16_wrprotect }, { (char *)"Flags", test_writeverify16_flags }, { (char *)"Dpo", test_writeverify16_dpo }, CU_TEST_INFO_NULL }; static CU_TestInfo tests_multipathio[] = { { (char *)"Simple", test_multipathio_simple }, { (char *)"Reset", test_multipathio_reset }, { (char *)"CompareAndWrite", test_multipathio_compareandwrite }, { (char *)"CompareAndWriteAsync", test_mpio_async_caw }, CU_TEST_INFO_NULL }; typedef struct libiscsi_suite_info { const char *pName; /**< Suite name. */ CU_InitializeFunc pInitFunc; /**< Suite initialization function. */ CU_CleanupFunc pCleanupFunc; /**< Suite cleanup function */ CU_SetUpFunc pSetUpFunc; /**< Test setup function. */ CU_TearDownFunc pTearDownFunc; /**< Test tear down function */ CU_TestInfo *pTests; /**< Test case array - must be NULL terminated. */ } libiscsi_suite_info; #define NON_PGR_FUNCS suite_init, suite_cleanup, test_setup, test_teardown /* SCSI protocol tests */ static libiscsi_suite_info scsi_suites[] = { { "CompareAndWrite", NON_PGR_FUNCS, tests_compareandwrite }, { "ExtendedCopy", NON_PGR_FUNCS, tests_extended_copy }, { "GetLBAStatus", NON_PGR_FUNCS, tests_get_lba_status }, { "Inquiry", NON_PGR_FUNCS, tests_inquiry }, { "Mandatory", NON_PGR_FUNCS, tests_mandatory }, { "ModeSense6", NON_PGR_FUNCS, tests_modesense6 }, { "NoMedia", NON_PGR_FUNCS, tests_nomedia }, { "OrWrite", NON_PGR_FUNCS, tests_orwrite }, { "Prefetch10", NON_PGR_FUNCS, tests_prefetch10 }, { "Prefetch16", NON_PGR_FUNCS, tests_prefetch16 }, { "PreventAllow", NON_PGR_FUNCS, tests_preventallow }, { "PrinReadKeys", NON_PGR_FUNCS, tests_prin_read_keys }, { "PrinServiceactionRange", NON_PGR_FUNCS, tests_prin_serviceaction_range }, { "PrinReportCapabilities", NON_PGR_FUNCS, tests_prin_report_caps }, { "ProutRegister", NON_PGR_FUNCS, tests_prout_register }, { "ProutReserve", NON_PGR_FUNCS, tests_prout_reserve }, { "ProutClear", NON_PGR_FUNCS, tests_prout_clear }, { "ProutPreempt", NON_PGR_FUNCS, tests_prout_preempt }, { "Read6", NON_PGR_FUNCS, tests_read6 }, { "Read10", NON_PGR_FUNCS, tests_read10 }, { "Read12", NON_PGR_FUNCS, tests_read12 }, { "Read16", NON_PGR_FUNCS, tests_read16 }, { "ReadCapacity10", NON_PGR_FUNCS, tests_readcapacity10 }, { "ReadCapacity16", NON_PGR_FUNCS, tests_readcapacity16 }, { "ReadOnly", NON_PGR_FUNCS, tests_readonly }, { "ReceiveCopyResults", NON_PGR_FUNCS, tests_receive_copy_results }, { "ReportSupportedOpcodes", NON_PGR_FUNCS, tests_report_supported_opcodes }, { "Reserve6", NON_PGR_FUNCS, tests_reserve6 }, { "Sanitize", NON_PGR_FUNCS, tests_sanitize }, { "StartStopUnit", NON_PGR_FUNCS, tests_startstopunit }, { "TestUnitReady", NON_PGR_FUNCS, tests_testunitready }, { "Unmap", NON_PGR_FUNCS, tests_unmap }, { "Verify10", NON_PGR_FUNCS, tests_verify10 }, { "Verify12", NON_PGR_FUNCS, tests_verify12 }, { "Verify16", NON_PGR_FUNCS, tests_verify16 }, { "Write10", NON_PGR_FUNCS, tests_write10 }, { "Write12", NON_PGR_FUNCS, tests_write12 }, { "Write16", NON_PGR_FUNCS, tests_write16 }, { "WriteAtomic16", NON_PGR_FUNCS, tests_writeatomic16 }, { "WriteSame10", NON_PGR_FUNCS, tests_writesame10 }, { "WriteSame16", NON_PGR_FUNCS, tests_writesame16 }, { "WriteVerify10", NON_PGR_FUNCS, tests_writeverify10 }, { "WriteVerify12", NON_PGR_FUNCS, tests_writeverify12 }, { "WriteVerify16", NON_PGR_FUNCS, tests_writeverify16 }, { "MultipathIO", NON_PGR_FUNCS, tests_multipathio }, { NULL, NULL, NULL, NULL, NULL, NULL } }; static CU_TestInfo tests_iscsi_cmdsn[] = { { (char *)"iSCSICmdSnTooHigh", test_iscsi_cmdsn_toohigh }, { (char *)"iSCSICmdSnTooLow", test_iscsi_cmdsn_toolow }, CU_TEST_INFO_NULL }; static CU_TestInfo tests_iscsi_datasn[] = { { (char *)"iSCSIDataSnInvalid", test_iscsi_datasn_invalid }, CU_TEST_INFO_NULL }; static CU_TestInfo tests_iscsi_residuals[] = { { (char *)"Read10Invalid", test_read10_invalid }, { (char *)"Read10Residuals", test_read10_residuals }, { (char *)"Read12Residuals", test_read12_residuals }, { (char *)"Read16Residuals", test_read16_residuals }, { (char *)"Write10Residuals", test_write10_residuals }, { (char *)"Write12Residuals", test_write12_residuals }, { (char *)"Write16Residuals", test_write16_residuals }, { (char *)"WriteVerify10Residuals", test_writeverify10_residuals }, { (char *)"WriteVerify12Residuals", test_writeverify12_residuals }, { (char *)"WriteVerify16Residuals", test_writeverify16_residuals }, CU_TEST_INFO_NULL }; static CU_TestInfo tests_iscsi_tmf[] = { { (char *)"AbortTaskSimpleAsync", test_async_abort_simple }, CU_TEST_INFO_NULL }; /* iSCSI protocol tests */ static libiscsi_suite_info iscsi_suites[] = { { "iSCSIcmdsn", NON_PGR_FUNCS, tests_iscsi_cmdsn }, { "iSCSIdatasn", NON_PGR_FUNCS, tests_iscsi_datasn }, { "iSCSIResiduals", NON_PGR_FUNCS, tests_iscsi_residuals }, { "iSCSITMF", NON_PGR_FUNCS, tests_iscsi_tmf }, { NULL, NULL, NULL, NULL, NULL, NULL } }; /* All tests */ static libiscsi_suite_info all_suites[] = { { "CompareAndWrite", NON_PGR_FUNCS, tests_compareandwrite }, { "ExtendedCopy", NON_PGR_FUNCS, tests_extended_copy }, { "GetLBAStatus", NON_PGR_FUNCS, tests_get_lba_status }, { "Inquiry", NON_PGR_FUNCS, tests_inquiry }, { "Mandatory", NON_PGR_FUNCS, tests_mandatory }, { "ModeSense6", NON_PGR_FUNCS, tests_modesense6 }, { "NoMedia", NON_PGR_FUNCS, tests_nomedia }, { "OrWrite", NON_PGR_FUNCS, tests_orwrite }, { "Prefetch10", NON_PGR_FUNCS, tests_prefetch10 }, { "Prefetch16", NON_PGR_FUNCS, tests_prefetch16 }, { "PreventAllow", NON_PGR_FUNCS, tests_preventallow }, { "PrinReadKeys", NON_PGR_FUNCS, tests_prin_read_keys }, { "PrinServiceactionRange", NON_PGR_FUNCS, tests_prin_serviceaction_range }, { "PrinReportCapabilities", NON_PGR_FUNCS, tests_prin_report_caps }, { "ProutRegister", NON_PGR_FUNCS, tests_prout_register }, { "ProutReserve", NON_PGR_FUNCS, tests_prout_reserve }, { "ProutClear", NON_PGR_FUNCS, tests_prout_clear }, { "ProutPreempt", NON_PGR_FUNCS, tests_prout_preempt }, { "Read6", NON_PGR_FUNCS, tests_read6 }, { "Read10", NON_PGR_FUNCS, tests_read10 }, { "Read12", NON_PGR_FUNCS, tests_read12 }, { "Read16", NON_PGR_FUNCS, tests_read16 }, { "ReadCapacity10", NON_PGR_FUNCS, tests_readcapacity10 }, { "ReadCapacity16", NON_PGR_FUNCS, tests_readcapacity16 }, { "ReadOnly", NON_PGR_FUNCS, tests_readonly }, { "ReceiveCopyResults", NON_PGR_FUNCS, tests_receive_copy_results }, { "ReportSupportedOpcodes", NON_PGR_FUNCS, tests_report_supported_opcodes }, { "Reserve6", NON_PGR_FUNCS, tests_reserve6 }, { "Sanitize", NON_PGR_FUNCS, tests_sanitize }, { "StartStopUnit", NON_PGR_FUNCS, tests_startstopunit }, { "TestUnitReady", NON_PGR_FUNCS, tests_testunitready }, { "Unmap", NON_PGR_FUNCS, tests_unmap }, { "Verify10", NON_PGR_FUNCS, tests_verify10 }, { "Verify12", NON_PGR_FUNCS, tests_verify12 }, { "Verify16", NON_PGR_FUNCS, tests_verify16 }, { "Write10", NON_PGR_FUNCS, tests_write10 }, { "Write12", NON_PGR_FUNCS, tests_write12 }, { "Write16", NON_PGR_FUNCS, tests_write16 }, { "WriteAtomic16", NON_PGR_FUNCS, tests_writeatomic16 }, { "WriteSame10", NON_PGR_FUNCS, tests_writesame10 }, { "WriteSame16", NON_PGR_FUNCS, tests_writesame16 }, { "WriteVerify10", NON_PGR_FUNCS, tests_writeverify10 }, { "WriteVerify12", NON_PGR_FUNCS, tests_writeverify12 }, { "WriteVerify16", NON_PGR_FUNCS, tests_writeverify16 }, { "iSCSIcmdsn", NON_PGR_FUNCS, tests_iscsi_cmdsn }, { "iSCSIdatasn", NON_PGR_FUNCS, tests_iscsi_datasn }, { "iSCSIResiduals", NON_PGR_FUNCS, tests_iscsi_residuals }, { "iSCSITMF", NON_PGR_FUNCS, tests_iscsi_tmf }, { "MultipathIO", NON_PGR_FUNCS, tests_multipathio }, { NULL, NULL, NULL, NULL, NULL, NULL }, }; static libiscsi_suite_info linux_suites[] = { { "CompareAndWrite", NON_PGR_FUNCS, tests_compareandwrite }, { "GetLBAStatus", NON_PGR_FUNCS, tests_get_lba_status }, { "Inquiry", NON_PGR_FUNCS, tests_inquiry }, { "Mandatory", NON_PGR_FUNCS, tests_mandatory }, { "ModeSense6", NON_PGR_FUNCS, tests_modesense6 }, { "OrWrite", NON_PGR_FUNCS, tests_orwrite }, { "Prefetch10", NON_PGR_FUNCS, tests_prefetch10 }, { "Prefetch16", NON_PGR_FUNCS, tests_prefetch16 }, { "Read10", NON_PGR_FUNCS, tests_read10 }, { "Read12", NON_PGR_FUNCS, tests_read12 }, { "Read16", NON_PGR_FUNCS, tests_read16 }, { "ReadCapacity10", NON_PGR_FUNCS, tests_readcapacity10 }, { "ReadCapacity16", NON_PGR_FUNCS, tests_readcapacity16 }, { "ReadOnly", NON_PGR_FUNCS, tests_readonly }, { "ReportSupportedOpcodes", NON_PGR_FUNCS, tests_report_supported_opcodes }, { "TestUnitReady", NON_PGR_FUNCS, tests_testunitready }, { "Unmap", NON_PGR_FUNCS, tests_unmap }, { "Verify10", NON_PGR_FUNCS, tests_verify10 }, { "Verify12", NON_PGR_FUNCS, tests_verify12 }, { "Verify16", NON_PGR_FUNCS, tests_verify16 }, { "Write10", NON_PGR_FUNCS, tests_write10 }, { "Write12", NON_PGR_FUNCS, tests_write12 }, { "Write16", NON_PGR_FUNCS, tests_write16 }, { "WriteAtomic16", NON_PGR_FUNCS, tests_writeatomic16 }, { "WriteSame10", NON_PGR_FUNCS, tests_writesame10 }, { "WriteSame16", NON_PGR_FUNCS, tests_writesame16 }, { "WriteVerify10", NON_PGR_FUNCS, tests_writeverify10 }, { "WriteVerify12", NON_PGR_FUNCS, tests_writeverify12 }, { "WriteVerify16", NON_PGR_FUNCS, tests_writeverify16 }, { "MultipathIO", NON_PGR_FUNCS, tests_multipathio }, { NULL, NULL, NULL, NULL, NULL, NULL }, }; struct test_family { const char *name; libiscsi_suite_info *suites; }; static struct test_family families[] = { { "ALL", all_suites }, { "SCSI", scsi_suites }, { "iSCSI", iscsi_suites }, { "LINUX", linux_suites }, { NULL, NULL} }; /* * globals for test setup and teardown */ struct scsi_task *task; unsigned char *read_write_buf; static void print_usage(void) { fprintf(stderr, "Usage: %s [-?|--help] print this message and exit\n", PROG); fprintf(stderr, "or %s [OPTIONS] [multipath-iscsi-url]\n", PROG); fprintf(stderr, "Where OPTIONS are from:\n"); fprintf(stderr, " -i|--initiator-name=iqn-name Initiatorname to use [%s]\n", initiatorname1); fprintf(stderr, " -I|--initiator-name-2=iqn-name 2nd Initiatorname to use [%s]\n", initiatorname2); fprintf(stderr, " -t|--test=test-name-reg-exp Test(s) to run [ALL] \n"); fprintf(stderr, " -l|--list List all tests and exit\n"); fprintf(stderr, " -d|--dataloss Allow destructive tests\n"); fprintf(stderr, " -S|--allow-sanitize Allow sanitize-opcode tests\n"); fprintf(stderr, " -g|--ignore Error Action: Ignore test errors [DEFAULT]\n"); fprintf(stderr, " -f|--fail Error Action: FAIL if any tests fail\n"); fprintf(stderr, " -A|--abort Error Action: ABORT if any tests fail\n"); fprintf(stderr, " -s|--silent Test Mode: Silent\n"); fprintf(stderr, " -n|--normal Test Mode: Normal\n"); fprintf(stderr, " -v|--verbose Test Mode: Verbose [DEFAULT]\n"); fprintf(stderr, " -x|--xml Test Mode: XML\n"); fprintf(stderr, " -V|--Verbose-scsi Enable verbose SCSI logging [default SILENT]\n"); fprintf(stderr, "\n"); fprintf(stderr, "Where iSCSI URL format is: %s\n", ISCSI_URL_SYNTAX); fprintf(stderr, "\n"); fprintf(stderr, " is one of:\n"); fprintf(stderr, " \"hostname\" e.g. iscsi.example\n"); fprintf(stderr, " \"ipv4-address\" e.g. 10.1.1.27\n"); fprintf(stderr, " \"ipv6-address\" e.g. [fce0::1]\n"); fprintf(stderr, "\n"); fprintf(stderr, "and is of the form: FAMILY[.SUITE[.TEST]]\n"); fprintf(stderr, "\n"); } void test_setup(void) { task = NULL; read_write_buf = NULL; } void test_teardown(void) { free(read_write_buf); read_write_buf = NULL; scsi_free_scsi_task(task); task = NULL; } int suite_init(void) { int i; char const *initiatornames[MPATH_MAX_DEVS] = { initiatorname1, initiatorname2 }; for (i = 0; i < mp_num_sds; i++) { if (!mp_sds[i]->iscsi_url) { continue; } if (mp_sds[i]->iscsi_ctx) { iscsi_logout_sync(mp_sds[i]->iscsi_ctx); iscsi_destroy_context(mp_sds[i]->iscsi_ctx); } mp_sds[i]->iscsi_ctx = iscsi_context_login(initiatornames[i], mp_sds[i]->iscsi_url, &mp_sds[i]->iscsi_lun); if (mp_sds[i]->iscsi_ctx == NULL) { fprintf(stderr, "error: Failed to login to target for test set-up\n"); return 1; } iscsi_set_no_ua_on_reconnect(mp_sds[i]->iscsi_ctx, 1); } #ifndef HAVE_CU_SUITEINFO_PSETUPFUNC /* libcunit version 1 */ test_setup(); #endif return 0; } int suite_cleanup(void) { int i; #ifndef HAVE_CU_SUITEINFO_PSETUPFUNC /* libcunit version 1 */ test_teardown(); #endif for (i = 0; i < mp_num_sds; i++) { if (mp_sds[i]->iscsi_url) { if (mp_sds[i]->iscsi_ctx) { iscsi_logout_sync(mp_sds[i]->iscsi_ctx); iscsi_destroy_context(mp_sds[i]->iscsi_ctx); mp_sds[i]->iscsi_ctx = NULL; } } } return 0; } static void list_all_tests(void) { struct test_family *fp; libiscsi_suite_info *sp; CU_TestInfo *tp; for (fp = families; fp->name; fp++) { printf("%s\n", fp->name); for (sp = fp->suites; sp->pName != NULL; sp++) { printf("%s.%s\n", fp->name,sp->pName); for (tp = sp->pTests; tp->pName != NULL; tp++) { printf("%s.%s.%s\n", fp->name,sp->pName, tp->pName); } } } } static CU_ErrorCode add_tests(const char *testname_re) { char *family_re = NULL; char *suite_re = NULL; char *test_re = NULL; char *cp; struct test_family *fp; libiscsi_suite_info *sp; CU_TestInfo *tp; /* if not testname(s) register all tests */ if (!testname_re) { family_re = strdup("*"); suite_re = strdup("*"); test_re = strdup("*"); } else { /* * break testname_re into family/suite/test * * syntax is: FAMILY[.SUITE[.TEST]] */ family_re = strdup(testname_re); if ((cp = strchr(family_re, '.')) != NULL) { *cp++ = 0; suite_re = strdup(cp); if ((cp = strchr(suite_re, '.')) != NULL) { *cp++ = 0; test_re = strdup(cp); } } if (!suite_re) suite_re = strdup("*"); if (!test_re) test_re = strdup("*"); if (!family_re) { fprintf(stderr, "error: can't parse test family name: %s\n", family_re); return CUE_NOTEST; } } /* * cycle through the test families/suites/tests, adding * ones that match */ for (fp = families; fp->name; fp++) { if (fnmatch(family_re, fp->name, 0) != 0) continue; for (sp = fp->suites; sp->pName != NULL; sp++) { int suite_added = 0; CU_pSuite pSuite = NULL; if (fnmatch(suite_re, sp->pName, 0) != 0) continue; for (tp = sp->pTests; tp->pName != NULL; tp++) { if (fnmatch(test_re, tp->pName, 0) != 0) { continue; } if (!suite_added) { suite_added++; #ifdef HAVE_CU_SUITEINFO_PSETUPFUNC pSuite = CU_add_suite_with_setup_and_teardown(sp->pName, sp->pInitFunc, sp->pCleanupFunc, sp->pSetUpFunc, sp->pTearDownFunc); #else pSuite = CU_add_suite(sp->pName, sp->pInitFunc, sp->pCleanupFunc); #endif } CU_add_test(pSuite, tp->pName, tp->pTestFunc); } } } /* all done -- clean up */ free(family_re); free(suite_re); free(test_re); return CUE_SUCCESS; } static void parse_and_add_tests(char *testname_re); static void parse_and_add_test(const char *test) { if (test && access(test, F_OK) == 0) { FILE *fh; char t[256]; if ((fh = fopen(test, "r")) == NULL) { printf("Failed to open test-list file %s\n", test); exit(10); } while (fgets(t, sizeof(t), fh) != NULL) { while (1) { int len = strlen(t); if (len == 0) { break; } if (!isprint(t[--len])) { t[len] = 0; continue; } break; } parse_and_add_tests(t); } fclose(fh); return; } if (add_tests(test) != CUE_SUCCESS) { fprintf(stderr, "error: suite registration failed: %s\n", CU_get_error_msg()); exit(1); } } static void parse_and_add_tests(char *testname_re) { if (testname_re != NULL) { char *testname; while ((testname = strrchr(testname_re, ',')) != NULL) { parse_and_add_test(testname + 1); *testname = 0; } } parse_and_add_test(testname_re); } static int connect_scsi_device(struct scsi_device *sdev, const char *initiatorname) { if (sdev->iscsi_url) { sdev->iscsi_ctx = iscsi_context_login(initiatorname, sdev->iscsi_url, &sdev->iscsi_lun); if (sdev->iscsi_ctx == NULL) { return -1; } iscsi_set_no_ua_on_reconnect(sdev->iscsi_ctx, 1); return 0; } #ifdef HAVE_SG_IO if (sdev->sgio_dev) { int version; if ((sdev->sgio_fd = open(sdev->sgio_dev, O_RDWR|O_NONBLOCK)) == -1) { fprintf(stderr, "Failed to open SG_IO device %s. Error:%s\n", sdev->sgio_dev, strerror(errno)); return -1; } if ((ioctl(sdev->sgio_fd, SG_GET_VERSION_NUM, &version) < 0) || (version < 30000)) { fprintf(stderr, "%s is not a SCSI device node\n", sdev->sgio_dev); close(sdev->sgio_fd); return -1; } if (ioctl(sdev->sgio_fd, BLKSECTGET, &maxsectors) < 0) { fprintf(stderr, "%s failed to read BLKMAXSECT\n", sdev->sgio_dev); close(sdev->sgio_fd); return -1; } return 0; } #endif return -1; } static void free_scsi_device(struct scsi_device *sdev) { if (sdev->error_str) { free(sdev->error_str); sdev->error_str = NULL; } if (sdev->iscsi_url) { free(sdev->iscsi_url); sdev->iscsi_url = NULL; } if (sdev->iscsi_ctx) { iscsi_logout_sync(sdev->iscsi_ctx); iscsi_destroy_context(sdev->iscsi_ctx); sdev->iscsi_ctx = NULL; } if (sdev->sgio_dev) { free(sdev->sgio_dev); sdev->sgio_dev = NULL; } if (sdev->sgio_fd != -1) { close(sdev->sgio_fd); sdev->sgio_fd = -1; } free(sdev); } /* Clear persistent reservations and reservation keys left by a previous run */ static int clear_pr(struct scsi_device *sdev) { int i, res; struct scsi_task *pr_task; struct scsi_persistent_reserve_in_read_keys *rk; res = 0; if (prin_read_keys(sdev, &pr_task, &rk) != 0) goto out; res = -1; if (rk->num_keys && data_loss == 0) goto out; res = 0; for (i = 0; i < rk->num_keys; i++) { prout_register_and_ignore(sdev, rk->keys[i]); prout_register_key(sdev, 0, rk->keys[i]); } scsi_free_scsi_task(pr_task); out: return res; } int main(int argc, char *argv[]) { char *testname_re = NULL; CU_BasicRunMode mode = CU_BRM_VERBOSE; CU_ErrorAction error_action = CUEA_IGNORE; int res; struct scsi_readcapacity10 *rc10; struct scsi_task *inq_task = NULL; struct scsi_task *inq_lbp_task = NULL; struct scsi_task *inq_bdc_task = NULL; struct scsi_task *inq_bl_task = NULL; struct scsi_task *rc16_task = NULL; struct scsi_task *rsop_task = NULL; int full_size; int xml_mode = 0; static struct option long_opts[] = { { "help", no_argument, 0, '?' }, { "list", no_argument, 0, 'l' }, { "initiator-name", required_argument, 0, 'i' }, { "initiator-name-2", required_argument, 0, 'I' }, { "test", required_argument, 0, 't' }, { "dataloss", no_argument, 0, 'd' }, { "allow-sanitize", no_argument, 0, 'S' }, { "ignore", no_argument, 0, 'g' }, { "fail", no_argument, 0, 'f' }, { "abort", no_argument, 0, 'A' }, { "silent", no_argument, 0, 's' }, { "normal", no_argument, 0, 'n' }, { "verbose", no_argument, 0, 'v' }, { "xml", no_argument, 0, 'x' }, { "Verbose-scsi", no_argument, 0, 'V' }, { NULL, 0, 0, 0 } }; int i, c; int opt_idx = 0; unsigned int failures = 0; int ret; while ((c = getopt_long(argc, argv, "?hli:I:t:sdgfAsSnvxV", long_opts, &opt_idx)) > 0) { switch (c) { case 'h': case '?': print_usage(); return 0; case 'l': list_all_tests(); return 0; case 'i': initiatorname1 = strdup(optarg); break; case 'I': initiatorname2 = strdup(optarg); break; case 't': testname_re = strdup(optarg); break; case 'd': data_loss++; break; case 'g': error_action = CUEA_IGNORE; /* default */ break; case 'f': error_action = CUEA_FAIL; break; case 'A': error_action = CUEA_ABORT; break; case 's': mode = CU_BRM_SILENT; break; case 'S': allow_sanitize = 1; break; case 'n': mode = CU_BRM_NORMAL; break; case 'v': mode = CU_BRM_VERBOSE; /* default */ break; case 'x': xml_mode = 1; break; case 'V': loglevel = LOG_VERBOSE; break; default: fprintf(stderr, "error: unknown option return: %c (option %s)\n", c, argv[optind]); return 1; } } /* parse all trailing arguments as device paths */ mp_num_sds = 0; while (optind < argc) { if (mp_num_sds >= MPATH_MAX_DEVS) { fprintf(stderr, "Too many multipath device URLs\n"); print_usage(); free(testname_re); return 10; } mp_sds[mp_num_sds] = malloc(sizeof(struct scsi_device)); memset(mp_sds[mp_num_sds], '\0', sizeof(struct scsi_device)); mp_sds[mp_num_sds]->sgio_fd = -1; if (!strncmp(argv[optind], "iscsi://", 8)) { mp_sds[mp_num_sds]->iscsi_url = strdup(argv[optind++]); #ifdef HAVE_SG_IO } else { mp_sds[mp_num_sds]->sgio_dev = strdup(argv[optind++]); #endif } mp_num_sds++; } /* So that we can override iscsi_queue_pdu in tests * and replace or mutate the blob that we are about to write to the * wire. * This allows such tests to do their mutates and then call out * to the real queueing function once they have modified the data. */ real_iscsi_queue_pdu = dlsym(RTLD_NEXT, "iscsi_queue_pdu"); if ((mp_num_sds == 0) || (mp_sds[0]->iscsi_url == NULL && mp_sds[0]->sgio_dev == NULL)) { #ifdef HAVE_SG_IO fprintf(stderr, "You must specify either an iSCSI URL or a device file\n"); #else fprintf(stderr, "You must specify an iSCSI URL\n"); #endif print_usage(); if (testname_re) free(testname_re); return 10; } /* sd remains an alias for the first device */ sd = mp_sds[0]; for (i = 0; i < mp_num_sds; i++) { res = connect_scsi_device(mp_sds[i], initiatorname1); if (res < 0) { fprintf(stderr, "Failed to connect to SCSI device %d\n", i); goto err_sds_free; } } if (mp_num_sds > 1) { /* check that all multipath sds identify as the same LU */ res = mpath_check_matching_ids(mp_num_sds, mp_sds); if (res < 0) { fprintf(stderr, "multipath devices don't match\n"); goto err_sds_free; } } /* * find the size of the LUN * All devices support readcapacity10 but only some support * readcapacity16 */ task = NULL; readcapacity10(sd, &task, 0, 0, EXPECT_STATUS_GOOD); if (task == NULL) { printf("Failed to send READCAPACITY10 command: %s\n", sd->error_str); goto err_sds_free; } if (task->status != SCSI_STATUS_GOOD) { printf("READCAPACITY10 command: failed with sense. %s\n", sd->error_str); scsi_free_scsi_task(task); goto err_sds_free; } rc10 = scsi_datain_unmarshall(task); if (rc10 == NULL) { printf("failed to unmarshall READCAPACITY10 data.\n"); scsi_free_scsi_task(task); goto err_sds_free; } block_size = rc10->block_size; num_blocks = rc10->lba + 1; scsi_free_scsi_task(task); rc16_task = NULL; readcapacity16(sd, &rc16_task, 96, EXPECT_STATUS_GOOD); if (rc16_task == NULL) { printf("Failed to send READCAPACITY16 command: %s\n", sd->error_str); goto err_sds_free; } if (rc16_task->status == SCSI_STATUS_GOOD) { rc16 = scsi_datain_unmarshall(rc16_task); if (rc16 == NULL) { printf("failed to unmarshall READCAPACITY16 data. %s\n", sd->error_str); scsi_free_scsi_task(rc16_task); goto err_sds_free; } block_size = rc16->block_length; num_blocks = rc16->returned_lba + 1; lbppb = 1 << rc16->lbppbe; } /* create a really big buffer we can use in the tests */ scratch = malloc(65536 * block_size); inq_task = NULL; inquiry(sd, &inq_task, 0, 0, 64, EXPECT_STATUS_GOOD); if (inq_task == NULL || inq_task->status != SCSI_STATUS_GOOD) { printf("Inquiry command failed : %s\n", sd->error_str); goto err_sds_free; } full_size = scsi_datain_getfullsize(inq_task); if (full_size > inq_task->datain.size) { scsi_free_scsi_task(inq_task); /* we need more data for the full list */ inq_task = NULL; inquiry(sd, &inq_task, 0, 0, full_size, EXPECT_STATUS_GOOD); if (inq_task == NULL) { printf("Inquiry command failed : %s\n", sd->error_str); goto err_sds_free; } } inq = scsi_datain_unmarshall(inq_task); if (inq == NULL) { printf("failed to unmarshall inquiry datain blob\n"); scsi_free_scsi_task(inq_task); goto err_sds_free; } sbc3_support = 0; for (i = 0; i < 8; i++) { if (inq->version_descriptor[i] == 0x04C0) { sbc3_support = 1; } } /* try reading block limits vpd */ inq_bl_task = NULL; inquiry(sd, &inq_bl_task, 1, SCSI_INQUIRY_PAGECODE_BLOCK_LIMITS, 64, EXPECT_STATUS_GOOD); if (inq_bl_task && inq_bl_task->status != SCSI_STATUS_GOOD) { scsi_free_scsi_task(inq_bl_task); inq_bl_task = NULL; } if (inq_bl_task) { full_size = scsi_datain_getfullsize(inq_bl_task); if (full_size > inq_bl_task->datain.size) { scsi_free_scsi_task(inq_bl_task); inq_bl_task = NULL; inquiry(sd, &inq_bl_task, 1, SCSI_INQUIRY_PAGECODE_BLOCK_LIMITS, full_size, EXPECT_STATUS_GOOD); if (inq_bl_task == NULL) { printf("Inquiry command failed : %s\n", sd->error_str); goto err_sds_free; } } inq_bl = scsi_datain_unmarshall(inq_bl_task); if (inq_bl == NULL) { printf("failed to unmarshall inquiry datain blob\n"); goto err_sds_free; } } /* try reading block device characteristics vpd */ inquiry(sd, &inq_bdc_task, 1, SCSI_INQUIRY_PAGECODE_BLOCK_DEVICE_CHARACTERISTICS, 255, EXPECT_STATUS_GOOD); if (inq_bdc_task == NULL || inq_bdc_task->status != SCSI_STATUS_GOOD) { printf("Failed to read Block Device Characteristics page\n"); } else { inq_bdc = scsi_datain_unmarshall(inq_bdc_task); if (inq_bdc == NULL) { printf("failed to unmarshall inquiry datain blob\n"); goto err_sds_free; } } /* if thin provisioned we also need to read the VPD page for it */ if (rc16 && rc16->lbpme != 0){ inq_lbp_task = NULL; inquiry(sd, &inq_lbp_task, 1, SCSI_INQUIRY_PAGECODE_LOGICAL_BLOCK_PROVISIONING, 64, EXPECT_STATUS_GOOD); if (inq_lbp_task == NULL || inq_lbp_task->status != SCSI_STATUS_GOOD) { printf("Inquiry command failed : %s\n", sd->error_str); goto err_sds_free; } full_size = scsi_datain_getfullsize(inq_lbp_task); if (full_size > inq_lbp_task->datain.size) { scsi_free_scsi_task(inq_lbp_task); /* we need more data for the full list */ inq_lbp_task = NULL; inquiry(sd, &inq_lbp_task, 1, SCSI_INQUIRY_PAGECODE_LOGICAL_BLOCK_PROVISIONING, full_size, EXPECT_STATUS_GOOD); if (inq_lbp_task == NULL) { printf("Inquiry command failed : %s\n", sd->error_str); goto err_sds_free; } } inq_lbp = scsi_datain_unmarshall(inq_lbp_task); if (inq_lbp == NULL) { printf("failed to unmarshall inquiry datain blob\n"); goto err_sds_free; } } rsop_task = NULL; report_supported_opcodes(sd, &rsop_task, 1, SCSI_REPORT_SUPPORTING_OPS_ALL, 0, 0, 65535, EXPECT_STATUS_GOOD); if (rsop_task == NULL) { printf("Failed to send REPORT_SUPPORTED_OPCODES command: %s\n", sd->error_str); goto err_sds_free; } if (rsop_task->status == SCSI_STATUS_GOOD) { rsop = scsi_datain_unmarshall(rsop_task); if (rsop == NULL) { printf("failed to unmarshall REPORT_SUPPORTED_OPCODES data.\n"); scsi_free_scsi_task(rsop_task); rsop_task = NULL; } } /* check if the device is write protected or not */ task = NULL; modesense6(sd, &task, 0, SCSI_MODESENSE_PC_CURRENT, SCSI_MODEPAGE_RETURN_ALL_PAGES, 0, 255, EXPECT_STATUS_GOOD); if (task == NULL) { printf("Failed to send MODE_SENSE6 command: %s\n", sd->error_str); goto err_sds_free; } if (task->status == SCSI_STATUS_GOOD) { struct scsi_mode_sense *ms; ms = scsi_datain_unmarshall(task); if (ms == NULL) { printf("failed to unmarshall mode sense datain blob\n"); scsi_free_scsi_task(task); goto err_sds_free; } readonly = !!(ms->device_specific_parameter & 0x80); } scsi_free_scsi_task(task); if (clear_pr(sd) < 0) { printf("One or more persistent reservations keys have been registered\n"); return -1; } if (maxsectors) { maximum_transfer_length = maxsectors; printf("Bus transfer size is limited to %d blocks. Clamping " "max transfers accordingly.\n", maxsectors); } if (CU_initialize_registry() != 0) { fprintf(stderr, "error: unable to initialize test registry\n"); goto err_sds_free; } if (CU_is_test_running()) { fprintf(stderr, "error: test suite(s) already running!?\n"); exit(1); } parse_and_add_tests(testname_re); if (testname_re) free(testname_re); CU_basic_set_mode(mode); CU_set_error_action(error_action); printf("\n"); /* * this actually runs the tests ... */ if (xml_mode) { CU_list_tests_to_file(); CU_automated_run_tests(); } else { res = CU_basic_run_tests(); printf("Tests completed with return value: %d\n", res); } failures = CU_get_number_of_failures(); CU_cleanup_registry(); if (inq_task != NULL) { scsi_free_scsi_task(inq_task); } if (inq_bl_task != NULL) { scsi_free_scsi_task(inq_bl_task); } if (inq_lbp_task != NULL) { scsi_free_scsi_task(inq_lbp_task); } if (inq_bdc_task != NULL) { scsi_free_scsi_task(inq_bdc_task); } if (rc16_task != NULL) { scsi_free_scsi_task(rc16_task); } if (rsop_task != NULL) { scsi_free_scsi_task(rsop_task); } for (i = 0; i < mp_num_sds; i++) { free_scsi_device(mp_sds[i]); } free(scratch); if (failures > 0) { ret = 1; } else { ret = 0; } return ret; err_sds_free: for (i = 0; i < mp_num_sds; i++) { free_scsi_device(mp_sds[i]); } free(scratch); return -1; } libiscsi-1.17.0/test-tool/iscsi-test-cu.h000066400000000000000000000231131271371262700202100ustar00rootroot00000000000000/* iscsi-test tool Copyright (C) 2012 by Lee Duncan 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, see . */ #ifndef _ISCSI_TEST_CU_H_ #define _ISCSI_TEST_CU_H_ #include #include #include #include #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "iscsi-support.h" /* globals between setup, tests, and teardown */ extern struct scsi_task *task; extern unsigned char *read_write_buf; #ifndef HAVE_CU_SUITEINFO_PSETUPFUNC /* libcunit version 1 */ typedef void (*CU_SetUpFunc)(void); typedef void (*CU_TearDownFunc)(void); #endif int suite_init(void); int suite_cleanup(void); int suite_init_pgr(void); int suite_cleanup_pgr(void); void test_setup(void); void test_teardown(void); void test_compareandwrite_simple(void); void test_compareandwrite_dpofua(void); void test_compareandwrite_miscompare(void); void test_extendedcopy_simple(void); void test_extendedcopy_param(void); void test_extendedcopy_descr_limits(void); void test_extendedcopy_descr_type(void); void test_extendedcopy_validate_tgt_descr(void); void test_extendedcopy_validate_seg_descr(void); void test_get_lba_status_simple(void); void test_get_lba_status_beyond_eol(void); void test_get_lba_status_unmap_single(void); void test_inquiry_alloc_length(void); void test_inquiry_block_limits(void); void test_inquiry_evpd(void); void test_inquiry_mandatory_vpd_sbc(void); void test_inquiry_standard(void); void test_inquiry_supported_vpd(void); void test_inquiry_version_descriptors(void); void test_iscsi_cmdsn_toohigh(void); void test_iscsi_cmdsn_toolow(void); void test_iscsi_datasn_invalid(void); void test_mandatory_sbc(void); void test_modesense6_all_pages(void); void test_modesense6_control(void); void test_modesense6_control_d_sense(void); void test_modesense6_control_swp(void); void test_modesense6_residuals(void); void test_nomedia_sbc(void); void test_orwrite_simple(void); void test_orwrite_beyond_eol(void); void test_orwrite_0blocks(void); void test_orwrite_wrprotect(void); void test_orwrite_dpofua(void); void test_orwrite_verify(void); void test_prefetch10_simple(void); void test_prefetch10_beyond_eol(void); void test_prefetch10_0blocks(void); void test_prefetch10_flags(void); void test_prefetch16_simple(void); void test_prefetch16_beyond_eol(void); void test_prefetch16_0blocks(void); void test_prefetch16_flags(void); void test_preventallow_simple(void); void test_preventallow_eject(void); void test_preventallow_itnexus_loss(void); void test_preventallow_logout(void); void test_preventallow_warm_reset(void); void test_preventallow_cold_reset(void); void test_preventallow_lun_reset(void); void test_preventallow_2_itnexuses(void); void test_prin_read_keys_simple(void); void test_prin_serviceaction_range(void); void test_prin_report_caps_simple(void); void test_prout_register_simple(void); void test_prout_reserve_simple(void); void test_prout_reserve_access_ea(void); void test_prout_reserve_access_we(void); void test_prout_reserve_access_earo(void); void test_prout_reserve_access_wero(void); void test_prout_reserve_access_eaar(void); void test_prout_reserve_access_wear(void); void test_prout_reserve_ownership_ea(void); void test_prout_reserve_ownership_we(void); void test_prout_reserve_ownership_earo(void); void test_prout_reserve_ownership_wero(void); void test_prout_reserve_ownership_eaar(void); void test_prout_reserve_ownership_wear(void); void test_prout_clear_simple(void); void test_prout_preempt_rm_reg(void); void test_read6_simple(void); void test_read6_beyond_eol(void); void test_read6_rdprotect(void); void test_read6_flags(void); void test_read10_simple(void); void test_read10_beyond_eol(void); void test_read10_0blocks(void); void test_read10_rdprotect(void); void test_read10_dpofua(void); void test_read10_residuals(void); void test_read10_invalid(void); void test_async_read(void); void test_read12_simple(void); void test_read12_beyond_eol(void); void test_read12_0blocks(void); void test_read12_rdprotect(void); void test_read12_dpofua(void); void test_read12_residuals(void); void test_read16_simple(void); void test_read16_beyond_eol(void); void test_read16_0blocks(void); void test_read16_rdprotect(void); void test_read16_dpofua(void); void test_read16_residuals(void); void test_readcapacity10_simple(void); void test_readcapacity16_alloclen(void); void test_readcapacity16_protection(void); void test_readcapacity16_simple(void); void test_readcapacity16_support(void); void test_readonly_sbc(void); void test_receive_copy_results_copy_status(void); void test_receive_copy_results_op_params(void); void test_report_supported_opcodes_one_command(void); void test_report_supported_opcodes_rctd(void); void test_report_supported_opcodes_servactv(void); void test_report_supported_opcodes_simple(void); void test_reserve6_simple(void); void test_reserve6_2initiators(void); void test_reserve6_logout(void); void test_reserve6_itnexus_loss(void); void test_reserve6_target_cold_reset(void); void test_reserve6_target_warm_reset(void); void test_reserve6_lun_reset(void); void test_sanitize_block_erase(void); void test_sanitize_block_erase_reserved(void); void test_sanitize_crypto_erase(void); void test_sanitize_crypto_erase_reserved(void); void test_sanitize_exit_failure_mode(void); void test_sanitize_invalid_serviceaction(void); void test_sanitize_overwrite(void); void test_sanitize_overwrite_reserved(void); void test_sanitize_readonly(void); void test_sanitize_reservations(void); void test_sanitize_reset(void); void test_startstopunit_simple(void); void test_startstopunit_pwrcnd(void); void test_startstopunit_noloej(void); void test_testunitready_simple(void); void test_unmap_simple(void); void test_unmap_0blocks(void); void test_unmap_vpd(void); void test_verify10_simple(void); void test_verify10_beyond_eol(void); void test_verify10_0blocks(void); void test_verify10_vrprotect(void); void test_verify10_flags(void); void test_verify10_dpo(void); void test_verify10_mismatch(void); void test_verify10_mismatch_no_cmp(void); void test_verify12_simple(void); void test_verify12_beyond_eol(void); void test_verify12_0blocks(void); void test_verify12_vrprotect(void); void test_verify12_flags(void); void test_verify12_dpo(void); void test_verify12_mismatch(void); void test_verify12_mismatch_no_cmp(void); void test_verify16_simple(void); void test_verify16_beyond_eol(void); void test_verify16_0blocks(void); void test_verify16_vrprotect(void); void test_verify16_flags(void); void test_verify16_dpo(void); void test_verify16_mismatch(void); void test_verify16_mismatch_no_cmp(void); void test_write10_simple(void); void test_write10_beyond_eol(void); void test_write10_0blocks(void); void test_write10_wrprotect(void); void test_write10_dpofua(void); void test_write10_residuals(void); void test_async_write(void); void test_write12_simple(void); void test_write12_beyond_eol(void); void test_write12_0blocks(void); void test_write12_wrprotect(void); void test_write12_dpofua(void); void test_write12_residuals(void); void test_write16_simple(void); void test_write16_beyond_eol(void); void test_write16_0blocks(void); void test_write16_wrprotect(void); void test_write16_dpofua(void); void test_write16_residuals(void); void test_writeatomic16_simple(void); void test_writeatomic16_beyond_eol(void); void test_writeatomic16_0blocks(void); void test_writeatomic16_wrprotect(void); void test_writeatomic16_dpofua(void); void test_writeatomic16_vpd(void); void test_writesame10_simple(void); void test_writesame10_beyond_eol(void); void test_writesame10_0blocks(void); void test_writesame10_wrprotect(void); void test_writesame10_unmap(void); void test_writesame10_unmap_unaligned(void); void test_writesame10_unmap_until_end(void); void test_writesame10_unmap_vpd(void); void test_writesame16_simple(void); void test_writesame16_beyond_eol(void); void test_writesame16_0blocks(void); void test_writesame16_wrprotect(void); void test_writesame16_unmap(void); void test_writesame16_unmap_unaligned(void); void test_writesame16_unmap_until_end(void); void test_writesame16_unmap_vpd(void); void test_writeverify10_simple(void); void test_writeverify10_beyond_eol(void); void test_writeverify10_0blocks(void); void test_writeverify10_wrprotect(void); void test_writeverify10_flags(void); void test_writeverify10_dpo(void); void test_writeverify10_residuals(void); void test_writeverify12_simple(void); void test_writeverify12_beyond_eol(void); void test_writeverify12_0blocks(void); void test_writeverify12_wrprotect(void); void test_writeverify12_flags(void); void test_writeverify12_dpo(void); void test_writeverify12_residuals(void); void test_writeverify16_simple(void); void test_writeverify16_beyond_eol(void); void test_writeverify16_0blocks(void); void test_writeverify16_wrprotect(void); void test_writeverify16_flags(void); void test_writeverify16_dpo(void); void test_writeverify16_residuals(void); void test_multipathio_simple(void); void test_multipathio_reset(void); void test_multipathio_compareandwrite(void); void test_mpio_async_caw(void); void test_async_abort_simple(void); #endif /* _ISCSI_TEST_CU_H_ */ libiscsi-1.17.0/test-tool/test_async_abort_simple.c000066400000000000000000000137231271371262700224310ustar00rootroot00000000000000/* Copyright (C) SUSE LINUX GmbH 2016 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, see . */ #include #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-support.h" #include "iscsi-test-cu.h" struct tests_async_abort_state { struct scsi_task *wtask; uint32_t wr_cancelled; uint32_t wr_good; uint32_t abort_ok; uint32_t abort_bad_itt; }; static void test_async_write_cb(struct iscsi_context *iscsi __attribute__((unused)), int status, void *command_data, void *private_data) { struct scsi_task *wtask = command_data; struct tests_async_abort_state *state = private_data; if (status == SCSI_STATUS_GOOD) { state->wr_good++; logging(LOG_VERBOSE, "WRITE10 successful: (CmdSN=0x%x, " "ITT=0x%x)", wtask->cmdsn, wtask->itt); } else if (status == SCSI_STATUS_CANCELLED) { state->wr_cancelled++; logging(LOG_VERBOSE, "WRITE10 cancelled: (CmdSN=0x%x, " "ITT=0x%x)", wtask->cmdsn, wtask->itt); } else { CU_ASSERT_NOT_EQUAL(status, SCSI_STATUS_CHECK_CONDITION); } } static void test_async_abort_cb(struct iscsi_context *iscsi __attribute__((unused)), int status, void *command_data, void *private_data) { uint32_t tmf_response = *(uint32_t *)command_data; struct tests_async_abort_state *state = private_data; logging(LOG_VERBOSE, "ABORT TASK: TMF response %d for" " RefCmdSN=0x%x, RefITT=0x%x", tmf_response, state->wtask->cmdsn, state->wtask->itt); if (tmf_response == ISCSI_TMR_FUNC_COMPLETE) { state->abort_ok++; logging(LOG_VERBOSE, "ABORT TASK completed"); } else if (tmf_response == ISCSI_TMR_TASK_DOES_NOT_EXIST) { /* expected if the write has already been handled by the tgt */ state->abort_bad_itt++; logging(LOG_VERBOSE, "ABORT TASK bad ITT"); } else { logging(LOG_NORMAL, "ABORT TASK: unexpected TMF response %d for" " RefCmdSN=0x%x, RefITT=0x%x", tmf_response, state->wtask->cmdsn, state->wtask->itt); CU_ASSERT_FATAL((tmf_response != ISCSI_TMR_FUNC_COMPLETE) && (tmf_response != ISCSI_TMR_TASK_DOES_NOT_EXIST)); } CU_ASSERT_NOT_EQUAL(status, SCSI_STATUS_CHECK_CONDITION); } void test_async_abort_simple(void) { int ret; struct tests_async_abort_state state = { NULL, 0, 0, 0, 0 }; int blocksize = 512; int blocks_per_io = 8; unsigned char buf[blocksize * blocks_per_io]; uint64_t timeout_sec; CHECK_FOR_DATALOSS; CHECK_FOR_SBC; if (sd->iscsi_ctx == NULL) { CU_PASS("[SKIPPED] Non-iSCSI"); return; } if (maximum_transfer_length && (maximum_transfer_length < (int)(blocks_per_io))) { CU_PASS("[SKIPPED] device too small for async_abort test"); return; } memset(buf, 0, blocksize * blocks_per_io); /* queue and dispatch write before the abort */ state.wtask = scsi_cdb_write10(0, blocks_per_io * blocksize, blocksize, 0, 0, 0, 0, 0); CU_ASSERT_PTR_NOT_NULL_FATAL(state.wtask); ret = scsi_task_add_data_out_buffer(state.wtask, blocks_per_io * blocksize, buf); CU_ASSERT_EQUAL(ret, 0); ret = iscsi_scsi_command_async(sd->iscsi_ctx, sd->iscsi_lun, state.wtask, test_async_write_cb, NULL, &state); CU_ASSERT_EQUAL(ret, 0); logging(LOG_VERBOSE, "WRITE10 queued: (CmdSN=0x%x, ITT=0x%x)", state.wtask->cmdsn, state.wtask->itt); CU_ASSERT_EQUAL(iscsi_out_queue_length(sd->iscsi_ctx), 1); logging(LOG_VERBOSE, "dispatching out queue..."); while ((uint32_t)iscsi_out_queue_length(sd->iscsi_ctx) > 0) { struct pollfd pfd; pfd.fd = iscsi_get_fd(sd->iscsi_ctx); pfd.events = POLLOUT; /* only send */ ret = poll(&pfd, 1, 1000); CU_ASSERT_NOT_EQUAL(ret, -1); ret = iscsi_service(sd->iscsi_ctx, pfd.revents); CU_ASSERT_EQUAL(ret, 0); } logging(LOG_VERBOSE, "dispatched"); /* * queue abort - shouldn't cancel the dispatched task. TMF req should * be sent to the target. */ ret = iscsi_task_mgmt_async(sd->iscsi_ctx, state.wtask->lun, ISCSI_TM_ABORT_TASK, state.wtask->itt, state.wtask->cmdsn, test_async_abort_cb, &state); CU_ASSERT_EQUAL(ret, 0); logging(LOG_VERBOSE, "ABORT queued: (RefCmdSN=0x%x, " "RefITT=0x%x)", state.wtask->cmdsn, state.wtask->itt); /* * wait for all responses, timeout in 5 seconds. Expected responses: * + WRITE:good, ABORT:bad_itt - write completed before abort * + WRITE:no-response, ABORT:ok - write aborted */ logging(LOG_VERBOSE, "dispatching abort and handling responses..."); timeout_sec = test_get_clock_sec() + 5; while (test_get_clock_sec() <= timeout_sec) { struct pollfd pfd; pfd.fd = iscsi_get_fd(sd->iscsi_ctx); pfd.events = iscsi_which_events(sd->iscsi_ctx); ret = poll(&pfd, 1, 1000); CU_ASSERT_NOT_EQUAL(ret, -1); ret = iscsi_service(sd->iscsi_ctx, pfd.revents); CU_ASSERT_EQUAL(ret, 0); if (((state.wr_good == 1) && (state.abort_bad_itt == 1)) || (state.abort_ok == 1)) { logging(LOG_VERBOSE, "received all expected responses"); break; } } logging(LOG_VERBOSE, "%d IOs completed, %d aborts successful, " "%d aborts unsuccessful", state.wr_good, state.abort_ok, state.abort_bad_itt); if (state.abort_ok == 1) { CU_ASSERT_EQUAL(state.wr_good, 0); CU_ASSERT_EQUAL(state.wr_cancelled, 0); CU_ASSERT_EQUAL(state.abort_bad_itt, 0); } else if (state.abort_bad_itt == 1) { CU_ASSERT_EQUAL(state.wr_good, 1); CU_ASSERT_EQUAL(state.wr_cancelled, 0); CU_ASSERT_EQUAL(state.abort_ok, 0); } else { CU_FAIL("unexpected WRITE/ABORT state"); } scsi_free_scsi_task(state.wtask); } libiscsi-1.17.0/test-tool/test_async_read.c000066400000000000000000000063511271371262700206630ustar00rootroot00000000000000/* Copyright (C) SUSE LINUX GmbH 2016 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, see . */ #include #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-support.h" #include "iscsi-test-cu.h" #include "iscsi-multipath.h" struct tests_async_read_state { uint32_t dispatched; uint32_t completed; uint32_t prev_cmdsn; }; static void test_async_read_cb(struct iscsi_context *iscsi __attribute__((unused)), int status, void *command_data, void *private_data) { struct scsi_task *atask = command_data; struct tests_async_read_state *state = private_data; state->completed++; logging(LOG_VERBOSE, "READ10 completed: %d of %d (CmdSN=%d)", state->completed, state->dispatched, atask->cmdsn); CU_ASSERT_NOT_EQUAL(status, SCSI_STATUS_CHECK_CONDITION); if ((state->completed > 1) && (atask->cmdsn != state->prev_cmdsn + 1)) { logging(LOG_VERBOSE, "out of order completion (CmdSN=%d, prev=%d)", atask->cmdsn, state->prev_cmdsn); } state->prev_cmdsn = atask->cmdsn; scsi_free_scsi_task(atask); } void test_async_read(void) { int i, ret; struct tests_async_read_state state = { 0, 0, 0 }; int blocksize = 512; int blocks_per_io = 8; int num_ios = 1000; /* IOs in flight concurrently, so need a buffer large enough for all */ unsigned char buf[blocksize * blocks_per_io * num_ios]; CHECK_FOR_DATALOSS; CHECK_FOR_SBC; if (sd->iscsi_ctx == NULL) { CU_PASS("[SKIPPED] Non-iSCSI"); return; } if (maximum_transfer_length && (maximum_transfer_length < (blocks_per_io * num_ios))) { CU_PASS("[SKIPPED] device too small for async_read test"); return; } memset(buf, 0, blocksize * blocks_per_io * num_ios); for (i = 0; i < num_ios; i++) { uint32_t lba = i * blocks_per_io; struct scsi_task *atask; atask = scsi_cdb_read10(lba, blocks_per_io * blocksize, blocksize, 0, 0, 0, 0, 0); CU_ASSERT_PTR_NOT_NULL_FATAL(atask); ret = scsi_task_add_data_in_buffer(atask, blocks_per_io * blocksize, &buf[lba * blocksize]); CU_ASSERT_EQUAL(ret, 0); ret = iscsi_scsi_command_async(sd->iscsi_ctx, sd->iscsi_lun, atask, test_async_read_cb, NULL, &state); CU_ASSERT_EQUAL(ret, 0); state.dispatched++; logging(LOG_VERBOSE, "READ10 dispatched: %d of %d (cmdsn=%d)", state.dispatched, num_ios, atask->cmdsn); } while (state.completed < state.dispatched) { struct pollfd pfd; pfd.fd = iscsi_get_fd(sd->iscsi_ctx); pfd.events = iscsi_which_events(sd->iscsi_ctx); ret = poll(&pfd, 1, -1); CU_ASSERT_NOT_EQUAL(ret, -1); ret = iscsi_service(sd->iscsi_ctx, pfd.revents); CU_ASSERT_EQUAL(ret, 0); } } libiscsi-1.17.0/test-tool/test_async_write.c000066400000000000000000000062521271371262700211020ustar00rootroot00000000000000/* Copyright (C) SUSE LINUX GmbH 2016 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, see . */ #include #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-support.h" #include "iscsi-test-cu.h" struct tests_async_write_state { uint32_t dispatched; uint32_t completed; uint32_t prev_cmdsn; }; static void test_async_write_cb(struct iscsi_context *iscsi __attribute__((unused)), int status, void *command_data, void *private_data) { struct scsi_task *atask = command_data; struct tests_async_write_state *state = private_data; state->completed++; logging(LOG_VERBOSE, "WRITE10 completed: %d of %d (CmdSN=%d)", state->completed, state->dispatched, atask->cmdsn); CU_ASSERT_NOT_EQUAL(status, SCSI_STATUS_CHECK_CONDITION); if ((state->completed > 1) && (atask->cmdsn != state->prev_cmdsn + 1)) { logging(LOG_VERBOSE, "out of order completion (CmdSN=%d, prev=%d)", atask->cmdsn, state->prev_cmdsn); } state->prev_cmdsn = atask->cmdsn; scsi_free_scsi_task(atask); } void test_async_write(void) { int i, ret; struct tests_async_write_state state = { 0, 0, 0 }; int blocksize = 512; int blocks_per_io = 8; int num_ios = 1000; /* IOs in flight concurrently, but all using the same src buffer */ unsigned char buf[blocksize * blocks_per_io]; CHECK_FOR_DATALOSS; CHECK_FOR_SBC; if (sd->iscsi_ctx == NULL) { CU_PASS("[SKIPPED] Non-iSCSI"); return; } if (maximum_transfer_length && (maximum_transfer_length < (blocks_per_io * num_ios))) { CU_PASS("[SKIPPED] device too small for async_write test"); return; } memset(buf, 0, blocksize * blocks_per_io); for (i = 0; i < num_ios; i++) { uint32_t lba = i * blocks_per_io; struct scsi_task *atask; atask = scsi_cdb_write10(lba, blocks_per_io * blocksize, blocksize, 0, 0, 0, 0, 0); CU_ASSERT_PTR_NOT_NULL_FATAL(atask); ret = scsi_task_add_data_out_buffer(atask, blocks_per_io * blocksize, buf); CU_ASSERT_EQUAL(ret, 0); ret = iscsi_scsi_command_async(sd->iscsi_ctx, sd->iscsi_lun, atask, test_async_write_cb, NULL, &state); CU_ASSERT_EQUAL(ret, 0); state.dispatched++; logging(LOG_VERBOSE, "WRITE10 dispatched: %d of %d (cmdsn=%d)", state.dispatched, num_ios, atask->cmdsn); } while (state.completed < state.dispatched) { struct pollfd pfd; pfd.fd = iscsi_get_fd(sd->iscsi_ctx); pfd.events = iscsi_which_events(sd->iscsi_ctx); ret = poll(&pfd, 1, -1); CU_ASSERT_NOT_EQUAL(ret, -1); ret = iscsi_service(sd->iscsi_ctx, pfd.revents); CU_ASSERT_EQUAL(ret, 0); } } libiscsi-1.17.0/test-tool/test_compareandwrite_dpofua.c000066400000000000000000000125701271371262700232750ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2014 by Ronnie Sahlberg 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, see . */ #include #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-test-cu.h" void test_compareandwrite_dpofua(void) { int ret, dpofua, usage_data_dpofua; struct scsi_task *ms_task = NULL; struct scsi_mode_sense *ms; struct scsi_task *rso_task = NULL; struct scsi_report_supported_op_codes_one_command *rsoc; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test COMPAREANDWRITE DPO/FUA flags"); CHECK_FOR_SBC; CHECK_FOR_DATALOSS; logging(LOG_VERBOSE, "Read the DPOFUA flag from mode sense data"); MODESENSE6(sd, &ms_task, 0, SCSI_MODESENSE_PC_CURRENT, SCSI_MODEPAGE_RETURN_ALL_PAGES, 0, 255, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "[SUCCESS] Mode sense returned status GOOD"); ms = scsi_datain_unmarshall(ms_task); dpofua = ms && (ms->device_specific_parameter & 0x10); scsi_free_scsi_task(ms_task); logging(LOG_VERBOSE, "Read the first block"); ret = read10(sd, NULL, 0, block_size, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); CU_ASSERT_EQUAL(ret, 0); if (ret == 0) memcpy(scratch + block_size, scratch, block_size); else memset(scratch, 0xa6, 2 * block_size); if (dpofua) { logging(LOG_VERBOSE, "DPOFUA flag is set. Device should allow " "DPO/FUA flags in CDBs"); } else { logging(LOG_VERBOSE, "DPOFUA flag is clear. Device should fail " "CDBs with DPO/FUA set"); } logging(LOG_VERBOSE, "Test COMPAREANDWRITE with DPO==1"); if (dpofua) { COMPAREANDWRITE(sd, 0, scratch, 2 * block_size, block_size, 0, 1, 0, 0, EXPECT_STATUS_GOOD); } else { COMPAREANDWRITE(sd, 0, scratch, 2 * block_size, block_size, 0, 1, 0, 0, EXPECT_INVALID_FIELD_IN_CDB); } logging(LOG_VERBOSE, "Test COMPAREANDWRITE with FUA==1"); if (dpofua) { COMPAREANDWRITE(sd, 0, scratch, 2 * block_size, block_size, 0, 0, 1, 0, EXPECT_STATUS_GOOD); } else { COMPAREANDWRITE(sd, 0, scratch, 2 * block_size, block_size, 0, 0, 1, 0, EXPECT_INVALID_FIELD_IN_CDB); } logging(LOG_VERBOSE, "Test COMPAREANDWRITE with DPO==1 FUA==1"); if (dpofua) { COMPAREANDWRITE(sd, 0, scratch, 2 * block_size, block_size, 0, 1, 1, 0, EXPECT_STATUS_GOOD); } else { COMPAREANDWRITE(sd, 0, scratch, 2 * block_size, block_size, 0, 1, 1, 0, EXPECT_INVALID_FIELD_IN_CDB); } logging(LOG_VERBOSE, "Try fetching REPORT_SUPPORTED_OPCODES " "for COMPAREANDWRITE"); REPORT_SUPPORTED_OPCODES(sd, &rso_task, 0, SCSI_REPORT_SUPPORTING_OPCODE, SCSI_OPCODE_COMPARE_AND_WRITE, 0, 65535, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Unmarshall the DATA-IN buffer"); rsoc = scsi_datain_unmarshall(rso_task); CU_ASSERT_PTR_NOT_NULL_FATAL(rsoc); usage_data_dpofua = rsoc->cdb_usage_data[1] & 0x18; if (dpofua) { logging(LOG_VERBOSE, "DPOFUA is set. Verify the " "DPO/FUA flags are set in the CDB_USAGE_DATA"); if (!usage_data_dpofua) { logging(LOG_NORMAL, "[FAILED] DpoFua not set " "in CDB_USAGE_DATE"); CU_FAIL("DpoFua not set in CDB_USAGE_DATE"); } } else { logging(LOG_VERBOSE, "DPOFUA is clear. Verify the " "DPO/FUA flags are clear in the CDB_USAGE_DATA"); if (usage_data_dpofua) { logging(LOG_NORMAL, "[FAILED] DpoFua not clear " "in CDB_USAGE_DATE"); CU_FAIL("DpoFua not clear in CDB_USAGE_DATE"); } } scsi_free_scsi_task(rso_task); } libiscsi-1.17.0/test-tool/test_compareandwrite_miscompare.c000066400000000000000000000146341271371262700241610ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg 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, see . */ #include #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-support.h" #include "iscsi-test-cu.h" void test_compareandwrite_miscompare(void) { int i; unsigned j; int maxbl; CHECK_FOR_DATALOSS; CHECK_FOR_SBC; if (inq_bl && inq_bl->max_cmp) { maxbl = inq_bl->max_cmp; } else { /* Assume we are not limited */ maxbl = 256; } logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test COMPARE_AND_WRITE of 1-256 blocks at the " "start of the LUN. One Byte miscompare in the final block."); for (i = 1; i < 256; i++) { logging(LOG_VERBOSE, "Write %d blocks of 'A' at LBA:0", i); memset(scratch, 'A', 2 * i * block_size); if (maximum_transfer_length && maximum_transfer_length < i) { break; } WRITE16(sd, 0, i * block_size, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Change byte 27 from the end to 'C' so that it does not match."); scratch[i * block_size - 27] = 'C'; if (i > maxbl) { logging(LOG_VERBOSE, "Number of blocks %d is greater than " "BlockLimits.MaximumCompareAndWriteLength(%d). " "Command should fail with INVALID_FIELD_IN_CDB", i, maxbl); COMPAREANDWRITE(sd, 0, scratch, 2 * i * block_size, block_size, 0, 0, 0, 0, EXPECT_INVALID_FIELD_IN_CDB); continue; } memset(scratch + i * block_size, 'B', i * block_size); logging(LOG_VERBOSE, "Overwrite %d blocks with 'B' " "at LBA:0 (if they all contain 'A')", i); COMPAREANDWRITE(sd, 0, scratch, 2 * i * block_size, block_size, 0, 0, 0, 0, EXPECT_MISCOMPARE); logging(LOG_VERBOSE, "Read %d blocks at LBA:0 and verify " "they are still unchanged as 'A'", i); READ16(sd, NULL, 0, i * block_size, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); for (j = 0; j < i * block_size; j++) { if (scratch[j] != 'A') { logging(LOG_VERBOSE, "[FAILED] Data changed " "eventhough there was a miscompare"); CU_FAIL("Block was written to"); return; } } } logging(LOG_VERBOSE, "Test COMPARE_AND_WRITE of 1-256 blocks at the " "end of the LUN"); for (i = 1; i < 256; i++) { logging(LOG_VERBOSE, "Write %d blocks of 'A' at LBA:%" PRIu64, i, num_blocks - i); memset(scratch, 'A', 2 * i * block_size); if (maximum_transfer_length && maximum_transfer_length < i) { break; } WRITE16(sd, num_blocks - i, i * block_size, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Change byte 27 from the end to 'C' so that it does not match."); scratch[i * block_size - 27] = 'C'; if (i > maxbl) { logging(LOG_VERBOSE, "Number of blocks %d is greater than " "BlockLimits.MaximumCompareAndWriteLength(%d). " "Command should fail with INVALID_FIELD_IN_CDB", i, maxbl); COMPAREANDWRITE(sd, 0, scratch, 2 * i * block_size, block_size, 0, 0, 0, 0, EXPECT_INVALID_FIELD_IN_CDB); continue; } memset(scratch + i * block_size, 'B', i * block_size); logging(LOG_VERBOSE, "Overwrite %d blocks with 'B' " "at LBA:%" PRIu64 " (if they all contain 'A')", i, num_blocks - i); COMPAREANDWRITE(sd, num_blocks - i, scratch, 2 * i * block_size, block_size, 0, 0, 0, 0, EXPECT_MISCOMPARE); logging(LOG_VERBOSE, "Read %d blocks at LBA:%" PRIu64 "they are still unchanged as 'A'", i, num_blocks - i); READ16(sd, NULL, num_blocks - i, i * block_size, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); for (j = 0; j < i * block_size; j++) { if (scratch[j] != 'A') { logging(LOG_VERBOSE, "[FAILED] Data changed " "eventhough there was a miscompare"); CU_FAIL("Block was written to"); return; } } } } libiscsi-1.17.0/test-tool/test_compareandwrite_simple.c000066400000000000000000000142451271371262700233110ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg 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, see . */ #include #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-support.h" #include "iscsi-test-cu.h" void test_compareandwrite_simple(void) { int i; unsigned j; int maxbl; CHECK_FOR_DATALOSS; CHECK_FOR_SBC; if (inq_bl && inq_bl->max_cmp) { maxbl = inq_bl->max_cmp; } else { /* Assume we are not limited */ maxbl = 256; } logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test COMPARE_AND_WRITE of 1-256 blocks at the " "start of the LUN"); for (i = 1; i < 256; i++) { logging(LOG_VERBOSE, "Write %d blocks of 'A' at LBA:0", i); memset(scratch, 'A', 2 * i * block_size); if (maximum_transfer_length && maximum_transfer_length < i) { break; } WRITE16(sd, 0, i * block_size, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); if (i > maxbl) { logging(LOG_VERBOSE, "Number of blocks %d is greater than " "BlockLimits.MaximumCompareAndWriteLength(%d). " "Command should fail with INVALID_FIELD_IN_CDB", i, maxbl); COMPAREANDWRITE(sd, 0, scratch, 2 * i * block_size, block_size, 0, 0, 0, 0, EXPECT_INVALID_FIELD_IN_CDB); continue; } memset(scratch + i * block_size, 'B', i * block_size); logging(LOG_VERBOSE, "Overwrite %d blocks with 'B' " "at LBA:0 (if they all contain 'A')", i); COMPAREANDWRITE(sd, 0, scratch, 2 * i * block_size, block_size, 0, 0, 0, 0, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Read %d blocks at LBA:0 and verify " "they are all 'B'", i); READ16(sd, NULL, 0, i * block_size, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); for (j = 0; j < i * block_size; j++) { if (scratch[j] != 'B') { logging(LOG_VERBOSE, "[FAILED] Data did not " "read back as 'B' (scratch[%d] = %#02x)", j, scratch[j]); CU_FAIL("Block was not written correctly"); return; } } } logging(LOG_VERBOSE, "Test COMPARE_AND_WRITE of 1-256 blocks at the " "end of the LUN"); for (i = 1; i < 256; i++) { logging(LOG_VERBOSE, "Write %d blocks of 'A' at LBA:%" PRIu64, i, num_blocks - i); memset(scratch, 'A', 2 * i * block_size); if (maximum_transfer_length && maximum_transfer_length < i) { break; } WRITE16(sd, num_blocks - i, i * block_size, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); if (i > maxbl) { logging(LOG_VERBOSE, "Number of blocks %d is greater than " "BlockLimits.MaximumCompareAndWriteLength(%d). " "Command should fail with INVALID_FIELD_IN_CDB", i, maxbl); COMPAREANDWRITE(sd, 0, scratch, 2 * i * block_size, block_size, 0, 0, 0, 0, EXPECT_INVALID_FIELD_IN_CDB); continue; } memset(scratch + i * block_size, 'B', i * block_size); logging(LOG_VERBOSE, "Overwrite %d blocks with 'B' " "at LBA:%" PRIu64 " (if they all contain 'A')", i, num_blocks - i); COMPAREANDWRITE(sd, num_blocks - i, scratch, 2 * i * block_size, block_size, 0, 0, 0, 0, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Read %d blocks at LBA:%" PRIu64 " and verify they are all 'B'", i, num_blocks - i); READ16(sd, NULL, num_blocks - i, i * block_size, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); for (j = 0; j < i * block_size; j++) { if (scratch[j] != 'B') { logging(LOG_VERBOSE, "[FAILED] Data did not " "read back as 'B' (scratch[%d] = %#02x)", j, scratch[j]); CU_FAIL("Block was not written correctly"); return; } } } } libiscsi-1.17.0/test-tool/test_extendedcopy_descr_limits.c000066400000000000000000000107361271371262700240110ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (c) 2015 SanDisk Corp. 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, see . */ #include #include #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-test-cu.h" int init_xcopy_descr(unsigned char *buf, int offset, int num_tgt_desc, int num_seg_desc, int *tgt_desc_len, int *seg_desc_len) { int i; /* Initialize target descriptor list with num_tgt_desc * target descriptor */ for (i = 0; i < num_tgt_desc; i++) offset += populate_tgt_desc(buf+offset, IDENT_DESCR_TGT_DESCR, LU_ID_TYPE_LUN, 0, 0, 0, 0, sd); *tgt_desc_len = offset - XCOPY_DESC_OFFSET; /* Iniitialize segment descriptor list with num_seg_desc * segment descriptor */ for (i = 0; i < num_seg_desc; i++) offset += populate_seg_desc_b2b(buf+offset, 0, 0, 0, 0, 2048, 0, num_blocks - 2048); *seg_desc_len = offset - XCOPY_DESC_OFFSET - *tgt_desc_len; return offset; } void test_extendedcopy_descr_limits(void) { struct scsi_task *edl_task; struct iscsi_data data; unsigned char *xcopybuf; struct scsi_copy_results_op_params *opp = NULL; int tgt_desc_len = 0, seg_desc_len = 0; unsigned int alloc_len; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test EXTENDED COPY descriptor limits"); CHECK_FOR_DATALOSS; logging(LOG_VERBOSE, "Issue RECEIVE COPY RESULTS (OPERATING PARAMS)"); RECEIVE_COPY_RESULTS(&edl_task, sd, SCSI_COPY_RESULTS_OP_PARAMS, 0, (void **)&opp, EXPECT_STATUS_GOOD); /* Allocate buffer to accommodate (MAX+1) target and * segment descriptors */ alloc_len = XCOPY_DESC_OFFSET + (opp->max_target_desc_count+1) * get_desc_len(IDENT_DESCR_TGT_DESCR) + (opp->max_segment_desc_count+1) * get_desc_len(BLK_TO_BLK_SEG_DESCR); data.data = alloca(alloc_len); xcopybuf = data.data; memset(xcopybuf, 0, alloc_len); logging(LOG_VERBOSE, "Test sending more than supported target descriptors"); data.size = init_xcopy_descr(xcopybuf, XCOPY_DESC_OFFSET, (opp->max_target_desc_count+1), 1, &tgt_desc_len, &seg_desc_len); populate_param_header(xcopybuf, 1, 0, 0, 0, tgt_desc_len, seg_desc_len, 0); EXTENDEDCOPY(sd, &data, EXPECT_TOO_MANY_DESCR); logging(LOG_VERBOSE, "Test sending more than supported segment descriptors"); memset(xcopybuf, 0, alloc_len); data.size = init_xcopy_descr(xcopybuf, XCOPY_DESC_OFFSET, 1, (opp->max_segment_desc_count+1), &tgt_desc_len, &seg_desc_len); populate_param_header(xcopybuf, 2, 0, 0, 0, tgt_desc_len, seg_desc_len, 0); EXTENDEDCOPY(sd, &data, EXPECT_TOO_MANY_DESCR); logging(LOG_VERBOSE, "Test sending descriptors > Maximum Descriptor List Length"); memset(xcopybuf, 0, alloc_len); if (opp->max_desc_list_length < alloc_len) { data.size = init_xcopy_descr(xcopybuf, XCOPY_DESC_OFFSET, (opp->max_target_desc_count+1), (opp->max_segment_desc_count+1), &tgt_desc_len, &seg_desc_len); populate_param_header(xcopybuf, 3, 0, 0, 0, tgt_desc_len, seg_desc_len, 0); EXTENDEDCOPY(sd, &data, EXPECT_PARAM_LIST_LEN_ERR); } scsi_free_scsi_task(edl_task); } libiscsi-1.17.0/test-tool/test_extendedcopy_descr_type.c000066400000000000000000000061341271371262700234660ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (c) 2015 SanDisk Corp. 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, see . */ #include #include #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-test-cu.h" int init_xcopybuf(unsigned char *buf, int tgt_desc_type, int seg_desc_type, int *tgt_desc_len, int *seg_desc_len) { int offset = XCOPY_DESC_OFFSET; offset += populate_tgt_desc(buf+offset, tgt_desc_type, LU_ID_TYPE_LUN, 0, 0, 0, 0, sd); *tgt_desc_len = offset - XCOPY_DESC_OFFSET; if (seg_desc_type == BLK_TO_BLK_SEG_DESCR) offset += populate_seg_desc_b2b(buf+offset, 0, 0, 0, 0, 2048, 0, num_blocks - 2048); else offset += populate_seg_desc_hdr(buf+offset, seg_desc_type, 0, 0, 0, 0); *seg_desc_len = offset - XCOPY_DESC_OFFSET - *tgt_desc_len; return offset; } void test_extendedcopy_descr_type(void) { int tgt_desc_len = 0, seg_desc_len = 0, alloc_len; struct iscsi_data data; unsigned char *xcopybuf; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test EXTENDED COPY unsupported descriptor types"); CHECK_FOR_DATALOSS; alloc_len = XCOPY_DESC_OFFSET + get_desc_len(IDENT_DESCR_TGT_DESCR) + get_desc_len(BLK_TO_BLK_SEG_DESCR); data.data = alloca(alloc_len); xcopybuf = data.data; memset(xcopybuf, 0, alloc_len); logging(LOG_VERBOSE, "Send Fibre Channel N_Port_Name target descriptor"); data.size = init_xcopybuf(xcopybuf, 0xE0, BLK_TO_BLK_SEG_DESCR, &tgt_desc_len, &seg_desc_len); populate_param_header(xcopybuf, 1, 0, 0, 0, tgt_desc_len, seg_desc_len, 0); EXTENDEDCOPY(sd, &data, EXPECT_UNSUPP_DESCR_CODE); logging(LOG_VERBOSE, "Send Stream-to-Stream Copy segment descriptor"); memset(xcopybuf, 0, alloc_len); data.size = init_xcopybuf(xcopybuf, IDENT_DESCR_TGT_DESCR, STRM_TO_STRM_SEG_DESCR, &tgt_desc_len, &seg_desc_len); populate_param_header(xcopybuf, 1, 0, 0, 0, tgt_desc_len, seg_desc_len, 0); EXTENDEDCOPY(sd, &data, EXPECT_UNSUPP_DESCR_CODE); } libiscsi-1.17.0/test-tool/test_extendedcopy_param.c000066400000000000000000000052741271371262700224310ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (c) 2015 SanDisk Corp. 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, see . */ #include #include #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-test-cu.h" void test_extendedcopy_param(void) { int tgt_desc_len = 0, seg_desc_len = 0, offset = XCOPY_DESC_OFFSET; struct iscsi_data data; unsigned char *xcopybuf; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test EXTENDED COPY parameter list length"); CHECK_FOR_DATALOSS; data.size = XCOPY_DESC_OFFSET + get_desc_len(IDENT_DESCR_TGT_DESCR) + get_desc_len(BLK_TO_BLK_SEG_DESCR); data.data = alloca(data.size); xcopybuf = data.data; memset(xcopybuf, 0, data.size); offset += populate_tgt_desc(xcopybuf+offset, IDENT_DESCR_TGT_DESCR, LU_ID_TYPE_LUN, 0, 0, 0, 0, sd); tgt_desc_len = offset - XCOPY_DESC_OFFSET; offset += populate_seg_desc_b2b(xcopybuf+offset, 0, 0, 0, 0, 2048, 0, num_blocks - 2048); seg_desc_len = offset - XCOPY_DESC_OFFSET - tgt_desc_len; populate_param_header(xcopybuf, 1, 0, 0, 0, tgt_desc_len, seg_desc_len, 0); logging(LOG_VERBOSE, "Test parameter list length truncating target descriptor"); data.size = XCOPY_DESC_OFFSET + get_desc_len(IDENT_DESCR_TGT_DESCR) - 1; EXTENDEDCOPY(sd, &data, EXPECT_PARAM_LIST_LEN_ERR); logging(LOG_VERBOSE, "Test parameter list length truncating segment descriptor"); data.size = XCOPY_DESC_OFFSET + get_desc_len(IDENT_DESCR_TGT_DESCR) + get_desc_len(BLK_TO_BLK_SEG_DESCR) - 1; EXTENDEDCOPY(sd, &data, EXPECT_PARAM_LIST_LEN_ERR); logging(LOG_VERBOSE, "Test parameter list length = 0"); data.size = 0; EXTENDEDCOPY(sd, &data, EXPECT_STATUS_GOOD); } libiscsi-1.17.0/test-tool/test_extendedcopy_simple.c000066400000000000000000000057501271371262700226210ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (c) 2015 SanDisk Corp. 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, see . */ #include #include #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-test-cu.h" void test_extendedcopy_simple(void) { int tgt_desc_len = 0, seg_desc_len = 0, offset = XCOPY_DESC_OFFSET; struct iscsi_data data; unsigned char *xcopybuf; unsigned char *buf1 = malloc(2048*block_size); unsigned char *buf2 = malloc(2048*block_size); logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test EXTENDED COPY of 2048 blocks from start of LUN to end of LUN"); CHECK_FOR_DATALOSS; logging(LOG_VERBOSE, "Write 2048 blocks of 'A' at LBA:0"); memset(buf1, 'A', 2048*block_size); WRITE16(sd, 0, 2048*block_size, block_size, 0, 0, 0, 0, 0, buf1, EXPECT_STATUS_GOOD); data.size = XCOPY_DESC_OFFSET + get_desc_len(IDENT_DESCR_TGT_DESCR) + get_desc_len(BLK_TO_BLK_SEG_DESCR); data.data = alloca(data.size); xcopybuf = data.data; memset(xcopybuf, 0, data.size); /* Initialize target descriptor list with one target descriptor */ offset += populate_tgt_desc(xcopybuf+offset, IDENT_DESCR_TGT_DESCR, LU_ID_TYPE_LUN, 0, 0, 0, 0, sd); tgt_desc_len = offset - XCOPY_DESC_OFFSET; /* Iniitialize segment descriptor list with one segment descriptor */ offset += populate_seg_desc_b2b(xcopybuf+offset, 0, 0, 0, 0, 2048, 0, num_blocks - 2048); seg_desc_len = offset - XCOPY_DESC_OFFSET - tgt_desc_len; /* Initialize the parameter list header */ populate_param_header(xcopybuf, 1, 0, LIST_ID_USAGE_DISCARD, 0, tgt_desc_len, seg_desc_len, 0); EXTENDEDCOPY(sd, &data, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Read 2048 blocks from end of the LUN"); READ16(sd, NULL, num_blocks - 2048, 2048*block_size, block_size, 0, 0, 0, 0, 0, buf2, EXPECT_STATUS_GOOD); if (memcmp(buf1, buf2, 2048)) { CU_FAIL("Blocks were not copied correctly"); } free(buf1); free(buf2); } libiscsi-1.17.0/test-tool/test_extendedcopy_validate_seg_descr.c000066400000000000000000000056301271371262700251340ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (c) 2015 SanDisk Corp. 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, see . */ #include #include #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-test-cu.h" void test_extendedcopy_validate_seg_descr(void) { int tgt_desc_len = 0, seg_desc_len = 0, offset = XCOPY_DESC_OFFSET; struct iscsi_data data; unsigned char *xcopybuf; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test EXTENDED COPY segment descriptor fields"); CHECK_FOR_DATALOSS; data.size = XCOPY_DESC_OFFSET + get_desc_len(IDENT_DESCR_TGT_DESCR) + get_desc_len(BLK_TO_BLK_SEG_DESCR); data.data = alloca(data.size); xcopybuf = data.data; memset(xcopybuf, 0, data.size); logging(LOG_VERBOSE, "Send invalid target descriptor index"); offset += populate_tgt_desc(xcopybuf+offset, IDENT_DESCR_TGT_DESCR, LU_ID_TYPE_LUN, 0, 0, 0, 0, sd); tgt_desc_len = offset - XCOPY_DESC_OFFSET; /* Inaccessible DESTINATION TARGET DESCRIPTOR INDEX */ offset += populate_seg_desc_b2b(xcopybuf+offset, 0, 0, 0, 1, 2048, 0, num_blocks - 2048); seg_desc_len = offset - XCOPY_DESC_OFFSET - tgt_desc_len; populate_param_header(xcopybuf, 1, 0, 0, 0, tgt_desc_len, seg_desc_len, 0); EXTENDEDCOPY(sd, &data, EXPECT_COPY_ABORTED); logging(LOG_VERBOSE, "Number of copy blocks beyond destination block device capacity"); memset(xcopybuf, 0, data.size); offset = XCOPY_DESC_OFFSET; offset += populate_tgt_desc(xcopybuf+offset, IDENT_DESCR_TGT_DESCR, LU_ID_TYPE_LUN, 0, 0, 0, 0, sd); tgt_desc_len = offset - XCOPY_DESC_OFFSET; /* Beyond EOL */ offset += populate_seg_desc_b2b(xcopybuf+offset, 0, 0, 0, 0, 2048, 0, num_blocks - 1); seg_desc_len = offset - XCOPY_DESC_OFFSET - tgt_desc_len; populate_param_header(xcopybuf, 1, 0, 0, 0, tgt_desc_len, seg_desc_len, 0); EXTENDEDCOPY(sd, &data, EXPECT_COPY_ABORTED); } libiscsi-1.17.0/test-tool/test_extendedcopy_validate_tgt_descr.c000066400000000000000000000055031271371262700251530ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (c) 2015 SanDisk Corp. 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, see . */ #include #include #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-test-cu.h" void test_extendedcopy_validate_tgt_descr(void) { int tgt_desc_len = 0, seg_desc_len = 0, offset = XCOPY_DESC_OFFSET; struct iscsi_data data; unsigned char *xcopybuf; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test EXTENDED COPY target descriptor fields"); CHECK_FOR_DATALOSS; data.size = XCOPY_DESC_OFFSET + get_desc_len(IDENT_DESCR_TGT_DESCR) + get_desc_len(BLK_TO_BLK_SEG_DESCR); data.data = alloca(data.size); xcopybuf = data.data; memset(xcopybuf, 0, data.size); logging(LOG_VERBOSE, "Unsupported LU_ID TYPE"); /* Unsupported LU ID TYPE */ offset += populate_tgt_desc(xcopybuf+offset, IDENT_DESCR_TGT_DESCR, LU_ID_TYPE_RSVD, 0, 0, 0, 0, sd); tgt_desc_len = offset - XCOPY_DESC_OFFSET; offset += populate_seg_desc_b2b(xcopybuf+offset, 0, 0, 0, 0, 2048, 0, num_blocks - 2048); seg_desc_len = offset - XCOPY_DESC_OFFSET - tgt_desc_len; populate_param_header(xcopybuf, 1, 0, 0, 0, tgt_desc_len, seg_desc_len, 0); EXTENDEDCOPY(sd, &data, EXPECT_INVALID_FIELD_IN_CDB); logging(LOG_VERBOSE, "Test NUL bit in target descriptor"); /* NUL bit */ memset(xcopybuf, 0, data.size); offset = XCOPY_DESC_OFFSET; offset += populate_tgt_desc(xcopybuf+offset, IDENT_DESCR_TGT_DESCR, LU_ID_TYPE_LUN, 1, 0, 0, 0, sd); tgt_desc_len = offset - XCOPY_DESC_OFFSET; offset += populate_seg_desc_b2b(xcopybuf+offset, 0, 0, 0, 0, 2048, 0, num_blocks - 2048); seg_desc_len = offset - XCOPY_DESC_OFFSET - tgt_desc_len; populate_param_header(xcopybuf, 1, 0, 0, 0, tgt_desc_len, seg_desc_len, 0); EXTENDEDCOPY(sd, &data, EXPECT_COPY_ABORTED); } libiscsi-1.17.0/test-tool/test_get_lba_status_beyond_eol.c000066400000000000000000000030001271371262700237360ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg 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, see . */ #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-support.h" #include "iscsi-test-cu.h" void test_get_lba_status_beyond_eol(void) { logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test GETLBASTATUS one block beyond the end of the LUN"); GETLBASTATUS(sd, NULL, num_blocks + 1, 24, EXPECT_LBA_OOB); logging(LOG_VERBOSE, "Test GETLBASTATUS at LBA 2^63"); GETLBASTATUS(sd, NULL, 0x8000000000000000ULL, 24, EXPECT_LBA_OOB); logging(LOG_VERBOSE, "Test GETLBASTATUS at LBA -1"); GETLBASTATUS(sd, NULL, 0xffffffffffffffffULL, 24, EXPECT_LBA_OOB); } libiscsi-1.17.0/test-tool/test_get_lba_status_simple.c000066400000000000000000000027751271371262700231320ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg 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, see . */ #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-support.h" #include "iscsi-test-cu.h" void test_get_lba_status_simple(void) { int i; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test GETLBASTATUS of 1-256 blocks at the start of the LUN"); for (i = 1; i <= 256; i++) { GETLBASTATUS(sd, NULL, i, 24, EXPECT_STATUS_GOOD); } logging(LOG_VERBOSE, "Test GETLBASTATUS of 1-256 blocks at the end of the LUN"); for (i = 1; i <= 256; i++) { GETLBASTATUS(sd, NULL, num_blocks - i, 24, EXPECT_STATUS_GOOD); } } libiscsi-1.17.0/test-tool/test_get_lba_status_unmap_single.c000066400000000000000000000136271271371262700243200ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2014 Ronnie Sahlberg 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, see . */ #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-support.h" #include "iscsi-test-cu.h" void test_get_lba_status_unmap_single(void) { uint64_t i; struct unmap_list list[1]; struct scsi_task *t = NULL; struct scsi_get_lba_status *lbas = NULL; struct scsi_lba_status_descriptor *lbasd = NULL; CHECK_FOR_DATALOSS; CHECK_FOR_THIN_PROVISIONING; CHECK_FOR_LBPU; memset(scratch, 'A', (256 + lbppb + 1) * block_size); logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test GETLBASTATUS for a single unmapped block " "at offset 0-255"); logging(LOG_VERBOSE, "We have %d logical blocks per physical block", lbppb); logging(LOG_VERBOSE, "Write the first %i blocks with a known " "pattern and thus map the blocks", 256 + lbppb); WRITE10(sd, 0, (256 + lbppb) * block_size, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); for (i = 0; i + lbppb <= 256; i += lbppb) { logging(LOG_VERBOSE, "Unmap a single physical block at LBA:%" PRIu64 " (number of logical blocks: %d)", i, lbppb); list[0].lba = i; list[0].num = lbppb; UNMAP(sd, 0, list, 1, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Read the status of the block at LBA:%" PRIu64, i); GETLBASTATUS(sd, NULL, i, 24, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Read the status of the block at LBA:%" PRIu64, i + lbppb); GETLBASTATUS(sd, &t, i + lbppb, 24, EXPECT_STATUS_GOOD); if (t == NULL) { CU_FAIL("[FAILED] GETLBASTATUS task is NULL"); return; } lbas = scsi_datain_unmarshall(t); if (lbas == NULL) { CU_FAIL("[FAILED] GETLBASTATUS command: failed " "to unmarshall data."); scsi_free_scsi_task(t); return; } lbasd = &lbas->descriptors[0]; if (lbasd->lba != i + lbppb) { CU_FAIL("[FAILED] GETLBASTATUS command: " "lba offset in first descriptor does not " "match request."); scsi_free_scsi_task(t); return; } if (lbasd->provisioning != SCSI_PROVISIONING_TYPE_MAPPED) { CU_FAIL("[FAILED] LBA should be mapped but isn't"); return; } scsi_free_scsi_task(t); } logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test GETLBASTATUS for a single range of 1-255 " "blocks at offset 0"); for (i = lbppb; i + lbppb <= 256; i += lbppb) { logging(LOG_VERBOSE, "Write the first %i blocks with a known " "pattern and thus map the blocks", (256 + lbppb)); WRITE10(sd, 0, (256 + lbppb) * block_size, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Unmap %" PRIu64 " blocks at LBA 0", i); list[0].lba = 0; list[0].num = i; UNMAP(sd, 0, list, 1, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Read the status of the block at LBA:0"); GETLBASTATUS(sd, NULL, 0, 24, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Read the status of the block at LBA:%" PRIu64, i + 1); GETLBASTATUS(sd, &t, i + 1, 24, EXPECT_STATUS_GOOD); if (t == NULL) { CU_FAIL("[FAILED] GETLBASTATUS task is NULL"); return; } lbas = scsi_datain_unmarshall(t); if (lbas == NULL) { CU_FAIL("[FAILED] GETLBASTATUS command: failed " "to unmarshall data."); scsi_free_scsi_task(t); return; } lbasd = &lbas->descriptors[0]; if (lbasd->lba != i + lbppb) { CU_FAIL("[FAILED] GETLBASTATUS command: " "lba offset in first descriptor does not " "match request."); scsi_free_scsi_task(t); return; } if (lbasd->provisioning != SCSI_PROVISIONING_TYPE_MAPPED) { CU_FAIL("[FAILED] LBA should be mapped but isn't"); return; } scsi_free_scsi_task(t); } } libiscsi-1.17.0/test-tool/test_inquiry_alloc_length.c000066400000000000000000000100411271371262700227550ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 by Ronnie Sahlberg 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, see . */ #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-support.h" #include "iscsi-test-cu.h" void test_inquiry_alloc_length(void) { int ret, i; struct scsi_inquiry_standard *std_inq; struct scsi_task *task2 = NULL; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test of the INQUIRY allocation length"); logging(LOG_VERBOSE, "Verify we can read standard INQUIRY page with alloc length from 5-255"); for (i = 5; i < 256 ; i++) { if (task != NULL) { scsi_free_scsi_task(task); task = NULL; } ret = inquiry(sd, &task, 0, 0, i, EXPECT_STATUS_GOOD); CU_ASSERT_EQUAL(ret, 0); } logging(LOG_VERBOSE, "Verify we got at least 36 bytes of data when reading with alloc length 255"); CU_ASSERT(task->datain.size >= 36); logging(LOG_VERBOSE, "Verify we can unmarshall the DATA-IN buffer"); std_inq = scsi_datain_unmarshall(task); CU_ASSERT_NOT_EQUAL(std_inq, NULL); if (std_inq == NULL) { logging(LOG_NORMAL, "[FAILED] Failed to unmarshall DATA-IN " "buffer"); return; } logging(LOG_VERBOSE, "Verify peripheral-qualifier is 0"); CU_ASSERT_EQUAL(std_inq->qualifier, 0); /* Final test. IF this claims SPC-3 or later then the target supports 16-bit allocation lengths. Try reading INQ data specifying 256 bytes as allocation length and make sure the target responds properly. */ logging(LOG_VERBOSE, "If version is SPC-3 or later INQUIRY supports 16-bit allocation lengths"); switch (std_inq->version) { case 0x5: case 0x6: break; default: logging(LOG_NORMAL, "[SKIPPED] This device does not claim " "SPC-3 or later"); CU_PASS("[SKIPPED] Not SPC-3 or later"); goto finished; } scsi_free_scsi_task(task); task = NULL; logging(LOG_VERBOSE, "Version is SPC-3 or later. Read INQUIRY data using 16-bit allocation length"); logging(LOG_VERBOSE, "Read INQUIRY data with allocation length 511 (low order byte is 0xff)"); ret = inquiry(sd, &task, 0, 0, 511, EXPECT_STATUS_GOOD); CU_ASSERT_EQUAL(ret, 0); logging(LOG_VERBOSE, "Read INQUIRY data with allocation length 512 (low order byte is 0x00)"); ret = inquiry(sd, &task2, 0, 0, 512, EXPECT_STATUS_GOOD); CU_ASSERT_EQUAL(ret, 0); logging(LOG_VERBOSE, "INQUIRY data should be the same when allocation length is 511 and 512 bytes"); ret = task->datain.size != task2->datain.size; CU_ASSERT_EQUAL(ret, 0); ret = memcmp(task->datain.data, task2->datain.data, task->datain.size); CU_ASSERT_EQUAL(ret, 0); finished: if (task != NULL) { scsi_free_scsi_task(task); task = NULL; } if (task2 != NULL) { scsi_free_scsi_task(task2); task2 = NULL; } } libiscsi-1.17.0/test-tool/test_inquiry_block_limits.c000066400000000000000000000163411271371262700230060ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 by Ronnie Sahlberg 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, see . */ #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-support.h" #include "iscsi-test-cu.h" static void check_lbp(int *supports_lbp) { *supports_lbp = 0; CHECK_FOR_THIN_PROVISIONING; *supports_lbp = 1; } void test_inquiry_block_limits(void) { int supports_lbp, ret; struct scsi_inquiry_block_limits *bl; struct scsi_task *bl_task = NULL; struct scsi_inquiry_logical_block_provisioning *lbp = NULL; struct scsi_task *lbp_task = NULL; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test of the INQUIRY Block Limits"); CHECK_FOR_SBC; logging(LOG_VERBOSE, "Block device. Verify that we can read Block " "Limits VPD"); ret = inquiry(sd, &bl_task, 1, SCSI_INQUIRY_PAGECODE_BLOCK_LIMITS, 255, EXPECT_STATUS_GOOD); CU_ASSERT_EQUAL(ret, 0); if (ret != 0) { logging(LOG_NORMAL, "[FAILURE] failed to send inquiry."); goto finished; } bl = scsi_datain_unmarshall(bl_task); if (bl == NULL) { logging(LOG_NORMAL, "[FAILURE] failed to unmarshall inquiry " "datain blob."); CU_FAIL("[FAILURE] failed to unmarshall inquiry " "datain blob."); goto finished; } logging(LOG_VERBOSE, "Verify that the PageLength matches up with the " "size of the DATA-IN buffer."); CU_ASSERT_EQUAL(bl_task->datain.size, bl_task->datain.data[3] + 4); if (bl_task->datain.size != bl_task->datain.data[3] + 4) { logging(LOG_NORMAL, "[FAILURE] Invalid PageLength returned. " "Was %d but expected %d", bl_task->datain.data[3], bl_task->datain.size - 4); } else { logging(LOG_VERBOSE, "[SUCCESS] PageLength matches DataIn " "buffer size"); } logging(LOG_VERBOSE, "Verify that the PageLength matches SCSI-level."); /* if it is not SBC3 then we assume it must be SBC2 */ if (sbc3_support) { logging(LOG_VERBOSE, "Device claims SBC-3. Verify that " "page size is >= 60"); } else { logging(LOG_VERBOSE, "Device is not SBC-3. Verify that " "PageLength == 8 (but allow >= 60 too. Some SBC-2 " "devices support some SBC-3 features."); } if (bl_task->datain.data[3] == 8) { if (sbc3_support) { logging(LOG_NORMAL, "[FAILURE] Invalid PageLength " "returned. SBC3 claimed but page length " "is 8."); CU_FAIL("[FAILED] Invalid pagelength returned. " "SBC3 claimed but page length is 8."); } } else if (bl_task->datain.size >= 60) { if (!sbc3_support) { logging(LOG_NORMAL, "[WARNING] SBC-3 pagelength " "(>=60) returned but SBC-3 support was not " "claimed in the standard inquiry page."); CU_FAIL("[WARNING] SBC-3 pagelength " "(>=60) returned but SBC-3 support was not " "claimed in the standard inquiry page."); } } if (bl_task->datain.data[3] != 0x3c) { goto finished; } check_lbp(&supports_lbp); if (!supports_lbp) goto finished; /* * MAXIMUM UNMAP LBA COUNT * MAXIMUM UNMAP BLOCK DESCRIPTOR COUNT */ logging(LOG_VERBOSE, "Try reading the logical block provisioning VPD"); ret = inquiry(sd, &lbp_task, 1, SCSI_INQUIRY_PAGECODE_LOGICAL_BLOCK_PROVISIONING, 255, EXPECT_STATUS_GOOD); if (ret == 0) { lbp = scsi_datain_unmarshall(lbp_task); if (lbp == NULL) { logging(LOG_NORMAL, "[FAILURE] failed to unmarshall " "inquiry datain blob."); } } if (lbp && lbp->lbpu) { /* We support UNMAP so MAXIMUM UNMAP LBA COUNT and * MAXIMUM UNMAP BLOCK DESCRIPTOR COUNT. * They must be > 0. * It can be 0xffffffff which means no limit, but if there is * an explicit limit set, then we check that it looks sane. * Sane here means < 1M. */ logging(LOG_VERBOSE, "Device claims UNMAP support via LBPU"); logging(LOG_VERBOSE, "Verify that MAXIMUM UNMAP LBA COUNT is " "not 0"); CU_ASSERT_NOT_EQUAL(bl->max_unmap, 0); logging(LOG_VERBOSE, "Verify that MAXIMUM UNMAP LBA COUNT is " "at least 2^LBPPBE"); CU_ASSERT_EQUAL(bl->max_unmap >= (1U << rc16->lbppbe), 1); if (bl->max_unmap != 0xffffffff) { logging(LOG_VERBOSE, "Verify that MAXIMUM UNMAP LBA " "COUNT is not insanely big"); CU_ASSERT_TRUE(bl->max_unmap <= 1024*1024); } logging(LOG_VERBOSE, "Verify that MAXIMUM UNMAP BLOCK " "DESCRIPTOR COUNT is not 0"); CU_ASSERT_NOT_EQUAL(bl->max_unmap_bdc, 0); if (bl->max_unmap_bdc != 0xffffffff) { logging(LOG_VERBOSE, "Verify that MAXIMUM UNMAP " "BLOCK DESCRIPTOR COUNT is not insanely big"); CU_ASSERT_TRUE(bl->max_unmap_bdc <= 1024*1024); } } else { logging(LOG_VERBOSE, "Device does not claim UNMAP support via " "LBPU"); logging(LOG_VERBOSE, "Verify that MAXIMUM UNMAP LBA COUNT is " "0"); CU_ASSERT_EQUAL(bl->max_unmap, 0); logging(LOG_VERBOSE, "Verify that MAXIMUM UNMAP BLOCK " "DESCRIPTOR COUNT is 0"); CU_ASSERT_EQUAL(bl->max_unmap_bdc, 0); } finished: scsi_free_scsi_task(bl_task); scsi_free_scsi_task(lbp_task); } libiscsi-1.17.0/test-tool/test_inquiry_evpd.c000066400000000000000000000024571271371262700212740ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 by Ronnie Sahlberg 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, see . */ #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-support.h" #include "iscsi-test-cu.h" void test_inquiry_evpd(void) { int ret; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test of the INQUIRY EVPD bit"); logging(LOG_VERBOSE, "Verify that INQUIRY with EVPD==0 and PC!=0 is an error"); ret = inquiry(sd, NULL, 0, 1, 256, EXPECT_INVALID_FIELD_IN_CDB); CU_ASSERT_EQUAL(ret, 0); } libiscsi-1.17.0/test-tool/test_inquiry_mandatory_vpd_sbc.c000066400000000000000000000033161271371262700240270ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 by Ronnie Sahlberg 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, see . */ #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-support.h" #include "iscsi-test-cu.h" void test_inquiry_mandatory_vpd_sbc(void) { int ret; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test INQUIRY support for mandatory SBC VPD"); CHECK_FOR_SBC; logging(LOG_VERBOSE, "SUPPORTED_VPD_PAGES is mandatory for SBC devices. Verify we can read it."); ret = inquiry(sd, NULL, 1, SCSI_INQUIRY_PAGECODE_SUPPORTED_VPD_PAGES, 255, EXPECT_STATUS_GOOD); CU_ASSERT_EQUAL(ret, 0); logging(LOG_VERBOSE, "DEVICE_IDENTIFICATION is mandatory for SBC devices. Verify we can read it."); ret = inquiry(sd, NULL, 1, SCSI_INQUIRY_PAGECODE_DEVICE_IDENTIFICATION, 255, EXPECT_STATUS_GOOD); CU_ASSERT_EQUAL(ret, 0); } libiscsi-1.17.0/test-tool/test_inquiry_standard.c000066400000000000000000000162641271371262700221370ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 by Ronnie Sahlberg 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, see . */ #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-support.h" #include "iscsi-test-cu.h" void test_inquiry_standard(void) { int ret, i; struct scsi_inquiry_standard *std_inq; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test of the standard INQUIRY page"); logging(LOG_VERBOSE, "Verify we can read standard INQUIRY page"); /* 260 bytes is the maximum possible size of the standard vpd */ ret = inquiry(sd, &task, 0, 0, 260, EXPECT_STATUS_GOOD); CU_ASSERT_EQUAL(ret, 0); logging(LOG_VERBOSE, "Verify we got at least 36 bytes of data"); CU_ASSERT(task->datain.size >= 36); logging(LOG_VERBOSE, "Verify we can unmarshall the DATA-IN buffer"); std_inq = scsi_datain_unmarshall(task); CU_ASSERT_NOT_EQUAL(std_inq, NULL); if (std_inq == NULL) { logging(LOG_NORMAL, "[FAILED] Failed to unmarshall DATA-IN " "buffer"); return; } logging(LOG_VERBOSE, "Verify peripheral-qualifier is 0"); CU_ASSERT_EQUAL(std_inq->qualifier, 0); logging(LOG_VERBOSE, "Verify version field is either 0x4, 0x5 or 0x6"); switch (std_inq->version) { case 0x0: logging(LOG_NORMAL, "[WARNING] Standard INQUIRY data claims " "conformance to no standard. Version==0. " "Bad sport."); break; case 0x4: case 0x5: case 0x6: break; default: logging(LOG_NORMAL, "[FAILED] Invalid version in standard " "INQUIRY data. Version %d found but only versions " "0x4,0x4,0x6 are valid.", std_inq->version); CU_FAIL("Invalid version in INQUIRY data"); } logging(LOG_VERBOSE, "Verify response-data-format is 2 " "(SPC-2 or later)"); if (std_inq->response_data_format != 2) { logging(LOG_NORMAL, "[FAILED] Response data format is " "invalid. Must be 2 but device returned %d", std_inq->response_data_format); } CU_ASSERT_EQUAL(std_inq->response_data_format, 2); logging(LOG_VERBOSE, "Verify additional-length is correct"); if (std_inq->additional_length > task->datain.size - 5) { logging(LOG_NORMAL, "[FAILED] Bad additional length " "returned. Should be %d but device returned %d.", task->datain.size - 5, std_inq->additional_length); logging(LOG_NORMAL, "[FAILED] Additional length points " "beyond end of data"); CU_FAIL("Additional length points beyond end of data"); } if (std_inq->additional_length < task->datain.size - 5) { logging(LOG_NORMAL, "[WARNING] Bad additional length " "returned. Should be %d but device returned %d. ", task->datain.size - 5, std_inq->additional_length); logging(LOG_VERBOSE, "Verify that all padding data is 0"); for (i = std_inq->additional_length + 6; i < task->datain.size; i++) { if (!task->datain.data[i]) continue; logging(LOG_NORMAL, "[FAILED] Padding data is not zero." " Are we leaking data?"); CU_FAIL("Padding data is not zero. Leaking data?"); } } logging(LOG_VERBOSE, "Verify VENDOR_IDENTIFICATION is in ASCII"); for (i = 8; i < 16; i++) { /* SPC-4 4.4.1 only characters 0x00 and 0x20-0x7E allowed */ if (task->datain.data[i] == 0) { continue; } if (task->datain.data[i] >= 0x20 && task->datain.data[i] <= 0x7e) { continue; } logging(LOG_NORMAL, "[FAILED] VENDOR_IDENTIFICATION contains " "non-ASCII characters"); CU_FAIL("Invalid characters in VENDOR_IDENTIFICATION"); break; } logging(LOG_VERBOSE, "Verify PRODUCT_IDENTIFICATION is in ASCII"); for (i = 16; i < 32; i++) { /* SPC-4 4.4.1 only characters 0x00 and 0x20-0x7E allowed */ if (task->datain.data[i] == 0) { continue; } if (task->datain.data[i] >= 0x20 && task->datain.data[i] <= 0x7e) { continue; } logging(LOG_NORMAL, "[FAILED] PRODUCT_IDENTIFICATION contains " "non-ASCII characters"); CU_FAIL("Invalid characters in PRODUCT_IDENTIFICATION"); break; } logging(LOG_VERBOSE, "Verify PRODUCT_REVISION_LEVEL is in ASCII"); for (i = 32; i < 36; i++) { /* SPC-4 4.4.1 only characters 0x00 and 0x20-0x7E allowed */ if (task->datain.data[i] == 0) { continue; } if (task->datain.data[i] >= 0x20 && task->datain.data[i] <= 0x7e) { continue; } logging(LOG_NORMAL, "[FAILED] PRODUCT_REVISON_LEVEL contains " "non-ASCII characters"); CU_FAIL("Invalid characters in PRODUCT_REVISON_LEVEL"); break; } logging(LOG_VERBOSE, "Verify AERC is clear in SPC-3 and later"); if (task->datain.data[3] & 0x80 && std_inq->version >= 5) { logging(LOG_NORMAL, "[FAILED] AERC is set but this device " "reports SPC-3 or later."); CU_FAIL("AERC is set but SPC-3+ is claimed"); } logging(LOG_VERBOSE, "Verify TRMTSK is clear in SPC-2 and later"); if (task->datain.data[3] & 0x40 && std_inq->version >= 4) { logging(LOG_NORMAL, "[FAILED] TRMTSK is set but this device " "reports SPC-2 or later."); CU_FAIL("TRMTSK is set but SPC-2+ is claimed"); } if (task != NULL) { scsi_free_scsi_task(task); task = NULL; } } libiscsi-1.17.0/test-tool/test_inquiry_supported_vpd.c000066400000000000000000000045651271371262700232360ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 by Ronnie Sahlberg 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, see . */ #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-support.h" #include "iscsi-test-cu.h" void test_inquiry_supported_vpd(void) { int ret, i; struct scsi_inquiry_supported_pages *sup_inq; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test INQUIRY supported VPD pages"); logging(LOG_VERBOSE, "Verify we can read the SUPPORTED VPD page"); ret = inquiry(sd, &task, 1, SCSI_INQUIRY_PAGECODE_SUPPORTED_VPD_PAGES, 255, EXPECT_STATUS_GOOD); CU_ASSERT_EQUAL(ret, 0); logging(LOG_VERBOSE, "Verify we got at least 4 bytes of data"); CU_ASSERT(task->datain.size >= 4); logging(LOG_VERBOSE, "Verify we can unmarshall the DATA-IN buffer"); sup_inq = scsi_datain_unmarshall(task); CU_ASSERT_NOT_EQUAL(sup_inq, NULL); if (sup_inq == NULL) { logging(LOG_NORMAL, "[FAILED] Failed to unmarshall DATA-IN " "buffer"); return; } logging(LOG_VERBOSE, "Verify we read all the supported pages"); for (i = 0; i < sup_inq->num_pages; i++) { logging(LOG_VERBOSE, "Verify we can read page 0x%02x", sup_inq->pages[i]); ret = inquiry(sd, NULL, 1, sup_inq->pages[i], 255, EXPECT_STATUS_GOOD); CU_ASSERT_EQUAL(ret, 0); } if (task != NULL) { scsi_free_scsi_task(task); task = NULL; } } libiscsi-1.17.0/test-tool/test_inquiry_version_descriptors.c000066400000000000000000000120441271371262700244350ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 by Ronnie Sahlberg 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, see . */ #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-support.h" #include "iscsi-test-cu.h" void test_inquiry_version_descriptors(void) { int i, claimed_ok; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test of the INQUIRY version descriptors"); switch (inq->device_type) { case SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_DIRECT_ACCESS: logging(LOG_VERBOSE, "Device is a block device"); logging(LOG_VERBOSE, "Verify it claim some version of SPC"); claimed_ok = 0; for (i = 0; i < 8; i++) { switch(inq->version_descriptor[i]) { case SCSI_VERSION_DESCRIPTOR_SPC: case SCSI_VERSION_DESCRIPTOR_SPC_ANSI_INCITS_301_1997: case SCSI_VERSION_DESCRIPTOR_SPC_T10_0995_D_R11A: case SCSI_VERSION_DESCRIPTOR_SPC_2: case SCSI_VERSION_DESCRIPTOR_SPC_2_ISO_IEC_14776_452: case SCSI_VERSION_DESCRIPTOR_SPC_2_ANSI_INCITS_351_2001: case SCSI_VERSION_DESCRIPTOR_SPC_2_T10_1236_D_R20: case SCSI_VERSION_DESCRIPTOR_SPC_2_T10_1236_D_R12: case SCSI_VERSION_DESCRIPTOR_SPC_2_T10_1236_D_R18: case SCSI_VERSION_DESCRIPTOR_SPC_2_T10_1236_D_R19: case SCSI_VERSION_DESCRIPTOR_SPC_3: case SCSI_VERSION_DESCRIPTOR_SPC_3_ISO_IEC_14776_453: case SCSI_VERSION_DESCRIPTOR_SPC_3_ANSI_INCITS_408_2005: case SCSI_VERSION_DESCRIPTOR_SPC_3_T10_1416_D_R7: case SCSI_VERSION_DESCRIPTOR_SPC_3_T10_1416_D_R21: case SCSI_VERSION_DESCRIPTOR_SPC_3_T10_1416_D_R22: case SCSI_VERSION_DESCRIPTOR_SPC_3_T10_1416_D_R23: case SCSI_VERSION_DESCRIPTOR_SPC_4: case SCSI_VERSION_DESCRIPTOR_SPC_4_T10_1731_D_R16: case SCSI_VERSION_DESCRIPTOR_SPC_4_T10_1731_D_R18: case SCSI_VERSION_DESCRIPTOR_SPC_4_T10_1731_D_R23: claimed_ok = 1; break; } } if (claimed_ok == 0) { logging(LOG_NORMAL, "[WARNING] Block device " "did not claim any version of SPC"); } else { logging(LOG_VERBOSE, "[SUCCESS] Block device " "claimed a version of SPC"); } logging(LOG_VERBOSE, "Verify it claim some version of SBC"); claimed_ok = 0; for (i = 0; i < 8; i++) { switch(inq->version_descriptor[i]) { case SCSI_VERSION_DESCRIPTOR_SBC: case SCSI_VERSION_DESCRIPTOR_SBC_ANSI_INCITS_306_1998: case SCSI_VERSION_DESCRIPTOR_SBC_T10_0996_D_R08C: case SCSI_VERSION_DESCRIPTOR_SBC_2: case SCSI_VERSION_DESCRIPTOR_SBC_2_ISO_IEC_14776_322: case SCSI_VERSION_DESCRIPTOR_SBC_2_ANSI_INCITS_405_2005: case SCSI_VERSION_DESCRIPTOR_SBC_2_T10_1417_D_R16: case SCSI_VERSION_DESCRIPTOR_SBC_2_T10_1417_D_R5A: case SCSI_VERSION_DESCRIPTOR_SBC_2_T10_1417_D_R15: case SCSI_VERSION_DESCRIPTOR_SBC_3: claimed_ok = 1; break; } } if (claimed_ok == 0) { logging(LOG_NORMAL, "[WARNING] Block device " "did not claim any version of SBC"); } else { logging(LOG_VERBOSE, "[SUCCESS] Block device " "claimed a version of SBC"); } break; default: logging(LOG_VERBOSE, "No version descriptor tests for device" " type %d yet.", inq->device_type); } } libiscsi-1.17.0/test-tool/test_iscsi_cmdsn_toohigh.c000066400000000000000000000064051271371262700225720ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 by Ronnie Sahlberg 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, see . */ #include #include #include #include "iscsi.h" #include "iscsi-private.h" #include "scsi-lowlevel.h" #include "iscsi-test-cu.h" static int change_cmdsn; static int my_iscsi_queue_pdu(struct iscsi_context *iscsi, struct iscsi_pdu *pdu) { switch (change_cmdsn) { case 1: /* change the cmdsn so it becomes too big */ scsi_set_uint32(&pdu->outdata.data[24], iscsi->maxcmdsn + 1); /* fudge the cmdsn value back to where it should be if this * pdu is ignored. */ iscsi->cmdsn = iscsi->expcmdsn; break; } change_cmdsn = 0; return 0; } void test_iscsi_cmdsn_toohigh(void) { int ret; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test sending invalid iSCSI CMDSN"); logging(LOG_VERBOSE, "CMDSN MUST be in the range EXPCMDSN and MAXCMDSN"); if (sd->iscsi_ctx == NULL) { const char *err = "[SKIPPED] This test is " "only supported for iSCSI backends"; logging(LOG_NORMAL, "%s", err); CU_PASS(err); return; } logging(LOG_VERBOSE, "RFC3720:3.2.2.1 CMDSN > MAXCMDSN must be silently ignored by the target"); logging(LOG_VERBOSE, "Send a TESTUNITREADY with CMDSN == MAXCMDSN+1. Should be ignored by the target."); sd->iscsi_ctx->use_immediate_data = ISCSI_IMMEDIATE_DATA_NO; sd->iscsi_ctx->target_max_recv_data_segment_length = block_size; local_iscsi_queue_pdu = my_iscsi_queue_pdu; change_cmdsn = 1; /* we don't want autoreconnect since some targets will incorrectly * drop the connection on this condition. */ iscsi_set_noautoreconnect(sd->iscsi_ctx, 1); iscsi_set_timeout(sd->iscsi_ctx, 3); ret = testunitready(sd, EXPECT_STATUS_GOOD); CU_ASSERT_EQUAL(ret, -1); if (ret == -1) { logging(LOG_VERBOSE, "[SUCCESS] We did not receive a reply"); } else { logging(LOG_VERBOSE, "[FAILURE] We got a response from the target but SMDSN was outside of the window."); } iscsi_set_noautoreconnect(sd->iscsi_ctx, 0); logging(LOG_VERBOSE, "Send a TESTUNITREADY with CMDSN == EXPCMDSN. should work again"); TESTUNITREADY(sd, EXPECT_STATUS_GOOD); } libiscsi-1.17.0/test-tool/test_iscsi_cmdsn_toolow.c000066400000000000000000000064051271371262700224540ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 by Ronnie Sahlberg 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, see . */ #include #include #include #include "iscsi.h" #include "iscsi-private.h" #include "scsi-lowlevel.h" #include "iscsi-test-cu.h" static int change_cmdsn; static int my_iscsi_queue_pdu(struct iscsi_context *iscsi, struct iscsi_pdu *pdu) { switch (change_cmdsn) { case 1: /* change the cmdsn so it becomes too big */ scsi_set_uint32(&pdu->outdata.data[24], iscsi->expcmdsn - 1); /* fudge the cmdsn value back to where it should be if this * pdu is ignored. */ iscsi->cmdsn = iscsi->expcmdsn; break; } change_cmdsn = 0; return 0; } void test_iscsi_cmdsn_toolow(void) { int ret; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test sending invalid iSCSI CMDSN"); logging(LOG_VERBOSE, "CMDSN MUST be in the range EXPCMDSN and MAXCMDSN"); if (sd->iscsi_ctx == NULL) { const char *err = "[SKIPPED] This test is " "only supported for iSCSI backends"; logging(LOG_NORMAL, "%s", err); CU_PASS(err); return; } logging(LOG_VERBOSE, "RFC3720:3.2.2.1 CMDSN < EXPCMDSN must be silently ignored by the target"); logging(LOG_VERBOSE, "Send a TESTUNITREADY with CMDSN == EXPCMDSN-1. Should be ignored by the target."); sd->iscsi_ctx->use_immediate_data = ISCSI_IMMEDIATE_DATA_NO; sd->iscsi_ctx->target_max_recv_data_segment_length = block_size; local_iscsi_queue_pdu = my_iscsi_queue_pdu; change_cmdsn = 1; /* we don't want autoreconnect since some targets will incorrectly * drop the connection on this condition. */ iscsi_set_noautoreconnect(sd->iscsi_ctx, 1); iscsi_set_timeout(sd->iscsi_ctx, 3); ret = testunitready(sd, EXPECT_STATUS_GOOD); CU_ASSERT_EQUAL(ret, -1); if (ret == -1) { logging(LOG_VERBOSE, "[SUCCESS] We did not receive a reply"); } else { logging(LOG_VERBOSE, "[FAILURE] We got a response from the target but SMDSN was outside of the window."); } iscsi_set_noautoreconnect(sd->iscsi_ctx, 0); logging(LOG_VERBOSE, "Send a TESTUNITREADY with CMDSN == EXPCMDSN. should work again"); TESTUNITREADY(sd, EXPECT_STATUS_GOOD); } libiscsi-1.17.0/test-tool/test_iscsi_datasn_invalid.c000066400000000000000000000136561271371262700227330ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2014 by Ronnie Sahlberg 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, see . */ #include #include #include #include "iscsi.h" #include "iscsi-private.h" #include "scsi-lowlevel.h" #include "iscsi-test-cu.h" static int change_datasn; static int my_iscsi_queue_pdu(struct iscsi_context *iscsi _U_, struct iscsi_pdu *pdu _U_) { uint32_t datasn; if (pdu->outdata.data[0] != ISCSI_PDU_DATA_OUT) { return 0; } switch (change_datasn) { case 1: /* change DataSN to 0 */ scsi_set_uint32(&pdu->outdata.data[36], 0); break; case 2: /* change DataSN to 27 */ scsi_set_uint32(&pdu->outdata.data[36], 27); break; case 3: /* change DataSN to -1 */ scsi_set_uint32(&pdu->outdata.data[36], -1); break; case 4: /* change DataSN from (0,1) to (1,0) */ datasn = scsi_get_uint32(&pdu->outdata.data[36]); scsi_set_uint32(&pdu->outdata.data[36], 1 - datasn); break; } return 0; } void test_iscsi_datasn_invalid(void) { int ret; CHECK_FOR_DATALOSS; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test sending invalid iSCSI DataSN"); if (sd->iscsi_ctx == NULL) { const char *err = "[SKIPPED] This test is " "only supported for iSCSI backends"; logging(LOG_NORMAL, "%s", err); CU_PASS(err); return; } logging(LOG_VERBOSE, "Send two Data-Out PDU's with DataSN==0. Should fail."); change_datasn = 1; sd->iscsi_ctx->use_immediate_data = ISCSI_IMMEDIATE_DATA_NO; sd->iscsi_ctx->target_max_recv_data_segment_length = block_size; local_iscsi_queue_pdu = my_iscsi_queue_pdu; iscsi_set_noautoreconnect(sd->iscsi_ctx, 1); iscsi_set_timeout(sd->iscsi_ctx, 3); memset(scratch, 0xa6, 2 * block_size); ret = write10(sd, 100, 2 * block_size, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); if (ret == -2) { logging(LOG_NORMAL, "[SKIPPED] WRITE10 is not implemented."); CU_PASS("WRITE10 is not implemented."); local_iscsi_queue_pdu = NULL; return; } CU_ASSERT_NOT_EQUAL(ret, 0); iscsi_set_noautoreconnect(sd->iscsi_ctx, 0); logging(LOG_VERBOSE, "Send Data-Out PDU with DataSN==27. Should fail"); change_datasn = 2; sd->iscsi_ctx->use_immediate_data = ISCSI_IMMEDIATE_DATA_NO; sd->iscsi_ctx->target_max_recv_data_segment_length = block_size; local_iscsi_queue_pdu = my_iscsi_queue_pdu; iscsi_set_noautoreconnect(sd->iscsi_ctx, 1); iscsi_set_timeout(sd->iscsi_ctx, 3); ret = write10(sd, 100, block_size, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); if (ret == -2) { logging(LOG_NORMAL, "[SKIPPED] WRITE10 is not implemented."); CU_PASS("WRITE10 is not implemented."); local_iscsi_queue_pdu = NULL; return; } CU_ASSERT_NOT_EQUAL(ret, 0); iscsi_set_noautoreconnect(sd->iscsi_ctx, 0); logging(LOG_VERBOSE, "Send Data-Out PDU with DataSN==-1. Should fail"); change_datasn = 3; sd->iscsi_ctx->use_immediate_data = ISCSI_IMMEDIATE_DATA_NO; sd->iscsi_ctx->target_max_recv_data_segment_length = block_size; local_iscsi_queue_pdu = my_iscsi_queue_pdu; iscsi_set_noautoreconnect(sd->iscsi_ctx, 1); iscsi_set_timeout(sd->iscsi_ctx, 3); ret = write10(sd, 100, block_size, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); if (ret == -2) { logging(LOG_NORMAL, "[SKIPPED] WRITE10 is not implemented."); CU_PASS("WRITE10 is not implemented."); local_iscsi_queue_pdu = NULL; return; } CU_ASSERT_NOT_EQUAL(ret, 0); iscsi_set_noautoreconnect(sd->iscsi_ctx, 0); logging(LOG_VERBOSE, "Send Data-Out PDU's in reverse order (DataSN == 1,0). Should fail"); change_datasn = 4; sd->iscsi_ctx->use_immediate_data = ISCSI_IMMEDIATE_DATA_NO; sd->iscsi_ctx->target_max_recv_data_segment_length = block_size; local_iscsi_queue_pdu = my_iscsi_queue_pdu; iscsi_set_noautoreconnect(sd->iscsi_ctx, 1); iscsi_set_timeout(sd->iscsi_ctx, 3); ret = write10(sd, 100, 2 * block_size, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); if (ret == -2) { logging(LOG_NORMAL, "[SKIPPED] WRITE10 is not implemented."); CU_PASS("WRITE10 is not implemented."); local_iscsi_queue_pdu = NULL; return; } CU_ASSERT_NOT_EQUAL(ret, 0); local_iscsi_queue_pdu = NULL; iscsi_set_noautoreconnect(sd->iscsi_ctx, 0); } libiscsi-1.17.0/test-tool/test_mandatory_sbc.c000066400000000000000000000050351271371262700213760ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 by Ronnie Sahlberg 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, see . */ #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-support.h" #include "iscsi-test-cu.h" void test_mandatory_sbc(void) { int ret; //unsigned char buf[4096]; //struct unmap_list list[1]; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test support for all mandatory opcodes on SBC devices"); CHECK_FOR_SBC; logging(LOG_VERBOSE, "Test INQUIRY."); ret = inquiry(sd, NULL, 0, 0, 255, EXPECT_STATUS_GOOD); CU_ASSERT_EQUAL(ret, 0); logging(LOG_VERBOSE, "Test READCAPACITY10."); ret = readcapacity10(sd, NULL, 0, 0, EXPECT_STATUS_GOOD); CU_ASSERT_EQUAL(ret, 0); if (sbc3_support) { logging(LOG_VERBOSE, "Test READCAPACITY16. The device claims SBC-3 support."); ret = readcapacity16(sd, NULL, 15, EXPECT_STATUS_GOOD); CU_ASSERT_EQUAL(ret, 0); } logging(LOG_VERBOSE, "Test READ10."); ret = read10(sd, NULL, 0, block_size, block_size, 0, 0, 0, 0, 0, NULL, EXPECT_STATUS_GOOD); CU_ASSERT_EQUAL(ret, 0); if (sbc3_support) { logging(LOG_VERBOSE, "Test READ16. the device claims SBC-3 support."); ret = read16(sd, NULL, 0, block_size, block_size, 0, 0, 0, 0, 0, NULL, EXPECT_STATUS_GOOD); CU_ASSERT_EQUAL(ret, 0); } logging(LOG_VERBOSE, "Test TESTUNITREADY."); ret = testunitready(sd, EXPECT_STATUS_GOOD); CU_ASSERT_EQUAL(ret, 0); } libiscsi-1.17.0/test-tool/test_modesense6_all_pages.c000066400000000000000000000044621271371262700226330ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 by Ronnie Sahlberg 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, see . */ #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-support.h" #include "iscsi-test-cu.h" void test_modesense6_all_pages(void) { struct scsi_mode_sense *ms; struct scsi_task *ms_task = NULL; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test of MODESENSE6 AllPages"); logging(LOG_VERBOSE, "Send MODESENSE6 command to fetch AllPages"); MODESENSE6(sd, &ms_task, 0, SCSI_MODESENSE_PC_CURRENT, SCSI_MODEPAGE_RETURN_ALL_PAGES, 0, 255, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "[SUCCESS] All Pages fetched."); logging(LOG_VERBOSE, "Try to unmarshall the DATA-IN buffer."); ms = scsi_datain_unmarshall(ms_task); if (ms == NULL) { logging(LOG_VERBOSE, "[FAILED] failed to unmarshall mode sense " "datain buffer"); CU_FAIL("[FAILED] Failed to unmarshall the data-in buffer."); scsi_free_scsi_task(ms_task); return; } logging(LOG_VERBOSE, "[SUCCESS] Unmarshalling successful."); logging(LOG_VERBOSE, "Verify that mode data length is >= 3"); if (ms->mode_data_length >= 3) { logging(LOG_VERBOSE, "[SUCCESS] Mode data length is >= 3"); } else { logging(LOG_VERBOSE, "[FAILED] Mode data length is < 3"); } CU_ASSERT_TRUE(ms->mode_data_length >= 3); scsi_free_scsi_task(ms_task); } libiscsi-1.17.0/test-tool/test_modesense6_control.c000066400000000000000000000243541271371262700223660ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2015 by Ronnie Sahlberg 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, see . */ #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-support.h" #include "iscsi-test-cu.h" void test_modesense6_control(void) { struct scsi_mode_sense *ms; struct scsi_mode_page *ap_page; struct scsi_mode_page *ct_page; struct scsi_task *ap_task = NULL; struct scsi_task *ct_task = NULL; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test of MODESENSE6 CONTROL page"); logging(LOG_VERBOSE, "Fetch the CONTROL page via AllPages"); logging(LOG_VERBOSE, "Send MODESENSE6 command to fetch AllPages"); MODESENSE6(sd, &ap_task, 0, SCSI_MODESENSE_PC_CURRENT, SCSI_MODEPAGE_RETURN_ALL_PAGES, 0, 255, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "[SUCCESS] All Pages fetched."); logging(LOG_VERBOSE, "Try to unmarshall the DATA-IN buffer."); ms = scsi_datain_unmarshall(ap_task); if (ms == NULL) { logging(LOG_NORMAL, "[FAILED] failed to unmarshall mode sense " "datain buffer"); CU_FAIL("[FAILED] Failed to unmarshall the data-in buffer."); goto finished; } logging(LOG_VERBOSE, "[SUCCESS] Unmarshalling successful."); logging(LOG_VERBOSE, "Verify that mode data length is >= 3"); if (ms->mode_data_length >= 3) { logging(LOG_VERBOSE, "[SUCCESS] Mode data length is >= 3"); } else { logging(LOG_NORMAL, "[FAILED] Mode data length is < 3"); } CU_ASSERT_TRUE(ms->mode_data_length >= 3); for (ap_page = ms->pages; ap_page; ap_page = ap_page->next) { if (ap_page->page_code == SCSI_MODEPAGE_CONTROL && ap_page->spf == 0) { break; } } if(ap_page == NULL) { logging(LOG_NORMAL, "[WARNING] CONTROL page was not returned " "from AllPages. All devices SHOULD implement this " "page."); } logging(LOG_VERBOSE, "Fetch the CONTROL page directly"); logging(LOG_VERBOSE, "Send MODESENSE6 command to fetch CONTROL"); MODESENSE6(sd, &ct_task, 0, SCSI_MODESENSE_PC_CURRENT, SCSI_MODEPAGE_CONTROL, 0, 255, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "[SUCCESS] CONTROL page fetched."); logging(LOG_VERBOSE, "Try to unmarshall the DATA-IN buffer."); ms = scsi_datain_unmarshall(ct_task); if (ms == NULL) { logging(LOG_NORMAL, "[FAILED] failed to unmarshall mode sense " "datain buffer"); CU_FAIL("[FAILED] Failed to unmarshall the data-in buffer."); goto finished; } logging(LOG_VERBOSE, "[SUCCESS] Unmarshalling successful."); logging(LOG_VERBOSE, "Verify that mode data length is >= 3"); if (ms->mode_data_length >= 3) { logging(LOG_VERBOSE, "[SUCCESS] Mode data length is >= 3"); } else { logging(LOG_NORMAL, "[FAILED] Mode data length is < 3"); } CU_ASSERT_TRUE(ms->mode_data_length >= 3); for (ct_page = ms->pages; ct_page; ct_page = ct_page->next) { if (ct_page->page_code == SCSI_MODEPAGE_CONTROL) { break; } } if(ct_page == NULL) { logging(LOG_NORMAL, "[WARNING] CONTROL page was not returned." "All devices SHOULD implement this page."); } if (ap_page == NULL && ct_page != NULL) { logging(LOG_NORMAL, "[FAILED] CONTROL page was not returned " "from AllPages."); CU_FAIL("[FAILED] CONTROL page is missing from AllPages"); goto finished; } if (ap_page != NULL && ct_page == NULL) { logging(LOG_NORMAL, "[FAILED] CONTROL page is only available " "from AllPages but not directly."); CU_FAIL("[FAILED] CONTROL page is missing"); goto finished; } if (ct_page == NULL) { logging(LOG_NORMAL, "[SKIPPED] CONTROL page is not " "implemented."); CU_PASS("CONTROL page is not implemented."); goto finished; } logging(LOG_VERBOSE, "Verify that the two pages are identical."); logging(LOG_VERBOSE, "Check TST field"); CU_ASSERT_EQUAL(ct_page->control.tst, ap_page->control.tst); logging(LOG_VERBOSE, "Check TMF_ONLY field"); CU_ASSERT_EQUAL(ct_page->control.tmf_only, ap_page->control.tmf_only); logging(LOG_VERBOSE, "Check dpicz field"); CU_ASSERT_EQUAL(ct_page->control.dpicz, ap_page->control.dpicz); logging(LOG_VERBOSE, "Check d_sense field"); CU_ASSERT_EQUAL(ct_page->control.d_sense, ap_page->control.d_sense); logging(LOG_VERBOSE, "Check gltsd field"); CU_ASSERT_EQUAL(ct_page->control.gltsd, ap_page->control.gltsd); logging(LOG_VERBOSE, "Check rlec field"); CU_ASSERT_EQUAL(ct_page->control.rlec, ap_page->control.rlec); logging(LOG_VERBOSE, "Check queue_algorithm_modifier field"); CU_ASSERT_EQUAL(ct_page->control.queue_algorithm_modifier, ap_page->control.queue_algorithm_modifier); logging(LOG_VERBOSE, "Check nuar field"); CU_ASSERT_EQUAL(ct_page->control.nuar, ap_page->control.nuar); logging(LOG_VERBOSE, "Check qerr field"); CU_ASSERT_EQUAL(ct_page->control.qerr, ap_page->control.qerr); logging(LOG_VERBOSE, "Check vs field"); CU_ASSERT_EQUAL(ct_page->control.vs, ap_page->control.vs); logging(LOG_VERBOSE, "Check rac field"); CU_ASSERT_EQUAL(ct_page->control.rac, ap_page->control.rac); logging(LOG_VERBOSE, "Check ua_intlck_ctrl field"); CU_ASSERT_EQUAL(ct_page->control.ua_intlck_ctrl, ap_page->control.ua_intlck_ctrl); logging(LOG_VERBOSE, "Check swp field"); CU_ASSERT_EQUAL(ct_page->control.swp, ap_page->control.swp); logging(LOG_VERBOSE, "Check ato field"); CU_ASSERT_EQUAL(ct_page->control.ato, ap_page->control.ato); logging(LOG_VERBOSE, "Check tas field"); CU_ASSERT_EQUAL(ct_page->control.tas, ap_page->control.tas); logging(LOG_VERBOSE, "Check atmpe field"); CU_ASSERT_EQUAL(ct_page->control.atmpe, ap_page->control.atmpe); logging(LOG_VERBOSE, "Check rwwp field"); CU_ASSERT_EQUAL(ct_page->control.rwwp, ap_page->control.rwwp); logging(LOG_VERBOSE, "Check autoload_mode field"); CU_ASSERT_EQUAL(ct_page->control.autoload_mode, ap_page->control.autoload_mode); logging(LOG_VERBOSE, "Check busy_timeout_period field"); CU_ASSERT_EQUAL(ct_page->control.busy_timeout_period, ap_page->control.busy_timeout_period); logging(LOG_VERBOSE, "Check extended_selftest_completion_time field"); CU_ASSERT_EQUAL(ct_page->control.extended_selftest_completion_time, ap_page->control.extended_selftest_completion_time); logging(LOG_VERBOSE, "Verify that the values are sane."); logging(LOG_VERBOSE, "Check that TST is 0 or 1."); if (ct_page->control.tst > 1) { logging(LOG_NORMAL, "[FAILED] TST value is invalid. Must be " "0, 1 but was %d", ct_page->control.tst); CU_FAIL("[FAILED] TST is invalid."); } logging(LOG_VERBOSE, "Check that QUEUE_ALGORITHM_MODIFIER is " "0, 1 or >7"); if (ct_page->control.queue_algorithm_modifier > 1 && ct_page->control.queue_algorithm_modifier < 8) { logging(LOG_NORMAL, "[FAILED] QUEUE_ALGORITHM_MODIFIER value " "is invalid. Must be 0, 1 or >7 but was %d", ct_page->control.queue_algorithm_modifier); CU_FAIL("[FAILED] QUEUE_ALGORITHM_MODIFIER is invalid."); } logging(LOG_VERBOSE, "Check that QERR is not 2"); if (ct_page->control.qerr == 2) { logging(LOG_NORMAL, "[FAILED] QERR value " "is invalid. Can not be 2"); CU_FAIL("[FAILED] QERR is invalid."); } logging(LOG_VERBOSE, "Check that UA_INTLCK_CTRL is not 1"); if (ct_page->control.ua_intlck_ctrl == 1) { logging(LOG_NORMAL, "[FAILED] UA_INTLCK_CTRL value " "is invalid. Can not be 1"); CU_FAIL("[FAILED] UA_INTLCK_CTRL is invalid."); } logging(LOG_VERBOSE, "Check that AUTOLOAD is 0, 1 or 2"); if (ct_page->control.autoload_mode > 2) { logging(LOG_NORMAL, "[FAILED] AUTOLOAD value " "is invalid. Must be 0, 1 or 2 but was %d", ct_page->control.autoload_mode); CU_FAIL("[FAILED] AUTOLOAD is invalid."); } logging(LOG_VERBOSE, "Check that BUSY_TIMEOUT_PERIOD is specified"); if (ct_page->control.busy_timeout_period == 0) { logging(LOG_NORMAL, "[WARNING] BUSY_TIMEOUT_PERIOD is " "undefined."); } finished: if (ap_task != NULL) { scsi_free_scsi_task(ap_task); } if (ct_task != NULL) { scsi_free_scsi_task(ct_task); } } libiscsi-1.17.0/test-tool/test_modesense6_control_d_sense.c000066400000000000000000000105451271371262700240630ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2015 by Ronnie Sahlberg 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, see . */ #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-support.h" #include "iscsi-test-cu.h" void test_modesense6_control_d_sense(void) { struct scsi_task *ms_task = NULL; struct scsi_task *r16_task = NULL; struct scsi_mode_sense *ms; struct scsi_mode_page *page; int ret; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test of MODESENSE6 CONTROL D_SENSE flag"); logging(LOG_VERBOSE, "Read the CONTROL page from the device"); ret = modesense6(sd, &ms_task, 0, SCSI_MODESENSE_PC_CURRENT, SCSI_MODEPAGE_CONTROL, 0, 255, EXPECT_STATUS_GOOD); if (ret != 0) { logging(LOG_NORMAL,"[WARNING] Could not read " "BlockDeviceCharacteristics."); goto finished; } logging(LOG_VERBOSE, "[SUCCESS] CONTROL page fetched."); logging(LOG_VERBOSE, "Try to unmarshall the DATA-IN buffer."); ms = scsi_datain_unmarshall(ms_task); if (ms == NULL) { logging(LOG_NORMAL, "[FAILED] failed to unmarshall mode sense " "datain buffer"); CU_FAIL("[FAILED] Failed to unmarshall the data-in buffer."); goto finished; } logging(LOG_VERBOSE, "[SUCCESS] Unmarshalling successful."); for (page = ms->pages; page; page = page->next) { if (page->page_code == SCSI_MODEPAGE_CONTROL) { break; } } if(page == NULL) { logging(LOG_NORMAL, "[WARNING] CONTROL page was not returned." "All devices SHOULD implement this page."); CU_PASS("[SKIPPED] CONTROL page not reported"); goto finished; } logging(LOG_VERBOSE, "Send a READ16 that will fail so we can check " "the type of sense data returned"); READ16(sd, &r16_task, 0xffffffffffffffffLL, block_size, block_size, 0, 0, 0, 0, 0, NULL, EXPECT_LBA_OOB); if (page->control.d_sense) { logging(LOG_VERBOSE, "D_SENSE is set, verify that sense format " "is descriptor format"); } else { logging(LOG_VERBOSE, "D_SENSE is clear, verify that sense format " "is fixed format"); } switch (r16_task->sense.error_type) { case SCSI_SENSE_DESCRIPTOR_CURRENT: case SCSI_SENSE_DESCRIPTOR_DEFERRED_ERRORS: if (!page->control.d_sense) { logging(LOG_NORMAL, "[FAILED] D_SENSE is set but " "returned sense is not descriptor format"); CU_FAIL("[FAILED] Wrong type of sense format returned"); goto finished; } break; case SCSI_SENSE_FIXED_CURRENT: case SCSI_SENSE_FIXED_DEFERRED_ERRORS: if (page->control.d_sense) { logging(LOG_NORMAL, "[FAILED] D_SENSE is cleat but " "returned sense is not fixed format"); CU_FAIL("[FAILED] Wrong type of sense format returned"); goto finished; } break; } finished: if (ms_task != NULL) { scsi_free_scsi_task(ms_task); } if (r16_task != NULL) { scsi_free_scsi_task(r16_task); } } libiscsi-1.17.0/test-tool/test_modesense6_control_swp.c000066400000000000000000000071031271371262700232500ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2015 by Ronnie Sahlberg 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, see . */ #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-support.h" #include "iscsi-test-cu.h" void test_modesense6_control_swp(void) { struct scsi_task *ms_task = NULL; struct scsi_mode_sense *ms; struct scsi_mode_page *page; int ret; CHECK_FOR_DATALOSS; CHECK_FOR_SBC; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test of MODESENSE6 CONTROL SWP flag"); logging(LOG_VERBOSE, "Set SWP to enable write protect"); ret = set_swp(sd); if (ret == -2) { CU_PASS("[SKIPPED] Target does not support changing SWP"); return; } CU_ASSERT_EQUAL(ret, 0); if (ret) { goto finished; } logging(LOG_VERBOSE, "Read the CONTROL page back from the device"); MODESENSE6(sd, &ms_task, 0, SCSI_MODESENSE_PC_CURRENT, SCSI_MODEPAGE_CONTROL, 0, 255, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "[SUCCESS] CONTROL page fetched."); logging(LOG_VERBOSE, "Try to unmarshall the DATA-IN buffer."); ms = scsi_datain_unmarshall(ms_task); if (ms == NULL) { logging(LOG_NORMAL, "[FAILED] failed to unmarshall mode sense " "datain buffer"); CU_FAIL("[FAILED] Failed to unmarshall the data-in buffer."); goto finished; } logging(LOG_VERBOSE, "[SUCCESS] Unmarshalling successful."); for (page = ms->pages; page; page = page->next) { if (page->page_code == SCSI_MODEPAGE_CONTROL) { break; } } if(page == NULL) { logging(LOG_NORMAL, "[WARNING] CONTROL page was not returned." "All devices SHOULD implement this page."); } logging(LOG_VERBOSE, "Verify that the SWP bit is set"); if (page->control.swp == 0) { logging(LOG_NORMAL, "[FAILED] SWP bit is not set"); CU_FAIL("[FAILED] SWP is not set"); goto finished; } logging(LOG_VERBOSE, "[SUCCESS] SWP was set successfully"); logging(LOG_VERBOSE, "Read a block from the now Read-Only device"); READ10(sd, NULL, 0, block_size, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Try to write a block to the Read-Only device"); WRITE10(sd, 0, block_size, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_WRITE_PROTECTED); finished: if (ms_task != NULL) { scsi_free_scsi_task(ms_task); } logging(LOG_VERBOSE, "Clear SWP to disable write protect"); clear_swp(sd); } libiscsi-1.17.0/test-tool/test_modesense6_residuals.c000066400000000000000000000102451271371262700226730ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 by Ronnie Sahlberg 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, see . */ #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-support.h" #include "iscsi-test-cu.h" void test_modesense6_residuals(void) { struct scsi_task *ms_task = NULL; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test of MODESENSE6 Residuals"); logging(LOG_VERBOSE, "MODESENSE6 command should not result in any " "residuals"); logging(LOG_VERBOSE, "Try a MODESENSE6 command with 4 bytes of " "transfer length and verify that we don't get residuals."); MODESENSE6(sd, &ms_task, 0, SCSI_MODESENSE_PC_CURRENT, SCSI_MODEPAGE_RETURN_ALL_PAGES, 0, 4, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "[SUCCESS] All Pages fetched."); logging(LOG_VERBOSE, "Verify that we got at most 4 bytes of DATA-IN"); if (ms_task->datain.size > 4) { logging(LOG_NORMAL, "[FAILED] got more than 4 bytes of " "DATA-IN."); } else { logging(LOG_VERBOSE, "[SUCCESS] <= 4 bytes of DATA-IN " "received."); } CU_ASSERT_TRUE(ms_task->datain.size <= 4); logging(LOG_VERBOSE, "Verify residual overflow flag not set"); if (ms_task->residual_status == SCSI_RESIDUAL_OVERFLOW) { logging(LOG_VERBOSE, "[FAILED] Target set residual " "overflow flag"); } CU_ASSERT_NOT_EQUAL(ms_task->residual_status, SCSI_RESIDUAL_OVERFLOW); logging(LOG_VERBOSE, "Try a MODESENSE6 command with 255 bytes of " "transfer length and verify that we get residuals if the target returns less than the requested amount of data."); scsi_free_scsi_task(ms_task); MODESENSE6(sd, &ms_task, 0, SCSI_MODESENSE_PC_CURRENT, SCSI_MODEPAGE_RETURN_ALL_PAGES, 0, 255, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "[SUCCESS] All Pages fetched."); if (ms_task->datain.size == 255) { logging(LOG_VERBOSE, "We got all 255 bytes of data back " "from the target. Verify that underflow is not set."); if (ms_task->residual_status == SCSI_RESIDUAL_UNDERFLOW) { logging(LOG_VERBOSE, "[FAILED] Target set residual " "underflow flag"); } else { logging(LOG_VERBOSE, "[SUCCESS] Residual underflow " "is not set"); } CU_ASSERT_NOT_EQUAL(ms_task->residual_status, SCSI_RESIDUAL_UNDERFLOW); } else { logging(LOG_VERBOSE, "We got less than the requested 255 bytes " "from the target. Verify that underflow is set."); if (ms_task->residual_status != SCSI_RESIDUAL_UNDERFLOW) { logging(LOG_VERBOSE, "[FAILED] Target did not set " "residual underflow flag"); } else { logging(LOG_VERBOSE, "[SUCCESS] Residual underflow " "is set"); } CU_ASSERT_EQUAL(ms_task->residual_status, SCSI_RESIDUAL_UNDERFLOW); } scsi_free_scsi_task(ms_task); } libiscsi-1.17.0/test-tool/test_multipathio_async_caw.c000066400000000000000000000115701271371262700231400ustar00rootroot00000000000000/* Copyright (C) SUSE LINUX GmbH 2016 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, see . */ #include #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-support.h" #include "iscsi-test-cu.h" #include "iscsi-multipath.h" struct test_mpio_async_caw_state { uint32_t dispatched; uint32_t completed; uint32_t mismatches; }; static void test_mpio_async_caw_cb(struct iscsi_context *iscsi __attribute__((unused)), int status, void *command_data, void *private_data) { struct scsi_task *atask = command_data; struct test_mpio_async_caw_state *state = private_data; state->completed++; if (status == SCSI_STATUS_CHECK_CONDITION) { CU_ASSERT_EQUAL(atask->sense.key, SCSI_SENSE_MISCOMPARE); CU_ASSERT_EQUAL(atask->sense.ascq, SCSI_SENSE_ASCQ_MISCOMPARE_DURING_VERIFY); state->mismatches++; logging(LOG_VERBOSE, "COMPARE_AND_WRITE mismatch: %d of %d " "(CmdSN=%d)", state->completed, state->dispatched, atask->cmdsn); } else { logging(LOG_VERBOSE, "COMPARE_AND_WRITE success: %d of %d " "(CmdSN=%d)", state->completed, state->dispatched, atask->cmdsn); } scsi_free_scsi_task(atask); } static void test_mpio_async_caw_init_bufs(unsigned char *cmp_buf, unsigned char *wr_buf, int blocksize, int num_mp_sds) { int sd_i; /* * Each compare and write attempts to modify on-disk data with the * assumption that the previous operation was successful. E.g. * * session 0 session 1 * --------- --------- * 0->1 (good) * 1->0 (good) * * This gives us some nice races if the target processes the requests * out of order. E.g. * 0->1 (good) * 1->0 (good) * 1->0 (mismatch!) * 0->1 (good) */ for (sd_i = 0; sd_i < num_mp_sds; sd_i++) { int wr_val; int cmp_val = sd_i; if (sd_i == num_mp_sds - 1) { wr_val = 0; } else { wr_val = sd_i + 1; } memset(&cmp_buf[sd_i * blocksize], cmp_val, blocksize); memset(&wr_buf[sd_i * blocksize], wr_val, blocksize); } } void test_mpio_async_caw(void) { int i, ret; int sd_i; struct test_mpio_async_caw_state state = { 0, 0, 0 }; int blocksize = 512; int num_ios = 1000; uint32_t lba = 0; unsigned char cmp_buf[blocksize * mp_num_sds]; unsigned char wr_buf[blocksize * mp_num_sds]; CHECK_FOR_DATALOSS; CHECK_FOR_SBC; MPATH_SKIP_IF_UNAVAILABLE(mp_sds, mp_num_sds); MPATH_SKIP_UNLESS_ISCSI(mp_sds, mp_num_sds); /* synchronously initialise zeros for first CAW */ memset(wr_buf, 0, block_size); WRITESAME10(mp_sds[0], 0, block_size, 1, 0, 0, 0, 0, wr_buf, EXPECT_STATUS_GOOD); test_mpio_async_caw_init_bufs(cmp_buf, wr_buf, blocksize, mp_num_sds); for (i = 0; i < num_ios; i++) { /* queue a one-block CAW task on each MPIO sessions */ for (sd_i = 0; sd_i < mp_num_sds; sd_i++) { struct scsi_task *atask; int buf_off = sd_i * blocksize; atask = scsi_cdb_compareandwrite(lba, blocksize * 2, blocksize, 0, 0, 0, 0, 0); CU_ASSERT_PTR_NOT_NULL_FATAL(atask); /* compare data is first, followed by write data */ ret = scsi_task_add_data_out_buffer(atask, blocksize, &cmp_buf[buf_off]); CU_ASSERT_EQUAL(ret, 0); ret = scsi_task_add_data_out_buffer(atask, blocksize, &wr_buf[buf_off]); CU_ASSERT_EQUAL(ret, 0); ret = iscsi_scsi_command_async(mp_sds[sd_i]->iscsi_ctx, mp_sds[sd_i]->iscsi_lun, atask, test_mpio_async_caw_cb, NULL, &state); CU_ASSERT_EQUAL(ret, 0); state.dispatched++; logging(LOG_VERBOSE, "COMPARE_AND_WRITE dispatched: " "%d of %d (cmdsn=%d)", state.dispatched, num_ios, atask->cmdsn); } } while (state.completed < state.dispatched) { struct pollfd pfd[mp_num_sds]; for (sd_i = 0; sd_i < mp_num_sds; sd_i++) { pfd[sd_i].fd = iscsi_get_fd(mp_sds[sd_i]->iscsi_ctx); pfd[sd_i].events = iscsi_which_events(mp_sds[sd_i]->iscsi_ctx); } ret = poll(pfd, mp_num_sds, -1); CU_ASSERT_NOT_EQUAL(ret, -1); for (sd_i = 0; sd_i < mp_num_sds; sd_i++) { if (!pfd[sd_i].revents) { continue; } ret = iscsi_service(mp_sds[sd_i]->iscsi_ctx, pfd[sd_i].revents); CU_ASSERT_EQUAL(ret, 0); } } logging(LOG_VERBOSE, "[OK] All %d COMPARE_AND_WRITE IOs complete, with " "%d mismatch(es)", state.completed, state.mismatches); } libiscsi-1.17.0/test-tool/test_multipathio_compareandwrite.c000066400000000000000000000066521271371262700243620ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg Copyright (C) 2015 David Disseldorp 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, see . */ #include #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-support.h" #include "iscsi-test-cu.h" #include "iscsi-multipath.h" void test_multipathio_compareandwrite(void) { int io_bl = 1; /* 1 block CAW IOs */ int path; int i, ret; int maxbl; CHECK_FOR_DATALOSS; CHECK_FOR_SBC; MPATH_SKIP_IF_UNAVAILABLE(mp_sds, mp_num_sds); if (inq_bl) { maxbl = inq_bl->max_cmp; } else { /* Assume we are not limited */ maxbl = 256; } if (maxbl < io_bl) { CU_PASS("[SKIPPED] MAXIMUM_COMPARE_AND_WRITE_LENGTH too small"); return; } logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Initialising data prior to COMPARE_AND_WRITE"); memset(scratch, 0, io_bl * block_size); ret = writesame10(mp_sds[0], 0, block_size, 256, 0, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); if (ret == -2) { CU_PASS("[SKIPPED] Target does not support WRITESAME10. Skipping test"); return; } CU_ASSERT_EQUAL(ret, 0); logging(LOG_VERBOSE, "Test multipath COMPARE_AND_WRITE"); for (i = 0; i < 256; i++) { for (path = 0; path < mp_num_sds; path++) { logging(LOG_VERBOSE, "Test COMPARE_AND_WRITE(%d->%d) using path %d", path, path + 1, path); /* compare data is first half */ memset(scratch, path, io_bl * block_size); /* write data is the second half, wrap around */ memset(scratch + io_bl * block_size, path + 1, io_bl * block_size); COMPAREANDWRITE(mp_sds[path], i, scratch, 2 * io_bl * block_size, block_size, 0, 0, 0, 0, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Test bad COMPARE_AND_WRITE(%d->%d)", path, path + 1); COMPAREANDWRITE(mp_sds[path], i, scratch, 2 * io_bl * block_size, block_size, 0, 0, 0, 0, EXPECT_MISCOMPARE); } } } libiscsi-1.17.0/test-tool/test_multipathio_reset.c000066400000000000000000000052021271371262700223060ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg Copyright (C) 2015 David Disseldorp 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, see . */ #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-support.h" #include "iscsi-test-cu.h" #include "iscsi-multipath.h" void test_multipathio_reset(void) { int reset_path; CHECK_FOR_DATALOSS; CHECK_FOR_SBC; MPATH_SKIP_IF_UNAVAILABLE(mp_sds, mp_num_sds); MPATH_SKIP_UNLESS_ISCSI(mp_sds, mp_num_sds); logging(LOG_VERBOSE, LOG_BLANK_LINE); for (reset_path = 0; reset_path < mp_num_sds; reset_path++) { int num_uas; int ret; int tur_path; struct scsi_device *reset_sd = mp_sds[reset_path]; logging(LOG_VERBOSE, "Awaiting good TUR"); ret = test_iscsi_tur_until_good(reset_sd, &num_uas); CU_ASSERT_EQUAL(ret, 0); logging(LOG_VERBOSE, "Test multipath LUN Reset using path %d", reset_path); ret = iscsi_task_mgmt_lun_reset_sync(reset_sd->iscsi_ctx, reset_sd->iscsi_lun); if (ret != 0) { logging(LOG_NORMAL, "LUN reset failed. %s", iscsi_get_error(reset_sd->iscsi_ctx)); } CU_ASSERT_EQUAL(ret, 0); /* check for and clear LU reset UA on all paths */ for (tur_path = 0; tur_path < mp_num_sds; tur_path++) { logging(LOG_VERBOSE, "check for LU reset unit " "attention via TUR on path %d", tur_path); ret = test_iscsi_tur_until_good(mp_sds[tur_path], &num_uas); CU_ASSERT_EQUAL(ret, 0); CU_ASSERT_NOT_EQUAL(num_uas, 0); } } } libiscsi-1.17.0/test-tool/test_multipathio_simple.c000066400000000000000000000051721271371262700224630ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg Copyright (C) 2015 David Disseldorp 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, see . */ #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-support.h" #include "iscsi-test-cu.h" #include "iscsi-multipath.h" void test_multipathio_simple(void) { int write_path; unsigned char *write_buf = alloca(256 * block_size); unsigned char *read_buf = alloca(256 * block_size); CHECK_FOR_DATALOSS; CHECK_FOR_SBC; MPATH_SKIP_IF_UNAVAILABLE(mp_sds, mp_num_sds); logging(LOG_VERBOSE, LOG_BLANK_LINE); memset(write_buf, 0xa6, 256 * block_size); for (write_path = 0; write_path < mp_num_sds; write_path++) { int i; int read_path; /* read back written data using a different path */ read_path = (write_path + 1) % mp_num_sds; logging(LOG_VERBOSE, "Test multipath WRITE10/READ10 of 1-256 blocks using " "path %d", write_path); for (i = 1; i <= 256; i++) { if (maximum_transfer_length && maximum_transfer_length < i) { break; } WRITE10(mp_sds[write_path], 0, i * block_size, block_size, 0, 0, 0, 0, 0, write_buf, EXPECT_STATUS_GOOD); READ10(mp_sds[read_path], NULL, 0, i * block_size, block_size, 0, 0, 0, 0, 0, read_buf, EXPECT_STATUS_GOOD); /* compare written and read data */ CU_ASSERT_EQUAL(0, memcmp(write_buf, read_buf, i * block_size)); } } } libiscsi-1.17.0/test-tool/test_nomedia_sbc.c000066400000000000000000000171021271371262700210120ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 by Ronnie Sahlberg 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, see . */ #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-support.h" #include "iscsi-test-cu.h" static void test_synchronizecache10(void) { logging(LOG_VERBOSE, "Test SYNCHRONIZECACHE10 when medium is ejected."); SYNCHRONIZECACHE10(sd, 0, 1, 1, 1, EXPECT_NO_MEDIUM); } static void test_synchronizecache16(void) { logging(LOG_VERBOSE, "Test SYNCHRONIZECACHE16 when medium is ejected."); SYNCHRONIZECACHE16(sd, 0, 1, 1, 1, EXPECT_NO_MEDIUM); } static void test_read10(void) { logging(LOG_VERBOSE, "Test READ10 when medium is ejected."); READ10(sd, NULL, 0, block_size, block_size, 0, 0, 0, 0, 0, NULL, EXPECT_NO_MEDIUM); } static void test_read12(void) { logging(LOG_VERBOSE, "Test READ12 when medium is ejected."); READ12(sd, NULL, 0, block_size, block_size, 0, 0, 0, 0, 0, NULL, EXPECT_NO_MEDIUM); } static void test_read16(void) { logging(LOG_VERBOSE, "Test READ16 when medium is ejected."); READ16(sd, NULL, 0, block_size, block_size, 0, 0, 0, 0, 0, NULL, EXPECT_NO_MEDIUM); } static void test_write10(void) { logging(LOG_VERBOSE, "Test WRITE10 when medium is ejected."); WRITE10(sd, 0, block_size, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_NO_MEDIUM); } static void test_write12(void) { logging(LOG_VERBOSE, "Test WRITE12 when medium is ejected."); WRITE12(sd, 0, block_size, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_NO_MEDIUM); } static void test_write16(void) { logging(LOG_VERBOSE, "Test WRITE16 when medium is ejected."); WRITE16(sd, 0, block_size, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_NO_MEDIUM); } static void test_writeverify10(void) { logging(LOG_VERBOSE, "Test WRITEVERIFY10 when medium is ejected."); WRITEVERIFY10(sd, 0, block_size, block_size, 0, 0, 0, 0, scratch, EXPECT_NO_MEDIUM); } static void test_writeverify12(void) { logging(LOG_VERBOSE, "Test WRITEVERIFY12 when medium is ejected."); WRITEVERIFY12(sd, 0, block_size, block_size, 0, 0, 0, 0, scratch, EXPECT_NO_MEDIUM); } static void test_writeverify16(void) { logging(LOG_VERBOSE, "Test WRITEVERIFY16 when medium is ejected."); WRITEVERIFY16(sd, 0, block_size, block_size, 0, 0, 0, 0, scratch, EXPECT_NO_MEDIUM); } static void test_verify10(void) { logging(LOG_VERBOSE, "Test VERIFY10 when medium is ejected."); VERIFY10(sd, 0, block_size, block_size, 0, 0, 1, scratch, EXPECT_NO_MEDIUM); } static void test_verify12(void) { logging(LOG_VERBOSE, "Test VERIFY12 when medium is ejected."); VERIFY12(sd, 0, block_size, block_size, 0, 0, 1, scratch, EXPECT_NO_MEDIUM); } static void test_verify16(void) { logging(LOG_VERBOSE, "Test VERIFY16 when medium is ejected."); VERIFY16(sd, 0, block_size, block_size, 0, 0, 1, scratch, EXPECT_NO_MEDIUM); } static void test_getlbastatus(void) { logging(LOG_VERBOSE, "Test GET_LBA_STATUS when medium is ejected."); GETLBASTATUS(sd, NULL, 0, 24, EXPECT_NO_MEDIUM); } static void test_prefetch10(void) { logging(LOG_VERBOSE, "Test PREFETCH10 when medium is ejected."); PREFETCH10(sd, 0, 1, 1, 0, EXPECT_NO_MEDIUM); } static void test_prefetch16(void) { logging(LOG_VERBOSE, "Test PREFETCH16 when medium is ejected."); PREFETCH16(sd, 0, 1, 1, 0, EXPECT_NO_MEDIUM); } static void test_orwrite(void) { logging(LOG_VERBOSE, "Test ORWRITE when medium is ejected."); ORWRITE(sd, 0, block_size, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_NO_MEDIUM); } static void test_compareandwrite(void) { logging(LOG_VERBOSE, "Test COMPAREANDWRITE when medium is ejected."); COMPAREANDWRITE(sd, 0, scratch, 2 * block_size, block_size, 0, 0, 0, 0, EXPECT_NO_MEDIUM); } static void test_writesame10(void) { logging(LOG_VERBOSE, "Test WRITESAME10 when medium is ejected."); WRITESAME10(sd, 0, block_size, 1, 0, 0, 0, 0, scratch, EXPECT_NO_MEDIUM); } static void test_writesame16(void) { logging(LOG_VERBOSE, "Test WRITESAME16 when medium is ejected."); WRITESAME16(sd, 0, block_size, 1, 0, 0, 0, 0, scratch, EXPECT_NO_MEDIUM); } static void test_unmap(void) { struct unmap_list list[1]; logging(LOG_VERBOSE, "Test UNMAP when medium is ejected."); list[0].lba = 0; list[0].num = lbppb; UNMAP(sd, 0, list, 1, EXPECT_NO_MEDIUM); } static void test_readcapacity10(void) { logging(LOG_VERBOSE, "Test READCAPACITY10 when medium is ejected."); READCAPACITY10(sd, NULL, 0, 0, EXPECT_NO_MEDIUM); } static void test_readcapacity16(void) { logging(LOG_VERBOSE, "Test READCAPACITY16 when medium is ejected."); READCAPACITY16(sd, NULL, 15, EXPECT_NO_MEDIUM); } void test_nomedia_sbc(void) { CHECK_FOR_SBC; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test that Medium commands fail when medium is ejected on SBC devices"); if (!inq->rmb) { logging(LOG_VERBOSE, "[SKIPPED] LUN is not removable. " "Skipping test."); return; } logging(LOG_VERBOSE, "Eject the medium."); STARTSTOPUNIT(sd, 1, 0, 0, 0, 1, 0, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Test TESTUNITREADY when medium is ejected."); TESTUNITREADY(sd, EXPECT_NO_MEDIUM); test_synchronizecache10(); test_synchronizecache16(); test_read10(); test_read12(); test_read16(); test_readcapacity10(); test_readcapacity16(); test_verify10(); test_verify12(); test_verify16(); test_getlbastatus(); test_prefetch10(); test_prefetch16(); if (!data_loss) { logging(LOG_VERBOSE, "[SKIPPING] Dataloss flag not set. Skipping test for WRITE commands"); goto finished; } test_compareandwrite(); test_orwrite(); test_unmap(); test_write10(); test_write12(); test_write16(); test_writesame10(); test_writesame16(); test_writeverify10(); test_writeverify12(); test_writeverify16(); finished: logging(LOG_VERBOSE, "Load the medium again."); STARTSTOPUNIT(sd, 1, 0, 0, 0, 1, 1, EXPECT_STATUS_GOOD); } libiscsi-1.17.0/test-tool/test_orwrite_0blocks.c000066400000000000000000000033201271371262700216540ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg 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, see . */ #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-test-cu.h" void test_orwrite_0blocks(void) { CHECK_FOR_DATALOSS; CHECK_FOR_SBC; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test ORWRITE 0-blocks at LBA==0"); ORWRITE(sd, 0, 0, block_size, 0, 0, 0, 0, 0, NULL, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Test ORWRITE 0-blocks one block past end-of-LUN"); ORWRITE(sd, num_blocks + 1, 0, block_size, 0, 0, 0, 0, 0, NULL, EXPECT_LBA_OOB); logging(LOG_VERBOSE, "Test ORWRITE 0-blocks at LBA==2^63"); ORWRITE(sd, 0x8000000000000000ULL, 0, block_size, 0, 0, 0, 0, 0, NULL, EXPECT_LBA_OOB); logging(LOG_VERBOSE, "Test ORWRITE 0-blocks at LBA==-1"); ORWRITE(sd, -1, 0, block_size, 0, 0, 0, 0, 0, NULL, EXPECT_LBA_OOB); } libiscsi-1.17.0/test-tool/test_orwrite_beyond_eol.c000066400000000000000000000053701271371262700224450ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg 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, see . */ #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-test-cu.h" void test_orwrite_beyond_eol(void) { int i; CHECK_FOR_DATALOSS; CHECK_FOR_SBC; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test ORWRITE 1-256 blocks one block beyond the end"); memset(scratch, 0xa6, 256 * block_size); for (i = 1; i <= 256; i++) { if (maximum_transfer_length && maximum_transfer_length < i) { break; } ORWRITE(sd, num_blocks + 1 - i, i * block_size, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_LBA_OOB); } logging(LOG_VERBOSE, "Test ORWRITE 1-256 blocks at LBA==2^63"); for (i = 1; i <= 256; i++) { if (maximum_transfer_length && maximum_transfer_length < i) { break; } ORWRITE(sd, 0x8000000000000000ULL, i * block_size, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_LBA_OOB); } logging(LOG_VERBOSE, "Test ORWRITE 1-256 blocks at LBA==-1"); for (i = 1; i <= 256; i++) { if (maximum_transfer_length && maximum_transfer_length < i) { break; } ORWRITE(sd, -1, i * block_size, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_LBA_OOB); } logging(LOG_VERBOSE, "Test ORWRITE 2-256 blocks all but one block beyond the end"); for (i = 2; i <= 256; i++) { if (maximum_transfer_length && maximum_transfer_length < i) { break; } ORWRITE(sd, num_blocks - 1, i * block_size, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_LBA_OOB); } } libiscsi-1.17.0/test-tool/test_orwrite_dpofua.c000066400000000000000000000112701271371262700216000ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2014 by Ronnie Sahlberg 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, see . */ #include #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-test-cu.h" void test_orwrite_dpofua(void) { int dpofua, usage_data_dpofua; struct scsi_task *ms_task = NULL; struct scsi_mode_sense *ms; struct scsi_task *rso_task = NULL; struct scsi_report_supported_op_codes_one_command *rsoc; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test ORWRITE DPO/FUA flags"); CHECK_FOR_SBC; CHECK_FOR_DATALOSS; logging(LOG_VERBOSE, "Read the DPOFUA flag from mode sense data"); MODESENSE6(sd, &ms_task, 0, SCSI_MODESENSE_PC_CURRENT, SCSI_MODEPAGE_RETURN_ALL_PAGES, 0, 255, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "[SUCCESS] Mode sense returned status GOOD"); ms = scsi_datain_unmarshall(ms_task); dpofua = ms && (ms->device_specific_parameter & 0x10); scsi_free_scsi_task(ms_task); if (dpofua) { logging(LOG_VERBOSE, "DPOFUA flag is set. Device should allow " "DPO/FUA flags in CDBs"); } else { logging(LOG_VERBOSE, "DPOFUA flag is clear. Device should fail " "CDBs with DPO/FUA set"); } logging(LOG_VERBOSE, "Test ORWRITE with DPO==1"); memset(scratch, 0xa6, block_size); if (dpofua) { ORWRITE(sd, 0, block_size, block_size, 0, 1, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); } else { ORWRITE(sd, 0, block_size, block_size, 0, 1, 0, 0, 0, scratch, EXPECT_INVALID_FIELD_IN_CDB); } logging(LOG_VERBOSE, "Test ORWRITE with FUA==1"); if (dpofua) { ORWRITE(sd, 0, block_size, block_size, 0, 0, 1, 0, 0, scratch, EXPECT_STATUS_GOOD); } else { ORWRITE(sd, 0, block_size, block_size, 0, 0, 1, 0, 0, scratch, EXPECT_INVALID_FIELD_IN_CDB); } logging(LOG_VERBOSE, "Test ORWRITE with DPO==1 FUA==1"); if (dpofua) { ORWRITE(sd, 0, block_size, block_size, 0, 1, 1, 0, 0, scratch, EXPECT_STATUS_GOOD); } else { ORWRITE(sd, 0, block_size, block_size, 0, 1, 1, 0, 0, scratch, EXPECT_INVALID_FIELD_IN_CDB); } logging(LOG_VERBOSE, "Try fetching REPORT_SUPPORTED_OPCODES " "for ORWRITE"); REPORT_SUPPORTED_OPCODES(sd, &rso_task, 0, SCSI_REPORT_SUPPORTING_OPCODE, SCSI_OPCODE_ORWRITE, 0, 65535, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Unmarshall the DATA-IN buffer"); rsoc = scsi_datain_unmarshall(rso_task); CU_ASSERT_PTR_NOT_NULL_FATAL(rsoc); usage_data_dpofua = rsoc->cdb_usage_data[1] & 0x18; if (dpofua) { logging(LOG_VERBOSE, "DPOFUA is set. Verify the " "DPO/FUA flags are set in the CDB_USAGE_DATA"); if (!usage_data_dpofua) { logging(LOG_NORMAL, "[FAILED] DpoFua not set " "in CDB_USAGE_DATE"); CU_FAIL("DpoFua not set in CDB_USAGE_DATE"); } } else { logging(LOG_VERBOSE, "DPOFUA is clear. Verify the " "DPO/FUA flags are clear in the CDB_USAGE_DATA"); if (usage_data_dpofua) { logging(LOG_NORMAL, "[FAILED] DpoFua not clear " "in CDB_USAGE_DATE"); CU_FAIL("DpoFua not clear in CDB_USAGE_DATE"); } } scsi_free_scsi_task(rso_task); } libiscsi-1.17.0/test-tool/test_orwrite_simple.c000066400000000000000000000037231271371262700216170ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg 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, see . */ #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-support.h" #include "iscsi-test-cu.h" void test_orwrite_simple(void) { int i; CHECK_FOR_DATALOSS; CHECK_FOR_SBC; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test ORWRITE of 1-256 blocks at the start of the LUN"); memset(scratch, 0xa6, 256 * block_size); for (i = 1; i <= 256; i++) { if (maximum_transfer_length && maximum_transfer_length < i) { break; } ORWRITE(sd, 0, i * block_size, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); } logging(LOG_VERBOSE, "Test ORWRITE of 1-256 blocks at the end of the LUN"); for (i = 1; i <= 256; i++) { if (maximum_transfer_length && maximum_transfer_length < i) { break; } ORWRITE(sd, num_blocks - i, i * block_size, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); } } libiscsi-1.17.0/test-tool/test_orwrite_verify.c000066400000000000000000000121421271371262700216250ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg 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, see . */ #include #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-support.h" #include "iscsi-test-cu.h" void test_orwrite_verify(void) { int i, ret; unsigned char *buf = &scratch[0]; unsigned char *readbuf = &scratch[256 * block_size]; CHECK_FOR_DATALOSS; CHECK_FOR_SBC; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test ORWRITE of 1-256 blocks at the start of the LUN"); for (i = 1; i <= 256; i++) { if (maximum_transfer_length && maximum_transfer_length < i) { break; } logging(LOG_VERBOSE, "Write %d blocks of all-zero", i); memset(buf, 0, block_size * i); ret = write10(sd, 0, i * block_size, block_size, 0, 0, 0, 0, 0, buf, EXPECT_STATUS_GOOD); CU_ASSERT_EQUAL(ret, 0); logging(LOG_VERBOSE, "OrWrite %d blocks with 0xa5", i); memset(buf, 0xa5, block_size * i); ORWRITE(sd, 0, i * block_size, block_size, 0, 0, 0, 0, 0, buf, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Read %d blocks back", i); READ10(sd, NULL, 0, i * block_size, block_size, 0, 0, 0, 0, 0, readbuf, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Verify that the blocks are all 0xa5"); ret = memcmp(buf, readbuf, block_size * i); CU_ASSERT_EQUAL(ret, 0); logging(LOG_VERBOSE, "OrWrite %d blocks with 0x5a", i); memset(buf, 0x5a, block_size * i); ORWRITE(sd, 0, i * block_size, block_size, 0, 0, 0, 0, 0, buf, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Read %d blocks back", i); READ10(sd, NULL, 0, i * block_size, block_size, 0, 0, 0, 0, 0, readbuf, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Verify that the blocks are all 0xff"); memset(buf, 0xff, block_size * i); ret = memcmp(buf, readbuf, block_size * i); CU_ASSERT_EQUAL(ret, 0); } logging(LOG_VERBOSE, "Test ORWRITE of 1-256 blocks at the end of the LUN"); for (i = 1; i <= 256; i++) { if (maximum_transfer_length && maximum_transfer_length < i) { break; } logging(LOG_VERBOSE, "Write %d blocks of all-zero", i); memset(buf, 0, block_size * i); WRITE16(sd, num_blocks - i, i * block_size, block_size, 0, 0, 0, 0, 0, buf, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "OrWrite %d blocks with 0xa5", i); memset(buf, 0xa5, block_size * i); ORWRITE(sd, num_blocks - i, i * block_size, block_size, 0, 0, 0, 0, 0, buf, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Read %d blocks back", i); READ16(sd, NULL, num_blocks - i, i * block_size, block_size, 0, 0, 0, 0, 0, readbuf, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Verify that the blocks are all 0xa5"); ret = memcmp(buf, readbuf, block_size * i); CU_ASSERT_EQUAL(ret, 0); logging(LOG_VERBOSE, "OrWrite %d blocks with 0x5a", i); memset(buf, 0x5a, block_size * i); ORWRITE(sd, num_blocks - i, i * block_size, block_size, 0, 0, 0, 0, 0, buf, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Read %d blocks back", i); READ16(sd, NULL, num_blocks - i, i * block_size, block_size, 0, 0, 0, 0, 0, readbuf, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Verify that the blocks are all 0xff"); memset(buf, 0xff, block_size * i); ret = memcmp(buf, readbuf, block_size * i); CU_ASSERT_EQUAL(ret, 0); } } libiscsi-1.17.0/test-tool/test_orwrite_wrprotect.c000066400000000000000000000035341271371262700223570ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg 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, see . */ #include #include #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-test-cu.h" void test_orwrite_wrprotect(void) { int i; /* * Try out different non-zero values for WRPROTECT. */ logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test ORWRITE with non-zero WRPROTECT"); CHECK_FOR_DATALOSS; CHECK_FOR_SBC; memset(scratch, 0xa6, block_size); if (!inq->protect || (rc16 != NULL && !rc16->prot_en)) { logging(LOG_VERBOSE, "Device does not support/use protection information. All commands should fail."); for (i = 1; i < 8; i++) { ORWRITE(sd, 0, block_size, block_size, i, 0, 0, 0, 0, scratch, EXPECT_INVALID_FIELD_IN_CDB); } return; } logging(LOG_NORMAL, "No tests for devices that support protection information yet."); } libiscsi-1.17.0/test-tool/test_prefetch10_0blocks.c000066400000000000000000000033141271371262700221250ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg 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, see . */ #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-test-cu.h" void test_prefetch10_0blocks(void) { logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test PREFETCH10 0-blocks at LBA==0"); PREFETCH10(sd, 0, 0, 0, 0, EXPECT_STATUS_GOOD); if (num_blocks > 0x80000000) { CU_PASS("[SKIPPED] LUN is too big"); return; } logging(LOG_VERBOSE, "Test PREFETCH10 0-blocks one block past end-of-LUN"); PREFETCH10(sd, num_blocks + 1, 0, 0, 0, EXPECT_LBA_OOB); logging(LOG_VERBOSE, "Test PREFETCH10 0-blocks at LBA==2^31"); PREFETCH10(sd, 0x80000000, 0, 0, 0, EXPECT_LBA_OOB); logging(LOG_VERBOSE, "Test PREFETCH10 0-blocks at LBA==-1"); PREFETCH10(sd, -1, 0, 0, 0, EXPECT_LBA_OOB); } libiscsi-1.17.0/test-tool/test_prefetch10_beyond_eol.c000066400000000000000000000041021271371262700227030ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg 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, see . */ #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-test-cu.h" void test_prefetch10_beyond_eol(void) { int i; if (num_blocks >= 0x80000000) { CU_PASS("LUN is too big for read-beyond-eol tests with PREFETCH10. Skipping test.\n"); return; } logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test PREFETCH10 1-256 blocks one block beyond the end"); for (i = 1; i <= 256; i++) { PREFETCH10(sd, num_blocks + 1 - i, i, 0, 0, EXPECT_LBA_OOB); } logging(LOG_VERBOSE, "Test PREFETCH10 1-256 blocks at LBA==2^31"); for (i = 1; i <= 256; i++) { PREFETCH10(sd, 0x80000000, i, 0, 0, EXPECT_LBA_OOB); } logging(LOG_VERBOSE, "Test PREFETCH10 1-256 blocks at LBA==-1"); for (i = 1; i <= 256; i++) { PREFETCH10(sd, -1, i, 0, 0, EXPECT_LBA_OOB); } logging(LOG_VERBOSE, "Test PREFETCH10 2-256 blocks all but one block beyond the end"); for (i = 2; i <= 256; i++) { PREFETCH10(sd, num_blocks - 1, i, 0, 0, EXPECT_LBA_OOB); } } libiscsi-1.17.0/test-tool/test_prefetch10_flags.c000066400000000000000000000027711271371262700216720ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg 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, see . */ #include #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-test-cu.h" void test_prefetch10_flags(void) { logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test PREFETCH10 flags"); logging(LOG_VERBOSE, "Test PREFETCH10 with IMMED==1"); PREFETCH10(sd, 0, 1, 1, 0, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Test PREFETCH10 with GROUP==3"); PREFETCH10(sd, 0, 1, 0, 3, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Test PREFETCH10 with IMMED=1 and GROUP==3"); PREFETCH10(sd, 0, 1, 1, 3, EXPECT_STATUS_GOOD); } libiscsi-1.17.0/test-tool/test_prefetch10_simple.c000066400000000000000000000027541271371262700220700ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg 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, see . */ #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-support.h" #include "iscsi-test-cu.h" void test_prefetch10_simple(void) { int i; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test PREFETCH10 of 1-256 blocks at the start of the LUN"); for (i = 1; i <= 256; i++) { PREFETCH10(sd, 0, i, 0, 0, EXPECT_STATUS_GOOD); } logging(LOG_VERBOSE, "Test PREFETCH10 of 1-256 blocks at the end of the LUN"); for (i = 1; i <= 256; i++) { PREFETCH10(sd, num_blocks - i, i, 0, 0, EXPECT_STATUS_GOOD); } } libiscsi-1.17.0/test-tool/test_prefetch16_0blocks.c000066400000000000000000000031301271371262700221270ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg 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, see . */ #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-test-cu.h" void test_prefetch16_0blocks(void) { logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test PREFETCH16 0-blocks at LBA==0"); PREFETCH16(sd, 0, 0, 0, 0, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Test PREFETCH16 0-blocks one block past end-of-LUN"); PREFETCH16(sd, num_blocks + 1, 0, 0, 0, EXPECT_LBA_OOB); logging(LOG_VERBOSE, "Test PREFETCH16 0-blocks at LBA==2^63"); PREFETCH16(sd, 0x8000000000000000ULL, 0, 0, 0, EXPECT_LBA_OOB); logging(LOG_VERBOSE, "Test PREFETCH16 0-blocks at LBA==-1"); PREFETCH16(sd, -1, 0, 0, 0, EXPECT_LBA_OOB); } libiscsi-1.17.0/test-tool/test_prefetch16_beyond_eol.c000066400000000000000000000036351271371262700227230ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg 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, see . */ #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-test-cu.h" void test_prefetch16_beyond_eol(void) { int i; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test PREFETCH16 1-256 blocks one block beyond the end"); for (i = 1; i <= 256; i++) { PREFETCH16(sd, num_blocks + 1 - i, i, 0, 0, EXPECT_LBA_OOB); } logging(LOG_VERBOSE, "Test PREFETCH16 1-256 blocks at LBA==2^63"); for (i = 1; i <= 256; i++) { PREFETCH16(sd, 0x8000000000000000ULL, i, 0, 0, EXPECT_LBA_OOB); } logging(LOG_VERBOSE, "Test PREFETCH16 1-256 blocks at LBA==-1"); for (i = 1; i <= 256; i++) { PREFETCH16(sd, -1, i, 0, 0, EXPECT_LBA_OOB); } logging(LOG_VERBOSE, "Test PREFETCH16 2-256 blocks all but one block beyond the end"); for (i = 2; i <= 256; i++) { PREFETCH16(sd, num_blocks - 1, i, 0, 0, EXPECT_LBA_OOB); } } libiscsi-1.17.0/test-tool/test_prefetch16_flags.c000066400000000000000000000027711271371262700217000ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg 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, see . */ #include #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-test-cu.h" void test_prefetch16_flags(void) { logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test PREFETCH16 flags"); logging(LOG_VERBOSE, "Test PREFETCH16 with IMMED==1"); PREFETCH16(sd, 0, 1, 1, 0, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Test PREFETCH16 with GROUP==3"); PREFETCH16(sd, 0, 1, 0, 3, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Test PREFETCH16 with IMMED=1 and GROUP==3"); PREFETCH16(sd, 0, 1, 1, 3, EXPECT_STATUS_GOOD); } libiscsi-1.17.0/test-tool/test_prefetch16_simple.c000066400000000000000000000027541271371262700220760ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg 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, see . */ #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-support.h" #include "iscsi-test-cu.h" void test_prefetch16_simple(void) { int i; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test PREFETCH16 of 1-256 blocks at the start of the LUN"); for (i = 1; i <= 256; i++) { PREFETCH16(sd, 0, i, 0, 0, EXPECT_STATUS_GOOD); } logging(LOG_VERBOSE, "Test PREFETCH16 of 1-256 blocks at the end of the LUN"); for (i = 1; i <= 256; i++) { PREFETCH16(sd, num_blocks - i, i, 0, 0, EXPECT_STATUS_GOOD); } } libiscsi-1.17.0/test-tool/test_preventallow_2_itnexuses.c000066400000000000000000000052541271371262700236260ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 by Ronnie Sahlberg 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, see . */ #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-support.h" #include "iscsi-test-cu.h" #include "iscsi-multipath.h" void test_preventallow_2_itnexuses(void) { int ret; struct scsi_device *sd2; CHECK_FOR_SBC; CHECK_FOR_REMOVABLE; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test that PREVENT MEDIUM REMOVAL are seen on other nexuses as well"); if (sd->iscsi_ctx == NULL) { const char *err = "[SKIPPED] This PREVENTALLOW test is " "only supported for iSCSI backends"; logging(LOG_NORMAL, "%s", err); CU_PASS(err); return; } logging(LOG_VERBOSE, "Set the PREVENT flag"); PREVENTALLOW(sd, 1); logging(LOG_VERBOSE, "Try to eject the medium"); STARTSTOPUNIT(sd, 0, 0, 0, 0, 1, 0, EXPECT_REMOVAL_PREVENTED); logging(LOG_VERBOSE, "Verify we can still access the media."); TESTUNITREADY(sd, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Create a second connection to the target"); ret = mpath_sd2_get_or_clone(sd, &sd2); CU_ASSERT_EQUAL(ret, 0); if (ret < 0) return; logging(LOG_VERBOSE, "Try to eject the medium on the second connection"); STARTSTOPUNIT(sd2, 0, 0, 0, 0, 1, 0, EXPECT_REMOVAL_PREVENTED); logging(LOG_VERBOSE, "Logout the second connection from target"); mpath_sd2_put(sd2); logging(LOG_VERBOSE, "Clear PREVENT and load medium in case target failed"); logging(LOG_VERBOSE, "Test we can clear PREVENT flag"); PREVENTALLOW(sd, 0); logging(LOG_VERBOSE, "Load the medium"); STARTSTOPUNIT(sd, 0, 0, 0, 0, 1, 1, EXPECT_STATUS_GOOD); } libiscsi-1.17.0/test-tool/test_preventallow_cold_reset.c000066400000000000000000000055471271371262700235060ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 by Ronnie Sahlberg 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, see . */ #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-support.h" #include "iscsi-test-cu.h" void test_preventallow_cold_reset(void) { int ret; CHECK_FOR_SBC; CHECK_FOR_REMOVABLE; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test that Target Warm Reset clears PREVENT MEDIUM REMOVAL"); if (sd->iscsi_ctx == NULL) { const char *err = "[SKIPPED] This PREVENTALLOW test is " "only supported for iSCSI backends"; logging(LOG_NORMAL, "%s", err); CU_PASS(err); return; } logging(LOG_VERBOSE, "Set the PREVENT flag"); PREVENTALLOW(sd, 1); logging(LOG_VERBOSE, "Try to eject the medium"); STARTSTOPUNIT(sd, 0, 0, 0, 0, 1, 0, EXPECT_REMOVAL_PREVENTED); logging(LOG_VERBOSE, "Verify we can still access the media."); TESTUNITREADY(sd, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Perform cold reset on target"); ret = iscsi_task_mgmt_target_cold_reset_sync(sd->iscsi_ctx); CU_ASSERT_EQUAL(ret, 0); logging(LOG_VERBOSE, "Wait until all unit attentions clear"); while (testunitready(sd, EXPECT_STATUS_GOOD) != 0) ; logging(LOG_VERBOSE, "Try to eject the medium"); STARTSTOPUNIT(sd, 0, 0, 0, 0, 1, 0, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Verify we can not access the media."); TESTUNITREADY(sd, EXPECT_NO_MEDIUM); logging(LOG_VERBOSE, "Load the medium"); STARTSTOPUNIT(sd, 0, 0, 0, 0, 1, 0, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Clear PREVENT and load medium in case target failed"); logging(LOG_VERBOSE, "Test we can clear PREVENT flag"); PREVENTALLOW(sd, 0); logging(LOG_VERBOSE, "Load the medium"); STARTSTOPUNIT(sd, 0, 0, 0, 0, 1, 1, EXPECT_STATUS_GOOD); } libiscsi-1.17.0/test-tool/test_preventallow_eject.c000066400000000000000000000045261271371262700224510ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 by Ronnie Sahlberg 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, see . */ #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-support.h" #include "iscsi-test-cu.h" void test_preventallow_eject(void) { CHECK_FOR_SBC; CHECK_FOR_REMOVABLE; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test that we can not eject medium when PREVENT is active"); logging(LOG_VERBOSE, "Set the PREVENT flag"); PREVENTALLOW(sd, 1); logging(LOG_VERBOSE, "Try to eject the medium"); STARTSTOPUNIT(sd, 0, 0, 0, 0, 1, 0, EXPECT_REMOVAL_PREVENTED); logging(LOG_VERBOSE, "Verify we can still access the media."); TESTUNITREADY(sd, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Test we can clear PREVENT flag"); PREVENTALLOW(sd, 0); logging(LOG_VERBOSE, "Try to eject the medium"); STARTSTOPUNIT(sd, 0, 0, 0, 0, 1, 0, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Verify we can not access the media."); TESTUNITREADY(sd, EXPECT_NO_MEDIUM); logging(LOG_VERBOSE, "Set the PREVENT flag"); PREVENTALLOW(sd, 1); logging(LOG_VERBOSE, "Try to load the medium"); STARTSTOPUNIT(sd, 0, 0, 0, 0, 1, 1, EXPECT_REMOVAL_PREVENTED); logging(LOG_VERBOSE, "Clear PREVENT flag"); PREVENTALLOW(sd, 0); logging(LOG_VERBOSE, "Load the medium again"); STARTSTOPUNIT(sd, 0, 0, 0, 0, 1, 1, EXPECT_STATUS_GOOD); } libiscsi-1.17.0/test-tool/test_preventallow_itnexus_loss.c000066400000000000000000000056421271371262700241160ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 by Ronnie Sahlberg 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, see . */ #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-support.h" #include "iscsi-test-cu.h" void test_preventallow_itnexus_loss(void) { CHECK_FOR_SBC; CHECK_FOR_REMOVABLE; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test that IT-Nexus loss clears PREVENT MEDIUM REMOVAL"); if (sd->iscsi_ctx == NULL) { const char *err = "[SKIPPED] This PREVENTALLOW test is " "only supported for iSCSI backends"; logging(LOG_NORMAL, "%s", err); CU_PASS(err); return; } logging(LOG_VERBOSE, "Set the PREVENT flag"); PREVENTALLOW(sd, 1); logging(LOG_VERBOSE, "Try to eject the medium"); STARTSTOPUNIT(sd, 0, 0, 0, 0, 1, 0, EXPECT_REMOVAL_PREVENTED); logging(LOG_VERBOSE, "Verify we can still access the media."); TESTUNITREADY(sd, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Disconnect from the target."); iscsi_destroy_context(sd->iscsi_ctx); logging(LOG_VERBOSE, "Reconnect to target"); sd->iscsi_ctx = iscsi_context_login(initiatorname1, sd->iscsi_url, &sd->iscsi_lun); if (sd->iscsi_ctx == NULL) { logging(LOG_VERBOSE, "Failed to login to target"); return; } logging(LOG_VERBOSE, "Try to eject the medium"); STARTSTOPUNIT(sd, 0, 0, 0, 0, 1, 0, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Verify we can not access the media."); TESTUNITREADY(sd, EXPECT_NO_MEDIUM); logging(LOG_VERBOSE, "Load the medium"); STARTSTOPUNIT(sd, 0, 0, 0, 0, 1, 0, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Clear PREVENT and load medium in case target failed"); logging(LOG_VERBOSE, "Test we can clear PREVENT flag"); PREVENTALLOW(sd, 0); logging(LOG_VERBOSE, "Load the medium"); STARTSTOPUNIT(sd, 0, 0, 0, 0, 1, 1, EXPECT_STATUS_GOOD); } libiscsi-1.17.0/test-tool/test_preventallow_logout.c000066400000000000000000000056611271371262700226710ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 by Ronnie Sahlberg 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, see . */ #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-support.h" #include "iscsi-test-cu.h" void test_preventallow_logout(void) { CHECK_FOR_SBC; CHECK_FOR_REMOVABLE; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test that Logout loss clears PREVENT MEDIUM REMOVAL"); if (sd->iscsi_ctx == NULL) { const char *err = "[SKIPPED] This PREVENTALLOW test is " "only supported for iSCSI backends"; logging(LOG_NORMAL, "%s", err); CU_PASS(err); return; } logging(LOG_VERBOSE, "Set the PREVENT flag"); PREVENTALLOW(sd, 1); logging(LOG_VERBOSE, "Try to eject the medium"); STARTSTOPUNIT(sd, 0, 0, 0, 0, 1, 0, EXPECT_REMOVAL_PREVENTED); logging(LOG_VERBOSE, "Verify we can still access the media."); TESTUNITREADY(sd, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Logout from target"); iscsi_logout_sync(sd->iscsi_ctx); iscsi_destroy_context(sd->iscsi_ctx); logging(LOG_VERBOSE, "Relogin to target"); sd->iscsi_ctx = iscsi_context_login(initiatorname1, sd->iscsi_url, &sd->iscsi_lun); if (sd->iscsi_ctx == NULL) { logging(LOG_VERBOSE, "Failed to login to target"); return; } logging(LOG_VERBOSE, "Try to eject the medium"); STARTSTOPUNIT(sd, 0, 0, 0, 0, 1, 0, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Verify we can not access the media."); TESTUNITREADY(sd, EXPECT_NO_MEDIUM); logging(LOG_VERBOSE, "Load the medium"); STARTSTOPUNIT(sd, 0, 0, 0, 0, 1, 0, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Clear PREVENT and load medium in case target failed"); logging(LOG_VERBOSE, "Test we can clear PREVENT flag"); PREVENTALLOW(sd, 0); logging(LOG_VERBOSE, "Load the medium"); STARTSTOPUNIT(sd, 0, 0, 0, 0, 1, 1, EXPECT_STATUS_GOOD); } libiscsi-1.17.0/test-tool/test_preventallow_lun_reset.c000066400000000000000000000055541271371262700233610ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 by Ronnie Sahlberg 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, see . */ #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-support.h" #include "iscsi-test-cu.h" void test_preventallow_lun_reset(void) { int ret; CHECK_FOR_SBC; CHECK_FOR_REMOVABLE; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test that Target Warm Reset clears PREVENT MEDIUM REMOVAL"); if (sd->iscsi_ctx == NULL) { const char *err = "[SKIPPED] This PREVENTALLOW test is " "only supported for iSCSI backends"; logging(LOG_NORMAL, "%s", err); CU_PASS(err); return; } logging(LOG_VERBOSE, "Set the PREVENT flag"); PREVENTALLOW(sd, 1); logging(LOG_VERBOSE, "Try to eject the medium"); STARTSTOPUNIT(sd, 0, 0, 0, 0, 1, 0, EXPECT_REMOVAL_PREVENTED); logging(LOG_VERBOSE, "Verify we can still access the media."); TESTUNITREADY(sd, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Perform LUN reset on target"); ret = iscsi_task_mgmt_lun_reset_sync(sd->iscsi_ctx, sd->iscsi_lun); CU_ASSERT_EQUAL(ret, 0); logging(LOG_VERBOSE, "Wait until all unit attentions clear"); while (testunitready(sd, EXPECT_STATUS_GOOD) != 0) ; logging(LOG_VERBOSE, "Try to eject the medium"); STARTSTOPUNIT(sd, 0, 0, 0, 0, 1, 0, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Verify we can not access the media."); TESTUNITREADY(sd, EXPECT_NO_MEDIUM); logging(LOG_VERBOSE, "Load the medium"); STARTSTOPUNIT(sd, 0, 0, 0, 0, 1, 0, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Clear PREVENT and load medium in case target failed"); logging(LOG_VERBOSE, "Test we can clear PREVENT flag"); PREVENTALLOW(sd, 0); logging(LOG_VERBOSE, "Load the medium"); STARTSTOPUNIT(sd, 0, 0, 0, 0, 1, 1, EXPECT_STATUS_GOOD); } libiscsi-1.17.0/test-tool/test_preventallow_simple.c000066400000000000000000000024661271371262700226510ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 by Ronnie Sahlberg 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, see . */ #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-support.h" #include "iscsi-test-cu.h" void test_preventallow_simple(void) { CHECK_FOR_SBC; CHECK_FOR_REMOVABLE; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test PREVENTALLOW basics"); logging(LOG_VERBOSE, "Test we can set PREVENT flag"); PREVENTALLOW(sd, 1); logging(LOG_VERBOSE, "Test we can clear PREVENT flag"); PREVENTALLOW(sd, 0); } libiscsi-1.17.0/test-tool/test_preventallow_warm_reset.c000066400000000000000000000055471271371262700235330ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 by Ronnie Sahlberg 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, see . */ #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-support.h" #include "iscsi-test-cu.h" void test_preventallow_warm_reset(void) { int ret; CHECK_FOR_SBC; CHECK_FOR_REMOVABLE; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test that Target Warm Reset clears PREVENT MEDIUM REMOVAL"); if (sd->iscsi_ctx == NULL) { const char *err = "[SKIPPED] This PREVENTALLOW test is " "only supported for iSCSI backends"; logging(LOG_NORMAL, "%s", err); CU_PASS(err); return; } logging(LOG_VERBOSE, "Set the PREVENT flag"); PREVENTALLOW(sd, 1); logging(LOG_VERBOSE, "Try to eject the medium"); STARTSTOPUNIT(sd, 0, 0, 0, 0, 1, 0, EXPECT_REMOVAL_PREVENTED); logging(LOG_VERBOSE, "Verify we can still access the media."); TESTUNITREADY(sd, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Perform warm reset on target"); ret = iscsi_task_mgmt_target_warm_reset_sync(sd->iscsi_ctx); CU_ASSERT_EQUAL(ret, 0); logging(LOG_VERBOSE, "Wait until all unit attentions clear"); while (testunitready(sd, EXPECT_STATUS_GOOD) != 0) ; logging(LOG_VERBOSE, "Try to eject the medium"); STARTSTOPUNIT(sd, 0, 0, 0, 0, 1, 0, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Verify we can not access the media."); TESTUNITREADY(sd, EXPECT_NO_MEDIUM); logging(LOG_VERBOSE, "Load the medium"); STARTSTOPUNIT(sd, 0, 0, 0, 0, 1, 0, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Clear PREVENT and load medium in case target failed"); logging(LOG_VERBOSE, "Test we can clear PREVENT flag"); PREVENTALLOW(sd, 0); logging(LOG_VERBOSE, "Load the medium"); STARTSTOPUNIT(sd, 0, 0, 0, 0, 1, 1, EXPECT_STATUS_GOOD); } libiscsi-1.17.0/test-tool/test_prin_read_keys_simple.c000066400000000000000000000040661271371262700231230ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 by Lee Duncan 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, see . */ #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-support.h" #include "iscsi-test-cu.h" void test_prin_read_keys_simple(void) { int ret = 0; int al; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test Persistent Reserve IN READ_KEYS works."); ret = prin_read_keys(sd, &task, NULL); if (ret == -2) { logging(LOG_NORMAL, "[SKIPPED] PERSISTEN RESERVE IN is not implemented."); CU_PASS("PERSISTENT RESERVE IN is not implemented."); return; } CU_ASSERT_EQUAL(ret, 0); logging(LOG_VERBOSE, "Test DATA-IN is at least 8 bytes."); if (task->datain.size < 8) { logging(LOG_NORMAL, "[FAILED] DATA-IN returned less than 8 bytes"); return; } logging(LOG_VERBOSE, "Test ADDITIONAL_LENGTH matches DATA_IN size."); al = scsi_get_uint32(&task->datain.data[4]); if (al != task->datain.size - 8) { logging(LOG_NORMAL, "[FAILED] ADDITIONAL_LENGTH was %d bytes but %d was expected.", al, task->datain.size - 8); return; } } libiscsi-1.17.0/test-tool/test_prin_report_caps.c000066400000000000000000000102411271371262700221150ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2015 David Disseldorp 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, see . */ #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-support.h" #include "iscsi-test-cu.h" static struct test_prin_report_caps_types { enum scsi_persistent_reservation_type_mask mask; enum scsi_persistent_out_type op; } report_caps_types_array[] = { { SCSI_PR_TYPE_MASK_WR_EX_AR, SCSI_PERSISTENT_RESERVE_TYPE_WRITE_EXCLUSIVE_ALL_REGISTRANTS }, { SCSI_PR_TYPE_MASK_EX_AC_RO, SCSI_PERSISTENT_RESERVE_TYPE_EXCLUSIVE_ACCESS_REGISTRANTS_ONLY }, { SCSI_PR_TYPE_MASK_WR_EX_RO, SCSI_PERSISTENT_RESERVE_TYPE_WRITE_EXCLUSIVE_REGISTRANTS_ONLY }, { SCSI_PR_TYPE_MASK_EX_AC, SCSI_PERSISTENT_RESERVE_TYPE_EXCLUSIVE_ACCESS }, { SCSI_PR_TYPE_MASK_WR_EX, SCSI_PERSISTENT_RESERVE_TYPE_WRITE_EXCLUSIVE }, { SCSI_PR_TYPE_MASK_EX_AC_AR, SCSI_PERSISTENT_RESERVE_TYPE_EXCLUSIVE_ACCESS_ALL_REGISTRANTS }, { 0, 0 } }; void test_prin_report_caps_simple(void) { int ret = 0; const unsigned long long key = rand_key(); struct scsi_task *tsk; struct scsi_persistent_reserve_in_report_capabilities *rcaps; struct test_prin_report_caps_types *type; CHECK_FOR_DATALOSS; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test Persistent Reserve In REPORT CAPABILITIES works."); /* register our reservation key with the target */ ret = prout_register_and_ignore(sd, key); if (ret == -2) { logging(LOG_NORMAL, "[SKIPPED] PERSISTENT RESERVE OUT is not implemented."); CU_PASS("PERSISTENT RESERVE OUT is not implemented."); return; } CU_ASSERT_EQUAL(ret, 0); ret = prin_report_caps(sd, &tsk, &rcaps); CU_ASSERT_EQUAL(ret, 0); logging(LOG_VERBOSE, "Checking PERSISTENT RESERVE IN REPORT CAPABILITIES fields."); CU_ASSERT_EQUAL(rcaps->length, 8); CU_ASSERT_TRUE(rcaps->allow_commands <= 5); CU_ASSERT_EQUAL(rcaps->persistent_reservation_type_mask & ~SCSI_PR_TYPE_MASK_ALL, 0); for (type = &report_caps_types_array[0]; type->mask != 0; type++) { if (!(rcaps->persistent_reservation_type_mask & type->mask)) { logging(LOG_NORMAL, "PERSISTENT RESERVE op 0x%x not supported", type->op); continue; } logging(LOG_VERBOSE, "PERSISTENT RESERVE OUT op 0x%x supported, testing", type->op); /* reserve the target */ ret = prout_reserve(sd, key, type->op); CU_ASSERT_EQUAL(ret, 0); /* verify target reservation */ ret = prin_verify_reserved_as(sd, pr_type_is_all_registrants(type->op) ? 0 : key, type->op); CU_ASSERT_EQUAL(0, ret); /* release the target */ ret = prout_release(sd, key, type->op); CU_ASSERT_EQUAL(ret, 0); } scsi_free_scsi_task(tsk); rcaps = NULL; /* freed with tsk */ /* drop registration */ ret = prout_register_key(sd, 0, key); CU_ASSERT_EQUAL(ret, 0); } libiscsi-1.17.0/test-tool/test_prin_serviceaction_range.c000066400000000000000000000036111271371262700236110ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 by Lee Duncan 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, see . */ #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-support.h" #include "iscsi-test-cu.h" void test_prin_serviceaction_range(void) { int ret = 0; int i; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test Persistent Reserve IN Serviceaction range."); /* verify PRIN/READ_KEYS works -- XXX redundant -- remove this? */ ret = prin_read_keys(sd, &task, NULL); if (ret == -2) { logging(LOG_NORMAL, "[SKIPPED] PERSISTEN RESERVE IN is not implemented."); CU_PASS("PERSISTENT RESERVE IN is not implemented."); return; } CU_ASSERT_EQUAL(ret, 0); /* verify that PRIN/SA={0,1,2,3} works ... */ for (i = 0; i < 4; i++) { ret = prin_task(sd, i, 1); CU_ASSERT_EQUAL(ret, 0); } /* verify that PRIN/SA={4..0x20} fails ... */ for (i = 4; i < 0x20; i++) { ret = prin_task(sd, i, 0); CU_ASSERT_EQUAL(ret, 0); } } libiscsi-1.17.0/test-tool/test_prout_clear_simple.c000066400000000000000000000055401271371262700224420ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2015 David Disseldorp 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, see . */ #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-support.h" #include "iscsi-test-cu.h" void test_prout_clear_simple(void) { int ret = 0; uint32_t old_gen; const unsigned long long key = rand_key(); struct scsi_task *tsk; struct scsi_persistent_reserve_in_read_keys *rk; CHECK_FOR_DATALOSS; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test Persistent Reserve OUT CLEAR works."); /* register our reservation key with the target */ ret = prout_register_and_ignore(sd, key); if (ret == -2) { logging(LOG_NORMAL, "[SKIPPED] PERSISTENT RESERVE OUT is not implemented."); CU_PASS("PERSISTENT RESERVE OUT is not implemented."); return; } CU_ASSERT_EQUAL(ret, 0); ret = prin_read_keys(sd, &tsk, &rk); CU_ASSERT_EQUAL(ret, 0); CU_ASSERT_NOT_EQUAL(rk->num_keys, 0); /* retain PR generation number to check for increments */ old_gen = rk->prgeneration; scsi_free_scsi_task(tsk); rk = NULL; /* freed with tsk */ /* reserve the target */ ret = prout_reserve(sd, key, SCSI_PERSISTENT_RESERVE_TYPE_EXCLUSIVE_ACCESS); CU_ASSERT_EQUAL(ret, 0); /* verify target reservation */ ret = prin_verify_reserved_as(sd, key, SCSI_PERSISTENT_RESERVE_TYPE_EXCLUSIVE_ACCESS); CU_ASSERT_EQUAL(ret, 0); /* clear reservation and registration */ ret = prout_clear(sd, key); CU_ASSERT_EQUAL(ret, 0); ret = prin_verify_not_reserved(sd); CU_ASSERT_EQUAL(ret, 0); ret = prin_read_keys(sd, &tsk, &rk); CU_ASSERT_EQUAL(ret, 0); CU_ASSERT_EQUAL(rk->num_keys, 0); /* generation incremented once for CLEAR (not for RESERVE) */ CU_ASSERT_EQUAL(rk->prgeneration, old_gen + 1); scsi_free_scsi_task(tsk); rk = NULL; /* freed with tsk */ } libiscsi-1.17.0/test-tool/test_prout_preempt.c000066400000000000000000000072421271371262700214600ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2015 David Disseldorp 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, see . */ #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-support.h" #include "iscsi-test-cu.h" #include "iscsi-multipath.h" void test_prout_preempt_rm_reg(void) { int ret = 0; const unsigned long long k1 = rand_key(); const unsigned long long k2 = rand_key(); struct scsi_device *sd2; struct scsi_task *tsk; uint32_t old_gen; int num_uas; struct scsi_persistent_reserve_in_read_keys *rk; CHECK_FOR_DATALOSS; if (sd->iscsi_ctx == NULL) { const char *err = "[SKIPPED] This PERSISTENT RESERVE test is " "only supported for iSCSI backends"; logging(LOG_NORMAL, "%s", err); CU_PASS(err); return; } logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test Persistent Reserve IN PREEMPT works."); ret = prout_register_and_ignore(sd, k1); if (ret == -2) { logging(LOG_NORMAL, "[SKIPPED] PERSISTEN RESERVE OUT is not implemented."); CU_PASS("PERSISTENT RESERVE OUT is not implemented."); return; } CU_ASSERT_EQUAL(ret, 0); /* clear all PR state */ ret = prout_clear(sd, k1); CU_ASSERT_EQUAL(ret, 0); /* need to reregister cleared key */ ret = prout_register_and_ignore(sd, k1); CU_ASSERT_EQUAL(ret, 0); ret = mpath_sd2_get_or_clone(sd, &sd2); CU_ASSERT_EQUAL(ret, 0); if (ret < 0) return; /* register secondary key */ ret = prout_register_and_ignore(sd2, k2); CU_ASSERT_EQUAL(ret, 0); /* confirm that k1 and k2 are registered */ ret = prin_read_keys(sd, &tsk, &rk); CU_ASSERT_EQUAL_FATAL(ret, 0); CU_ASSERT_EQUAL(rk->num_keys, 2); /* retain PR generation number to check for increments */ old_gen = rk->prgeneration; scsi_free_scsi_task(tsk); rk = NULL; /* freed with tsk */ /* use second connection to clear k1 registration */ ret = prout_preempt(sd2, k1, k2, SCSI_PERSISTENT_RESERVE_TYPE_EXCLUSIVE_ACCESS); CU_ASSERT_EQUAL(ret, 0); /* clear any UAs generated by preempt */ ret = test_iscsi_tur_until_good(sd, &num_uas); CU_ASSERT_EQUAL(ret, 0); ret = test_iscsi_tur_until_good(sd2, &num_uas); CU_ASSERT_EQUAL(ret, 0); ret = prin_read_keys(sd, &tsk, &rk); CU_ASSERT_EQUAL_FATAL(ret, 0); CU_ASSERT_EQUAL(rk->num_keys, 1); /* ensure preempt bumped generation number */ CU_ASSERT_EQUAL(rk->prgeneration, old_gen + 1); /* ensure k2 is retained */ CU_ASSERT_EQUAL(rk->keys[0], k2); /* unregister k2 */ ret = prout_register_key(sd2, 0, k2); CU_ASSERT_EQUAL(ret, 0); } libiscsi-1.17.0/test-tool/test_prout_register_simple.c000066400000000000000000000040641271371262700232000ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 by Lee Duncan 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, see . */ #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-support.h" #include "iscsi-test-cu.h" void test_prout_register_simple(void) { const unsigned long long key = rand_key(); int ret = 0; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test Persistent Reserve IN REGISTER works."); /* register our reservation key with the target */ ret = prout_register_and_ignore(sd, key); if (ret == -2) { logging(LOG_NORMAL, "[SKIPPED] PERSISTEN RESERVE OUT is not implemented."); CU_PASS("PERSISTENT RESERVE OUT is not implemented."); return; } CU_ASSERT_EQUAL(ret, 0); /* verify we can read the registration */ ret = prin_verify_key_presence(sd, key, 1); CU_ASSERT_EQUAL(ret, 0); /* try to reregister, which should fail */ ret = prout_reregister_key_fails(sd, key+1); CU_ASSERT_EQUAL(ret, 0); /* release from the target */ ret = prout_register_key(sd, 0, key); CU_ASSERT_EQUAL(ret, 0); /* Verify the registration is gone */ ret = prin_verify_key_presence(sd, key, 0); CU_ASSERT_EQUAL(ret, 0); } libiscsi-1.17.0/test-tool/test_prout_reserve_access.c000066400000000000000000000177511271371262700230060ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 by Lee Duncan 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, see . */ #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-test-cu.h" #include "iscsi-multipath.h" static void verify_persistent_reserve_access(struct scsi_device *sd1, struct scsi_device *sd2, const enum scsi_persistent_out_type pr_type, int reg_i2_can_read, int reg_i2_can_write, int unreg_i2_can_read, int unreg_i2_can_write) { int ret; const unsigned long long key = rand_key(); const unsigned long long key2 = rand_key(); logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Verify access for reservation type: %s", scsi_pr_type_str(pr_type)); /* send TURs to clear possible check conditions */ (void) testunitready_clear_ua(sd1); (void) testunitready_clear_ua(sd2); /* register our reservation key with the target */ ret = prout_register_and_ignore(sd1, key); if (ret == -2) { logging(LOG_NORMAL, "[SKIPPED] PERSISTEN RESERVE OUT is not implemented."); CU_PASS("PERSISTENT RESERVE OUT is not implemented."); return; } CU_ASSERT_EQUAL(0, ret); ret = prout_register_and_ignore(sd2, key2); CU_ASSERT_EQUAL(0, ret); /* reserve the target through initiator 1 */ ret = prout_reserve(sd1, key, pr_type); CU_ASSERT_EQUAL(0, ret); /* verify target reservation */ ret = prin_verify_reserved_as(sd1, pr_type_is_all_registrants(pr_type) ? 0 : key, pr_type); CU_ASSERT_EQUAL(0, ret); CU_ASSERT_PTR_NOT_NULL_FATAL(scratch); /* make sure init1 can read */ ret = verify_read_works(sd1, scratch); CU_ASSERT_EQUAL(0, ret); /* make sure init1 can write */ ret = verify_write_works(sd1, scratch); CU_ASSERT_EQUAL(0, ret); /* verify registered init2 read access */ if (reg_i2_can_read) ret = verify_read_works(sd2, scratch); else ret = verify_read_fails(sd2, scratch); CU_ASSERT_EQUAL(0, ret); /* verify registered init2 write access */ if (reg_i2_can_write) ret = verify_write_works(sd2, scratch); else ret = verify_write_fails(sd2, scratch); CU_ASSERT_EQUAL(0, ret); /* unregister init2 */ ret = prout_register_key(sd2, 0, key2); CU_ASSERT_EQUAL(0, ret); /* verify unregistered init2 read access */ if (unreg_i2_can_read) ret = verify_read_works(sd2, scratch); else ret = verify_read_fails(sd2, scratch); CU_ASSERT_EQUAL(0, ret); /* verify unregistered init2 write access */ if (unreg_i2_can_write) ret = verify_write_works(sd2, scratch); else ret = verify_write_fails(sd2, scratch); CU_ASSERT_EQUAL(0, ret); /* release our reservation */ ret = prout_release(sd1, key, pr_type); CU_ASSERT_EQUAL(0, ret); /* remove our key from the target */ ret = prout_register_key(sd1, 0, key); CU_ASSERT_EQUAL(0, ret); } void test_prout_reserve_access_ea(void) { struct scsi_device *sd2; int ret; if (sd->iscsi_ctx == NULL) { const char *err = "[SKIPPED] This PERSISTENT RESERVE test is " "only supported for iSCSI backends"; logging(LOG_NORMAL, "%s", err); CU_PASS(err); return; } ret = mpath_sd2_get_or_clone(sd, &sd2); CU_ASSERT_EQUAL(ret, 0); if (ret < 0) return; verify_persistent_reserve_access(sd, sd2, SCSI_PERSISTENT_RESERVE_TYPE_EXCLUSIVE_ACCESS, 0, 0, 0, 0); mpath_sd2_put(sd2); } void test_prout_reserve_access_we(void) { struct scsi_device *sd2; int ret; if (sd->iscsi_ctx == NULL) { const char *err = "[SKIPPED] This PERSISTENT RESERVE test is " "only supported for iSCSI backends"; logging(LOG_NORMAL, "%s", err); CU_PASS(err); return; } ret = mpath_sd2_get_or_clone(sd, &sd2); CU_ASSERT_EQUAL(ret, 0); if (ret < 0) return; verify_persistent_reserve_access(sd, sd2, SCSI_PERSISTENT_RESERVE_TYPE_WRITE_EXCLUSIVE, 1, 0, 1, 0); mpath_sd2_put(sd2); } void test_prout_reserve_access_earo(void) { struct scsi_device *sd2; int ret; if (sd->iscsi_ctx == NULL) { const char *err = "[SKIPPED] This PERSISTENT RESERVE test is " "only supported for iSCSI backends"; logging(LOG_NORMAL, "%s", err); CU_PASS(err); return; } ret = mpath_sd2_get_or_clone(sd, &sd2); CU_ASSERT_EQUAL(ret, 0); if (ret < 0) return; verify_persistent_reserve_access(sd, sd2, SCSI_PERSISTENT_RESERVE_TYPE_EXCLUSIVE_ACCESS_REGISTRANTS_ONLY, 1, 1, 0, 0); mpath_sd2_put(sd2); } void test_prout_reserve_access_wero(void) { struct scsi_device *sd2; int ret; if (sd->iscsi_ctx == NULL) { const char *err = "[SKIPPED] This PERSISTENT RESERVE test is " "only supported for iSCSI backends"; logging(LOG_NORMAL, "%s", err); CU_PASS(err); return; } ret = mpath_sd2_get_or_clone(sd, &sd2); CU_ASSERT_EQUAL(ret, 0); if (ret < 0) return; verify_persistent_reserve_access(sd, sd2, SCSI_PERSISTENT_RESERVE_TYPE_WRITE_EXCLUSIVE_REGISTRANTS_ONLY, 1, 1, 1, 0); mpath_sd2_put(sd2); } void test_prout_reserve_access_eaar(void) { struct scsi_device *sd2; int ret; if (sd->iscsi_ctx == NULL) { const char *err = "[SKIPPED] This PERSISTENT RESERVE test is " "only supported for iSCSI backends"; logging(LOG_NORMAL, "%s", err); CU_PASS(err); return; } ret = mpath_sd2_get_or_clone(sd, &sd2); CU_ASSERT_EQUAL(ret, 0); if (ret < 0) return; verify_persistent_reserve_access(sd, sd2, SCSI_PERSISTENT_RESERVE_TYPE_EXCLUSIVE_ACCESS_ALL_REGISTRANTS, 1, 1, 0, 0); mpath_sd2_put(sd2); } void test_prout_reserve_access_wear(void) { struct scsi_device *sd2; int ret; if (sd->iscsi_ctx == NULL) { const char *err = "[SKIPPED] This PERSISTENT RESERVE test is " "only supported for iSCSI backends"; logging(LOG_NORMAL, "%s", err); CU_PASS(err); return; } ret = mpath_sd2_get_or_clone(sd, &sd2); CU_ASSERT_EQUAL(ret, 0); if (ret < 0) return; verify_persistent_reserve_access(sd, sd2, SCSI_PERSISTENT_RESERVE_TYPE_WRITE_EXCLUSIVE_ALL_REGISTRANTS, 1, 1, 1, 0); mpath_sd2_put(sd2); } libiscsi-1.17.0/test-tool/test_prout_reserve_ownership.c000066400000000000000000000163641271371262700235620ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 by Lee Duncan 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, see . */ #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-test-cu.h" #include "iscsi-multipath.h" static void verify_persistent_reserve_ownership(struct scsi_device *sd1, struct scsi_device *sd2, const enum scsi_persistent_out_type pr_type, int resvn_is_shared) { int ret; const unsigned long long key1 = rand_key(); const unsigned long long key2 = rand_key(); logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Verify ownership for reservation type: %s", scsi_pr_type_str(pr_type)); /* send TURs to clear possible check conditions */ (void) testunitready_clear_ua(sd1); (void) testunitready_clear_ua(sd2); /* register our reservation key with the target */ ret = prout_register_and_ignore(sd1, key1); if (ret == -2) { logging(LOG_NORMAL, "[SKIPPED] PERSISTEN RESERVE OUT is not implemented."); CU_PASS("PERSISTENT RESERVE OUT is not implemented."); return; } CU_ASSERT_EQUAL(0, ret); ret = prout_register_and_ignore(sd2, key2); CU_ASSERT_EQUAL(0, ret); /* reserve the target through initiator 1 */ ret = prout_reserve(sd1, key1, pr_type); CU_ASSERT_EQUAL(0, ret); /* verify target reservation */ ret = prin_verify_reserved_as(sd1, pr_type_is_all_registrants(pr_type) ? 0 : key1, pr_type); CU_ASSERT_EQUAL(0, ret); /* unregister init1 */ ret = prout_register_key(sd1, 0, key1); CU_ASSERT_EQUAL(0, ret); /* verify if reservation is still present */ if (resvn_is_shared) { /* verify target reservation */ ret = prin_verify_reserved_as(sd1, pr_type_is_all_registrants(pr_type) ? 0 : key1, pr_type); CU_ASSERT_EQUAL(0, ret); /* release our reservation */ ret = prout_release(sd2, key2, pr_type); CU_ASSERT_EQUAL(0, ret); } else { /* verify target is not reserved now */ ret = prin_verify_not_reserved(sd1); CU_ASSERT_EQUAL(0, ret); /* send TUR to clear possible check condition */ (void) testunitready_clear_ua(sd2); } /* remove our remaining key from the target */ ret = prout_register_key(sd2, 0, key2); CU_ASSERT_EQUAL(0, ret); } void test_prout_reserve_ownership_ea(void) { struct scsi_device *sd2; int ret; if (sd->iscsi_ctx == NULL) { const char *err = "[SKIPPED] This PERSISTENT RESERVE test is " "only supported for iSCSI backends"; logging(LOG_NORMAL, "%s", err); CU_PASS(err); return; } ret = mpath_sd2_get_or_clone(sd, &sd2); CU_ASSERT_EQUAL(ret, 0); if (ret < 0) return; verify_persistent_reserve_ownership(sd, sd2, SCSI_PERSISTENT_RESERVE_TYPE_EXCLUSIVE_ACCESS, 0); mpath_sd2_put(sd2); } void test_prout_reserve_ownership_we(void) { struct scsi_device *sd2; int ret; if (sd->iscsi_ctx == NULL) { const char *err = "[SKIPPED] This PERSISTENT RESERVE test is " "only supported for iSCSI backends"; logging(LOG_NORMAL, "%s", err); CU_PASS(err); return; } ret = mpath_sd2_get_or_clone(sd, &sd2); CU_ASSERT_EQUAL(ret, 0); if (ret < 0) return; verify_persistent_reserve_ownership(sd, sd2, SCSI_PERSISTENT_RESERVE_TYPE_WRITE_EXCLUSIVE, 0); mpath_sd2_put(sd2); } void test_prout_reserve_ownership_earo(void) { struct scsi_device *sd2; int ret; if (sd->iscsi_ctx == NULL) { const char *err = "[SKIPPED] This PERSISTENT RESERVE test is " "only supported for iSCSI backends"; logging(LOG_NORMAL, "%s", err); CU_PASS(err); return; } ret = mpath_sd2_get_or_clone(sd, &sd2); CU_ASSERT_EQUAL(ret, 0); if (ret < 0) return; verify_persistent_reserve_ownership(sd, sd2, SCSI_PERSISTENT_RESERVE_TYPE_EXCLUSIVE_ACCESS_REGISTRANTS_ONLY, 0); mpath_sd2_put(sd2); } void test_prout_reserve_ownership_wero(void) { struct scsi_device *sd2; int ret; if (sd->iscsi_ctx == NULL) { const char *err = "[SKIPPED] This PERSISTENT RESERVE test is " "only supported for iSCSI backends"; logging(LOG_NORMAL, "%s", err); CU_PASS(err); return; } ret = mpath_sd2_get_or_clone(sd, &sd2); CU_ASSERT_EQUAL(ret, 0); if (ret < 0) return; verify_persistent_reserve_ownership(sd, sd2, SCSI_PERSISTENT_RESERVE_TYPE_WRITE_EXCLUSIVE_REGISTRANTS_ONLY, 0); mpath_sd2_put(sd2); } void test_prout_reserve_ownership_eaar(void) { struct scsi_device *sd2; int ret; if (sd->iscsi_ctx == NULL) { const char *err = "[SKIPPED] This PERSISTENT RESERVE test is " "only supported for iSCSI backends"; logging(LOG_NORMAL, "%s", err); CU_PASS(err); return; } ret = mpath_sd2_get_or_clone(sd, &sd2); CU_ASSERT_EQUAL(ret, 0); if (ret < 0) return; verify_persistent_reserve_ownership(sd, sd2, SCSI_PERSISTENT_RESERVE_TYPE_EXCLUSIVE_ACCESS_ALL_REGISTRANTS, 1); mpath_sd2_put(sd2); } void test_prout_reserve_ownership_wear(void) { struct scsi_device *sd2; int ret; if (sd->iscsi_ctx == NULL) { const char *err = "[SKIPPED] This PERSISTENT RESERVE test is " "only supported for iSCSI backends"; logging(LOG_NORMAL, "%s", err); CU_PASS(err); return; } ret = mpath_sd2_get_or_clone(sd, &sd2); CU_ASSERT_EQUAL(ret, 0); if (ret < 0) return; verify_persistent_reserve_ownership(sd, sd2, SCSI_PERSISTENT_RESERVE_TYPE_WRITE_EXCLUSIVE_ALL_REGISTRANTS, 1); mpath_sd2_put(sd2); } libiscsi-1.17.0/test-tool/test_prout_reserve_simple.c000066400000000000000000000056041271371262700230300ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 by Lee Duncan 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, see . */ #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-support.h" #include "iscsi-test-cu.h" /* * list of persistent reservation types to test, in order */ static enum scsi_persistent_out_type pr_types_to_test[] = { SCSI_PERSISTENT_RESERVE_TYPE_WRITE_EXCLUSIVE, SCSI_PERSISTENT_RESERVE_TYPE_EXCLUSIVE_ACCESS, SCSI_PERSISTENT_RESERVE_TYPE_WRITE_EXCLUSIVE_REGISTRANTS_ONLY, SCSI_PERSISTENT_RESERVE_TYPE_EXCLUSIVE_ACCESS_REGISTRANTS_ONLY, SCSI_PERSISTENT_RESERVE_TYPE_WRITE_EXCLUSIVE_ALL_REGISTRANTS, SCSI_PERSISTENT_RESERVE_TYPE_EXCLUSIVE_ACCESS_ALL_REGISTRANTS, 0 }; void test_prout_reserve_simple(void) { int ret = 0; int i; const unsigned long long key = rand_key(); logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test Persistent Reserve IN RESERVE works."); /* register our reservation key with the target */ ret = prout_register_and_ignore(sd, key); if (ret == -2) { logging(LOG_NORMAL, "[SKIPPED] PERSISTEN RESERVE OUT is not implemented."); CU_PASS("PERSISTENT RESERVE OUT is not implemented."); return; } CU_ASSERT_EQUAL(ret, 0); /* test each reservatoin type */ for (i = 0; pr_types_to_test[i] != 0; i++) { enum scsi_persistent_out_type pr_type = pr_types_to_test[i]; /* reserve the target */ ret = prout_reserve(sd, key, pr_type); CU_ASSERT_EQUAL(ret, 0); /* verify target reservation */ ret = prin_verify_reserved_as(sd, pr_type_is_all_registrants(pr_type) ? 0 : key, pr_type); CU_ASSERT_EQUAL(ret, 0); /* release our reservation */ ret = prout_release(sd, key, pr_type); CU_ASSERT_EQUAL(ret, 0); } /* remove our key from the target */ ret = prout_register_key(sd, 0, key); CU_ASSERT_EQUAL(ret, 0); } libiscsi-1.17.0/test-tool/test_read10_0blocks.c000066400000000000000000000034251271371262700212430ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2012 by Lee Duncan 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, see . */ #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-test-cu.h" void test_read10_0blocks(void) { logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test READ10 0-blocks at LBA==0"); READ10(sd, NULL, 0, 0, block_size, 0, 0, 0, 0, 0, NULL, EXPECT_STATUS_GOOD); if (num_blocks > 0x80000000) { CU_PASS("[SKIPPED] LUN is too big"); return; } logging(LOG_VERBOSE, "Test READ10 0-blocks one block past end-of-LUN"); READ10(sd, NULL, num_blocks + 1, 0, block_size, 0, 0, 0, 0, 0, NULL, EXPECT_LBA_OOB); logging(LOG_VERBOSE, "Test READ10 0-blocks at LBA==2^31"); READ10(sd, NULL, 0x80000000, 0, block_size, 0, 0, 0, 0, 0, NULL, EXPECT_LBA_OOB); logging(LOG_VERBOSE, "Test READ10 0-blocks at LBA==-1"); READ10(sd, NULL, -1, 0, block_size, 0, 0, 0, 0, 0, NULL, EXPECT_LBA_OOB); } libiscsi-1.17.0/test-tool/test_read10_beyond_eol.c000066400000000000000000000054231271371262700220250ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2012 by Lee Duncan 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, see . */ #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-test-cu.h" void test_read10_beyond_eol(void) { int i; if (num_blocks >= 0x80000000) { CU_PASS("LUN is too big for read-beyond-eol tests with READ10. Skipping test.\n"); return; } logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test READ10 1-256 blocks one block beyond the end"); for (i = 1; i <= 256; i++) { if (maximum_transfer_length && maximum_transfer_length < i) { break; } READ10(sd, NULL, num_blocks + 1 - i, i * block_size, block_size, 0, 0, 0, 0, 0, NULL, EXPECT_LBA_OOB); } logging(LOG_VERBOSE, "Test READ10 1-256 blocks at LBA==2^31"); for (i = 1; i <= 256; i++) { if (maximum_transfer_length && maximum_transfer_length < i) { break; } READ10(sd, NULL, 0x80000000, i * block_size, block_size, 0, 0, 0, 0, 0, NULL, EXPECT_LBA_OOB); } logging(LOG_VERBOSE, "Test READ10 1-256 blocks at LBA==-1"); for (i = 1; i <= 256; i++) { if (maximum_transfer_length && maximum_transfer_length < i) { break; } READ10(sd, NULL, -1, i * block_size, block_size, 0, 0, 0, 0, 0, NULL, EXPECT_LBA_OOB); } logging(LOG_VERBOSE, "Test READ10 2-256 blocks all but one block beyond the end"); for (i = 2; i <= 256; i++) { if (maximum_transfer_length && maximum_transfer_length < i) { break; } READ10(sd, NULL, num_blocks - 1, i * block_size, block_size, 0, 0, 0, 0, 0, NULL, EXPECT_LBA_OOB); } } libiscsi-1.17.0/test-tool/test_read10_dpofua.c000066400000000000000000000114051271371262700211610ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2014 by Ronnie Sahlberg 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, see . */ #include #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-test-cu.h" void test_read10_dpofua(void) { int dpofua, usage_data_dpofua; struct scsi_task *ms_task = NULL; struct scsi_mode_sense *ms; struct scsi_task *rso_task = NULL; struct scsi_report_supported_op_codes_one_command *rsoc; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test READ10 DPO/FUA flags"); CHECK_FOR_SBC; logging(LOG_VERBOSE, "Read the DPOFUA flag from mode sense data"); MODESENSE6(sd, &ms_task, 0, SCSI_MODESENSE_PC_CURRENT, SCSI_MODEPAGE_RETURN_ALL_PAGES, 0, 255, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "[SUCCESS] Mode sense returned status GOOD"); ms = scsi_datain_unmarshall(ms_task); dpofua = ms && (ms->device_specific_parameter & 0x10); scsi_free_scsi_task(ms_task); if (dpofua) { logging(LOG_VERBOSE, "DPOFUA flag is set. Device should allow " "DPO/FUA flags in CDBs"); } else { logging(LOG_VERBOSE, "DPOFUA flag is clear. Device should fail " "CDBs with DPO/FUA set"); } logging(LOG_VERBOSE, "Test READ10 with DPO==1"); if (dpofua) { READ10(sd, NULL, 0, block_size, block_size, 0, 1, 0, 0, 0, NULL, EXPECT_STATUS_GOOD); } else { READ10(sd, NULL, 0, block_size, block_size, 0, 1, 0, 0, 0, NULL, EXPECT_INVALID_FIELD_IN_CDB); } logging(LOG_VERBOSE, "Test READ10 with FUA==1"); if (dpofua) { READ10(sd, NULL, 0, block_size, block_size, 0, 0, 1, 0, 0, NULL, EXPECT_STATUS_GOOD); } else { READ10(sd, NULL, 0, block_size, block_size, 0, 0, 1, 0, 0, NULL, EXPECT_INVALID_FIELD_IN_CDB); } logging(LOG_VERBOSE, "Test READ10 with DPO==1 FUA==1"); if (dpofua) { READ10(sd, NULL, 0, block_size, block_size, 0, 1, 1, 0, 0, NULL, EXPECT_STATUS_GOOD); } else { READ10(sd, NULL, 0, block_size, block_size, 0, 1, 1, 0, 0, NULL, EXPECT_INVALID_FIELD_IN_CDB); } logging(LOG_VERBOSE, "Try fetching REPORT_SUPPORTED_OPCODES " "for READ10"); REPORT_SUPPORTED_OPCODES(sd, &rso_task, 0, SCSI_REPORT_SUPPORTING_OPCODE, SCSI_OPCODE_READ10, 0, 65535, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Unmarshall the DATA-IN buffer"); rsoc = scsi_datain_unmarshall(rso_task); CU_ASSERT_PTR_NOT_NULL_FATAL(rsoc); usage_data_dpofua = rsoc->cdb_usage_data[1] & 0x18; if (dpofua) { logging(LOG_VERBOSE, "DPOFUA is set. Verify the " "DPO/FUA flags are set in the CDB_USAGE_DATA"); if (!usage_data_dpofua) { logging(LOG_NORMAL, "[FAILED] DpoFua not set " "in CDB_USAGE_DATE"); CU_FAIL("DpoFua not set in CDB_USAGE_DATE"); } } else { logging(LOG_VERBOSE, "DPOFUA is clear. Verify the " "DPO/FUA flags are clear in the CDB_USAGE_DATA"); if (usage_data_dpofua) { logging(LOG_NORMAL, "[FAILED] DpoFua not clear " "in CDB_USAGE_DATE"); CU_FAIL("DpoFua not clear in CDB_USAGE_DATE"); } } scsi_free_scsi_task(rso_task); } libiscsi-1.17.0/test-tool/test_read10_invalid.c000066400000000000000000000242741271371262700213410ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2012 by Lee Duncan 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, see . */ #include #include #include #include #include #include "iscsi.h" #include "iscsi-private.h" #include "scsi-lowlevel.h" #include "iscsi-test-cu.h" void test_read10_invalid(void) { struct iscsi_data data; struct scsi_task *task_ret; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test invalid READ10 commands"); logging(LOG_VERBOSE, "Block size is %zu", block_size); if (sd->iscsi_ctx == NULL) { const char *err = "[SKIPPED] This READ10 test is only " "supported for iSCSI backends"; logging(LOG_NORMAL, "%s", err); CU_PASS(err); return; } /* Try a read10 of 1 block but xferlength == 0 */ task = malloc(sizeof(struct scsi_task)); CU_ASSERT_PTR_NOT_NULL_FATAL(task); memset(task, 0, sizeof(struct scsi_task)); task->cdb[0] = SCSI_OPCODE_READ10; task->cdb[8] = 1; task->cdb_size = 10; task->xfer_dir = SCSI_XFER_READ; task->expxferlen = 0; /* * we don't want autoreconnect since some targets will drop the session * on this condition. */ iscsi_set_noautoreconnect(sd->iscsi_ctx, 1); logging(LOG_VERBOSE, "Try reading one block but with iSCSI expected transfer length==0"); task_ret = iscsi_scsi_command_sync(sd->iscsi_ctx, sd->iscsi_lun, task, NULL); CU_ASSERT_PTR_NOT_NULL_FATAL(task_ret); CU_ASSERT_NOT_EQUAL(task->status, SCSI_STATUS_CANCELLED); /* XXX redundant? */ logging(LOG_VERBOSE, "Verify that the target returned SUCCESS"); if (task->status != SCSI_STATUS_GOOD) { logging(LOG_VERBOSE, "[FAILED] Target returned error %s", iscsi_get_error(sd->iscsi_ctx)); } CU_ASSERT_EQUAL(task->status, SCSI_STATUS_GOOD); logging(LOG_VERBOSE, "Verify residual overflow flag is set"); if (task->residual_status != SCSI_RESIDUAL_OVERFLOW) { logging(LOG_VERBOSE, "[FAILED] Target did not set residual " "overflow flag"); } CU_ASSERT_EQUAL(task->residual_status, SCSI_RESIDUAL_OVERFLOW); logging(LOG_VERBOSE, "Verify we got %zu bytes of residual overflow", block_size); if (task->residual != block_size) { logging(LOG_VERBOSE, "[FAILED] Target did not set correct " "amount of residual. Expected %zu but got %zu.", block_size, task->residual); } CU_ASSERT_EQUAL(task->residual, block_size); scsi_free_scsi_task(task); task = NULL; /* in case the previous test failed the session */ iscsi_set_noautoreconnect(sd->iscsi_ctx, 0); logging(LOG_VERBOSE, "Try reading one block but with iSCSI expected transfer length==10000"); task = malloc(sizeof(struct scsi_task)); CU_ASSERT_PTR_NOT_NULL_FATAL(task); memset(task, 0, sizeof(struct scsi_task)); task->cdb[0] = SCSI_OPCODE_READ10; task->cdb[8] = 1; task->cdb_size = 10; task->xfer_dir = SCSI_XFER_READ; task->expxferlen = 10000; task_ret = iscsi_scsi_command_sync(sd->iscsi_ctx, sd->iscsi_lun, task, NULL); CU_ASSERT_PTR_NOT_NULL_FATAL(task_ret); logging(LOG_VERBOSE, "Verify that the target returned SUCCESS"); if (task->status != SCSI_STATUS_GOOD) { logging(LOG_VERBOSE, "[FAILED] Target returned error %s", iscsi_get_error(sd->iscsi_ctx)); } CU_ASSERT_EQUAL(task->status, SCSI_STATUS_GOOD); logging(LOG_VERBOSE, "Verify we got a whole block back from the target"); CU_ASSERT_EQUAL(task->datain.size, (int)block_size); logging(LOG_VERBOSE, "Verify residual underflow flag is set"); if (task->residual_status != SCSI_RESIDUAL_UNDERFLOW) { logging(LOG_VERBOSE, "[FAILED] Target did not set residual " "underflow flag"); } CU_ASSERT_EQUAL(task->residual_status, SCSI_RESIDUAL_UNDERFLOW); logging(LOG_VERBOSE, "Verify we got %zu bytes of residual underflow", 10000 - block_size); if (task->residual != 10000 - block_size) { logging(LOG_VERBOSE, "[FAILED] Target did not set correct " "amount of residual. Expected %zu but got %zu.", 10000 - block_size, task->residual); } CU_ASSERT_EQUAL(task->residual, 10000 - block_size); scsi_free_scsi_task(task); task = NULL; logging(LOG_VERBOSE, "Try reading one block but with iSCSI expected transfer length==200"); task = malloc(sizeof(struct scsi_task)); CU_ASSERT_PTR_NOT_NULL_FATAL(task); memset(task, 0, sizeof(struct scsi_task)); task->cdb[0] = SCSI_OPCODE_READ10; task->cdb[8] = 1; task->cdb_size = 10; task->xfer_dir = SCSI_XFER_READ; task->expxferlen = 200; task_ret = iscsi_scsi_command_sync(sd->iscsi_ctx, sd->iscsi_lun, task, NULL); CU_ASSERT_PTR_NOT_NULL_FATAL(task_ret); logging(LOG_VERBOSE, "Verify that the target returned SUCCESS"); if (task->status != SCSI_STATUS_GOOD) { logging(LOG_VERBOSE, "[FAILED] Target returned error %s", iscsi_get_error(sd->iscsi_ctx)); } CU_ASSERT_EQUAL(task->status, SCSI_STATUS_GOOD); logging(LOG_VERBOSE, "Verify we got 200 bytes back from the target"); CU_ASSERT_EQUAL(task->datain.size, 200); logging(LOG_VERBOSE, "Verify residual overflow flag is set"); if (task->residual_status != SCSI_RESIDUAL_OVERFLOW) { logging(LOG_VERBOSE, "[FAILED] Target did not set residual " "overflow flag"); } CU_ASSERT_EQUAL(task->residual_status, SCSI_RESIDUAL_OVERFLOW); logging(LOG_VERBOSE, "Verify we got %zu bytes of residual overflow", block_size - 200); if (task->residual != block_size - 200) { logging(LOG_VERBOSE, "[FAILED] Target did not set correct " "amount of residual. Expected %zu but got %zu.", block_size - 200, task->residual); } CU_ASSERT_EQUAL(task->residual, block_size - 200); scsi_free_scsi_task(task); task = NULL; logging(LOG_VERBOSE, "Try reading two blocks but iSCSI expected " "transfer length==%zu (==one block)", block_size); task = malloc(sizeof(struct scsi_task)); CU_ASSERT_PTR_NOT_NULL_FATAL(task); memset(task, 0, sizeof(struct scsi_task)); task->cdb[0] = SCSI_OPCODE_READ10; task->cdb[8] = 2; task->cdb_size = 10; task->xfer_dir = SCSI_XFER_READ; task->expxferlen = block_size; task_ret = iscsi_scsi_command_sync(sd->iscsi_ctx, sd->iscsi_lun, task, NULL); CU_ASSERT_PTR_NOT_NULL_FATAL(task_ret); logging(LOG_VERBOSE, "Verify that the target returned SUCCESS"); if (task->status != SCSI_STATUS_GOOD) { logging(LOG_VERBOSE, "[FAILED] Target returned error %s", iscsi_get_error(sd->iscsi_ctx)); } CU_ASSERT_EQUAL(task->status, SCSI_STATUS_GOOD); logging(LOG_VERBOSE, "Verify we got one whole block back from the target"); CU_ASSERT_EQUAL(task->datain.size, (int)block_size); logging(LOG_VERBOSE, "Verify residual overflow flag is set"); if (task->residual_status != SCSI_RESIDUAL_OVERFLOW) { logging(LOG_VERBOSE, "[FAILED] Target did not set residual " "overflow flag"); } CU_ASSERT_EQUAL(task->residual_status, SCSI_RESIDUAL_OVERFLOW); logging(LOG_VERBOSE, "Verify we got one block of residual overflow"); if (task->residual != block_size) { logging(LOG_VERBOSE, "[FAILED] Target did not set correct " "amount of residual. Expected %zu but got %zu.", block_size, task->residual); } CU_ASSERT_EQUAL(task->residual, block_size); scsi_free_scsi_task(task); task = NULL; logging(LOG_VERBOSE, "Try READ10 for one block but flag it as a write on the iSCSI layer."); task = malloc(sizeof(struct scsi_task)); CU_ASSERT_PTR_NOT_NULL_FATAL(task); memset(task, 0, sizeof(struct scsi_task)); task->cdb[0] = SCSI_OPCODE_READ10; task->cdb[8] = 1; task->cdb_size = 10; task->xfer_dir = SCSI_XFER_WRITE; task->expxferlen = block_size; memset(scratch, 0xa6, block_size); data.size = block_size; data.data = (unsigned char *)scratch; iscsi_set_noautoreconnect(sd->iscsi_ctx, 1); iscsi_set_timeout(sd->iscsi_ctx, 3); task_ret = iscsi_scsi_command_sync(sd->iscsi_ctx, sd->iscsi_lun, task, &data); iscsi_set_noautoreconnect(sd->iscsi_ctx, 0); CU_ASSERT_PTR_NOT_NULL_FATAL(task_ret); logging(LOG_VERBOSE, "Verify that the target returned SUCCESS"); if (task->status != SCSI_STATUS_GOOD) { logging(LOG_VERBOSE, "[FAILED] Target returned error %s", iscsi_get_error(sd->iscsi_ctx)); } CU_ASSERT_EQUAL(task->status, SCSI_STATUS_GOOD); scsi_free_scsi_task(task); task = NULL; } libiscsi-1.17.0/test-tool/test_read10_rdprotect.c000066400000000000000000000033701271371262700217130ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2012 by Lee Duncan 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, see . */ #include #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-test-cu.h" void test_read10_rdprotect(void) { int i; /* * Try out different non-zero values for RDPROTECT. */ logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test READ10 with non-zero RDPROTECT"); CHECK_FOR_SBC; if (!inq->protect || (rc16 != NULL && !rc16->prot_en)) { logging(LOG_VERBOSE, "Device does not support/use protection information. All commands should fail."); for (i = 1; i < 8; i++) { READ10(sd, NULL, 0, block_size, block_size, i, 0, 0, 0, 0, NULL, EXPECT_INVALID_FIELD_IN_CDB); } return; } logging(LOG_NORMAL, "No tests for devices that support protection information yet."); } libiscsi-1.17.0/test-tool/test_read10_residuals.c000066400000000000000000000225041271371262700217000ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 by Ronnie Sahlberg Based on test_read10_invalid.c : Copyright (C) 2012 by Lee Duncan 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, see . */ #include #include #include #include #include "iscsi.h" #include "iscsi-private.h" #include "scsi-lowlevel.h" #include "iscsi-test-cu.h" void test_read10_residuals(void) { struct scsi_task *task_ret; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test READ10 commands with residuals"); logging(LOG_VERBOSE, "Block size is %zu", block_size); if (sd->iscsi_ctx == NULL) { const char *err = "[SKIPPED] This READ10 test is only " "supported for iSCSI backends"; logging(LOG_NORMAL, "%s", err); CU_PASS(err); return; } /* Try a read10 of 1 block but xferlength == 0 */ task = malloc(sizeof(struct scsi_task)); CU_ASSERT_PTR_NOT_NULL_FATAL(task); memset(task, 0, sizeof(struct scsi_task)); task->cdb[0] = SCSI_OPCODE_READ10; task->cdb[8] = 1; task->cdb_size = 10; task->xfer_dir = SCSI_XFER_READ; task->expxferlen = 0; /* * we don't want autoreconnect since some targets will drop the session * on this condition. */ iscsi_set_noautoreconnect(sd->iscsi_ctx, 1); logging(LOG_VERBOSE, "Try reading one block but with iSCSI expected transfer length==0"); task_ret = iscsi_scsi_command_sync(sd->iscsi_ctx, sd->iscsi_lun, task, NULL); CU_ASSERT_PTR_NOT_NULL_FATAL(task_ret); CU_ASSERT_NOT_EQUAL(task->status, SCSI_STATUS_CANCELLED); /* XXX redundant? */ logging(LOG_VERBOSE, "Verify that the target returned SUCCESS"); if (task->status != SCSI_STATUS_GOOD) { logging(LOG_VERBOSE, "[FAILED] Target returned error %s", iscsi_get_error(sd->iscsi_ctx)); } CU_ASSERT_EQUAL(task->status, SCSI_STATUS_GOOD); logging(LOG_VERBOSE, "Verify residual overflow flag is set"); if (task->residual_status != SCSI_RESIDUAL_OVERFLOW) { logging(LOG_VERBOSE, "[FAILED] Target did not set residual " "overflow flag"); } CU_ASSERT_EQUAL(task->residual_status, SCSI_RESIDUAL_OVERFLOW); logging(LOG_VERBOSE, "Verify we got %zu bytes of residual overflow", block_size); if (task->residual != block_size) { logging(LOG_VERBOSE, "[FAILED] Target did not set correct " "amount of residual. Expected %zu but got %zu.", block_size, task->residual); } CU_ASSERT_EQUAL(task->residual, block_size); scsi_free_scsi_task(task); task = NULL; /* in case the previous test failed the session */ iscsi_set_noautoreconnect(sd->iscsi_ctx, 0); logging(LOG_VERBOSE, "Try reading one block but with iSCSI expected transfer length==10000"); task = malloc(sizeof(struct scsi_task)); CU_ASSERT_PTR_NOT_NULL_FATAL(task); memset(task, 0, sizeof(struct scsi_task)); task->cdb[0] = SCSI_OPCODE_READ10; task->cdb[8] = 1; task->cdb_size = 10; task->xfer_dir = SCSI_XFER_READ; task->expxferlen = 10000; task_ret = iscsi_scsi_command_sync(sd->iscsi_ctx, sd->iscsi_lun, task, NULL); CU_ASSERT_PTR_NOT_NULL_FATAL(task_ret); logging(LOG_VERBOSE, "Verify that the target returned SUCCESS"); if (task->status != SCSI_STATUS_GOOD) { logging(LOG_VERBOSE, "[FAILED] Target returned error %s", iscsi_get_error(sd->iscsi_ctx)); } CU_ASSERT_EQUAL(task->status, SCSI_STATUS_GOOD); logging(LOG_VERBOSE, "Verify we got one whole block back from the target"); if (task->datain.size != (int)block_size) { logging(LOG_VERBOSE, "[FAILED] Target returned %u bytes " "of data but should have returned %zu bytes.", task->datain.size,block_size); } CU_ASSERT_EQUAL(task->datain.size, (int)block_size); logging(LOG_VERBOSE, "Verify residual underflow flag is set"); if (task->residual_status != SCSI_RESIDUAL_UNDERFLOW) { logging(LOG_VERBOSE, "[FAILED] Target did not set residual " "underflow flag"); } CU_ASSERT_EQUAL(task->residual_status, SCSI_RESIDUAL_UNDERFLOW); logging(LOG_VERBOSE, "Verify we got %zu bytes of residual underflow", 10000 - block_size); if (task->residual != 10000 - block_size) { logging(LOG_VERBOSE, "[FAILED] Target did not set correct " "amount of residual. Expected %zu but got %zu.", 10000 - block_size, task->residual); } CU_ASSERT_EQUAL(task->residual, 10000 - block_size); scsi_free_scsi_task(task); task = NULL; logging(LOG_VERBOSE, "Try reading one block but with iSCSI expected transfer length==200"); task = malloc(sizeof(struct scsi_task)); CU_ASSERT_PTR_NOT_NULL_FATAL(task); memset(task, 0, sizeof(struct scsi_task)); task->cdb[0] = SCSI_OPCODE_READ10; task->cdb[8] = 1; task->cdb_size = 10; task->xfer_dir = SCSI_XFER_READ; task->expxferlen = 200; task_ret = iscsi_scsi_command_sync(sd->iscsi_ctx, sd->iscsi_lun, task, NULL); CU_ASSERT_PTR_NOT_NULL_FATAL(task_ret); logging(LOG_VERBOSE, "Verify that the target returned SUCCESS"); if (task->status != SCSI_STATUS_GOOD) { logging(LOG_VERBOSE, "[FAILED] Target returned error %s", iscsi_get_error(sd->iscsi_ctx)); } CU_ASSERT_EQUAL(task->status, SCSI_STATUS_GOOD); logging(LOG_VERBOSE, "Verify we got 200 bytes back from the target"); CU_ASSERT_EQUAL(task->datain.size, 200); logging(LOG_VERBOSE, "Verify residual overflow flag is set"); if (task->residual_status != SCSI_RESIDUAL_OVERFLOW) { logging(LOG_VERBOSE, "[FAILED] Target did not set residual " "overflow flag"); } CU_ASSERT_EQUAL(task->residual_status, SCSI_RESIDUAL_OVERFLOW); logging(LOG_VERBOSE, "Verify we got %zu bytes of residual overflow", block_size - 200); if (task->residual != block_size - 200) { logging(LOG_VERBOSE, "[FAILED] Target did not set correct " "amount of residual. Expected %zu but got %zu.", block_size - 200, task->residual); } CU_ASSERT_EQUAL(task->residual, block_size - 200); scsi_free_scsi_task(task); task = NULL; logging(LOG_VERBOSE, "Try reading two blocks but iSCSI expected " "transfer length==%zu (==one block)", block_size); task = malloc(sizeof(struct scsi_task)); CU_ASSERT_PTR_NOT_NULL_FATAL(task); memset(task, 0, sizeof(struct scsi_task)); task->cdb[0] = SCSI_OPCODE_READ10; task->cdb[8] = 2; task->cdb_size = 10; task->xfer_dir = SCSI_XFER_READ; task->expxferlen = block_size; task_ret = iscsi_scsi_command_sync(sd->iscsi_ctx, sd->iscsi_lun, task, NULL); CU_ASSERT_PTR_NOT_NULL_FATAL(task_ret); logging(LOG_VERBOSE, "Verify that the target returned SUCCESS"); if (task->status != SCSI_STATUS_GOOD) { logging(LOG_VERBOSE, "[FAILED] Target returned error %s", iscsi_get_error(sd->iscsi_ctx)); } CU_ASSERT_EQUAL(task->status, SCSI_STATUS_GOOD); logging(LOG_VERBOSE, "Verify we got one whole block back from the target"); CU_ASSERT_EQUAL(task->datain.size, (int)block_size); logging(LOG_VERBOSE, "Verify residual overflow flag is set"); if (task->residual_status != SCSI_RESIDUAL_OVERFLOW) { logging(LOG_VERBOSE, "[FAILED] Target did not set residual " "overflow flag"); } CU_ASSERT_EQUAL(task->residual_status, SCSI_RESIDUAL_OVERFLOW); logging(LOG_VERBOSE, "Verify we got one block of residual overflow"); if (task->residual != block_size) { logging(LOG_VERBOSE, "[FAILED] Target did not set correct " "amount of residual. Expected %zu but got %zu.", block_size, task->residual); } CU_ASSERT_EQUAL(task->residual, block_size); scsi_free_scsi_task(task); task = NULL; } libiscsi-1.17.0/test-tool/test_read10_simple.c000066400000000000000000000035201271371262700211730ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2012 by Lee Duncan 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, see . */ #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-support.h" #include "iscsi-test-cu.h" void test_read10_simple(void) { int i; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test READ10 of 1-256 blocks at the start of the LUN"); for (i = 1; i <= 256; i++) { if (maximum_transfer_length && maximum_transfer_length < i) { break; } READ10(sd, NULL, 0, i * block_size, block_size, 0, 0, 0, 0, 0, NULL, EXPECT_STATUS_GOOD); } logging(LOG_VERBOSE, "Test READ10 of 1-256 blocks at the end of the LUN"); for (i = 1; i <= 256; i++) { if (maximum_transfer_length && maximum_transfer_length < i) { break; } READ10(sd, NULL, num_blocks - i, i * block_size, block_size, 0, 0, 0, 0, 0, NULL, EXPECT_STATUS_GOOD); } } libiscsi-1.17.0/test-tool/test_read12_0blocks.c000066400000000000000000000034331271371262700212440ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg 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, see . */ #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-test-cu.h" void test_read12_0blocks(void) { logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test READ12 0-blocks at LBA==0"); READ12(sd, NULL, 0, 0, block_size, 0, 0, 0, 0, 0, NULL, EXPECT_STATUS_GOOD); if (num_blocks > 0x80000000) { CU_PASS("[SKIPPED] LUN is too big"); return; } logging(LOG_VERBOSE, "Test READ12 0-blocks one block past end-of-LUN"); READ12(sd, NULL, num_blocks + 1, 0, block_size, 0, 0, 0, 0, 0, NULL, EXPECT_LBA_OOB); logging(LOG_VERBOSE, "Test READ12 0-blocks at LBA==2^31"); READ12(sd, NULL, 0x80000000, 0, block_size, 0, 0, 0, 0, 0, NULL, EXPECT_LBA_OOB); logging(LOG_VERBOSE, "Test READ12 0-blocks at LBA==-1"); READ12(sd, NULL, -1, 0, block_size, 0, 0, 0, 0, 0, NULL, EXPECT_LBA_OOB); } libiscsi-1.17.0/test-tool/test_read12_beyond_eol.c000066400000000000000000000054321271371262700220270ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg 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, see . */ #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-test-cu.h" void test_read12_beyond_eol(void) { int i; if (num_blocks >= 0x80000000) { CU_PASS("LUN is too big for read-beyond-eol tests with READ12. Skipping test.\n"); return; } logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test READ12 1-256 blocks one block beyond the end"); for (i = 1; i <= 256; i++) { if (maximum_transfer_length && maximum_transfer_length < i) { break; } READ12(sd, NULL, num_blocks + 1 - i, i * block_size, block_size, 0, 0, 0, 0, 0, NULL, EXPECT_LBA_OOB); } logging(LOG_VERBOSE, "Test READ12 1-256 blocks at LBA==2^31"); for (i = 1; i <= 256; i++) { if (maximum_transfer_length && maximum_transfer_length < i) { break; } READ12(sd, NULL, 0x80000000, i * block_size, block_size, 0, 0, 0, 0, 0, NULL, EXPECT_LBA_OOB); } logging(LOG_VERBOSE, "Test READ12 1-256 blocks at LBA==-1"); for (i = 1; i <= 256; i++) { if (maximum_transfer_length && maximum_transfer_length < i) { break; } READ12(sd, NULL, -1, i * block_size, block_size, 0, 0, 0, 0, 0, NULL, EXPECT_LBA_OOB); } logging(LOG_VERBOSE, "Test READ12 2-256 blocks all but one block beyond the end"); for (i = 2; i <= 256; i++) { if (maximum_transfer_length && maximum_transfer_length < i) { break; } READ12(sd, NULL, num_blocks - 1, i * block_size, block_size, 0, 0, 0, 0, 0, NULL, EXPECT_LBA_OOB); } } libiscsi-1.17.0/test-tool/test_read12_dpofua.c000066400000000000000000000114041271371262700211620ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2014 by Ronnie Sahlberg 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, see . */ #include #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-test-cu.h" void test_read12_dpofua(void) { int dpofua, usage_data_dpofua; struct scsi_task *ms_task = NULL; struct scsi_mode_sense *ms; struct scsi_task *rso_task = NULL; struct scsi_report_supported_op_codes_one_command *rsoc; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test READ12 DPO/FUA flags"); CHECK_FOR_SBC; logging(LOG_VERBOSE, "Read the DPOFUA flag from mode sense data"); MODESENSE6(sd, &ms_task, 0, SCSI_MODESENSE_PC_CURRENT, SCSI_MODEPAGE_RETURN_ALL_PAGES, 0, 255, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "[SUCCESS] Mode sense returned status GOOD"); ms = scsi_datain_unmarshall(ms_task); dpofua = ms && (ms->device_specific_parameter & 0x10); scsi_free_scsi_task(ms_task); if (dpofua) { logging(LOG_VERBOSE, "DPOFUA flag is set. Device should allow " "DPO/FUA flags in CDBs"); } else { logging(LOG_VERBOSE, "DPOFUA flag is clear. Device should fail " "CDBs with DPO/FUA set"); } logging(LOG_VERBOSE, "Test READ12 with DPO==1"); if (dpofua) { READ12(sd, NULL, 0, block_size, block_size, 0, 1, 0, 0, 0, NULL, EXPECT_STATUS_GOOD); } else { READ12(sd, NULL, 0, block_size, block_size, 0, 1, 0, 0, 0, NULL, EXPECT_INVALID_FIELD_IN_CDB); } logging(LOG_VERBOSE, "Test READ12 with FUA==1"); if (dpofua) { READ12(sd, NULL, 0, block_size, block_size, 0, 0, 1, 0, 0, NULL, EXPECT_STATUS_GOOD); } else { READ12(sd, NULL, 0, block_size, block_size, 0, 0, 1, 0, 0, NULL, EXPECT_INVALID_FIELD_IN_CDB); } logging(LOG_VERBOSE, "Test READ12 with DPO==1 FUA==1"); if (dpofua) { READ12(sd, NULL, 0, block_size, block_size, 0, 1, 1, 0, 0, NULL, EXPECT_STATUS_GOOD); } else { READ12(sd, NULL, 0, block_size, block_size, 0, 1, 1, 0, 0, NULL, EXPECT_INVALID_FIELD_IN_CDB); } logging(LOG_VERBOSE, "Try fetching REPORT_SUPPORTED_OPCODES " "for READ12"); REPORT_SUPPORTED_OPCODES(sd, &rso_task, 0, SCSI_REPORT_SUPPORTING_OPCODE, SCSI_OPCODE_READ12, 0, 65535, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Unmarshall the DATA-IN buffer"); rsoc = scsi_datain_unmarshall(rso_task); CU_ASSERT_PTR_NOT_NULL_FATAL(rsoc); usage_data_dpofua = rsoc->cdb_usage_data[1] & 0x18; if (dpofua) { logging(LOG_VERBOSE, "DPOFUA is set. Verify the " "DPO/FUA flags are set in the CDB_USAGE_DATA"); if (!usage_data_dpofua) { logging(LOG_NORMAL, "[FAILED] DpoFua not set " "in CDB_USAGE_DATE"); CU_FAIL("DpoFua not set in CDB_USAGE_DATE"); } } else { logging(LOG_VERBOSE, "DPOFUA is clear. Verify the " "DPO/FUA flags are clear in the CDB_USAGE_DATA"); if (usage_data_dpofua) { logging(LOG_NORMAL, "[FAILED] DpoFua not clear " "in CDB_USAGE_DATE"); CU_FAIL("DpoFua not clear in CDB_USAGE_DATE"); } } scsi_free_scsi_task(rso_task); } libiscsi-1.17.0/test-tool/test_read12_rdprotect.c000066400000000000000000000033771271371262700217240ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg 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, see . */ #include #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-test-cu.h" void test_read12_rdprotect(void) { int i; /* * Try out different non-zero values for RDPROTECT. */ logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test READ12 with non-zero RDPROTECT"); CHECK_FOR_SBC; if (!inq->protect || (rc16 != NULL && !rc16->prot_en)) { logging(LOG_VERBOSE, "Device does not support/use protection information. All commands should fail."); for (i = 1; i < 8; i++) { READ12(sd, NULL, 0, block_size, block_size, i, 0, 0, 0, 0, NULL, EXPECT_INVALID_FIELD_IN_CDB); } return; } logging(LOG_NORMAL, "No tests for devices that support protection information yet."); } libiscsi-1.17.0/test-tool/test_read12_residuals.c000066400000000000000000000230561271371262700217050ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg 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, see . */ #include #include #include #include #include "iscsi.h" #include "iscsi-private.h" #include "scsi-lowlevel.h" #include "iscsi-test-cu.h" void test_read12_residuals(void) { struct scsi_task *task_ret; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test READ12 commands with residuals"); logging(LOG_VERBOSE, "Block size is %zu", block_size); if (sd->iscsi_ctx == NULL) { const char *err = "[SKIPPED] This READ12 test is only " "supported for iSCSI backends"; logging(LOG_NORMAL, "%s", err); CU_PASS(err); return; } task = malloc(sizeof(struct scsi_task)); CU_ASSERT_PTR_NOT_NULL_FATAL(task); memset(task, 0, sizeof(struct scsi_task)); task->cdb[0] = SCSI_OPCODE_READ12; task->cdb[9] = 1; task->cdb_size = 12; task->xfer_dir = SCSI_XFER_READ; task->expxferlen = 0; /* * we don't want autoreconnect since some targets will drop the session * on this condition. */ iscsi_set_noautoreconnect(sd->iscsi_ctx, 1); logging(LOG_VERBOSE, "Try reading one block but with iSCSI expected transfer length==0"); task_ret = iscsi_scsi_command_sync(sd->iscsi_ctx, sd->iscsi_lun, task, NULL); CU_ASSERT_PTR_NOT_NULL_FATAL(task_ret); CU_ASSERT_NOT_EQUAL(task->status, SCSI_STATUS_CANCELLED); /* XXX redundant? */ if (task->status == SCSI_STATUS_CHECK_CONDITION && task->sense.key == SCSI_SENSE_ILLEGAL_REQUEST && task->sense.ascq == SCSI_SENSE_ASCQ_INVALID_OPERATION_CODE) { logging(LOG_NORMAL, "[SKIPPED] READ12 is not implemented on this target."); CU_PASS("READ12 is not implemented."); return; } logging(LOG_VERBOSE, "Verify that the target returned SUCCESS"); if (task->status != SCSI_STATUS_GOOD) { logging(LOG_VERBOSE, "[FAILED] Target returned error %s", iscsi_get_error(sd->iscsi_ctx)); } CU_ASSERT_EQUAL(task->status, SCSI_STATUS_GOOD); logging(LOG_VERBOSE, "Verify residual overflow flag is set"); if (task->residual_status != SCSI_RESIDUAL_OVERFLOW) { logging(LOG_VERBOSE, "[FAILED] Target did not set residual " "overflow flag"); } CU_ASSERT_EQUAL(task->residual_status, SCSI_RESIDUAL_OVERFLOW); logging(LOG_VERBOSE, "Verify we got %zu bytes of residual overflow", block_size); if (task->residual != block_size) { logging(LOG_VERBOSE, "[FAILED] Target did not set correct " "amount of residual. Expected %zu but got %zu.", block_size, task->residual); } CU_ASSERT_EQUAL(task->residual, block_size); scsi_free_scsi_task(task); task = NULL; /* in case the previous test failed the session */ iscsi_set_noautoreconnect(sd->iscsi_ctx, 0); logging(LOG_VERBOSE, "Try reading one block but with iSCSI expected transfer length==10000"); task = malloc(sizeof(struct scsi_task)); CU_ASSERT_PTR_NOT_NULL_FATAL(task); memset(task, 0, sizeof(struct scsi_task)); task->cdb[0] = SCSI_OPCODE_READ12; task->cdb[9] = 1; task->cdb_size = 12; task->xfer_dir = SCSI_XFER_READ; task->expxferlen = 10000; task_ret = iscsi_scsi_command_sync(sd->iscsi_ctx, sd->iscsi_lun, task, NULL); CU_ASSERT_PTR_NOT_NULL_FATAL(task_ret); logging(LOG_VERBOSE, "Verify that the target returned SUCCESS"); if (task->status != SCSI_STATUS_GOOD) { logging(LOG_VERBOSE, "[FAILED] Target returned error %s", iscsi_get_error(sd->iscsi_ctx)); } CU_ASSERT_EQUAL(task->status, SCSI_STATUS_GOOD); logging(LOG_VERBOSE, "Verify we got one whole block back from the target"); if (task->datain.size != (int)block_size) { logging(LOG_VERBOSE, "[FAILED] Target returned %u bytes " "of data but should have returned %zu bytes.", task->datain.size,block_size); } CU_ASSERT_EQUAL(task->datain.size, (int)block_size); logging(LOG_VERBOSE, "Verify residual underflow flag is set"); if (task->residual_status != SCSI_RESIDUAL_UNDERFLOW) { logging(LOG_VERBOSE, "[FAILED] Target did not set residual " "underflow flag"); } CU_ASSERT_EQUAL(task->residual_status, SCSI_RESIDUAL_UNDERFLOW); logging(LOG_VERBOSE, "Verify we got %zu bytes of residual underflow", 10000 - block_size); if (task->residual != 10000 - block_size) { logging(LOG_VERBOSE, "[FAILED] Target did not set correct " "amount of residual. Expected %zu but got %zu.", 10000 - block_size, task->residual); } CU_ASSERT_EQUAL(task->residual, 10000 - block_size); scsi_free_scsi_task(task); task = NULL; logging(LOG_VERBOSE, "Try reading one block but with iSCSI expected transfer length==200"); task = malloc(sizeof(struct scsi_task)); CU_ASSERT_PTR_NOT_NULL_FATAL(task); memset(task, 0, sizeof(struct scsi_task)); task->cdb[0] = SCSI_OPCODE_READ12; task->cdb[9] = 1; task->cdb_size = 12; task->xfer_dir = SCSI_XFER_READ; task->expxferlen = 200; task_ret = iscsi_scsi_command_sync(sd->iscsi_ctx, sd->iscsi_lun, task, NULL); CU_ASSERT_PTR_NOT_NULL_FATAL(task_ret); logging(LOG_VERBOSE, "Verify that the target returned SUCCESS"); if (task->status != SCSI_STATUS_GOOD) { logging(LOG_VERBOSE, "[FAILED] Target returned error %s", iscsi_get_error(sd->iscsi_ctx)); } CU_ASSERT_EQUAL(task->status, SCSI_STATUS_GOOD); logging(LOG_VERBOSE, "Verify we got 200 bytes back from the target"); CU_ASSERT_EQUAL(task->datain.size, 200); logging(LOG_VERBOSE, "Verify residual overflow flag is set"); if (task->residual_status != SCSI_RESIDUAL_OVERFLOW) { logging(LOG_VERBOSE, "[FAILED] Target did not set residual " "overflow flag"); } CU_ASSERT_EQUAL(task->residual_status, SCSI_RESIDUAL_OVERFLOW); logging(LOG_VERBOSE, "Verify we got %zu bytes of residual overflow", block_size - 200); if (task->residual != block_size - 200) { logging(LOG_VERBOSE, "[FAILED] Target did not set correct " "amount of residual. Expected %zu but got %zu.", block_size - 200, task->residual); } CU_ASSERT_EQUAL(task->residual, block_size - 200); scsi_free_scsi_task(task); task = NULL; logging(LOG_VERBOSE, "Try reading two blocks but iSCSI expected " "transfer length==%zu (==one block)", block_size); task = malloc(sizeof(struct scsi_task)); CU_ASSERT_PTR_NOT_NULL_FATAL(task); memset(task, 0, sizeof(struct scsi_task)); task->cdb[0] = SCSI_OPCODE_READ12; task->cdb[9] = 2; task->cdb_size = 12; task->xfer_dir = SCSI_XFER_READ; task->expxferlen = block_size; task_ret = iscsi_scsi_command_sync(sd->iscsi_ctx, sd->iscsi_lun, task, NULL); CU_ASSERT_PTR_NOT_NULL_FATAL(task_ret); logging(LOG_VERBOSE, "Verify that the target returned SUCCESS"); if (task->status != SCSI_STATUS_GOOD) { logging(LOG_VERBOSE, "[FAILED] Target returned error %s", iscsi_get_error(sd->iscsi_ctx)); } CU_ASSERT_EQUAL(task->status, SCSI_STATUS_GOOD); logging(LOG_VERBOSE, "Verify we got one whole block back from the target"); CU_ASSERT_EQUAL(task->datain.size, (int)block_size); logging(LOG_VERBOSE, "Verify residual overflow flag is set"); if (task->residual_status != SCSI_RESIDUAL_OVERFLOW) { logging(LOG_VERBOSE, "[FAILED] Target did not set residual " "overflow flag"); } CU_ASSERT_EQUAL(task->residual_status, SCSI_RESIDUAL_OVERFLOW); logging(LOG_VERBOSE, "Verify we got one block of residual overflow"); if (task->residual != block_size) { logging(LOG_VERBOSE, "[FAILED] Target did not set correct " "amount of residual. Expected %zu but got %zu.", block_size, task->residual); } CU_ASSERT_EQUAL(task->residual, block_size); scsi_free_scsi_task(task); task = NULL; } libiscsi-1.17.0/test-tool/test_read12_simple.c000066400000000000000000000035301271371262700211760ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg 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, see . */ #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-support.h" #include "iscsi-test-cu.h" void test_read12_simple(void) { int i; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test READ12 of 1-256 blocks at the start of the LUN"); for (i = 1; i <= 256; i++) { if (maximum_transfer_length && maximum_transfer_length < i) { break; } READ12(sd, NULL, 0, i * block_size, block_size, 0, 0, 0, 0, 0, NULL, EXPECT_STATUS_GOOD); } logging(LOG_VERBOSE, "Test READ12 of 1-256 blocks at the end of the LUN"); for (i = 1; i <= 256; i++) { if (maximum_transfer_length && maximum_transfer_length < i) { break; } READ12(sd, NULL, num_blocks - i, i * block_size, block_size, 0, 0, 0, 0, 0, NULL, EXPECT_STATUS_GOOD); } } libiscsi-1.17.0/test-tool/test_read16_0blocks.c000066400000000000000000000033161271371262700212500ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg 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, see . */ #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-test-cu.h" void test_read16_0blocks(void) { CHECK_FOR_SBC; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test READ16 0-blocks at LBA==0"); READ16(sd, NULL, 0, 0, block_size, 0, 0, 0, 0, 0, NULL, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Test READ16 0-blocks one block past end-of-LUN"); READ16(sd, NULL, num_blocks + 1, 0, block_size, 0, 0, 0, 0, 0, NULL, EXPECT_LBA_OOB); logging(LOG_VERBOSE, "Test READ16 0-blocks at LBA==2^63"); READ16(sd, NULL, 0x8000000000000000ULL, 0, block_size, 0, 0, 0, 0, 0, NULL, EXPECT_LBA_OOB); logging(LOG_VERBOSE, "Test READ16 0-blocks at LBA==-1"); READ16(sd, NULL, -1, 0, block_size, 0, 0, 0, 0, 0, NULL, EXPECT_LBA_OOB); } libiscsi-1.17.0/test-tool/test_read16_beyond_eol.c000066400000000000000000000073041271371262700220330ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg 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, see . */ #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-test-cu.h" static int ilog2(size_t i) { int j = -1; while (i) { i >>= 1; j++; } return j; } void test_read16_beyond_eol(void) { int i; CHECK_FOR_SBC; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test READ16 1-256 blocks one block beyond the end"); for (i = 1; i <= 256; i++) { if (maximum_transfer_length && maximum_transfer_length < i) { break; } READ16(sd, NULL, num_blocks + 1 - i, i * block_size, block_size, 0, 0, 0, 0, 0, NULL, EXPECT_LBA_OOB); } logging(LOG_VERBOSE, "Test READ16 1-256 blocks at LBA==2^63"); for (i = 1; i <= 256; i++) { if (maximum_transfer_length && maximum_transfer_length < i) { break; } READ16(sd, NULL, 0x8000000000000000ULL, i * block_size, block_size, 0, 0, 0, 0, 0, NULL, EXPECT_LBA_OOB); } logging(LOG_VERBOSE, "Test READ16 1-256 blocks at LBA==2^%d", 64 - ilog2(block_size)); for (i = 1; i <= 256; i++) { if (maximum_transfer_length && maximum_transfer_length < i) { break; } READ16(sd, NULL, 1ULL << (64 - ilog2(block_size)), i * block_size, block_size, 0, 0, 0, 0, 0, NULL, EXPECT_LBA_OOB); } logging(LOG_VERBOSE, "Test READ16 1-256 blocks at LBA==2^%d", 63 - ilog2(block_size)); for (i = 1; i <= 256; i++) { if (maximum_transfer_length && maximum_transfer_length < i) { break; } READ16(sd, NULL, 1ULL << (63 - ilog2(block_size)), i * block_size, block_size, 0, 0, 0, 0, 0, NULL, EXPECT_LBA_OOB); } logging(LOG_VERBOSE, "Test READ16 1-256 blocks at LBA==-1"); for (i = 1; i <= 256; i++) { if (maximum_transfer_length && maximum_transfer_length < i) { break; } READ16(sd, NULL, -1, i * block_size, block_size, 0, 0, 0, 0, 0, NULL, EXPECT_LBA_OOB); } logging(LOG_VERBOSE, "Test READ16 2-256 blocks all but one block beyond the end"); for (i = 2; i <= 256; i++) { if (maximum_transfer_length && maximum_transfer_length < i) { break; } READ16(sd, NULL, num_blocks - 1, i * block_size, block_size, 0, 0, 0, 0, 0, NULL, EXPECT_LBA_OOB); } } libiscsi-1.17.0/test-tool/test_read16_dpofua.c000066400000000000000000000111721271371262700211700ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2014 by Ronnie Sahlberg 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, see . */ #include #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-test-cu.h" void test_read16_dpofua(void) { int dpofua, usage_data_dpofua; struct scsi_task *ms_task = NULL; struct scsi_mode_sense *ms; struct scsi_task *rso_task = NULL; struct scsi_report_supported_op_codes_one_command *rsoc; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test READ16 DPO/FUA flags"); CHECK_FOR_SBC; logging(LOG_VERBOSE, "Read the DPOFUA flag from mode sense data"); MODESENSE6(sd, &ms_task, 0, SCSI_MODESENSE_PC_CURRENT, SCSI_MODEPAGE_RETURN_ALL_PAGES, 0, 255, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "[SUCCESS] Mode sense returned status GOOD"); ms = scsi_datain_unmarshall(ms_task); dpofua = ms && (ms->device_specific_parameter & 0x10); scsi_free_scsi_task(ms_task); if (dpofua) { logging(LOG_VERBOSE, "DPOFUA flag is set. Device should allow " "DPO/FUA flags in CDBs"); } else { logging(LOG_VERBOSE, "DPOFUA flag is clear. Device should fail " "CDBs with DPO/FUA set"); } logging(LOG_VERBOSE, "Test READ16 with DPO==1"); if (dpofua) { READ16(sd, NULL, 0, block_size, block_size, 0, 1, 0, 0, 0, NULL, EXPECT_STATUS_GOOD); } else { READ16(sd, NULL, 0, block_size, block_size, 0, 1, 0, 0, 0, NULL, EXPECT_INVALID_FIELD_IN_CDB); } logging(LOG_VERBOSE, "Test READ16 with FUA==1"); if (dpofua) { READ16(sd, NULL, 0, block_size, block_size, 0, 0, 1, 0, 0, NULL, EXPECT_STATUS_GOOD); } else { READ16(sd, NULL, 0, block_size, block_size, 0, 0, 1, 0, 0, NULL, EXPECT_INVALID_FIELD_IN_CDB); } logging(LOG_VERBOSE, "Test READ16 with DPO==1 FUA==1"); if (dpofua) { READ16(sd, NULL, 0, block_size, block_size, 0, 1, 1, 0, 0, NULL, EXPECT_STATUS_GOOD); } else { READ16(sd, NULL, 0, block_size, block_size, 0, 1, 1, 0, 0, NULL, EXPECT_INVALID_FIELD_IN_CDB); } logging(LOG_VERBOSE, "Try fetching REPORT_SUPPORTED_OPCODES " "for READ16"); REPORT_SUPPORTED_OPCODES(sd, &rso_task, 0, SCSI_REPORT_SUPPORTING_OPCODE, SCSI_OPCODE_READ16, 0, 65535, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Unmarshall the DATA-IN buffer"); rsoc = scsi_datain_unmarshall(rso_task); CU_ASSERT_PTR_NOT_NULL_FATAL(rsoc); usage_data_dpofua = rsoc->cdb_usage_data[1] & 0x18; if (dpofua) { logging(LOG_VERBOSE, "DPOFUA is set. Verify the " "DPO/FUA flags are set in the CDB_USAGE_DATA"); if (!usage_data_dpofua) { logging(LOG_NORMAL, "[FAILED] DpoFua not set " "in CDB_USAGE_DATE"); CU_FAIL("DpoFua not set in CDB_USAGE_DATE"); } } else { logging(LOG_VERBOSE, "DPOFUA is clear. Verify the " "DPO/FUA flags are clear in the CDB_USAGE_DATA"); if (usage_data_dpofua) { logging(LOG_NORMAL, "[FAILED] DpoFua not clear " "in CDB_USAGE_DATE"); CU_FAIL("DpoFua not clear in CDB_USAGE_DATE"); } } scsi_free_scsi_task(rso_task); } libiscsi-1.17.0/test-tool/test_read16_rdprotect.c000066400000000000000000000033761271371262700217270ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg 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, see . */ #include #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-test-cu.h" void test_read16_rdprotect(void) { int i; /* * Try out different non-zero values for RDPROTECT. */ logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test READ16 with non-zero RDPROTECT"); CHECK_FOR_SBC; if (!inq->protect || (rc16 != NULL && !rc16->prot_en)) { logging(LOG_VERBOSE, "Device does not support/use protection information. All commands should fail."); for (i = 1; i < 8; i++) { READ16(sd, NULL, 0, block_size, block_size, i, 0, 0, 0, 0, NULL, EXPECT_INVALID_FIELD_IN_CDB); } return; } logging(LOG_NORMAL, "No tests for devices that support protection information yet."); } libiscsi-1.17.0/test-tool/test_read16_residuals.c000066400000000000000000000231631271371262700217100ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg 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, see . */ #include #include #include #include #include "iscsi.h" #include "iscsi-private.h" #include "scsi-lowlevel.h" #include "iscsi-test-cu.h" void test_read16_residuals(void) { struct scsi_task *task_ret; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test READ16 commands with residuals"); logging(LOG_VERBOSE, "Block size is %zu", block_size); if (sd->iscsi_ctx == NULL) { const char *err = "[SKIPPED] This READ16 test is only " "supported for iSCSI backends"; logging(LOG_NORMAL, "%s", err); CU_PASS(err); return; } task = malloc(sizeof(struct scsi_task)); CU_ASSERT_PTR_NOT_NULL_FATAL(task); memset(task, 0, sizeof(struct scsi_task)); task->cdb[0] = SCSI_OPCODE_READ16; task->cdb[13] = 1; task->cdb_size = 16; task->xfer_dir = SCSI_XFER_READ; task->expxferlen = 0; /* * we don't want autoreconnect since some targets will drop the session * on this condition. */ iscsi_set_noautoreconnect(sd->iscsi_ctx, 1); logging(LOG_VERBOSE, "Try reading one block but with iSCSI expected transfer length==0"); task_ret = iscsi_scsi_command_sync(sd->iscsi_ctx, sd->iscsi_lun, task, NULL); CU_ASSERT_PTR_NOT_NULL_FATAL(task_ret); CU_ASSERT_NOT_EQUAL(task->status, SCSI_STATUS_CANCELLED); /* XXX redundant? */ if (task->status == SCSI_STATUS_CHECK_CONDITION && task->sense.key == SCSI_SENSE_ILLEGAL_REQUEST && task->sense.ascq == SCSI_SENSE_ASCQ_INVALID_OPERATION_CODE) { logging(LOG_NORMAL, "[SKIPPED] READ16 is not implemented on this target and it does not claim SBC-3 support."); CU_PASS("READ16 is not implemented and no SBC-3 support claimed."); return; } logging(LOG_VERBOSE, "Verify that the target returned SUCCESS"); if (task->status != SCSI_STATUS_GOOD) { logging(LOG_VERBOSE, "[FAILED] Target returned error %s", iscsi_get_error(sd->iscsi_ctx)); } CU_ASSERT_EQUAL(task->status, SCSI_STATUS_GOOD); logging(LOG_VERBOSE, "Verify residual overflow flag is set"); if (task->residual_status != SCSI_RESIDUAL_OVERFLOW) { logging(LOG_VERBOSE, "[FAILED] Target did not set residual " "overflow flag"); } CU_ASSERT_EQUAL(task->residual_status, SCSI_RESIDUAL_OVERFLOW); logging(LOG_VERBOSE, "Verify we got %zu bytes of residual overflow", block_size); if (task->residual != block_size) { logging(LOG_VERBOSE, "[FAILED] Target did not set correct " "amount of residual. Expected %zu but got %zu.", block_size, task->residual); } CU_ASSERT_EQUAL(task->residual, block_size); scsi_free_scsi_task(task); task = NULL; /* in case the previous test failed the session */ iscsi_set_noautoreconnect(sd->iscsi_ctx, 0); logging(LOG_VERBOSE, "Try reading one block but with iSCSI expected transfer length==10000"); task = malloc(sizeof(struct scsi_task)); CU_ASSERT_PTR_NOT_NULL_FATAL(task); memset(task, 0, sizeof(struct scsi_task)); task->cdb[0] = SCSI_OPCODE_READ16; task->cdb[13] = 1; task->cdb_size = 16; task->xfer_dir = SCSI_XFER_READ; task->expxferlen = 10000; task_ret = iscsi_scsi_command_sync(sd->iscsi_ctx, sd->iscsi_lun, task, NULL); CU_ASSERT_PTR_NOT_NULL_FATAL(task_ret); logging(LOG_VERBOSE, "Verify that the target returned SUCCESS"); if (task->status != SCSI_STATUS_GOOD) { logging(LOG_VERBOSE, "[FAILED] Target returned error %s", iscsi_get_error(sd->iscsi_ctx)); } CU_ASSERT_EQUAL(task->status, SCSI_STATUS_GOOD); logging(LOG_VERBOSE, "Verify we got one whole block back from the target"); if (task->datain.size != (int)block_size) { logging(LOG_VERBOSE, "[FAILED] Target returned %u bytes " "of data but should have returned %zu bytes.", task->datain.size,block_size); } CU_ASSERT_EQUAL(task->datain.size, (int)block_size); logging(LOG_VERBOSE, "Verify residual underflow flag is set"); if (task->residual_status != SCSI_RESIDUAL_UNDERFLOW) { logging(LOG_VERBOSE, "[FAILED] Target did not set residual " "underflow flag"); } CU_ASSERT_EQUAL(task->residual_status, SCSI_RESIDUAL_UNDERFLOW); logging(LOG_VERBOSE, "Verify we got %zu bytes of residual underflow", 10000 - block_size); if (task->residual != 10000 - block_size) { logging(LOG_VERBOSE, "[FAILED] Target did not set correct " "amount of residual. Expected %zu but got %zu.", 10000 - block_size, task->residual); } CU_ASSERT_EQUAL(task->residual, 10000 - block_size); scsi_free_scsi_task(task); task = NULL; logging(LOG_VERBOSE, "Try reading one block but with iSCSI expected transfer length==200"); task = malloc(sizeof(struct scsi_task)); CU_ASSERT_PTR_NOT_NULL_FATAL(task); memset(task, 0, sizeof(struct scsi_task)); task->cdb[0] = SCSI_OPCODE_READ16; task->cdb[13] = 1; task->cdb_size = 16; task->xfer_dir = SCSI_XFER_READ; task->expxferlen = 200; task_ret = iscsi_scsi_command_sync(sd->iscsi_ctx, sd->iscsi_lun, task, NULL); CU_ASSERT_PTR_NOT_NULL_FATAL(task_ret); logging(LOG_VERBOSE, "Verify that the target returned SUCCESS"); if (task->status != SCSI_STATUS_GOOD) { logging(LOG_VERBOSE, "[FAILED] Target returned error %s", iscsi_get_error(sd->iscsi_ctx)); } CU_ASSERT_EQUAL(task->status, SCSI_STATUS_GOOD); logging(LOG_VERBOSE, "Verify we got 200 bytes back from the target"); CU_ASSERT_EQUAL(task->datain.size, 200); logging(LOG_VERBOSE, "Verify residual overflow flag is set"); if (task->residual_status != SCSI_RESIDUAL_OVERFLOW) { logging(LOG_VERBOSE, "[FAILED] Target did not set residual " "overflow flag"); } CU_ASSERT_EQUAL(task->residual_status, SCSI_RESIDUAL_OVERFLOW); logging(LOG_VERBOSE, "Verify we got %zu bytes of residual overflow", block_size - 200); if (task->residual != block_size - 200) { logging(LOG_VERBOSE, "[FAILED] Target did not set correct " "amount of residual. Expected %zu but got %zu.", block_size - 200, task->residual); } CU_ASSERT_EQUAL(task->residual, block_size - 200); scsi_free_scsi_task(task); task = NULL; logging(LOG_VERBOSE, "Try reading two blocks but iSCSI expected " "transfer length==%zu (==one block)", block_size); task = malloc(sizeof(struct scsi_task)); CU_ASSERT_PTR_NOT_NULL_FATAL(task); memset(task, 0, sizeof(struct scsi_task)); task->cdb[0] = SCSI_OPCODE_READ16; task->cdb[13] = 2; task->cdb_size = 16; task->xfer_dir = SCSI_XFER_READ; task->expxferlen = block_size; task_ret = iscsi_scsi_command_sync(sd->iscsi_ctx, sd->iscsi_lun, task, NULL); CU_ASSERT_PTR_NOT_NULL_FATAL(task_ret); logging(LOG_VERBOSE, "Verify that the target returned SUCCESS"); if (task->status != SCSI_STATUS_GOOD) { logging(LOG_VERBOSE, "[FAILED] Target returned error %s", iscsi_get_error(sd->iscsi_ctx)); } CU_ASSERT_EQUAL(task->status, SCSI_STATUS_GOOD); logging(LOG_VERBOSE, "Verify we got one whole block back from the target"); CU_ASSERT_EQUAL(task->datain.size, (int)block_size); logging(LOG_VERBOSE, "Verify residual overflow flag is set"); if (task->residual_status != SCSI_RESIDUAL_OVERFLOW) { logging(LOG_VERBOSE, "[FAILED] Target did not set residual " "overflow flag"); } CU_ASSERT_EQUAL(task->residual_status, SCSI_RESIDUAL_OVERFLOW); logging(LOG_VERBOSE, "Verify we got one block of residual overflow"); if (task->residual != block_size) { logging(LOG_VERBOSE, "[FAILED] Target did not set correct " "amount of residual. Expected %zu but got %zu.", block_size, task->residual); } CU_ASSERT_EQUAL(task->residual, block_size); scsi_free_scsi_task(task); task = NULL; } libiscsi-1.17.0/test-tool/test_read16_simple.c000066400000000000000000000035601271371262700212050ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg 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, see . */ #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-support.h" #include "iscsi-test-cu.h" void test_read16_simple(void) { int i; CHECK_FOR_SBC; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test READ16 of 1-256 blocks at the start of the LUN"); for (i = 1; i <= 256; i++) { if (maximum_transfer_length && maximum_transfer_length < i) { break; } READ16(sd, NULL, 0, i * block_size, block_size, 0, 0, 0, 0, 0, NULL, EXPECT_STATUS_GOOD); } logging(LOG_VERBOSE, "Test READ16 of 1-256 blocks at the end of the LUN"); for (i = 1; i <= 256; i++) { if (maximum_transfer_length && maximum_transfer_length < i) { break; } READ16(sd, NULL, num_blocks - i, i * block_size, block_size, 0, 0, 0, 0, 0, NULL, EXPECT_STATUS_GOOD); } } libiscsi-1.17.0/test-tool/test_read6_beyond_eol.c000066400000000000000000000042041271371262700217460ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg 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, see . */ #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-test-cu.h" void test_read6_beyond_eol(void) { int i; if (num_blocks > 0x1fffff) { CU_PASS("LUN is too big for read-beyond-eol tests with READ6. Skipping test.\n"); return; } logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test READ6 1-255 blocks one block beyond the end"); for (i = 1; i <= 255 && i + 0U <= num_blocks + 1; i++) { READ6(sd, NULL, num_blocks + 1 - i, i * block_size, block_size, NULL, EXPECT_LBA_OOB); } logging(LOG_VERBOSE, "Test READ6 1-255 blocks at LBA==0x1fffff"); for (i = 1; i <= 255; i++) { READ6(sd, NULL, 0x1fffff, i * block_size, block_size, NULL, EXPECT_LBA_OOB); } if (num_blocks == 0) { CU_PASS("LUN is too small for read-beyond-eol tests with READ6. Skipping test.\n"); return; } logging(LOG_VERBOSE, "Test READ6 2-255 blocks all but one block beyond the end"); for (i = 2; i <= 255; i++) { READ6(sd, NULL, num_blocks - 1, i * block_size, block_size, NULL, EXPECT_LBA_OOB); } } libiscsi-1.17.0/test-tool/test_read6_simple.c000066400000000000000000000063571271371262700211330ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg 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, see . */ #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-support.h" #include "iscsi-test-cu.h" void test_read6_simple(void) { int i; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test READ6 of 1-255 blocks at the start of the LUN"); for (i = 1; i <= 255; i++) { if (maximum_transfer_length && maximum_transfer_length < i) { break; } READ6(sd, NULL, 0, i * block_size, block_size, NULL, EXPECT_STATUS_GOOD); } logging(LOG_VERBOSE, "Test READ6 of 1-255 blocks at the end of the LUN"); if (num_blocks > 0x200000) { CU_PASS("LUN is too big for read-at-eol tests with READ6. Skipping test.\n"); } else { for (i = 1; i <= 255 && i + 0U <= num_blocks; i++) { if (maximum_transfer_length && maximum_transfer_length < i) { break; } READ6(sd, NULL, num_blocks - i, i * block_size, block_size, NULL, EXPECT_STATUS_GOOD); } } /* 256 is converted to 0 when the CDB is marshalled by the helper */ if (maximum_transfer_length >= 256) { logging(LOG_VERBOSE, "Transfer length == 0 means we want to " "transfer 256 blocks"); READ6(sd, &task, 0, 256 * block_size, block_size, NULL, EXPECT_STATUS_GOOD); if (task->status != SCSI_STATUS_GOOD) { logging(LOG_NORMAL, "[FAILED] READ6 command: " "failed with sense. %s", sd->error_str ); } CU_ASSERT_EQUAL(task->status, SCSI_STATUS_GOOD); logging(LOG_VERBOSE, "Verify that we did get 256 blocks of " "data back"); if (task->datain.size == (int)(256 * block_size)) { logging(LOG_VERBOSE, "[SUCCESS] Target returned 256 " "blocks of data"); } else { logging(LOG_NORMAL, "[FAILED] Target did not return " "256 blocks of data"); } CU_ASSERT_EQUAL(task->datain.size, (int)(256 * block_size)); } } libiscsi-1.17.0/test-tool/test_readcapacity10_simple.c000066400000000000000000000022371271371262700227150ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 by Ronnie Sahlberg 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, see . */ #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-support.h" #include "iscsi-test-cu.h" void test_readcapacity10_simple(void) { logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test basic READCAPACITY10"); READCAPACITY10(sd, NULL, 0, 0, EXPECT_STATUS_GOOD); } libiscsi-1.17.0/test-tool/test_readcapacity16_alloclen.c000066400000000000000000000024161271371262700232220ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 by Ronnie Sahlberg 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, see . */ #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-support.h" #include "iscsi-test-cu.h" void test_readcapacity16_alloclen(void) { int i; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test that READCAPACITY16 with alloc_len 0-15 is not an error"); for (i = 0; i < 16; i++) { READCAPACITY16(sd, NULL, i, EXPECT_STATUS_GOOD); } } libiscsi-1.17.0/test-tool/test_readcapacity16_protection.c000066400000000000000000000113511271371262700236150ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 by Ronnie Sahlberg 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, see . */ #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-support.h" #include "iscsi-test-cu.h" void test_readcapacity16_protection(void) { logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test READCAPACITY16 Protection Information"); CHECK_FOR_SBC; if (rc16 == NULL) { logging(LOG_NORMAL, "[SKIPPED] READCAPACITY16 is not implemented on this target."); CU_PASS("READCAPACITY16 is not implemented."); return; } if (!inq->protect) { logging(LOG_VERBOSE, "This device does not support PI. " "Verify that all relevant fields in READCAPACITY16 " "are 0"); logging(LOG_VERBOSE, "Verify that PROT_EN is 0"); if (rc16->prot_en) { logging(LOG_VERBOSE, "[FAILED] PROT_EN is set but " "the device does not claim support for " "protection information in the standard " "inquiry VPD."); } else { logging(LOG_VERBOSE, "[SUCCESS] PROT_EN is 0"); } CU_ASSERT_EQUAL(rc16->prot_en, 0); logging(LOG_VERBOSE, "Verify that P_TYPE is 0"); if (rc16->p_type) { logging(LOG_VERBOSE, "[FAILED] P_TYPE is non-zero but " "the device does not claim support for " "protection information in the standard " "inquiry VPD."); } else { logging(LOG_VERBOSE, "[SUCCESS] P_TYPE is 0"); } CU_ASSERT_EQUAL(rc16->p_type, 0); logging(LOG_VERBOSE, "Verify that P_I_EXP is 0"); if (rc16->p_i_exp) { logging(LOG_VERBOSE, "[FAILED] P_I_EXP is non-zero but " "the device does not claim support for " "protection information in the standard " "inquiry VPD."); } else { logging(LOG_VERBOSE, "[SUCCESS] P_I_EXP is 0"); } CU_ASSERT_EQUAL(rc16->p_i_exp, 0); return; } logging(LOG_VERBOSE, "This device supports PI. " "Verify that all relevant fields are sane"); if (!rc16->prot_en) { logging(LOG_VERBOSE, "Protection is not enabled. Verify " "that all relevant fields are zero"); logging(LOG_VERBOSE, "Verify that P_TYPE is 0"); if (rc16->p_type) { logging(LOG_VERBOSE, "[FAILED] P_TYPE is non-zero but " "protection information is not enabled."); } else { logging(LOG_VERBOSE, "[SUCCESS] P_TYPE is 0"); } CU_ASSERT_EQUAL(rc16->p_type, 0); logging(LOG_VERBOSE, "Verify that P_I_EXP is 0"); if (rc16->p_i_exp) { logging(LOG_VERBOSE, "[FAILED] P_I_EXP is non-zero but " "protection information is not enabled"); } else { logging(LOG_VERBOSE, "[SUCCESS] P_I_EXP is 0"); } CU_ASSERT_EQUAL(rc16->p_i_exp, 0); return; } logging(LOG_VERBOSE, "Protection is enabled. Verify " "that all relevant fields are sane"); switch (rc16->p_type) { case 0: case 1: case 2: break; default: logging(LOG_VERBOSE, "[FAILED] P_TYPE is invalid. Must be " "0,1,2 but was %d", rc16->p_type); CU_FAIL("P_TYPE is invalid"); } } libiscsi-1.17.0/test-tool/test_readcapacity16_simple.c000066400000000000000000000022421271371262700227170ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 by Ronnie Sahlberg 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, see . */ #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-support.h" #include "iscsi-test-cu.h" void test_readcapacity16_simple(void) { logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test that READCAPACITY16 works"); READCAPACITY16(sd, NULL, 16, EXPECT_STATUS_GOOD); } libiscsi-1.17.0/test-tool/test_readcapacity16_support.c000066400000000000000000000033511271371262700231440ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 by Ronnie Sahlberg 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, see . */ #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-support.h" #include "iscsi-test-cu.h" void test_readcapacity16_support(void) { int ret; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test that READCAPACITY16 is supported on SBC3"); ret = readcapacity16(sd, NULL, 15, EXPECT_STATUS_GOOD); if (ret == -2) { if (sbc3_support) { logging(LOG_NORMAL, "[FAILED] READCAPACITY16 is not available but the device claims SBC-3 support."); CU_FAIL("READCAPACITY16 failed but the device claims SBC-3 support."); } else { logging(LOG_NORMAL, "[SKIPPED] READCAPACITY16 is not implemented on this target and it does not claim SBC-3 support."); } } else { CU_ASSERT_EQUAL(ret, 0); } } libiscsi-1.17.0/test-tool/test_readonly_sbc.c000066400000000000000000000114461271371262700212200ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 by Ronnie Sahlberg 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, see . */ #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-support.h" #include "iscsi-test-cu.h" static void test_write10(void) { logging(LOG_VERBOSE, "Test WRITE10 fails with WRITE_PROTECTED"); memset(scratch, 0xa6, block_size); WRITE10(sd, 0, block_size, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_WRITE_PROTECTED); } static void test_write12(void) { logging(LOG_VERBOSE, "Test WRITE12 fails with WRITE_PROTECTED"); memset(scratch, 0xa6, block_size); WRITE12(sd, 0, block_size, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_WRITE_PROTECTED); } static void test_write16(void) { logging(LOG_VERBOSE, "Test WRITE16 fails with WRITE_PROTECTED"); memset(scratch, 0xa6, block_size); WRITE16(sd, 0, block_size, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_WRITE_PROTECTED); } static void test_writesame10(void) { logging(LOG_VERBOSE, "Test WRITE_SAME10 fails with WRITE_PROTECTED"); WRITESAME10(sd, 0, block_size, 1, 0, 0, 0, 0, scratch, EXPECT_WRITE_PROTECTED); logging(LOG_VERBOSE, "Test WRITE_SAME10 UNMAP fails with " "WRITE_PROTECTED"); WRITESAME10(sd, 0, block_size, 1, 0, 1, 0, 0, NULL, EXPECT_WRITE_PROTECTED); } static void test_writesame16(void) { logging(LOG_VERBOSE, "Test WRITE_SAME16 fails with WRITE_PROTECTED"); WRITESAME16(sd, 0, block_size, 1, 0, 0, 0, 0, scratch, EXPECT_WRITE_PROTECTED); logging(LOG_VERBOSE, "Test WRITE_SAME16 UNMAP fails with " "WRITE_PROTECTED"); WRITESAME16(sd, 0, block_size, 1, 0, 1, 0, 0, NULL, EXPECT_WRITE_PROTECTED); } static void test_writeverify10(void) { logging(LOG_VERBOSE, "Test WRITEVERIFY10 fails with WRITE_PROTECTED"); WRITEVERIFY10(sd, 0, block_size, block_size, 0, 0, 0, 0, scratch, EXPECT_WRITE_PROTECTED); } static void test_writeverify12(void) { logging(LOG_VERBOSE, "Test WRITEVERIFY12 fails with WRITE_PROTECTED"); WRITEVERIFY12(sd, 0, block_size, block_size, 0, 0, 0, 0, scratch, EXPECT_WRITE_PROTECTED); } static void test_writeverify16(void) { logging(LOG_VERBOSE, "Test WRITEVERIFY16 fails with WRITE_PROTECTED"); WRITEVERIFY16(sd, 0, block_size, block_size, 0, 0, 0, 0, scratch, EXPECT_WRITE_PROTECTED); } static void test_orwrite(void) { logging(LOG_VERBOSE, "Test ORWRITE fails with WRITE_PROTECTED"); ORWRITE(sd, 0, block_size, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_WRITE_PROTECTED); } static void test_compareandwrite(void) { logging(LOG_VERBOSE, "Test COMPAREANDWRITE fails with WRITE_PROTECTED"); COMPAREANDWRITE(sd, 0, scratch, 2 * block_size, block_size, 0, 0, 0, 0, EXPECT_WRITE_PROTECTED); } static void test_unmap(void) { struct unmap_list list[1]; logging(LOG_VERBOSE, "Test UNMAP of one physical block fails with " "WRITE_PROTECTED"); list[0].lba = 0; list[0].num = lbppb; UNMAP(sd, 0, list, 1, EXPECT_WRITE_PROTECTED); logging(LOG_VERBOSE, "Test UNMAP of one logical block fails with " "WRITE_PROTECTED"); list[0].lba = 0; list[0].num = 1; UNMAP(sd, 0, list, 1, EXPECT_WRITE_PROTECTED); } void test_readonly_sbc(void) { CHECK_FOR_DATALOSS; CHECK_FOR_READONLY; CHECK_FOR_SBC; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test that Medium write commands fail for READ-ONLY SBC devices"); test_compareandwrite(); test_orwrite(); test_unmap(); test_write10(); test_write12(); test_write16(); test_writesame10(); test_writesame16(); test_writeverify10(); test_writeverify12(); test_writeverify16(); } libiscsi-1.17.0/test-tool/test_receive_copy_results_copy_status.c000066400000000000000000000057001271371262700254420ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (c) 2015 SanDisk Corp. 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, see . */ #include #include #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-test-cu.h" void test_receive_copy_results_copy_status(void) { struct scsi_task *cs_task; struct scsi_copy_results_copy_status *csp; int tgt_desc_len = 0, seg_desc_len = 0; int offset = XCOPY_DESC_OFFSET, list_id = 1; struct iscsi_data data; unsigned char *xcopybuf; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test RECEIVE COPY RESULTS, COPY STATUS"); logging(LOG_VERBOSE, "No copy in progress"); RECEIVE_COPY_RESULTS(&cs_task, sd, SCSI_COPY_RESULTS_COPY_STATUS, list_id, NULL, EXPECT_INVALID_FIELD_IN_CDB); scsi_free_scsi_task(cs_task); cs_task = NULL; CHECK_FOR_DATALOSS; logging(LOG_VERBOSE, "Issue Extended Copy"); data.size = XCOPY_DESC_OFFSET + get_desc_len(IDENT_DESCR_TGT_DESCR) + get_desc_len(BLK_TO_BLK_SEG_DESCR); data.data = alloca(data.size); xcopybuf = data.data; memset(xcopybuf, 0, data.size); /* Initialize target descriptor list with one target descriptor */ offset += populate_tgt_desc(xcopybuf+offset, IDENT_DESCR_TGT_DESCR, LU_ID_TYPE_LUN, 0, 0, 0, 0, sd); tgt_desc_len = offset - XCOPY_DESC_OFFSET; /* Initialize segment descriptor list with one segment descriptor */ offset += populate_seg_desc_b2b(xcopybuf+offset, 0, 0, 0, 0, 2048, 0, num_blocks - 2048); seg_desc_len = offset - XCOPY_DESC_OFFSET - tgt_desc_len; /* Initialize the parameter list header */ populate_param_header(xcopybuf, list_id, 0, 0, 0, tgt_desc_len, seg_desc_len, 0); EXTENDEDCOPY(sd, &data, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Copy Status for the above Extended Copy command"); RECEIVE_COPY_RESULTS(&cs_task, sd, SCSI_COPY_RESULTS_COPY_STATUS, list_id, (void **)&csp, EXPECT_STATUS_GOOD); scsi_free_scsi_task(cs_task); } libiscsi-1.17.0/test-tool/test_receive_copy_results_op_params.c000066400000000000000000000030621271371262700250450ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (c) 2015 SanDisk Corp. 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, see . */ #include #include #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-test-cu.h" void test_receive_copy_results_op_params(void) { struct scsi_task *op_task = NULL; struct scsi_copy_results_op_params *opp; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test RECEIVE COPY RESULTS, OPERATING PARAMS"); RECEIVE_COPY_RESULTS(&op_task, sd, SCSI_COPY_RESULTS_OP_PARAMS, 0, (void **)&opp, EXPECT_STATUS_GOOD); logging(LOG_NORMAL, "max_target_desc=%d, max_seg_desc=%d", opp->max_target_desc_count, opp->max_segment_desc_count); scsi_free_scsi_task(op_task); } libiscsi-1.17.0/test-tool/test_report_supported_opcodes_one_command.c000066400000000000000000000153011271371262700262410ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 by Ronnie Sahlberg 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, see . */ #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-support.h" #include "iscsi-test-cu.h" void test_report_supported_opcodes_one_command(void) { int i; struct scsi_task *rso_task; struct scsi_task *one_task; struct scsi_report_supported_op_codes *rsoc; struct scsi_report_supported_op_codes_one_command *rsoc_one; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test READ_SUPPORTED_OPCODES reading one-command"); logging(LOG_VERBOSE, "Fetch list of all supported opcodes"); REPORT_SUPPORTED_OPCODES(sd, &rso_task, 0, SCSI_REPORT_SUPPORTING_OPS_ALL, 0, 0, 65535, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Unmarshall the DATA-IN buffer"); rsoc = scsi_datain_unmarshall(rso_task); CU_ASSERT_PTR_NOT_NULL_FATAL(rsoc); logging(LOG_VERBOSE, "Verify read one-command works for all supported " "opcodes"); for (i = 0; i < rsoc->num_descriptors; i++) { logging(LOG_VERBOSE, "Check opcode:0x%02x ServiceAction:0x%02x", rsoc->descriptors[i].opcode, rsoc->descriptors[i].sa); if (rsoc->descriptors[i].servactv) { logging(LOG_VERBOSE, "This opcode has service actions. " "Reporting Options 001b should fail"); REPORT_SUPPORTED_OPCODES(sd, NULL, 0, SCSI_REPORT_SUPPORTING_OPCODE, rsoc->descriptors[i].opcode, rsoc->descriptors[i].sa, 65535, EXPECT_INVALID_FIELD_IN_CDB); } else { logging(LOG_VERBOSE, "This opcode does not have " "service actions. Reporting Options 001b " "should work"); REPORT_SUPPORTED_OPCODES(sd, NULL, 0, SCSI_REPORT_SUPPORTING_OPCODE, rsoc->descriptors[i].opcode, rsoc->descriptors[i].sa, 65535, EXPECT_STATUS_GOOD); } if (rsoc->descriptors[i].servactv) { logging(LOG_VERBOSE, "This opcode has service actions. " "Reporting Options 002b should work"); REPORT_SUPPORTED_OPCODES(sd, NULL, 0, SCSI_REPORT_SUPPORTING_SERVICEACTION, rsoc->descriptors[i].opcode, rsoc->descriptors[i].sa, 65535, EXPECT_STATUS_GOOD); } else { logging(LOG_VERBOSE, "This opcode does not have " "service actions. Reporting Options 002b " "should fail"); REPORT_SUPPORTED_OPCODES(sd, NULL, 0, SCSI_REPORT_SUPPORTING_SERVICEACTION, rsoc->descriptors[i].opcode, rsoc->descriptors[i].sa, 65535, EXPECT_INVALID_FIELD_IN_CDB); } } logging(LOG_VERBOSE, "Verify read one-command CDB looks sane"); for (i = 0; i < rsoc->num_descriptors; i++) { logging(LOG_VERBOSE, "Check CDB for opcode:0x%02x " "ServiceAction:0x%02x", rsoc->descriptors[i].opcode, rsoc->descriptors[i].sa); REPORT_SUPPORTED_OPCODES(sd, &one_task, 0, rsoc->descriptors[i].servactv ? SCSI_REPORT_SUPPORTING_SERVICEACTION : SCSI_REPORT_SUPPORTING_OPCODE, rsoc->descriptors[i].opcode, rsoc->descriptors[i].sa, 65535, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Unmarshall the DATA-IN buffer"); rsoc_one = scsi_datain_unmarshall(one_task); CU_ASSERT_PTR_NOT_NULL_FATAL(rsoc_one); logging(LOG_VERBOSE, "Verify CDB length is not 0"); CU_ASSERT_NOT_EQUAL(rsoc_one->cdb_length, 0); if (rsoc_one->cdb_length == 0) { logging(LOG_NORMAL, "[FAILED] CDB length is 0"); } logging(LOG_VERBOSE, "Verify CDB[0] Usage Data == "); CU_ASSERT_EQUAL(rsoc_one->cdb_usage_data[0], rsoc->descriptors[i].opcode); if (rsoc_one->cdb_usage_data[0] != rsoc->descriptors[i].opcode) { logging(LOG_NORMAL, "[FAILED] CDB[0] Usage Data was " "0x%02x, expected 0x%02x for opcode 0x%02x", rsoc_one->cdb_usage_data[0], rsoc->descriptors[i].opcode, rsoc->descriptors[i].opcode); } scsi_free_scsi_task(one_task); } scsi_free_scsi_task(rso_task); } libiscsi-1.17.0/test-tool/test_report_supported_opcodes_rctd.c000066400000000000000000000075741271371262700247330ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 by Ronnie Sahlberg 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, see . */ #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-support.h" #include "iscsi-test-cu.h" void test_report_supported_opcodes_rctd(void) { int i; struct scsi_task *rso_task; struct scsi_report_supported_op_codes *rsoc; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test READ_SUPPORTED_OPCODES RCTD flag"); logging(LOG_VERBOSE, "Test READ_SUPPORTED_OPCODES report ALL opcodes " "without timeout descriptors. RCTD==0"); REPORT_SUPPORTED_OPCODES(sd, &rso_task, 0, SCSI_REPORT_SUPPORTING_OPS_ALL, 0, 0, 65535, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Unmarshall the DATA-IN buffer"); rsoc = scsi_datain_unmarshall(rso_task); CU_ASSERT_PTR_NOT_NULL_FATAL(rsoc); logging(LOG_VERBOSE, "Verify that all returned command descriptors " "lack timeout description"); for (i = 0; i < rsoc->num_descriptors; i++) { if (rsoc->descriptors[i].ctdp) { logging(LOG_NORMAL, "[FAILED] Command descriptor with " "CTDP set received when RCTD==0"); CU_FAIL("[FAILED] Command descriptor with " "CTDP set"); } } scsi_free_scsi_task(rso_task); logging(LOG_VERBOSE, "Test READ_SUPPORTED_OPCODES report ALL opcodes " "with timeout descriptors. RCTD==1"); REPORT_SUPPORTED_OPCODES(sd, &rso_task, 1, SCSI_REPORT_SUPPORTING_OPS_ALL, 0, 0, 65535, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Unmarshall the DATA-IN buffer"); rsoc = scsi_datain_unmarshall(rso_task); CU_ASSERT_NOT_EQUAL(rsoc, NULL); logging(LOG_VERBOSE, "Verify that all returned command descriptors " "have a timeout description"); for (i = 0; i < rsoc->num_descriptors; i++) { if (!rsoc->descriptors[i].ctdp) { logging(LOG_NORMAL, "[FAILED] Command descriptor " "with CTDP clear when RCTD==1"); CU_FAIL("[FAILED] Command descriptor without " "CTDP set"); } } logging(LOG_VERBOSE, "Verify that all timeout descriptors have the " "correct length"); for (i = 0; i < rsoc->num_descriptors; i++) { if (rsoc->descriptors[i].ctdp && rsoc->descriptors[i].to.descriptor_length != 0x0a) { logging(LOG_NORMAL, "[FAILED] Command descriptor " "with invalid TimeoutDescriptor length"); CU_FAIL("[FAILED] Command descriptor with " "invalid TimeoutDescriptor length"); } } scsi_free_scsi_task(rso_task); } libiscsi-1.17.0/test-tool/test_report_supported_opcodes_servactv.c000066400000000000000000000042261271371262700256230ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 by Ronnie Sahlberg 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, see . */ #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-support.h" #include "iscsi-test-cu.h" void test_report_supported_opcodes_servactv(void) { int i; struct scsi_task *rso_task; struct scsi_report_supported_op_codes *rsoc; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test READ_SUPPORTED_OPCODES SERVACTV flag"); REPORT_SUPPORTED_OPCODES(sd, &rso_task, 0, SCSI_REPORT_SUPPORTING_OPS_ALL, 0, 0, 65535, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Unmarshall the DATA-IN buffer"); rsoc = scsi_datain_unmarshall(rso_task); CU_ASSERT_PTR_NOT_NULL_FATAL(rsoc); logging(LOG_VERBOSE, "Verify that when SERVACTV is clear then " "ServiceAction must be zero."); for (i = 0; i < rsoc->num_descriptors; i++) { if (!rsoc->descriptors[i].servactv && rsoc->descriptors[i].sa) { logging(LOG_NORMAL, "[FAILED] ServiceAction is " "non-zero but SERVACTV is clear"); CU_FAIL("[FAILED] ServiceAction is " "non-zero but SERVACTV is clear"); } } scsi_free_scsi_task(rso_task); } libiscsi-1.17.0/test-tool/test_report_supported_opcodes_simple.c000066400000000000000000000024601271371262700252550ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 by Ronnie Sahlberg 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, see . */ #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-support.h" #include "iscsi-test-cu.h" void test_report_supported_opcodes_simple(void) { logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test basic READ_SUPPORTED_OPCODES"); REPORT_SUPPORTED_OPCODES(sd, NULL, 0, SCSI_REPORT_SUPPORTING_OPS_ALL, 0, 0, 1024, EXPECT_STATUS_GOOD); } libiscsi-1.17.0/test-tool/test_reserve6_2initiators.c000066400000000000000000000060051271371262700226370ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg 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, see . */ #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-support.h" #include "iscsi-test-cu.h" #include "iscsi-multipath.h" void test_reserve6_2initiators(void) { int ret; struct scsi_device *sd2; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test RESERVE6/RELEASE6 across two initiators"); if (sd->iscsi_ctx == NULL) { const char *err = "[SKIPPED] This RESERVE6 test is only " "supported for iSCSI backends"; logging(LOG_NORMAL, "%s", err); CU_PASS(err); return; } logging(LOG_NORMAL, "Take out a RESERVE6 from the first initiator"); RESERVE6(sd); logging(LOG_NORMAL, "Verify that the first initiator can re-RESERVE6 the same reservation"); RESERVE6(sd); ret = mpath_sd2_get_or_clone(sd, &sd2); CU_ASSERT_EQUAL(ret, 0); if (ret < 0) return; logging(LOG_NORMAL, "Try to take out a RESERVE6 from the second initiator"); ret = reserve6_conflict(sd2); CU_ASSERT_EQUAL(ret, 0); logging(LOG_NORMAL, "Try to RELEASE from the second initiator. Should be a nop"); RELEASE6(sd2); logging(LOG_NORMAL, "Test we can still send MODE SENSE from the first initiator"); MODESENSE6(sd, NULL, 0, SCSI_MODESENSE_PC_CURRENT, SCSI_MODEPAGE_RETURN_ALL_PAGES, 0, 255, EXPECT_STATUS_GOOD); logging(LOG_NORMAL, "MODE SENSE should fail from the second initiator"); MODESENSE6(sd2, NULL, 0, SCSI_MODESENSE_PC_CURRENT, SCSI_MODEPAGE_RETURN_ALL_PAGES, 0, 255, EXPECT_RESERVATION_CONFLICT); logging(LOG_NORMAL, "RESERVE6 from the second initiator should still fail"); ret = reserve6_conflict(sd2); CU_ASSERT_EQUAL(ret, 0); logging(LOG_NORMAL, "RELEASE6 from the first initiator"); RELEASE6(sd); logging(LOG_NORMAL, "RESERVE6 from the second initiator should work now"); RESERVE6(sd2); logging(LOG_NORMAL, "RELEASE6 from the second initiator"); RELEASE6(sd2); mpath_sd2_put(sd2); } libiscsi-1.17.0/test-tool/test_reserve6_itnexus_loss.c000066400000000000000000000051721271371262700231330ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg 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, see . */ #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-support.h" #include "iscsi-test-cu.h" #include "iscsi-multipath.h" void test_reserve6_itnexus_loss(void) { int ret; struct scsi_device *sd2; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test that RESERVE6 is released on it-nexus loss"); if (sd->iscsi_ctx == NULL) { const char *err = "[SKIPPED] This RESERVE6 test is only " "supported for iSCSI backends"; logging(LOG_NORMAL, "%s", err); CU_PASS(err); return; } logging(LOG_NORMAL, "Take out a RESERVE6 from the first initiator"); RESERVE6(sd); logging(LOG_VERBOSE, "Create a second connection to the target"); ret = mpath_sd2_get_or_clone(sd, &sd2); CU_ASSERT_EQUAL(ret, 0); if (ret < 0) return; logging(LOG_NORMAL, "Try to take out a RESERVE6 from the second initiator"); ret = reserve6_conflict(sd2); CU_ASSERT_EQUAL(ret, 0); logging(LOG_VERBOSE, "Disconnect from the target."); iscsi_destroy_context(sd->iscsi_ctx); logging(LOG_VERBOSE, "Sleep for three seconds incase the target is slow to reset"); sleep(3); logging(LOG_VERBOSE, "Reconnect to target"); sd->iscsi_ctx = iscsi_context_login(initiatorname1, sd->iscsi_url, &sd->iscsi_lun); if (sd->iscsi_ctx == NULL) { logging(LOG_VERBOSE, "Failed to login to target"); goto finished; } logging(LOG_NORMAL, "RESERVE6 from the second initiator should work now"); RESERVE6(sd2); logging(LOG_NORMAL, "RELEASE6 from the second initiator"); RELEASE6(sd2); finished: mpath_sd2_put(sd2); } libiscsi-1.17.0/test-tool/test_reserve6_logout.c000066400000000000000000000050141271371262700217000ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg 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, see . */ #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-support.h" #include "iscsi-test-cu.h" #include "iscsi-multipath.h" void test_reserve6_logout(void) { int ret; struct scsi_device *sd2; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test that RESERVE6 is released on logout"); if (sd->iscsi_ctx == NULL) { const char *err = "[SKIPPED] This RESERVE6 test is only " "supported for iSCSI backends"; logging(LOG_NORMAL, "%s", err); CU_PASS(err); return; } logging(LOG_NORMAL, "Take out a RESERVE6 from the first initiator"); RESERVE6(sd); logging(LOG_VERBOSE, "Create a second connection to the target"); ret = mpath_sd2_get_or_clone(sd, &sd2); CU_ASSERT_EQUAL(ret, 0); if (ret < 0) return; logging(LOG_NORMAL, "Try to take out a RESERVE6 from the second initiator"); ret = reserve6_conflict(sd2); CU_ASSERT_EQUAL(ret, 0); logging(LOG_VERBOSE, "Logout from target"); iscsi_logout_sync(sd->iscsi_ctx); iscsi_destroy_context(sd->iscsi_ctx); logging(LOG_VERBOSE, "Relogin to target"); sd->iscsi_ctx = iscsi_context_login(initiatorname1, sd->iscsi_url, &sd->iscsi_lun); if (sd->iscsi_ctx == NULL) { logging(LOG_VERBOSE, "Failed to login to target"); return; } logging(LOG_NORMAL, "RESERVE6 from the second initiator should work now"); RESERVE6(sd2); logging(LOG_NORMAL, "RELEASE6 from the second initiator"); RELEASE6(sd2); mpath_sd2_put(sd2); } libiscsi-1.17.0/test-tool/test_reserve6_lun_reset.c000066400000000000000000000051361271371262700223740ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg 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, see . */ #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-support.h" #include "iscsi-test-cu.h" void test_reserve6_lun_reset(void) { int ret; struct scsi_device sd2; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test that RESERVE6 is released on lun reset"); if (sd->iscsi_ctx == NULL) { const char *err = "[SKIPPED] This RESERVE6 test is only " "supported for iSCSI backends"; logging(LOG_NORMAL, "%s", err); CU_PASS(err); return; } logging(LOG_VERBOSE, "Take out a RESERVE6 from the first initiator"); RESERVE6(sd); logging(LOG_VERBOSE, "Send a LUN Reset"); ret = iscsi_task_mgmt_lun_reset_sync(sd->iscsi_ctx, sd->iscsi_lun); if (ret != 0) { logging(LOG_NORMAL, "LUN reset failed. %s", iscsi_get_error(sd->iscsi_ctx)); } CU_ASSERT_EQUAL(ret, 0); logging(LOG_VERBOSE, "Sleep for three seconds incase the target is slow to reset"); sleep(3); logging(LOG_VERBOSE, "Create a second connection to the target"); memset(&sd2, 0, sizeof(sd2)); sd2.iscsi_url = sd->iscsi_url; sd2.iscsi_lun = sd->iscsi_lun; sd2.iscsi_ctx = iscsi_context_login(initiatorname2, sd2.iscsi_url, &sd2.iscsi_lun); if (sd2.iscsi_ctx == NULL) { logging(LOG_VERBOSE, "Failed to login to target"); return; } logging(LOG_VERBOSE, "RESERVE6 from the second initiator should work now"); RESERVE6(&sd2); logging(LOG_VERBOSE, "RELEASE6 from the second initiator"); RELEASE6(&sd2); iscsi_logout_sync(sd2.iscsi_ctx); iscsi_destroy_context(sd2.iscsi_ctx); } libiscsi-1.17.0/test-tool/test_reserve6_simple.c000066400000000000000000000022111271371262700216540ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg 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, see . */ #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-support.h" #include "iscsi-test-cu.h" void test_reserve6_simple(void) { logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test basic RESERVE6/RELEASE6 commands if supported"); RESERVE6(sd); RELEASE6(sd); } libiscsi-1.17.0/test-tool/test_reserve6_target_cold_reset.c000066400000000000000000000045701271371262700240660ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg 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, see . */ #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-support.h" #include "iscsi-test-cu.h" #include "iscsi-multipath.h" void test_reserve6_target_cold_reset(void) { int ret; struct scsi_device *sd2; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test that RESERVE6 is released on target cold reset"); if (sd->iscsi_ctx == NULL) { const char *err = "[SKIPPED] This RESERVE6 test is only " "supported for iSCSI backends"; logging(LOG_NORMAL, "%s", err); CU_PASS(err); return; } logging(LOG_VERBOSE, "Take out a RESERVE6 from the first initiator"); RESERVE6(sd); logging(LOG_VERBOSE, "Send a Cold Reset to the target"); ret = iscsi_task_mgmt_target_cold_reset_sync(sd->iscsi_ctx); if (ret != 0) { logging(LOG_NORMAL, "Cold reset failed. %s", iscsi_get_error(sd->iscsi_ctx)); } CU_ASSERT_EQUAL(ret, 0); logging(LOG_VERBOSE, "Sleep for three seconds incase the target is slow to reset"); sleep(3); logging(LOG_VERBOSE, "Create a second connection to the target"); ret = mpath_sd2_get_or_clone(sd, &sd2); CU_ASSERT_EQUAL(ret, 0); if (ret < 0) return; logging(LOG_VERBOSE, "RESERVE6 from the second initiator should work now"); RESERVE6(sd2); logging(LOG_VERBOSE, "RELEASE6 from the second initiator"); RELEASE6(sd2); mpath_sd2_put(sd2); } libiscsi-1.17.0/test-tool/test_reserve6_target_warm_reset.c000066400000000000000000000045711271371262700241140ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg 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, see . */ #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-support.h" #include "iscsi-test-cu.h" #include "iscsi-multipath.h" void test_reserve6_target_warm_reset(void) { int ret; struct scsi_device *sd2; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test that RESERVE6 is released on target warm reset"); if (sd->iscsi_ctx == NULL) { const char *err = "[SKIPPED] This RESERVE6 test is only " "supported for iSCSI backends"; logging(LOG_NORMAL, "%s", err); CU_PASS(err); return; } logging(LOG_VERBOSE, "Take out a RESERVE6 from the first initiator"); RESERVE6(sd); logging(LOG_VERBOSE, "Send a Warm Reset to the target"); ret = iscsi_task_mgmt_target_warm_reset_sync(sd->iscsi_ctx); if (ret != 0) { logging(LOG_NORMAL, "Warm reset failed. %s", iscsi_get_error(sd->iscsi_ctx)); } CU_ASSERT_EQUAL(ret, 0); logging(LOG_VERBOSE, "Sleep for three seconds incase the target is slow to reset"); sleep(3); logging(LOG_VERBOSE, "Create a second connection to the target"); ret = mpath_sd2_get_or_clone(sd, &sd2); CU_ASSERT_EQUAL(ret, 0); if (ret < 0) return; logging(LOG_VERBOSE, "RESERVE6 from the second initiator should work now"); RESERVE6(sd2); logging(LOG_VERBOSE, "RELEASE6 from the second initiator"); RELEASE6(sd2); mpath_sd2_put(sd2); } libiscsi-1.17.0/test-tool/test_sanitize_block_erase.c000066400000000000000000000343651271371262700227400ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg 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, see . */ #include #include #include #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-test-cu.h" static void check_wabereq(void) { struct scsi_task *task_ret = NULL; logging(LOG_VERBOSE, "Read one block from LBA 0"); READ10(sd, &task_ret, 0, block_size, block_size, 0, 0, 0, 0, 0, NULL, EXPECT_STATUS_GOOD); CU_ASSERT_PTR_NOT_NULL_FATAL(task_ret); if (task_ret == NULL) { return; } CU_ASSERT_NOT_EQUAL(task_ret->status, SCSI_STATUS_CANCELLED); switch (inq_bdc->wabereq) { case 0: logging(LOG_NORMAL, "[FAILED] SANITIZE BLOCK ERASE " "opcode is supported but WABEREQ is 0"); CU_FAIL("[FAILED] SANITIZE BLOCK ERASE " "opcode is supported but WABEREQ is 0"); break; case 1: logging(LOG_VERBOSE, "WABEREQ==1. Reads from the " "device should be successful."); if (task_ret->status == SCSI_STATUS_GOOD) { logging(LOG_VERBOSE, "[SUCCESS] Read was " "successful after SANITIZE"); break; } logging(LOG_NORMAL, "[FAILED] Read after " "SANITIZE failed but WABEREQ is 1"); CU_FAIL("[FAILED] Read after SANITIZE failed " "but WABEREQ is 1"); break; case 2: logging(LOG_VERBOSE, "WABEREQ==2. Reads from the " "device should fail."); if (task_ret->status == SCSI_STATUS_CHECK_CONDITION && task_ret->sense.key == SCSI_SENSE_MEDIUM_ERROR && task_ret->sense.ascq != SCSI_SENSE_ASCQ_WRITE_AFTER_SANITIZE_REQUIRED) { logging(LOG_VERBOSE, "[SUCCESS] Read failed " "with CHECK_CONDITION/MEDIUM_ERROR/" "!WRITE_AFTER_SANITIZE_REQUIRED"); break; } logging(LOG_VERBOSE, "[FAILED] Read should have failed " "with CHECK_CONDITION/MEDIUM_ERROR/" "!WRITE_AFTER_SANITIZE_REQUIRED"); CU_FAIL("[FAILED] Read should have failed " "with CHECK_CONDITION/MEDIUM_ERROR/" "!WRITE_AFTER_SANITIZE_REQUIRED"); break; case 3: logging(LOG_VERBOSE, "WABEREQ==3. Reads from the " "device should fail."); if (task_ret->status == SCSI_STATUS_CHECK_CONDITION && task_ret->sense.key == SCSI_SENSE_MEDIUM_ERROR && task_ret->sense.ascq == SCSI_SENSE_ASCQ_WRITE_AFTER_SANITIZE_REQUIRED) { logging(LOG_VERBOSE, "[SUCCESS] Read failed " "with CHECK_CONDITION/MEDIUM_ERROR/" "WRITE_AFTER_SANITIZE_REQUIRED"); break; } logging(LOG_VERBOSE, "[FAILED] Read should have failed " "with CHECK_CONDITION/MEDIUM_ERROR/" "WRITE_AFTER_SANITIZE_REQUIRED"); CU_FAIL("[FAILED] Read should have failed " "with CHECK_CONDITION/MEDIUM_ERROR/" "WRITE_AFTER_SANITIZE_REQUIRED"); break; } scsi_free_scsi_task(task_ret); } static void check_unmap(void) { int i; struct scsi_task *task_ret = NULL; struct scsi_get_lba_status *lbas; uint64_t lba; logging(LOG_VERBOSE, "Read LBA mapping from the target"); GETLBASTATUS(sd, &task_ret, 0, 256, EXPECT_STATUS_GOOD); if (task_ret == NULL) { logging(LOG_VERBOSE, "[FAILED] Failed to read LBA mapping " "from the target."); CU_FAIL("[FAILED] Failed to read LBA mapping " "from the target."); return; } if (task_ret->status != SCSI_STATUS_GOOD) { logging(LOG_VERBOSE, "[FAILED] Failed to read LBA mapping " "from the target. Sense: %s", sd->error_str); CU_FAIL("[FAILED] Failed to read LBA mapping " "from the target."); scsi_free_scsi_task(task_ret); return; } logging(LOG_VERBOSE, "Unmarshall LBA mapping datain buffer"); lbas = scsi_datain_unmarshall(task_ret); if (lbas == NULL) { logging(LOG_VERBOSE, "[FAILED] Failed to unmarshall LBA " "mapping"); CU_FAIL("[FAILED] Failed to read unmarshall LBA mapping"); scsi_free_scsi_task(task_ret); return; } logging(LOG_VERBOSE, "Verify we got at least one status descriptor " "from the target"); if (lbas->num_descriptors < 1) { logging(LOG_VERBOSE, "[FAILED] Wrong number of LBA status " "descriptors. Expected >=1 but got %d descriptors", lbas->num_descriptors); CU_FAIL("[FAILED] Wrong number of LBA status descriptors."); scsi_free_scsi_task(task_ret); return; } logging(LOG_VERBOSE, "Verify that all descriptors are either " "DEALLOCATED or ANCHORED."); for (i = 0; i < (int)lbas->num_descriptors; i++) { logging(LOG_VERBOSE, "Check descriptor %d LBA:%" PRIu64 "-%" PRIu64 " that it is not MAPPED", i, lbas->descriptors[i].lba, lbas->descriptors[i].lba + lbas->descriptors[i].num_blocks); if (lbas->descriptors[i].provisioning == SCSI_PROVISIONING_TYPE_MAPPED) { logging(LOG_VERBOSE, "[FAILED] Descriptor %d is MAPPED." "All descriptors shoudl be either DEALLOCATED " "or ANCHORED after SANITIZE", i); CU_FAIL("[FAILED] LBA status descriptor is MAPPED."); } } logging(LOG_VERBOSE, "Verify that the descriptors cover the whole LUN"); lba = 0; for (i = 0; i < (int)lbas->num_descriptors; i++) { logging(LOG_VERBOSE, "Check descriptor %d LBA:%" PRIu64 "-%" PRIu64 " that it is in order", i, lbas->descriptors[i].lba, lbas->descriptors[i].lba + lbas->descriptors[i].num_blocks); if (lba != lbas->descriptors[i].lba) { logging(LOG_VERBOSE, "[FAILED] LBA status descriptors " "are not in order."); CU_FAIL("[FAILED] LBA status descriptors not in order"); } lba += lbas->descriptors[i].num_blocks; } if (lba != num_blocks) { logging(LOG_VERBOSE, "[FAILED] The LUN is not fully" "DEALLOCATED/ANCHORED"); CU_FAIL("[FAILED] The LUN is not fully" "DEALLOCATED/ANCHORED"); } scsi_free_scsi_task(task_ret); } static void check_lun_is_wiped(unsigned char *buf, uint64_t lba) { unsigned char *rbuf = alloca(256 * block_size); READ16(sd, NULL, lba, 256 * block_size, block_size, 0, 0, 0, 0, 0, rbuf, EXPECT_STATUS_GOOD); if (rc16 == NULL) { return; } if (rc16->lbprz) { logging(LOG_VERBOSE, "LBPRZ==1 All blocks " "should read back as 0"); if (all_zero(rbuf, 256 * block_size) == 0) { logging(LOG_NORMAL, "[FAILED] Blocks did not " "read back as zero"); CU_FAIL("[FAILED] Blocks did not read back " "as zero"); } else { logging(LOG_VERBOSE, "[SUCCESS] Blocks read " "back as zero"); } } else { logging(LOG_VERBOSE, "LBPRZ==0 Blocks should not read back as " "all 'a' any more"); if (!memcmp(buf, rbuf, 256 * block_size)) { logging(LOG_NORMAL, "[FAILED] Blocks were not wiped"); CU_FAIL("[FAILED] Blocks were not wiped"); } else { logging(LOG_VERBOSE, "[SUCCESS] Blocks were wiped"); } } } void test_sanitize_block_erase(void) { struct iscsi_data data; struct scsi_command_descriptor *cd; unsigned char *buf = alloca(256 * block_size); logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test SANITIZE BLOCK ERASE"); CHECK_FOR_SANITIZE; CHECK_FOR_DATALOSS; logging(LOG_VERBOSE, "Check that SANITIZE BLOCK_ERASE is supported " "in REPORT_SUPPORTED_OPCODES"); cd = get_command_descriptor(SCSI_OPCODE_SANITIZE, SCSI_SANITIZE_BLOCK_ERASE); if (cd == NULL) { logging(LOG_VERBOSE, "Opcode is not supported. Verify that " "WABEREQ is zero."); if (inq_bdc && inq_bdc->wabereq) { logging(LOG_NORMAL, "[FAILED] WABEREQ is not 0 but " "SANITIZE BLOCK ERASE opcode is not supported"); CU_FAIL("[FAILED] WABEREQ is not 0 but BLOCK ERASE " "is not supported."); } logging(LOG_NORMAL, "[SKIPPED] SANITIZE BLOCK_ERASE is not " "implemented according to REPORT_SUPPORTED_OPCODES."); CU_PASS("SANITIZE is not implemented."); return; } logging(LOG_VERBOSE, "Verify that we have BlockDeviceCharacteristics " "VPD page."); if (inq_bdc == NULL) { logging(LOG_NORMAL, "[FAILED] SANITIZE BLOCK ERASE opcode is " "supported but BlockDeviceCharacteristics VPD page is " "missing"); CU_FAIL("[FAILED] BlockDeviceCharacteristics VPD " "page is missing"); } logging(LOG_VERBOSE, "Verify that we have READCAPACITY16"); if (!rc16) { logging(LOG_NORMAL, "[FAILED] SANITIZE BLOCK ERASE opcode is " "supported but READCAPACITY16 is missing."); CU_FAIL("[FAILED] READCAPACITY16 is missing"); } logging(LOG_VERBOSE, "Verify that logical block provisioning (LBPME) " "is available."); if (!rc16 || !(rc16->lbpme)) { logging(LOG_NORMAL, "[FAILED] SANITIZE BLOCK ERASE opcode is " "supported but LBPME==0."); CU_FAIL("[FAILED] SANITIZE BLOCK ERASE opcode is " "supported but LBPME==0."); } logging(LOG_VERBOSE, "Check MediumRotationRate whether this is a HDD " "or a SSD device."); if (inq_bdc && inq_bdc->medium_rotation_rate != 0) { logging(LOG_NORMAL, "This is a HDD device"); logging(LOG_NORMAL, "[WARNING] SANITIZE BLOCK ERASE opcode is " "supported but MediumRotationRate is not 0 " "indicating that this is a HDD. Only SSDs should " "implement BLOCK ERASE"); } else { logging(LOG_NORMAL, "This is a HDD device"); } logging(LOG_VERBOSE, "Write 'a' to the first 256 LBAs"); memset(scratch, 'a', 256 * block_size); WRITE16(sd, 0, 256 * block_size, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Write 'a' to the last 256 LBAs"); WRITE16(sd, num_blocks - 256, 256 * block_size, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Test we can perform basic BLOCK ERASE SANITIZE"); SANITIZE(sd, 0, 0, SCSI_SANITIZE_BLOCK_ERASE, 0, NULL, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Check that the first 256 LBAs are wiped."); check_lun_is_wiped(buf, 0); logging(LOG_VERBOSE, "Check that the last 256 LBAs are wiped."); check_lun_is_wiped(buf, num_blocks - 256); data.size = 8; data.data = alloca(data.size); memset(data.data, 0, data.size); logging(LOG_VERBOSE, "BLOCK_ERASE parameter list length must be 0"); logging(LOG_VERBOSE, "Test that non-zero param length is an error for " "BLOCK ERASE"); SANITIZE(sd, 0, 0, SCSI_SANITIZE_BLOCK_ERASE, 8, &data, EXPECT_INVALID_FIELD_IN_CDB); if (inq_bdc) { logging(LOG_VERBOSE, "Check WABEREQ setting and that READ " "after SANITIZE works correctly."); check_wabereq(); } logging(LOG_VERBOSE, "Verify that all blocks are unmapped after " "SANITIZE BLOCK_ERASE"); check_unmap(); } libiscsi-1.17.0/test-tool/test_sanitize_block_erase_reserved.c000066400000000000000000000046111271371262700246260ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 by Ronnie Sahlberg 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, see . */ #include #include #include #include "iscsi.h" #include "iscsi-private.h" #include "scsi-lowlevel.h" #include "iscsi-test-cu.h" static int change_num; static int my_iscsi_queue_pdu(struct iscsi_context *iscsi _U_, struct iscsi_pdu *pdu) { switch (change_num) { case 1: /* Set reserved bit 0x40 in byte 1 of the CDB */ pdu->outdata.data[33] |= 0x40; break; case 2: case 3: case 4: case 5: case 6: /* Set reserved byte in the CDB */ pdu->outdata.data[32 + change_num] = change_num; break; } change_num = 0; return 0; } void test_sanitize_block_erase_reserved(void) { int i; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test SANITIZE BLOCK_ERASE Reserved bits/bytes"); CHECK_FOR_SANITIZE; CHECK_FOR_DATALOSS; local_iscsi_queue_pdu = my_iscsi_queue_pdu; logging(LOG_VERBOSE, "Send SANITIZE command with the reserved " "bit in byte 1 set to 1"); change_num = 1; SANITIZE(sd, 0, 0, SCSI_SANITIZE_BLOCK_ERASE, 0, NULL, EXPECT_INVALID_FIELD_IN_CDB); for (i = 2; i < 7; i++) { logging(LOG_VERBOSE, "Send SANITIZE command with the reserved " "byte %d set to non-zero", i); change_num = i; SANITIZE(sd, 0, 0, SCSI_SANITIZE_BLOCK_ERASE, 0, NULL, EXPECT_INVALID_FIELD_IN_CDB); } } libiscsi-1.17.0/test-tool/test_sanitize_crypto_erase.c000066400000000000000000000202001271371262700231450ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg 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, see . */ #include #include #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-test-cu.h" static void check_wacereq(void) { struct scsi_task *task_ret = NULL; logging(LOG_VERBOSE, "Read one block from LBA 0"); READ10(sd, &task_ret, 0, block_size, block_size, 0, 0, 0, 0, 0, NULL, EXPECT_STATUS_GOOD); CU_ASSERT_PTR_NOT_NULL_FATAL(task_ret); if (task_ret == NULL) { return; } CU_ASSERT_NOT_EQUAL(task_ret->status, SCSI_STATUS_CANCELLED); switch (inq_bdc->wabereq) { case 0: logging(LOG_NORMAL, "[FAILED] SANITIZE BLOCK ERASE " "opcode is supported but WACEREQ is 0"); CU_FAIL("[FAILED] SANITIZE BLOCK ERASE " "opcode is supported but WACEREQ is 0"); break; case 1: logging(LOG_VERBOSE, "WACEREQ==1. Reads from the " "device should be successful."); if (task_ret->status == SCSI_STATUS_GOOD) { logging(LOG_VERBOSE, "[SUCCESS] Read was " "successful after SANITIZE"); break; } logging(LOG_NORMAL, "[FAILED] Read after " "SANITIZE failed but WACEREQ is 1"); CU_FAIL("[FAILED] Read after SANITIZE failed " "but WACEREQ is 1"); break; case 2: logging(LOG_VERBOSE, "WACEREQ==2. Reads from the " "device should fail."); if (task_ret->status == SCSI_STATUS_CHECK_CONDITION && task_ret->sense.key == SCSI_SENSE_MEDIUM_ERROR && task_ret->sense.ascq != SCSI_SENSE_ASCQ_WRITE_AFTER_SANITIZE_REQUIRED) { logging(LOG_VERBOSE, "[SUCCESS] Read failed " "with CHECK_CONDITION/MEDIUM_ERROR/" "!WRITE_AFTER_SANITIZE_REQUIRED"); break; } logging(LOG_VERBOSE, "[FAILED] Read should have failed " "with CHECK_CONDITION/MEDIUM_ERROR/" "!WRITE_AFTER_SANITIZE_REQUIRED"); CU_FAIL("[FAILED] Read should have failed " "with CHECK_CONDITION/MEDIUM_ERROR/" "!WRITE_AFTER_SANITIZE_REQUIRED"); break; case 3: logging(LOG_VERBOSE, "WACEREQ==3. Reads from the " "device should fail."); if (task_ret->status == SCSI_STATUS_CHECK_CONDITION && task_ret->sense.key == SCSI_SENSE_MEDIUM_ERROR && task_ret->sense.ascq == SCSI_SENSE_ASCQ_WRITE_AFTER_SANITIZE_REQUIRED) { logging(LOG_VERBOSE, "[SUCCESS] Read failed " "with CHECK_CONDITION/MEDIUM_ERROR/" "WRITE_AFTER_SANITIZE_REQUIRED"); break; } logging(LOG_VERBOSE, "[FAILED] Read should have failed " "with CHECK_CONDITION/MEDIUM_ERROR/" "WRITE_AFTER_SANITIZE_REQUIRED"); CU_FAIL("[FAILED] Read should have failed " "with CHECK_CONDITION/MEDIUM_ERROR/" "WRITE_AFTER_SANITIZE_REQUIRED"); break; } scsi_free_scsi_task(task_ret); } static void check_lun_is_wiped(unsigned char *buf, uint64_t lba) { unsigned char *rbuf = alloca(256 * block_size); READ16(sd, NULL, lba, 256 * block_size, block_size, 0, 0, 0, 0, 0, rbuf, EXPECT_STATUS_GOOD); if (!memcmp(buf, rbuf, 256 * block_size)) { logging(LOG_NORMAL, "[FAILED] Blocks were not wiped"); CU_FAIL("[FAILED] Blocks were not wiped"); } else { logging(LOG_VERBOSE, "[SUCCESS] Blocks were wiped"); } } void test_sanitize_crypto_erase(void) { struct iscsi_data data; struct scsi_command_descriptor *cd; unsigned char *buf = alloca(256 * block_size); logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test SANITIZE CRYPTO ERASE"); CHECK_FOR_SANITIZE; CHECK_FOR_DATALOSS; logging(LOG_VERBOSE, "Check that SANITIZE CRYPTO_ERASE is supported " "in REPORT_SUPPORTED_OPCODES"); cd = get_command_descriptor(SCSI_OPCODE_SANITIZE, SCSI_SANITIZE_CRYPTO_ERASE); if (cd == NULL) { logging(LOG_VERBOSE, "Opcode is not supported. Verify that " "WACEREQ is zero."); if (inq_bdc && inq_bdc->wacereq) { logging(LOG_NORMAL, "[FAILED] WACEREQ is not 0 but " "SANITIZE CRYPTO ERASE opcode is not " "supported"); CU_FAIL("[FAILED] WACEREQ is not 0 but CRYPTO ERASE " "is not supported."); } logging(LOG_NORMAL, "[SKIPPED] SANITIZE CRYPTO_ERASE is not " "implemented according to REPORT_SUPPORTED_OPCODES."); CU_PASS("SANITIZE is not implemented."); return; } logging(LOG_VERBOSE, "Verify that we have BlockDeviceCharacteristics " "VPD page."); if (inq_bdc == NULL) { logging(LOG_NORMAL, "[FAILED] SANITIZE CRYPTO ERASE opcode is " "supported but BlockDeviceCharacteristics VPD page is " "missing"); CU_FAIL("[FAILED] BlockDeviceCharacteristics VPD " "page is missing"); } logging(LOG_VERBOSE, "Write 'a' to the first 256 LBAs"); memset(scratch, 'a', 256 * block_size); WRITE16(sd, 0, 256 * block_size, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Write 'a' to the last 256 LBAs"); WRITE16(sd, num_blocks - 256, 256 * block_size, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Test we can perform basic CRYPTO ERASE SANITIZE"); SANITIZE(sd, 0, 0, SCSI_SANITIZE_CRYPTO_ERASE, 0, NULL, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Check that the first 256 LBAs are wiped."); check_lun_is_wiped(buf, 0); logging(LOG_VERBOSE, "Check that the last 256 LBAs are wiped."); check_lun_is_wiped(buf, num_blocks - 256); data.size = 8; data.data = alloca(data.size); memset(data.data, 0, data.size); logging(LOG_VERBOSE, "CRYPTO_ERASE parameter list length must be 0"); logging(LOG_VERBOSE, "Test that non-zero param length is an error for " "CRYPTO ERASE"); SANITIZE(sd, 0, 0, SCSI_SANITIZE_CRYPTO_ERASE, 8, &data, EXPECT_INVALID_FIELD_IN_CDB); if (inq_bdc) { logging(LOG_VERBOSE, "Check WACEREQ setting and that READ " "after SANITIZE works correctly."); check_wacereq(); } } libiscsi-1.17.0/test-tool/test_sanitize_crypto_erase_reserved.c000066400000000000000000000046151271371262700250600ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 by Ronnie Sahlberg 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, see . */ #include #include #include #include "iscsi.h" #include "iscsi-private.h" #include "scsi-lowlevel.h" #include "iscsi-test-cu.h" static int change_num; static int my_iscsi_queue_pdu(struct iscsi_context *iscsi _U_, struct iscsi_pdu *pdu) { switch (change_num) { case 1: /* Set reserved bit 0x40 in byte 1 of the CDB */ pdu->outdata.data[33] |= 0x40; break; case 2: case 3: case 4: case 5: case 6: /* Set reserved byte in the CDB */ pdu->outdata.data[32 + change_num] = change_num; break; } change_num = 0; return 0; } void test_sanitize_crypto_erase_reserved(void) { int i; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test SANITIZE CRYPTO_ERASE Reserved bits/bytes"); CHECK_FOR_SANITIZE; CHECK_FOR_DATALOSS; local_iscsi_queue_pdu = my_iscsi_queue_pdu; logging(LOG_VERBOSE, "Send SANITIZE command with the reserved " "bit in byte 1 set to 1"); change_num = 1; SANITIZE(sd, 0, 0, SCSI_SANITIZE_CRYPTO_ERASE, 0, NULL, EXPECT_INVALID_FIELD_IN_CDB); for (i = 2; i < 7; i++) { logging(LOG_VERBOSE, "Send SANITIZE command with the reserved " "byte %d set to non-zero", i); change_num = i; SANITIZE(sd, 0, 0, SCSI_SANITIZE_CRYPTO_ERASE, 0, NULL, EXPECT_INVALID_FIELD_IN_CDB); } } libiscsi-1.17.0/test-tool/test_sanitize_exit_failure_mode.c000066400000000000000000000043521271371262700241440ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg 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, see . */ #include #include #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-test-cu.h" void test_sanitize_exit_failure_mode(void) { struct iscsi_data data; struct scsi_command_descriptor *cd; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test SANITIZE EXIT FAILURE MODE"); CHECK_FOR_SANITIZE; CHECK_FOR_DATALOSS; logging(LOG_VERBOSE, "Check that SANITIZE EXIT FAILURE MODE is " "supported in REPORT_SUPPORTED_OPCODES"); cd = get_command_descriptor(SCSI_OPCODE_SANITIZE, SCSI_SANITIZE_EXIT_FAILURE_MODE); if (cd == NULL) { logging(LOG_NORMAL, "[SKIPPED] SANITIZE EXIT FAILURE MODE is " "not implemented according to " "REPORT_SUPPORTED_OPCODES."); CU_PASS("SANITIZE is not implemented."); return; } data.size = 8; data.data = alloca(data.size); memset(data.data, 0, data.size); logging(LOG_VERBOSE, "EXIT_FAILURE_MODE parameter list length must " "be 0"); logging(LOG_VERBOSE, "Test that non-zero param length is an error for " "EXIT_FAILURE_MODE"); SANITIZE(sd, 0, 0, SCSI_SANITIZE_EXIT_FAILURE_MODE, 8, &data, EXPECT_INVALID_FIELD_IN_CDB); } libiscsi-1.17.0/test-tool/test_sanitize_invalid_serviceaction.c000066400000000000000000000033531271371262700250240ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg 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, see . */ #include #include #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-test-cu.h" void test_sanitize_invalid_serviceaction(void) { int i; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test SANITIZE for invalid serviceactions"); CHECK_FOR_SANITIZE; logging(LOG_VERBOSE, "Test all invalid service actions and make sure " "they fail with an error"); for (i = 0; i <= 0x1f; i++) { switch (i) { case 1: case 2: case 3: case 0x1f: continue; } logging(LOG_VERBOSE, "Verify that ServiceAction:0x%02d is " "an error.", i); SANITIZE(sd, 0, 0, i, 0, NULL, EXPECT_INVALID_FIELD_IN_CDB); } } libiscsi-1.17.0/test-tool/test_sanitize_overwrite.c000066400000000000000000000156621271371262700225140ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg 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, see . */ #include #include #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-test-cu.h" static void check_lun_is_wiped(uint64_t lba, unsigned char c) { unsigned char *rbuf = alloca(256 * block_size); unsigned char *zbuf = alloca(256 * block_size); READ16(sd, NULL, lba, 256 * block_size, block_size, 0, 0, 0, 0, 0, rbuf, EXPECT_STATUS_GOOD); memset(zbuf, c, 256 * block_size); if (memcmp(zbuf, rbuf, 256 * block_size)) { logging(LOG_NORMAL, "[FAILED] Blocks did not read back as %#x", c); CU_FAIL("[FAILED] Blocks did not read back as expected"); } else { logging(LOG_VERBOSE, "[SUCCESS] Blocks read back as %#x", c); } } void test_sanitize_overwrite(void) { int i; struct iscsi_data data; struct scsi_command_descriptor *cd; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test SANITIZE OVERWRITE"); CHECK_FOR_SANITIZE; CHECK_FOR_DATALOSS; logging(LOG_VERBOSE, "Check that SANITIZE OVERWRITE is supported " "in REPORT_SUPPORTED_OPCODES"); cd = get_command_descriptor(SCSI_OPCODE_SANITIZE, SCSI_SANITIZE_OVERWRITE); if (cd == NULL) { logging(LOG_NORMAL, "[SKIPPED] SANITIZE OVERWRITE is not " "implemented according to REPORT_SUPPORTED_OPCODES."); CU_PASS("SANITIZE is not implemented."); return; } logging(LOG_VERBOSE, "Verify that we have BlockDeviceCharacteristics " "VPD page."); if (inq_bdc == NULL) { logging(LOG_NORMAL, "[FAILED] SANITIZE OVERWRITE opcode is " "supported but BlockDeviceCharacteristics VPD page is " "missing"); CU_FAIL("[FAILED] BlockDeviceCharacteristics VPD " "page is missing"); } logging(LOG_VERBOSE, "Check MediumRotationRate whether this is a HDD " "or a SSD device."); if (inq_bdc && inq_bdc->medium_rotation_rate == 0) { logging(LOG_NORMAL, "This is a HDD device"); } else { logging(LOG_NORMAL, "This is a SSD device"); } logging(LOG_VERBOSE, "Write 'a' to the first 256 LBAs"); memset(scratch, 'a', 256 * block_size); WRITE16(sd, 0, 256 * block_size, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Write 'a' to the last 256 LBAs"); WRITE16(sd, num_blocks - 256, 256 * block_size, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Test SANITIZE OVERWRITE with initialization pattern of one full block"); data.size = block_size + 4; data.data = alloca(data.size); memset(&data.data[4], 0xaa, block_size); data.data[0] = 0x01; data.data[1] = 0x00; data.data[2] = block_size >> 8; data.data[3] = block_size & 0xff; SANITIZE(sd, 0, 0, SCSI_SANITIZE_OVERWRITE, data.size, &data, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Check that the first 256 LBAs are wiped."); check_lun_is_wiped(0, 0xaa); logging(LOG_VERBOSE, "Check that the last 256 LBAs are wiped."); check_lun_is_wiped(num_blocks - 256, 0xaa); logging(LOG_VERBOSE, "Test SANITIZE OVERWRITE with initialization pattern of one half block"); data.size = block_size / 2 + 4; data.data[2] = (block_size / 2) >> 8; data.data[3] = (block_size / 2 ) & 0xff; SANITIZE(sd, 0, 0, SCSI_SANITIZE_OVERWRITE, data.size, &data, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Test SANITIZE OVERWRITE with initialization pattern of 4 bytes"); data.size = 4 + 4; data.data[2] = 0; data.data[3] = 4; SANITIZE(sd, 0, 0, SCSI_SANITIZE_OVERWRITE, data.size, &data, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "OVERWRITE parameter list length must " "be > 4 and < blocksize+5"); for (i = 0; i < 5; i++) { logging(LOG_VERBOSE, "Test OVERWRITE with ParamLen:%d is an " "error.", i); SANITIZE(sd, 0, 0, SCSI_SANITIZE_OVERWRITE, i, &data, EXPECT_INVALID_FIELD_IN_CDB); } logging(LOG_VERBOSE, "Test OVERWRITE with ParamLen:%zd (blocksize+5) " "is an error.", block_size + 5); data.size = block_size + 8; data.data = alloca(block_size + 8); /* so we can send IP > blocksize */ memset(data.data, 0, data.size); SANITIZE(sd, 0, 0, SCSI_SANITIZE_OVERWRITE, block_size + 5, &data, EXPECT_INVALID_FIELD_IN_CDB); logging(LOG_VERBOSE, "Test OVERWRITE COUNT == 0 is an error"); data.size = block_size + 4; data.data[0] = 0x00; data.data[1] = 0x00; data.data[2] = block_size >> 8; data.data[3] = block_size & 0xff; SANITIZE(sd, 0, 0, SCSI_SANITIZE_OVERWRITE, data.size, &data, EXPECT_INVALID_FIELD_IN_CDB); logging(LOG_VERBOSE, "Test INITIALIZATION PATTERN LENGTH == 0 is an " "error"); data.size = block_size + 4; data.data[0] = 0x00; data.data[1] = 0x00; data.data[2] = 0x00; data.data[3] = 0x00; SANITIZE(sd, 0, 0, SCSI_SANITIZE_OVERWRITE, data.size, &data, EXPECT_INVALID_FIELD_IN_CDB); logging(LOG_VERBOSE, "Test INITIALIZATION PATTERN LENGTH == %zd > %zd " "(blocksize) is an error", block_size + 4, block_size); data.size = block_size + 4; data.data[0] = 0x00; data.data[1] = 0x00; data.data[2] = (block_size + 4) >> 8; data.data[3] = (block_size + 4) & 0xff; SANITIZE(sd, 0, 0, SCSI_SANITIZE_OVERWRITE, data.size, &data, EXPECT_INVALID_FIELD_IN_CDB); } libiscsi-1.17.0/test-tool/test_sanitize_overwrite_reserved.c000066400000000000000000000053201271371262700244010ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 by Ronnie Sahlberg 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, see . */ #include #include #include #include #include "iscsi.h" #include "iscsi-private.h" #include "scsi-lowlevel.h" #include "iscsi-test-cu.h" static int change_num; static int my_iscsi_queue_pdu(struct iscsi_context *iscsi _U_, struct iscsi_pdu *pdu) { switch (change_num) { case 1: /* Set reserved bit 0x40 in byte 1 of the CDB */ pdu->outdata.data[33] |= 0x40; break; case 2: case 3: case 4: case 5: case 6: /* Set reserved byte in the CDB */ pdu->outdata.data[32 + change_num] = change_num; break; } change_num = 0; return 0; } void test_sanitize_overwrite_reserved(void) { int i; struct iscsi_data data; data.size = block_size + 4; data.data = alloca(data.size); memset(&data.data[4], 0xaa, block_size); data.data[0] = 0x01; data.data[1] = 0x00; data.data[2] = block_size >> 8; data.data[3] = block_size & 0xff; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test SANITIZE OVERWRITE Reserved bits/bytes"); CHECK_FOR_SANITIZE; CHECK_FOR_DATALOSS; local_iscsi_queue_pdu = my_iscsi_queue_pdu; logging(LOG_VERBOSE, "Send SANITIZE command with the reserved " "bit in byte 1 set to 1"); change_num = 1; SANITIZE(sd, 0, 0, SCSI_SANITIZE_OVERWRITE, data.size, &data, EXPECT_INVALID_FIELD_IN_CDB); for (i = 2; i < 7; i++) { logging(LOG_VERBOSE, "Send SANITIZE command with the reserved " "byte %d set to non-zero", i); change_num = i; SANITIZE(sd, 0, 0, SCSI_SANITIZE_OVERWRITE, data.size, &data, EXPECT_INVALID_FIELD_IN_CDB); } } libiscsi-1.17.0/test-tool/test_sanitize_readonly.c000066400000000000000000000115061271371262700222740ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg 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, see . */ #include #include #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-test-cu.h" #include "iscsi-multipath.h" void test_sanitize_readonly(void) { int ret; struct iscsi_data data; struct scsi_command_descriptor *cd; struct scsi_device *sd2; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test SANITIZE with READONLY devices"); CHECK_FOR_SANITIZE; CHECK_FOR_DATALOSS; if (sd->iscsi_ctx == NULL) { const char *err = "[SKIPPED] This SANITIZE test is only " "supported for iSCSI backends"; logging(LOG_NORMAL, "%s", err); CU_PASS(err); return; } logging(LOG_VERBOSE, "Create a second connection to the target"); ret = mpath_sd2_get_or_clone(sd, &sd2); CU_ASSERT_EQUAL(ret, 0); if (ret < 0) return; logging(LOG_VERBOSE, "Set Software Write Protect on the second connection"); ret = set_swp(sd2); CU_ASSERT_EQUAL(ret, 0); if (ret != 0) { return; } logging(LOG_VERBOSE, "Use TESTUNITREADY to clear unit attention on " "first connection"); while (testunitready_clear_ua(sd)) { sleep(1); } logging(LOG_VERBOSE, "Check if SANITIZE OVERWRITE is supported " "in REPORT_SUPPORTED_OPCODES"); cd = get_command_descriptor(SCSI_OPCODE_SANITIZE, SCSI_SANITIZE_OVERWRITE); if (cd == NULL) { logging(LOG_NORMAL, "[SKIPPED] SANITIZE OVERWRITE is not " "implemented according to REPORT_SUPPORTED_OPCODES."); } else { logging(LOG_VERBOSE, "Test SANITIZE OVERWRITE with " "initialization pattern of one full block"); data.size = block_size + 4; data.data = alloca(data.size); memset(&data.data[4], 0xaa, block_size); data.data[0] = 0x01; data.data[1] = 0x00; data.data[2] = block_size >> 8; data.data[3] = block_size & 0xff; SANITIZE(sd, 0, 0, SCSI_SANITIZE_OVERWRITE, data.size, &data, EXPECT_WRITE_PROTECTED); } logging(LOG_VERBOSE, "Check if SANITIZE BLOCK_ERASE is supported " "in REPORT_SUPPORTED_OPCODES"); cd = get_command_descriptor(SCSI_OPCODE_SANITIZE, SCSI_SANITIZE_BLOCK_ERASE); if (cd == NULL) { logging(LOG_NORMAL, "[SKIPPED] SANITIZE BLOCK_ERASE is not " "implemented according to REPORT_SUPPORTED_OPCODES."); } else { logging(LOG_VERBOSE, "Test SANITIZE BLOCK_ERASE"); SANITIZE(sd, 0, 0, SCSI_SANITIZE_BLOCK_ERASE, 0, NULL, EXPECT_WRITE_PROTECTED); } logging(LOG_VERBOSE, "Check if SANITIZE CRYPTO_ERASE is supported " "in REPORT_SUPPORTED_OPCODES"); cd = get_command_descriptor(SCSI_OPCODE_SANITIZE, SCSI_SANITIZE_CRYPTO_ERASE); if (cd == NULL) { logging(LOG_NORMAL, "[SKIPPED] SANITIZE CRYPTO_ERASE is not " "implemented according to REPORT_SUPPORTED_OPCODES."); } else { logging(LOG_VERBOSE, "Test SANITIZE CRYPTO_ERASE"); SANITIZE(sd, 0, 0, SCSI_SANITIZE_CRYPTO_ERASE, 0, NULL, EXPECT_WRITE_PROTECTED); } logging(LOG_VERBOSE, "Clear Software Write Protect on the second connection"); ret = clear_swp(sd2); CU_ASSERT_EQUAL(ret, 0); logging(LOG_VERBOSE, "Use TESTUNITREADY to clear unit attention on " "first connection"); while (testunitready_clear_ua(sd)) { sleep(1); } mpath_sd2_put(sd2); } libiscsi-1.17.0/test-tool/test_sanitize_reservations.c000066400000000000000000000103571271371262700232060ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg 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, see . */ #include #include #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-test-cu.h" #include "iscsi-multipath.h" void test_sanitize_reservations(void) { int ret; struct iscsi_data data; struct scsi_command_descriptor *cd; struct scsi_device *sd2; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test SANITIZE with RESERVATIONS"); CHECK_FOR_SANITIZE; CHECK_FOR_DATALOSS; if (sd->iscsi_ctx == NULL) { const char *err = "[SKIPPED] This SANITIZE test is only " "supported for iSCSI backends"; logging(LOG_NORMAL, "%s", err); CU_PASS(err); return; } logging(LOG_VERBOSE, "Create a second connection to the target"); ret = mpath_sd2_get_or_clone(sd, &sd2); CU_ASSERT_EQUAL(ret, 0); if (ret < 0) return; logging(LOG_VERBOSE, "Take out a RESERVE6 from the second " "initiator"); RESERVE6(sd2); logging(LOG_VERBOSE, "Check if SANITIZE OVERWRITE is supported " "in REPORT_SUPPORTED_OPCODES"); cd = get_command_descriptor(SCSI_OPCODE_SANITIZE, SCSI_SANITIZE_OVERWRITE); if (cd == NULL) { logging(LOG_NORMAL, "[SKIPPED] SANITIZE OVERWRITE is not " "implemented according to REPORT_SUPPORTED_OPCODES."); } else { logging(LOG_VERBOSE, "Test SANITIZE OVERWRITE with " "initialization pattern of one full block"); data.size = block_size + 4; data.data = alloca(data.size); memset(&data.data[4], 0xaa, block_size); data.data[0] = 0x01; data.data[1] = 0x00; data.data[2] = block_size >> 8; data.data[3] = block_size & 0xff; SANITIZE(sd, 0, 0, SCSI_SANITIZE_OVERWRITE, data.size, &data, EXPECT_RESERVATION_CONFLICT); } logging(LOG_VERBOSE, "Check if SANITIZE BLOCK_ERASE is supported " "in REPORT_SUPPORTED_OPCODES"); cd = get_command_descriptor(SCSI_OPCODE_SANITIZE, SCSI_SANITIZE_BLOCK_ERASE); if (cd == NULL) { logging(LOG_NORMAL, "[SKIPPED] SANITIZE BLOCK_ERASE is not " "implemented according to REPORT_SUPPORTED_OPCODES."); } else { logging(LOG_VERBOSE, "Test SANITIZE BLOCK_ERASE"); SANITIZE(sd, 0, 0, SCSI_SANITIZE_BLOCK_ERASE, 0, NULL, EXPECT_RESERVATION_CONFLICT); } logging(LOG_VERBOSE, "Check if SANITIZE CRYPTO_ERASE is supported " "in REPORT_SUPPORTED_OPCODES"); cd = get_command_descriptor(SCSI_OPCODE_SANITIZE, SCSI_SANITIZE_CRYPTO_ERASE); if (cd == NULL) { logging(LOG_NORMAL, "[SKIPPED] SANITIZE CRYPTO_ERASE is not " "implemented according to REPORT_SUPPORTED_OPCODES."); } else { logging(LOG_VERBOSE, "Test SANITIZE CRYPTO_ERASE"); SANITIZE(sd, 0, 0, SCSI_SANITIZE_CRYPTO_ERASE, 0, NULL, EXPECT_RESERVATION_CONFLICT); } mpath_sd2_put(sd2); } libiscsi-1.17.0/test-tool/test_sanitize_reset.c000066400000000000000000000143741271371262700216070ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg 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, see . */ #include #include #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-test-cu.h" static void sanitize_cb(struct iscsi_context *iscsi _U_, int status _U_, void *command_data _U_, void *private_data _U_) { } void test_sanitize_reset(void) { int ret; struct scsi_command_descriptor *cd; struct scsi_task *sanitize_task; struct scsi_task *rl_task; struct iscsi_data data; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test SANITIZE with Task/Lun/Target/Session reset"); CHECK_FOR_SANITIZE; CHECK_FOR_DATALOSS; if (sd->iscsi_ctx == NULL) { const char *err = "[SKIPPED] This SANITIZE test is only " "supported for iSCSI backends"; logging(LOG_NORMAL, "%s", err); CU_PASS(err); return; } logging(LOG_VERBOSE, "Check that SANITIZE OVERWRITE will continue " "even after Task/Lun/Target/* reset."); cd = get_command_descriptor(SCSI_OPCODE_SANITIZE, SCSI_SANITIZE_OVERWRITE); if (cd == NULL) { logging(LOG_NORMAL, "[SKIPPED] SANITIZE OVERWRITE is not " "implemented according to REPORT_SUPPORTED_OPCODES."); CU_PASS("SANITIZE is not implemented."); return; } logging(LOG_VERBOSE, "Send an asyncronous SANITIZE to the target."); data.size = block_size + 4; data.data = alloca(data.size); memset(&data.data[4], 0, block_size); data.data[0] = 0x01; data.data[1] = 0x00; data.data[2] = block_size >> 8; data.data[3] = block_size & 0xff; sanitize_task = iscsi_sanitize_task(sd->iscsi_ctx, sd->iscsi_lun, 0, 0, SCSI_SANITIZE_OVERWRITE, data.size, &data, sanitize_cb, NULL); CU_ASSERT_NOT_EQUAL(sanitize_task, NULL); /* just send something so that we know the sanitize command is sent * to the target */ rl_task = iscsi_reportluns_sync(sd->iscsi_ctx, 0, 64); if (rl_task) { scsi_free_scsi_task(rl_task); } logging(LOG_VERBOSE, "Sleep for three seconds incase the target is " "slow to start the SANITIZE"); sleep(3); logging(LOG_VERBOSE, "Verify that the SANITIZE has started and that " "TESTUNITREADY fails with SANITIZE_IN_PROGRESS"); TESTUNITREADY(sd, EXPECT_SANITIZE); logging(LOG_VERBOSE, "Verify that STARTSTOPUNIT fails with " "SANITIZE_IN_PROGRESS"); STARTSTOPUNIT(sd, 1, 0, 1, 0, 1, 0, EXPECT_SANITIZE); logging(LOG_VERBOSE, "Verify that READ16 fails with " "SANITIZE_IN_PROGRESS"); READ16(sd, NULL, 0, block_size, block_size, 0, 0, 0, 0, 0, NULL, EXPECT_SANITIZE); logging(LOG_VERBOSE, "Verify that INQUIRY is still allowed while " "SANITIZE is in progress"); ret = inquiry(sd, NULL, 0, 0, 255, EXPECT_STATUS_GOOD); CU_ASSERT_EQUAL(ret, 0); logging(LOG_VERBOSE, "Send an ABORT TASK"); ret = iscsi_task_mgmt_abort_task_sync(sd->iscsi_ctx, sanitize_task); if (ret != 0) { logging(LOG_NORMAL, "ABORT TASK failed. %s", iscsi_get_error(sd->iscsi_ctx)); } logging(LOG_VERBOSE, "Send an ABORT TASK SET"); ret = iscsi_task_mgmt_abort_task_set_sync(sd->iscsi_ctx, sd->iscsi_lun); if (ret != 0) { logging(LOG_NORMAL, "ABORT TASK SET failed. %s", iscsi_get_error(sd->iscsi_ctx)); } logging(LOG_VERBOSE, "Send a LUN Reset"); ret = iscsi_task_mgmt_lun_reset_sync(sd->iscsi_ctx, sd->iscsi_lun); if (ret != 0) { logging(LOG_NORMAL, "LUN reset failed. %s", iscsi_get_error(sd->iscsi_ctx)); } logging(LOG_VERBOSE, "Send a Warm Reset"); ret = iscsi_task_mgmt_target_warm_reset_sync(sd->iscsi_ctx); if (ret != 0) { logging(LOG_NORMAL, "Warm reset failed. %s", iscsi_get_error(sd->iscsi_ctx)); } logging(LOG_VERBOSE, "Send a Cold Reset"); ret = iscsi_task_mgmt_target_cold_reset_sync(sd->iscsi_ctx); if (ret != 0) { logging(LOG_NORMAL, "Cold reset failed. %s", iscsi_get_error(sd->iscsi_ctx)); } logging(LOG_VERBOSE, "Disconnect from the target."); iscsi_destroy_context(sd->iscsi_ctx); logging(LOG_VERBOSE, "Sleep for one seconds incase the target is " "slow to reset"); sleep(1); logging(LOG_VERBOSE, "Reconnect to target"); sd->iscsi_ctx = iscsi_context_login(initiatorname1, sd->iscsi_url, &sd->iscsi_lun); if (sd->iscsi_ctx == NULL) { logging(LOG_VERBOSE, "Failed to login to target"); return; } logging(LOG_VERBOSE, "Verify that the SANITIZE is still going."); TESTUNITREADY(sd, EXPECT_SANITIZE); logging(LOG_VERBOSE, "Wait until the SANITIZE operation has finished"); while (testunitready_clear_ua(sd)) { sleep(60); } } libiscsi-1.17.0/test-tool/test_startstopunit_noloej.c000066400000000000000000000101441271371262700230570ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg 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, see . */ #include #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-test-cu.h" void test_startstopunit_noloej(void) { logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test STARTSTOPUNIT LOEJ==0"); if (!inq->rmb) { logging(LOG_VERBOSE, "[SKIPPED] LUN is not removable. " "Skipping test."); return; } logging(LOG_VERBOSE, "Test that media is not ejected when LOEJ==0 IMMED==0 NO_FLUSH==0 START==0"); STARTSTOPUNIT(sd, 0, 0, 0, 0, 0, 0, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Test TESTUNITREADY that medium is not ejected."); TESTUNITREADY(sd, EXPECT_SANITIZE); logging(LOG_VERBOSE, "Test that media is not ejected when LOEJ==0 IMMED==0 NO_FLUSH==0 START==1"); STARTSTOPUNIT(sd, 0, 0, 0, 0, 0, 1, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Test TESTUNITREADY that medium is not ejected."); TESTUNITREADY(sd, EXPECT_SANITIZE); logging(LOG_VERBOSE, "Test that media is not ejected when LOEJ==0 IMMED==1 NO_FLUSH==0 START==0"); STARTSTOPUNIT(sd, 1, 0, 0, 0, 0, 0, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Test TESTUNITREADY that medium is not ejected."); TESTUNITREADY(sd, EXPECT_SANITIZE); logging(LOG_VERBOSE, "Test that media is not ejected when LOEJ==0 IMMED==1 NO_FLUSH==0 START==1"); STARTSTOPUNIT(sd, 1, 0, 0, 0, 0, 1, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Test TESTUNITREADY that medium is not ejected."); TESTUNITREADY(sd, EXPECT_SANITIZE); logging(LOG_VERBOSE, "Test that media is not ejected when LOEJ==0 IMMED==0 NO_FLUSH==1 START==0"); STARTSTOPUNIT(sd, 0, 0, 0, 1, 0, 0, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Test TESTUNITREADY that medium is not ejected."); TESTUNITREADY(sd, EXPECT_SANITIZE); logging(LOG_VERBOSE, "Test that media is not ejected when LOEJ==0 IMMED==0 NO_FLUSH==1 START==1"); STARTSTOPUNIT(sd, 0, 0, 0, 1, 0, 1, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Test TESTUNITREADY that medium is not ejected."); TESTUNITREADY(sd, EXPECT_SANITIZE); logging(LOG_VERBOSE, "Test that media is not ejected when LOEJ==0 IMMED==1 NO_FLUSH==1 START==0"); STARTSTOPUNIT(sd, 1, 0, 0, 1, 0, 0, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Test TESTUNITREADY that medium is not ejected."); TESTUNITREADY(sd, EXPECT_SANITIZE); logging(LOG_VERBOSE, "Test that media is not ejected when LOEJ==0 IMMED==1 NO_FLUSH==1 START==1"); STARTSTOPUNIT(sd, 1, 0, 0, 1, 0, 1, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Test TESTUNITREADY that medium is not ejected."); TESTUNITREADY(sd, EXPECT_SANITIZE); logging(LOG_VERBOSE, "In case the target did eject the medium, load it again."); STARTSTOPUNIT(sd, 1, 0, 0, 0, 1, 1, EXPECT_STATUS_GOOD); } libiscsi-1.17.0/test-tool/test_startstopunit_pwrcnd.c000066400000000000000000000035261271371262700230740ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg 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, see . */ #include #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-test-cu.h" void test_startstopunit_pwrcnd(void) { int i; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test STARTSTOPUNIT PowerCondition"); if (!inq->rmb) { logging(LOG_VERBOSE, "[SKIPPED] LUN is not removable. " "Skipping test."); return; } logging(LOG_VERBOSE, "Test that media is not ejected when PC!=0"); for (i = 1; i < 16; i++) { STARTSTOPUNIT(sd, 1, 0, i, 0, 1, 0, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Test TESTUNITREADY that medium is not ejected."); TESTUNITREADY(sd, EXPECT_SANITIZE); } logging(LOG_VERBOSE, "In case the target did eject the medium, load it again."); STARTSTOPUNIT(sd, 1, 0, 0, 0, 1, 1, EXPECT_STATUS_GOOD); } libiscsi-1.17.0/test-tool/test_startstopunit_simple.c000066400000000000000000000053161271371262700230670ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg 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, see . */ #include #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-test-cu.h" void test_startstopunit_simple(void) { int ret; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test basic STARTSTOPUNIT"); logging(LOG_VERBOSE, "Test we can eject removable the media with IMMED==1"); if (inq->rmb) { logging(LOG_VERBOSE, "Media is removable. STARTSTOPUNIT should work"); } else { logging(LOG_VERBOSE, "Media is not removable. STARTSTOPUNIT should fail"); } ret = startstopunit(sd, 1, 0, 0, 0, 1, 0, EXPECT_STATUS_GOOD); if (!inq->rmb) { CU_ASSERT_NOT_EQUAL(ret, 0); return; } CU_ASSERT_EQUAL(ret, 0); logging(LOG_VERBOSE, "Test TESTUNITREADY that medium is ejected."); TESTUNITREADY(sd, EXPECT_NO_MEDIUM); logging(LOG_VERBOSE, "Test we can load the removable the media with IMMED==1"); STARTSTOPUNIT(sd, 1, 0, 0, 0, 1, 1, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Verify we can read from the media."); TESTUNITREADY(sd, EXPECT_SANITIZE); logging(LOG_VERBOSE, "Test we can eject removable the media with IMMED==1"); STARTSTOPUNIT(sd, 0, 0, 0, 0, 1, 0, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Test TESTUNITREADY that medium is ejected."); TESTUNITREADY(sd, EXPECT_NO_MEDIUM); logging(LOG_VERBOSE, "Test we can load the removable the media with IMMED==1"); STARTSTOPUNIT(sd, 0, 0, 0, 0, 1, 1, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Verify we can access the media again."); TESTUNITREADY(sd, EXPECT_SANITIZE); } libiscsi-1.17.0/test-tool/test_testunitready_simple.c000066400000000000000000000021301271371262700230170ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2012 by Lee Duncan 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, see . */ #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-test-cu.h" void test_testunitready_simple(void) { logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test TESTUNITREADY"); TESTUNITREADY(sd, EXPECT_STATUS_GOOD); } libiscsi-1.17.0/test-tool/test_unmap_0blocks.c000066400000000000000000000050131271371262700213020ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg 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, see . */ #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-support.h" #include "iscsi-test-cu.h" void test_unmap_0blocks(void) { int i; struct unmap_list list[257]; CHECK_FOR_DATALOSS; CHECK_FOR_THIN_PROVISIONING; CHECK_FOR_SBC; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test UNMAP of 0 blocks at LBA:0-255 as a single descriptor"); for (i = 0; i < 256; i++) { list[0].lba = i; list[0].num = 0; UNMAP(sd, 0, list, 1, EXPECT_STATUS_GOOD); } logging(LOG_VERBOSE, "Test UNMAP of 0 blocks at end-of-LUN"); list[0].lba = num_blocks; list[0].num = 0; UNMAP(sd, 0, list, 1, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Test UNMAP without any descriptors."); UNMAP(sd, 0, list, 0, EXPECT_STATUS_GOOD); if (inq_bl->max_unmap_bdc <= 1) { CU_PASS("[SKIPPING] Test UNMAP of 0 blocks with multiple descriptos not supported"); return; } logging(LOG_VERBOSE, "Test UNMAP of 0 blocks at LBA:0-255 with one descriptor per block"); for (i = 0; i < 256; i++) { list[i].lba = i; list[i].num = 0; UNMAP(sd, 0, list, i + 1, EXPECT_STATUS_GOOD); } logging(LOG_VERBOSE, "Test UNMAP of 0 blocks at LBA:0-255 with one descriptor per block, possibly \"overlapping\"."); for (i = 0; i < 256; i++) { list[i].lba = i/2; list[i].num = 0; } UNMAP(sd, 0, list, 256, EXPECT_STATUS_GOOD); } libiscsi-1.17.0/test-tool/test_unmap_simple.c000066400000000000000000000064761271371262700212540ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg 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, see . */ #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-support.h" #include "iscsi-test-cu.h" void test_unmap_simple(void) { int i; struct unmap_list list[257]; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test basic UNMAP"); CHECK_FOR_DATALOSS; CHECK_FOR_THIN_PROVISIONING; CHECK_FOR_SBC; logging(LOG_VERBOSE, "Test UNMAP of 1-256 blocks at the start of the " "LUN as a single descriptor"); logging(LOG_VERBOSE, "Write 'a' to the first 256 LBAs"); memset(scratch, 'a', 256 * block_size); WRITE10(sd, 0, 256 * block_size, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); for (i = 1; i <= 256; i++) { logging(LOG_VERBOSE, "UNMAP blocks 0-%d", i); list[0].lba = 0; list[0].num = i; UNMAP(sd, 0, list, 1, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Read blocks 0-%d", i); READ10(sd, NULL, 0, i * block_size, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); if (rc16 && rc16->lbprz) { logging(LOG_VERBOSE, "LBPRZ==1 All UNMAPPED blocks " "should read back as 0"); ALL_ZERO(scratch, i * block_size); } } logging(LOG_VERBOSE, "Test UNMAP of 1-256 blocks at the start of the " "LUN with one descriptor per block"); logging(LOG_VERBOSE, "Write 'a' to the first 256 LBAs"); memset(scratch, 'a', 256 * block_size); WRITE10(sd, 0, 256 * block_size, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); for (i = 0; i < 256; i++) { list[i].lba = i; list[i].num = 1; UNMAP(sd, 0, list, i + 1, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Read blocks 0-%d", i); READ10(sd, NULL, 0, i * block_size, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); if (rc16 && rc16->lbprz) { logging(LOG_VERBOSE, "LBPRZ==1 All UNMAPPED blocks " "should read back as 0"); ALL_ZERO(scratch, i * block_size); } } } libiscsi-1.17.0/test-tool/test_unmap_vpd.c000066400000000000000000000057401271371262700205450ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg 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, see . */ #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-support.h" #include "iscsi-test-cu.h" void test_unmap_vpd(void) { int ret; struct unmap_list list[1]; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test UNMAP availability is consistent with VPD settings"); CHECK_FOR_DATALOSS; CHECK_FOR_SBC; logging(LOG_VERBOSE, "Check if UNMAP is available."); list[0].lba = 0; list[0].num = 0; ret = unmap(sd, 0, list, 1, EXPECT_STATUS_GOOD); if (ret != 0) { logging(LOG_VERBOSE, "UNMAP is not available. Verify that VPD " "settings reflect this."); logging(LOG_VERBOSE, "Verify that LBPU is clear."); if (inq_lbp && inq_lbp->lbpu) { logging(LOG_NORMAL, "[FAILED] UNMAP is not implemented " "but LBPU is set"); CU_FAIL("[FAILED] UNMAP is unavailable but LBPU==1"); } else { logging(LOG_VERBOSE, "[SUCCESS] LBPU is clear."); } } else { logging(LOG_VERBOSE, "UNMAP is available. Verify that VPD " "settings reflect this."); logging(LOG_VERBOSE, "Verify that LBPME is set."); if (rc16 && rc16->lbpme) { logging(LOG_VERBOSE, "[SUCCESS] LBPME is set."); } else { logging(LOG_NORMAL, "[FAILED] UNMAP is implemented " "but LBPME is not set"); CU_FAIL("[FAILED] UNMAP is available but LBPME==0"); } logging(LOG_VERBOSE, "Verify that LBPU is set."); if (inq_lbp && inq_lbp->lbpu) { logging(LOG_VERBOSE, "[SUCCESS] LBPU is set."); } else { logging(LOG_NORMAL, "[FAILED] UNMAP is implemented " "but LBPU is not set"); CU_FAIL("[FAILED] UNMAP is available but LBPU==0"); } } } libiscsi-1.17.0/test-tool/test_verify10_0blocks.c000066400000000000000000000030521271371262700216300ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg 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, see . */ #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-test-cu.h" void test_verify10_0blocks(void) { logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test VERIFY10 0-blocks at LBA==0"); logging(LOG_VERBOSE, "Test VERIFY10 0-blocks one block past end-of-LUN"); VERIFY10(sd, num_blocks + 1, 0, block_size, 0, 0, 1, NULL, EXPECT_LBA_OOB); logging(LOG_VERBOSE, "Test VERIFY10 0-blocks at LBA==2^31"); VERIFY10(sd, 0x80000000, 0, block_size, 0, 0, 1, NULL, EXPECT_LBA_OOB); logging(LOG_VERBOSE, "Test VERIFY10 0-blocks at LBA==-1"); VERIFY10(sd, -1, 0, block_size, 0, 0, 1, NULL, EXPECT_LBA_OOB); } libiscsi-1.17.0/test-tool/test_verify10_beyond_eol.c000066400000000000000000000055071271371262700224210ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg 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, see . */ #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-test-cu.h" void test_verify10_beyond_eol(void) { int i; if (num_blocks >= 0x80000000) { CU_PASS("LUN is too big for read-beyond-eol tests with VERIFY10. Skipping test.\n"); return; } logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test VERIFY10 1-256 blocks one block beyond the end"); memset(scratch, 0xa6, 256 * block_size); for (i = 1; i <= 256; i++) { if (maximum_transfer_length && maximum_transfer_length < i) { break; } VERIFY10(sd, num_blocks + 1 - i, i * block_size, block_size, 0, 0, 1, scratch, EXPECT_LBA_OOB); } logging(LOG_VERBOSE, "Test VERIFY10 1-256 blocks at LBA==2^31"); for (i = 1; i <= 256; i++) { if (maximum_transfer_length && maximum_transfer_length < i) { break; } VERIFY10(sd, 0x80000000, i * block_size, block_size, 0, 0, 1, scratch, EXPECT_LBA_OOB); } logging(LOG_VERBOSE, "Test VERIFY10 1-256 blocks at LBA==-1"); for (i = 1; i <= 256; i++) { if (maximum_transfer_length && maximum_transfer_length < i) { break; } VERIFY10(sd, -1, i * block_size, block_size, 0, 0, 1, scratch, EXPECT_LBA_OOB); } logging(LOG_VERBOSE, "Test VERIFY10 2-256 blocks all but one block beyond the end"); for (i = 2; i <= 256; i++) { if (maximum_transfer_length && maximum_transfer_length < i) { break; } VERIFY10(sd, num_blocks - 1, i * block_size, block_size, 0, 0, 1, scratch, EXPECT_LBA_OOB); } } libiscsi-1.17.0/test-tool/test_verify10_dpo.c000066400000000000000000000071261271371262700210630ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2014 by Ronnie Sahlberg 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, see . */ #include #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-test-cu.h" void test_verify10_dpo(void) { int dpofua, usage_data_dpo; struct scsi_task *ms_task = NULL; struct scsi_mode_sense *ms; struct scsi_task *rso_task = NULL; struct scsi_report_supported_op_codes_one_command *rsoc; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test VERIFY10 DPO flag"); CHECK_FOR_SBC; READ10(sd, NULL, 0, block_size, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Read the DPOFUA flag from mode sense data"); MODESENSE6(sd, &ms_task, 0, SCSI_MODESENSE_PC_CURRENT, SCSI_MODEPAGE_RETURN_ALL_PAGES, 0, 255, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "[SUCCESS] Mode sense returned status GOOD"); ms = scsi_datain_unmarshall(ms_task); dpofua = ms && (ms->device_specific_parameter & 0x10); scsi_free_scsi_task(ms_task); if (dpofua) { logging(LOG_VERBOSE, "DPOFUA flag is set. Device should allow " "DPO/FUA flags in CDBs"); } else { logging(LOG_VERBOSE, "DPOFUA flag is clear. Device should fail " "CDBs with DPO/FUA set"); } logging(LOG_VERBOSE, "Test VERIFY10 with DPO==1"); if (dpofua) { VERIFY10(sd, 0, block_size, block_size, 0, 1, 0, scratch, EXPECT_STATUS_GOOD); } else { VERIFY10(sd, 0, block_size, block_size, 0, 1, 0, scratch, EXPECT_INVALID_FIELD_IN_CDB); } logging(LOG_VERBOSE, "Try fetching REPORT_SUPPORTED_OPCODES " "for VERIFY10"); REPORT_SUPPORTED_OPCODES(sd, &rso_task, 0, SCSI_REPORT_SUPPORTING_OPCODE, SCSI_OPCODE_VERIFY10, 0, 65535, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Unmarshall the DATA-IN buffer"); rsoc = scsi_datain_unmarshall(rso_task); CU_ASSERT_PTR_NOT_NULL_FATAL(rsoc); usage_data_dpo = rsoc ? rsoc->cdb_usage_data[1] & 0x10 : -1; if (dpofua) { logging(LOG_VERBOSE, "DPOFUA is set. Verify the DPO flag " "is set in the CDB_USAGE_DATA"); CU_ASSERT_EQUAL(usage_data_dpo, 0x10); } else { logging(LOG_VERBOSE, "DPOFUA is clear. Verify the DPO " "flag is clear in the CDB_USAGE_DATA"); CU_ASSERT_EQUAL(usage_data_dpo, 0x00); } scsi_free_scsi_task(rso_task); } libiscsi-1.17.0/test-tool/test_verify10_flags.c000066400000000000000000000025551271371262700213760ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg 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, see . */ #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-support.h" #include "iscsi-test-cu.h" void test_verify10_flags(void) { logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test VERIFY10 flags"); READ10(sd, NULL, 0, block_size, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Test VERIFY10 with BYTCHK==1"); VERIFY10(sd, 0, block_size, block_size, 0, 0, 1, scratch, EXPECT_STATUS_GOOD); } libiscsi-1.17.0/test-tool/test_verify10_mismatch.c000066400000000000000000000050721271371262700221040ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg 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, see . */ #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-support.h" #include "iscsi-test-cu.h" void test_verify10_mismatch(void) { int i; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test VERIFY10 for blocks 1-255"); for (i = 1; i <= 256; i++) { int offset = random() % (i * block_size); if (maximum_transfer_length && maximum_transfer_length < i) { break; } READ10(sd, NULL, 0, i * block_size, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); /* flip a random byte in the data */ scratch[offset] ^= 'X'; logging(LOG_VERBOSE, "Flip some bits in the data"); VERIFY10(sd, 0, i * block_size, block_size, 0, 0, 1, scratch, EXPECT_MISCOMPARE); } logging(LOG_VERBOSE, "Test VERIFY10 of 1-256 blocks at the end of the LUN"); for (i = 1; i <= 256; i++) { int offset = random() % (i * block_size); if (maximum_transfer_length && maximum_transfer_length < i) { break; } READ10(sd, NULL, num_blocks - i, i * block_size, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); /* flip a random byte in the data */ scratch[offset] ^= 'X'; logging(LOG_VERBOSE, "Flip some bits in the data"); VERIFY10(sd, num_blocks - i, i * block_size, block_size, 0, 0, 1, scratch, EXPECT_MISCOMPARE); } } libiscsi-1.17.0/test-tool/test_verify10_mismatch_no_cmp.c000066400000000000000000000051411271371262700234340ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg 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, see . */ #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-support.h" #include "iscsi-test-cu.h" void test_verify10_mismatch_no_cmp(void) { int i; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test VERIFY10 without BYTCHK for blocks 1-255"); for (i = 1; i <= 256; i++) { int offset = random() % (i * block_size); if (maximum_transfer_length && maximum_transfer_length < i) { break; } READ10(sd, NULL, 0, i * block_size, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); /* flip a random byte in the data */ scratch[offset] ^= 'X'; logging(LOG_VERBOSE, "Flip some bits in the data"); VERIFY10(sd, 0, i * block_size, block_size, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); } logging(LOG_VERBOSE, "Test VERIFY10 without BYTCHK of 1-256 blocks at the end of the LUN"); for (i = 1; i <= 256; i++) { int offset = random() % (i * block_size); if (maximum_transfer_length && maximum_transfer_length < i) { break; } READ10(sd, NULL, num_blocks - i, i * block_size, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); /* flip a random byte in the data */ scratch[offset] ^= 'X'; logging(LOG_VERBOSE, "Flip some bits in the data"); VERIFY10(sd, num_blocks - i, i * block_size, block_size, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); } } libiscsi-1.17.0/test-tool/test_verify10_simple.c000066400000000000000000000042251271371262700215670ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg 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, see . */ #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-support.h" #include "iscsi-test-cu.h" void test_verify10_simple(void) { int i; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test VERIFY10 of 1-256 blocks at the start of the LUN"); for (i = 1; i <= 256; i++) { if (maximum_transfer_length && maximum_transfer_length < i) { break; } READ10(sd, NULL, 0, i * block_size, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); VERIFY10(sd, 0, i * block_size, block_size, 0, 0, 1, scratch, EXPECT_STATUS_GOOD); } logging(LOG_VERBOSE, "Test VERIFY10 of 1-256 blocks at the end of the LUN"); for (i = 1; i <= 256; i++) { if (maximum_transfer_length && maximum_transfer_length < i) { break; } READ10(sd, NULL, num_blocks - i, i * block_size, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); VERIFY10(sd, num_blocks - i, i * block_size, block_size, 0, 0, 1, scratch, EXPECT_STATUS_GOOD); } } libiscsi-1.17.0/test-tool/test_verify10_vrprotect.c000066400000000000000000000035441271371262700223310ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg 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, see . */ #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-support.h" #include "iscsi-test-cu.h" void test_verify10_vrprotect(void) { int i; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test VERIFY10 with non-zero VRPROTECT"); CHECK_FOR_SBC; if (!inq->protect || (rc16 != NULL && !rc16->prot_en)) { logging(LOG_VERBOSE, "Device does not support/use protection information. All commands should fail."); for (i = 1; i < 8; i++) { READ10(sd, NULL, 0, block_size, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); VERIFY10(sd, 0, block_size, block_size, i, 0, 1, scratch, EXPECT_INVALID_FIELD_IN_CDB); } return; } logging(LOG_NORMAL, "No tests for devices that support protection information yet."); } libiscsi-1.17.0/test-tool/test_verify12_0blocks.c000066400000000000000000000030521271371262700216320ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg 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, see . */ #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-test-cu.h" void test_verify12_0blocks(void) { logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test VERIFY12 0-blocks at LBA==0"); logging(LOG_VERBOSE, "Test VERIFY12 0-blocks one block past end-of-LUN"); VERIFY12(sd, num_blocks + 1, 0, block_size, 0, 0, 1, NULL, EXPECT_LBA_OOB); logging(LOG_VERBOSE, "Test VERIFY12 0-blocks at LBA==2^31"); VERIFY12(sd, 0x80000000, 0, block_size, 0, 0, 1, NULL, EXPECT_LBA_OOB); logging(LOG_VERBOSE, "Test VERIFY12 0-blocks at LBA==-1"); VERIFY12(sd, -1, 0, block_size, 0, 0, 1, NULL, EXPECT_LBA_OOB); } libiscsi-1.17.0/test-tool/test_verify12_beyond_eol.c000066400000000000000000000055371271371262700224260ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg 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, see . */ #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-test-cu.h" void test_verify12_beyond_eol(void) { int i; if (num_blocks >= 0x80000000) { CU_PASS("LUN is too big for read-beyond-eol tests with VERIFY12. Skipping test.\n"); return; } logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test VERIFY12 1-256 blocks one block beyond the end"); memset(scratch, 0xa6, 256 * block_size); for (i = 1; i <= 256; i++) { if (maximum_transfer_length && maximum_transfer_length < i) { break; } VERIFY12(sd, num_blocks + 1 - i, i * block_size, block_size, 0, 0, 1, scratch, EXPECT_LBA_OOB); } logging(LOG_VERBOSE, "Test VERIFY12 1-256 blocks at LBA==2^31"); for (i = 1; i <= 256; i++) { if (maximum_transfer_length && maximum_transfer_length < i) { break; } VERIFY12(sd, 0x80000000, i * block_size, block_size, 0, 0, 1, scratch, EXPECT_LBA_OOB); } logging(LOG_VERBOSE, "Test VERIFY12 1-256 blocks at LBA==-1"); for (i = 1; i <= 256; i++) { if (maximum_transfer_length && maximum_transfer_length < i) { break; } VERIFY12(sd, -1, i * block_size, block_size, 0, 0, 1, scratch, EXPECT_LBA_OOB); } logging(LOG_VERBOSE, "Test VERIFY12 2-256 blocks all but one block beyond the end"); for (i = 2; i <= 256; i++) { if (maximum_transfer_length && maximum_transfer_length < i) { break; } VERIFY12(sd, num_blocks - 1, i * block_size, block_size, 0, 0, 1, scratch, EXPECT_LBA_OOB); } } libiscsi-1.17.0/test-tool/test_verify12_dpo.c000066400000000000000000000071261271371262700210650ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2014 by Ronnie Sahlberg 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, see . */ #include #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-test-cu.h" void test_verify12_dpo(void) { int dpofua, usage_data_dpo; struct scsi_task *ms_task = NULL; struct scsi_mode_sense *ms; struct scsi_task *rso_task = NULL; struct scsi_report_supported_op_codes_one_command *rsoc; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test VERIFY12 DPO flag"); CHECK_FOR_SBC; READ10(sd, NULL, 0, block_size, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Read the DPOFUA flag from mode sense data"); MODESENSE6(sd, &ms_task, 0, SCSI_MODESENSE_PC_CURRENT, SCSI_MODEPAGE_RETURN_ALL_PAGES, 0, 255, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "[SUCCESS] Mode sense returned status GOOD"); ms = scsi_datain_unmarshall(ms_task); dpofua = ms && (ms->device_specific_parameter & 0x10); scsi_free_scsi_task(ms_task); if (dpofua) { logging(LOG_VERBOSE, "DPOFUA flag is set. Device should allow " "DPO/FUA flags in CDBs"); } else { logging(LOG_VERBOSE, "DPOFUA flag is clear. Device should fail " "CDBs with DPO/FUA set"); } logging(LOG_VERBOSE, "Test VERIFY12 with DPO==1"); if (dpofua) { VERIFY12(sd, 0, block_size, block_size, 0, 1, 0, scratch, EXPECT_STATUS_GOOD); } else { VERIFY12(sd, 0, block_size, block_size, 0, 1, 0, scratch, EXPECT_INVALID_FIELD_IN_CDB); } logging(LOG_VERBOSE, "Try fetching REPORT_SUPPORTED_OPCODES " "for VERIFY12"); REPORT_SUPPORTED_OPCODES(sd, &rso_task, 0, SCSI_REPORT_SUPPORTING_OPCODE, SCSI_OPCODE_VERIFY12, 0, 65535, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Unmarshall the DATA-IN buffer"); rsoc = scsi_datain_unmarshall(rso_task); CU_ASSERT_PTR_NOT_NULL_FATAL(rsoc); usage_data_dpo = rsoc ? rsoc->cdb_usage_data[1] & 0x10 : -1; if (dpofua) { logging(LOG_VERBOSE, "DPOFUA is set. Verify the DPO flag " "is set in the CDB_USAGE_DATA"); CU_ASSERT_EQUAL(usage_data_dpo, 0x10); } else { logging(LOG_VERBOSE, "DPOFUA is clear. Verify the DPO " "flag is clear in the CDB_USAGE_DATA"); CU_ASSERT_EQUAL(usage_data_dpo, 0x00); } scsi_free_scsi_task(rso_task); } libiscsi-1.17.0/test-tool/test_verify12_flags.c000066400000000000000000000025471271371262700214010ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg 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, see . */ #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-support.h" #include "iscsi-test-cu.h" void test_verify12_flags(void) { logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test VERIFY12 flags"); READ12(sd, NULL, 0, block_size, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Test VERIFY12 with BYTCHK==1"); VERIFY12(sd, 0, block_size, block_size, 0, 0, 1, scratch, EXPECT_STATUS_GOOD); } libiscsi-1.17.0/test-tool/test_verify12_mismatch.c000066400000000000000000000050721271371262700221060ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg 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, see . */ #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-support.h" #include "iscsi-test-cu.h" void test_verify12_mismatch(void) { int i; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test VERIFY12 for blocks 1-255"); for (i = 1; i <= 256; i++) { int offset = random() % (i * block_size); if (maximum_transfer_length && maximum_transfer_length < i) { break; } READ12(sd, NULL, 0, i * block_size, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); /* flip a random byte in the data */ scratch[offset] ^= 'X'; logging(LOG_VERBOSE, "Flip some bits in the data"); VERIFY12(sd, 0, i * block_size, block_size, 0, 0, 1, scratch, EXPECT_MISCOMPARE); } logging(LOG_VERBOSE, "Test VERIFY12 of 1-256 blocks at the end of the LUN"); for (i = 1; i <= 256; i++) { int offset = random() % (i * block_size); if (maximum_transfer_length && maximum_transfer_length < i) { break; } READ12(sd, NULL, num_blocks - i, i * block_size, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); /* flip a random byte in the data */ scratch[offset] ^= 'X'; logging(LOG_VERBOSE, "Flip some bits in the data"); VERIFY12(sd, num_blocks - i, i * block_size, block_size, 0, 0, 1, scratch, EXPECT_MISCOMPARE); } } libiscsi-1.17.0/test-tool/test_verify12_mismatch_no_cmp.c000066400000000000000000000051411271371262700234360ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg 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, see . */ #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-support.h" #include "iscsi-test-cu.h" void test_verify12_mismatch_no_cmp(void) { int i; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test VERIFY12 without BYTCHK for blocks 1-255"); for (i = 1; i <= 256; i++) { int offset = random() % (i * block_size); if (maximum_transfer_length && maximum_transfer_length < i) { break; } READ12(sd, NULL, 0, i * block_size, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); /* flip a random byte in the data */ scratch[offset] ^= 'X'; logging(LOG_VERBOSE, "Flip some bits in the data"); VERIFY12(sd, 0, i * block_size, block_size, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); } logging(LOG_VERBOSE, "Test VERIFY12 without BYTCHK of 1-256 blocks at the end of the LUN"); for (i = 1; i <= 256; i++) { int offset = random() % (i * block_size); if (maximum_transfer_length && maximum_transfer_length < i) { break; } READ12(sd, NULL, num_blocks - i, i * block_size, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); /* flip a random byte in the data */ scratch[offset] ^= 'X'; logging(LOG_VERBOSE, "Flip some bits in the data"); VERIFY12(sd, num_blocks - i, i * block_size, block_size, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); } } libiscsi-1.17.0/test-tool/test_verify12_simple.c000066400000000000000000000042251271371262700215710ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg 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, see . */ #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-support.h" #include "iscsi-test-cu.h" void test_verify12_simple(void) { int i; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test VERIFY12 of 1-256 blocks at the start of the LUN"); for (i = 1; i <= 256; i++) { if (maximum_transfer_length && maximum_transfer_length < i) { break; } READ12(sd, NULL, 0, i * block_size, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); VERIFY12(sd, 0, i * block_size, block_size, 0, 0, 1, scratch, EXPECT_STATUS_GOOD); } logging(LOG_VERBOSE, "Test VERIFY12 of 1-256 blocks at the end of the LUN"); for (i = 1; i <= 256; i++) { if (maximum_transfer_length && maximum_transfer_length < i) { break; } READ12(sd, NULL, num_blocks - i, i * block_size, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); VERIFY12(sd, num_blocks - i, i * block_size, block_size, 0, 0, 1, scratch, EXPECT_STATUS_GOOD); } } libiscsi-1.17.0/test-tool/test_verify12_vrprotect.c000066400000000000000000000035441271371262700223330ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg 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, see . */ #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-support.h" #include "iscsi-test-cu.h" void test_verify12_vrprotect(void) { int i; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test VERIFY12 with non-zero VRPROTECT"); CHECK_FOR_SBC; if (!inq->protect || (rc16 != NULL && !rc16->prot_en)) { logging(LOG_VERBOSE, "Device does not support/use protection information. All commands should fail."); for (i = 1; i < 8; i++) { READ10(sd, NULL, 0, block_size, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); VERIFY12(sd, 0, block_size, block_size, i, 0, 1, scratch, EXPECT_INVALID_FIELD_IN_CDB); } return; } logging(LOG_NORMAL, "No tests for devices that support protection information yet."); } libiscsi-1.17.0/test-tool/test_verify16_0blocks.c000066400000000000000000000030651271371262700216420ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg 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, see . */ #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-test-cu.h" void test_verify16_0blocks(void) { logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test VERIFY16 0-blocks at LBA==0"); logging(LOG_VERBOSE, "Test VERIFY16 0-blocks one block past end-of-LUN"); VERIFY16(sd, num_blocks + 1, 0, block_size, 0, 0, 1, NULL, EXPECT_LBA_OOB); logging(LOG_VERBOSE, "Test VERIFY16 0-blocks at LBA==2^63"); VERIFY16(sd, 0x8000000000000000ULL, 0, block_size, 0, 0, 1, NULL, EXPECT_LBA_OOB); logging(LOG_VERBOSE, "Test VERIFY16 0-blocks at LBA==-1"); VERIFY16(sd, -1, 0, block_size, 0, 0, 1, NULL, EXPECT_LBA_OOB); } libiscsi-1.17.0/test-tool/test_verify16_beyond_eol.c000066400000000000000000000052411271371262700224220ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg 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, see . */ #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-test-cu.h" void test_verify16_beyond_eol(void) { int i; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test VERIFY16 1-256 blocks one block beyond the end"); memset(scratch, 0xa6, 256 * block_size); for (i = 1; i <= 256; i++) { if (maximum_transfer_length && maximum_transfer_length < i) { break; } VERIFY16(sd, num_blocks + 1 - i, i * block_size, block_size, 0, 0, 1, scratch, EXPECT_LBA_OOB); } logging(LOG_VERBOSE, "Test VERIFY16 1-256 blocks at LBA==2^63"); for (i = 1; i <= 256; i++) { if (maximum_transfer_length && maximum_transfer_length < i) { break; } VERIFY16(sd, 0x8000000000000000ULL, i * block_size, block_size, 0, 0, 1, scratch, EXPECT_LBA_OOB); } logging(LOG_VERBOSE, "Test VERIFY16 1-256 blocks at LBA==-1"); for (i = 1; i <= 256; i++) { if (maximum_transfer_length && maximum_transfer_length < i) { break; } VERIFY16(sd, -1, i * block_size, block_size, 0, 0, 1, scratch, EXPECT_LBA_OOB); } logging(LOG_VERBOSE, "Test VERIFY16 2-256 blocks all but one block beyond the end"); for (i = 2; i <= 256; i++) { if (maximum_transfer_length && maximum_transfer_length < i) { break; } VERIFY16(sd, num_blocks - 1, i * block_size, block_size, 0, 0, 1, scratch, EXPECT_LBA_OOB); } } libiscsi-1.17.0/test-tool/test_verify16_dpo.c000066400000000000000000000071271271371262700210720ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2014 by Ronnie Sahlberg 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, see . */ #include #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-test-cu.h" void test_verify16_dpo(void) { int dpofua, usage_data_dpo; struct scsi_task *ms_task = NULL; struct scsi_mode_sense *ms; struct scsi_task *rso_task = NULL; struct scsi_report_supported_op_codes_one_command *rsoc; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test VERIFY16 DPO flag"); CHECK_FOR_SBC; READ10(sd, NULL, 0, block_size, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Read the DPOFUA flag from mode sense data"); MODESENSE6(sd, &ms_task, 0, SCSI_MODESENSE_PC_CURRENT, SCSI_MODEPAGE_RETURN_ALL_PAGES, 0, 255, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "[SUCCESS] Mode sense returned status GOOD"); ms = scsi_datain_unmarshall(ms_task); dpofua = ms && (ms->device_specific_parameter & 0x10); scsi_free_scsi_task(ms_task); if (dpofua) { logging(LOG_VERBOSE, "DPOFUA flag is set. Device should allow " "DPO/FUA flags in CDBs"); } else { logging(LOG_VERBOSE, "DPOFUA flag is clear. Device should fail " "CDBs with DPO/FUA set"); } logging(LOG_VERBOSE, "Test VERIFY16 with DPO==1"); if (dpofua) { VERIFY16(sd, 0, block_size, block_size, 0, 1, 0, scratch, EXPECT_STATUS_GOOD); } else { VERIFY16(sd, 0, block_size, block_size, 0, 1, 0, scratch, EXPECT_INVALID_FIELD_IN_CDB); } logging(LOG_VERBOSE, "Try fetching REPORT_SUPPORTED_OPCODES " "for VERIFY16"); REPORT_SUPPORTED_OPCODES(sd, &rso_task, 0, SCSI_REPORT_SUPPORTING_OPCODE, SCSI_OPCODE_VERIFY16, 0, 65535, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Unmarshall the DATA-IN buffer"); rsoc = scsi_datain_unmarshall(rso_task); CU_ASSERT_PTR_NOT_NULL_FATAL(rsoc); usage_data_dpo = rsoc ? rsoc->cdb_usage_data[1] & 0x10 : -1; if (dpofua) { logging(LOG_VERBOSE, "DPOFUA is set. Verify the DPO flag " "is set in the CDB_USAGE_DATA"); CU_ASSERT_EQUAL(usage_data_dpo, 0x10); } else { logging(LOG_VERBOSE, "DPOFUA is clear. Verify the DPO " "flag is clear in the CDB_USAGE_DATA"); CU_ASSERT_EQUAL(usage_data_dpo, 0x00); } scsi_free_scsi_task(rso_task); } libiscsi-1.17.0/test-tool/test_verify16_flags.c000066400000000000000000000025471271371262700214050ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg 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, see . */ #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-support.h" #include "iscsi-test-cu.h" void test_verify16_flags(void) { logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test VERIFY16 flags"); READ16(sd, NULL, 0, block_size, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Test VERIFY16 with BYTCHK==1"); VERIFY16(sd, 0, block_size, block_size, 0, 0, 1, scratch, EXPECT_STATUS_GOOD); } libiscsi-1.17.0/test-tool/test_verify16_mismatch.c000066400000000000000000000050721271371262700221120ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg 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, see . */ #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-support.h" #include "iscsi-test-cu.h" void test_verify16_mismatch(void) { int i; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test VERIFY16 for blocks 1-255"); for (i = 1; i <= 256; i++) { int offset = random() % (i * block_size); if (maximum_transfer_length && maximum_transfer_length < i) { break; } READ16(sd, NULL, 0, i * block_size, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); /* flip a random byte in the data */ scratch[offset] ^= 'X'; logging(LOG_VERBOSE, "Flip some bits in the data"); VERIFY16(sd, 0, i * block_size, block_size, 0, 0, 1, scratch, EXPECT_MISCOMPARE); } logging(LOG_VERBOSE, "Test VERIFY16 of 1-256 blocks at the end of the LUN"); for (i = 1; i <= 256; i++) { int offset = random() % (i * block_size); if (maximum_transfer_length && maximum_transfer_length < i) { break; } READ16(sd, NULL, num_blocks - i, i * block_size, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); /* flip a random byte in the data */ scratch[offset] ^= 'X'; logging(LOG_VERBOSE, "Flip some bits in the data"); VERIFY16(sd, num_blocks - i, i * block_size, block_size, 0, 0, 1, scratch, EXPECT_MISCOMPARE); } } libiscsi-1.17.0/test-tool/test_verify16_mismatch_no_cmp.c000066400000000000000000000051411271371262700234420ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg 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, see . */ #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-support.h" #include "iscsi-test-cu.h" void test_verify16_mismatch_no_cmp(void) { int i; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test VERIFY16 without BYTCHK for blocks 1-255"); for (i = 1; i <= 256; i++) { int offset = random() % (i * block_size); if (maximum_transfer_length && maximum_transfer_length < i) { break; } READ16(sd, NULL, 0, i * block_size, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); /* flip a random byte in the data */ scratch[offset] ^= 'X'; logging(LOG_VERBOSE, "Flip some bits in the data"); VERIFY16(sd, 0, i * block_size, block_size, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); } logging(LOG_VERBOSE, "Test VERIFY16 without BYTCHK of 1-256 blocks at the end of the LUN"); for (i = 1; i <= 256; i++) { int offset = random() % (i * block_size); if (maximum_transfer_length && maximum_transfer_length < i) { break; } READ16(sd, NULL, num_blocks - i, i * block_size, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); /* flip a random byte in the data */ scratch[offset] ^= 'X'; logging(LOG_VERBOSE, "Flip some bits in the data"); VERIFY16(sd, num_blocks - i, i * block_size, block_size, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); } } libiscsi-1.17.0/test-tool/test_verify16_simple.c000066400000000000000000000042561271371262700216010ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg 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, see . */ #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-support.h" #include "iscsi-test-cu.h" void test_verify16_simple(void) { int i; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test VERIFY16 of 1-256 blocks at the start of the LUN"); for (i = 1; i <= 256; i++) { if (maximum_transfer_length && maximum_transfer_length < i) { break; } READ16(sd, NULL, 0, i * block_size, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); VERIFY16(sd, 0, i * block_size, block_size, 0, 0, 1, scratch, EXPECT_STATUS_GOOD); } logging(LOG_VERBOSE, "Test VERIFY16 of 1-256 blocks at the end of the LUN"); for (i = 1; i <= 256; i++) { if (maximum_transfer_length && maximum_transfer_length < i) { break; } READ16(sd, NULL, num_blocks - i, i * block_size, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); VERIFY16(sd, num_blocks - i, i * block_size, block_size, 0, 0, 1, scratch, EXPECT_STATUS_GOOD); } } libiscsi-1.17.0/test-tool/test_verify16_vrprotect.c000066400000000000000000000035441271371262700223370ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg 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, see . */ #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-support.h" #include "iscsi-test-cu.h" void test_verify16_vrprotect(void) { int i; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test VERIFY16 with non-zero VRPROTECT"); CHECK_FOR_SBC; if (!inq->protect || (rc16 != NULL && !rc16->prot_en)) { logging(LOG_VERBOSE, "Device does not support/use protection information. All commands should fail."); for (i = 1; i < 8; i++) { READ16(sd, NULL, 0, block_size, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); VERIFY16(sd, 0, block_size, block_size, i, 0, 1, scratch, EXPECT_INVALID_FIELD_IN_CDB); } return; } logging(LOG_NORMAL, "No tests for devices that support protection information yet."); } libiscsi-1.17.0/test-tool/test_write10_0blocks.c000066400000000000000000000035351271371262700214640ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg 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, see . */ #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-test-cu.h" void test_write10_0blocks(void) { CHECK_FOR_DATALOSS; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test WRITE10 0-blocks at LBA==0"); WRITE10(sd, 0, 0, block_size, 0, 0, 0, 0, 0, NULL, EXPECT_STATUS_GOOD); if (num_blocks >= 0x80000000) { CU_PASS("LUN is too big for read-beyond-eol tests with WRITE10. Skipping test.\n"); return; } logging(LOG_VERBOSE, "Test WRITE10 0-blocks one block past end-of-LUN"); WRITE10(sd, num_blocks + 1, 0, block_size, 0, 0, 0, 0, 0, NULL, EXPECT_LBA_OOB); logging(LOG_VERBOSE, "Test WRITE10 0-blocks at LBA==2^31"); WRITE10(sd, 0x80000000, 0, block_size, 0, 0, 0, 0, 0, NULL, EXPECT_LBA_OOB); logging(LOG_VERBOSE, "Test WRITE10 0-blocks at LBA==-1"); WRITE10(sd, -1, 0, block_size, 0, 0, 0, 0, 0, NULL, EXPECT_LBA_OOB); } libiscsi-1.17.0/test-tool/test_write10_beyond_eol.c000066400000000000000000000056031271371262700222440ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg 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, see . */ #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-test-cu.h" void test_write10_beyond_eol(void) { int i; CHECK_FOR_DATALOSS; if (num_blocks >= 0x80000000) { CU_PASS("LUN is too big for write-beyond-eol tests with WRITE10. Skipping test.\n"); return; } logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test WRITE10 1-256 blocks one block beyond the end"); memset(scratch, 0xa6, 256 * block_size); for (i = 1; i <= 256; i++) { if (maximum_transfer_length && maximum_transfer_length < i) { break; } WRITE10(sd, num_blocks + 1 - i, i * block_size, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_LBA_OOB); } logging(LOG_VERBOSE, "Test WRITE10 1-256 blocks at LBA==2^31"); for (i = 1; i <= 256; i++) { if (maximum_transfer_length && maximum_transfer_length < i) { break; } WRITE10(sd, 0x80000000, i * block_size, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_LBA_OOB); } logging(LOG_VERBOSE, "Test WRITE10 1-256 blocks at LBA==-1"); for (i = 1; i <= 256; i++) { if (maximum_transfer_length && maximum_transfer_length < i) { break; } WRITE10(sd, -1, i * block_size, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_LBA_OOB); } logging(LOG_VERBOSE, "Test WRITE10 2-256 blocks all but one block beyond the end"); for (i = 2; i <= 256; i++) { if (maximum_transfer_length && maximum_transfer_length < i) { break; } WRITE10(sd, num_blocks - 1, i * block_size, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_LBA_OOB); } } libiscsi-1.17.0/test-tool/test_write10_dpofua.c000066400000000000000000000112721271371262700214020ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2014 by Ronnie Sahlberg 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, see . */ #include #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-test-cu.h" void test_write10_dpofua(void) { int dpofua, usage_data_dpofua; struct scsi_task *ms_task = NULL; struct scsi_mode_sense *ms; struct scsi_task *rso_task = NULL; struct scsi_report_supported_op_codes_one_command *rsoc; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test WRITE10 DPO/FUA flags"); CHECK_FOR_SBC; CHECK_FOR_DATALOSS; logging(LOG_VERBOSE, "Read the DPOFUA flag from mode sense data"); MODESENSE6(sd, &ms_task, 0, SCSI_MODESENSE_PC_CURRENT, SCSI_MODEPAGE_RETURN_ALL_PAGES, 0, 255, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "[SUCCESS] Mode sense returned status GOOD"); ms = scsi_datain_unmarshall(ms_task); dpofua = ms && (ms->device_specific_parameter & 0x10); scsi_free_scsi_task(ms_task); if (dpofua) { logging(LOG_VERBOSE, "DPOFUA flag is set. Device should allow " "DPO/FUA flags in CDBs"); } else { logging(LOG_VERBOSE, "DPOFUA flag is clear. Device should fail " "CDBs with DPO/FUA set"); } logging(LOG_VERBOSE, "Test WRITE10 with DPO==1"); memset(scratch, 0xa6, block_size); if (dpofua) { WRITE10(sd, 0, block_size, block_size, 0, 1, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); } else { WRITE10(sd, 0, block_size, block_size, 0, 1, 0, 0, 0, scratch, EXPECT_INVALID_FIELD_IN_CDB); } logging(LOG_VERBOSE, "Test WRITE10 with FUA==1"); if (dpofua) { WRITE10(sd, 0, block_size, block_size, 0, 0, 1, 0, 0, scratch, EXPECT_STATUS_GOOD); } else { WRITE10(sd, 0, block_size, block_size, 0, 0, 1, 0, 0, scratch, EXPECT_INVALID_FIELD_IN_CDB); } logging(LOG_VERBOSE, "Test WRITE10 with DPO==1 FUA==1"); if (dpofua) { WRITE10(sd, 0, block_size, block_size, 0, 1, 1, 0, 0, scratch, EXPECT_STATUS_GOOD); } else { WRITE10(sd, 0, block_size, block_size, 0, 1, 1, 0, 0, scratch, EXPECT_INVALID_FIELD_IN_CDB); } logging(LOG_VERBOSE, "Try fetching REPORT_SUPPORTED_OPCODES " "for WRITE10"); REPORT_SUPPORTED_OPCODES(sd, &rso_task, 0, SCSI_REPORT_SUPPORTING_OPCODE, SCSI_OPCODE_WRITE10, 0, 65535, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Unmarshall the DATA-IN buffer"); rsoc = scsi_datain_unmarshall(rso_task); CU_ASSERT_PTR_NOT_NULL_FATAL(rsoc); usage_data_dpofua = rsoc->cdb_usage_data[1] & 0x18; if (dpofua) { logging(LOG_VERBOSE, "DPOFUA is set. Verify the " "DPO/FUA flags are set in the CDB_USAGE_DATA"); if (!usage_data_dpofua) { logging(LOG_NORMAL, "[FAILED] DpoFua not set " "in CDB_USAGE_DATE"); CU_FAIL("DpoFua not set in CDB_USAGE_DATE"); } } else { logging(LOG_VERBOSE, "DPOFUA is clear. Verify the " "DPO/FUA flags are clear in the CDB_USAGE_DATA"); if (usage_data_dpofua) { logging(LOG_NORMAL, "[FAILED] DpoFua not clear " "in CDB_USAGE_DATE"); CU_FAIL("DpoFua not clear in CDB_USAGE_DATE"); } } scsi_free_scsi_task(rso_task); } libiscsi-1.17.0/test-tool/test_write10_flags.c000066400000000000000000000040231271371262700212140ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg 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, see . */ #include #include #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-test-cu.h" void test_write10_flags(void) { CHECK_FOR_DATALOSS; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test WRITE10 flags"); logging(LOG_VERBOSE, "Test WRITE10 with DPO==1"); memset(scratch, 0xa6, block_size); WRITE10(sd, 0, block_size, block_size, 0, 1, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Test WRITE10 with FUA==1 FUA_NV==0"); WRITE10(sd, 0, block_size, block_size, 0, 0, 1, 0, 0, scratch, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Test WRITE10 with FUA==1 FUA_NV==1"); WRITE10(sd, 0, block_size, block_size, 0, 0, 1, 1, 0, scratch, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Test WRITE10 with FUA==0 FUA_NV==1"); WRITE10(sd, 0, block_size, block_size, 0, 0, 0, 1, 0, scratch, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Test WRITE10 with DPO==1 FUA==1 FUA_NV==1"); WRITE10(sd, 0, block_size, block_size, 0, 1, 1, 1, 0, scratch, EXPECT_STATUS_GOOD); } libiscsi-1.17.0/test-tool/test_write10_residuals.c000066400000000000000000000367611271371262700221310ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 by Ronnie Sahlberg 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, see . */ #include #include #include #include #include "iscsi.h" #include "iscsi-private.h" #include "scsi-lowlevel.h" #include "iscsi-test-cu.h" void test_write10_residuals(void) { struct scsi_task *task_ret; unsigned char buf[10000]; struct iscsi_data data; int ok; unsigned int i; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test WRITE10 commands with residuals"); logging(LOG_VERBOSE, "Block size is %zu", block_size); CHECK_FOR_DATALOSS; CHECK_FOR_SBC; if (sd->iscsi_ctx == NULL) { const char *err = "[SKIPPED] This WRITE10 test is only " "supported for iSCSI backends"; logging(LOG_NORMAL, "%s", err); CU_PASS(err); return; } /* Try a write10 of 1 block but xferlength == 0 */ task = malloc(sizeof(struct scsi_task)); CU_ASSERT_PTR_NOT_NULL_FATAL(task); memset(task, 0, sizeof(struct scsi_task)); task->cdb[0] = SCSI_OPCODE_WRITE10; task->cdb[8] = 1; task->cdb_size = 10; task->xfer_dir = SCSI_XFER_WRITE; task->expxferlen = 0; /* * we don't want autoreconnect since some targets will drop the session * on this condition. */ iscsi_set_noautoreconnect(sd->iscsi_ctx, 1); logging(LOG_VERBOSE, "Try writing one block but with iSCSI expected transfer length==0"); task_ret = iscsi_scsi_command_sync(sd->iscsi_ctx, sd->iscsi_lun, task, NULL); CU_ASSERT_PTR_NOT_NULL_FATAL(task_ret); CU_ASSERT_NOT_EQUAL(task->status, SCSI_STATUS_CANCELLED); /* XXX redundant? */ if (task->status == SCSI_STATUS_CHECK_CONDITION && task->sense.key == SCSI_SENSE_ILLEGAL_REQUEST && task->sense.ascq == SCSI_SENSE_ASCQ_INVALID_OPERATION_CODE) { logging(LOG_NORMAL, "[SKIPPED] WRITE10 is not implemented."); CU_PASS("WRITE10 is not implemented."); return; } logging(LOG_VERBOSE, "Verify that the target returned SUCCESS"); if (task->status != SCSI_STATUS_GOOD) { logging(LOG_VERBOSE, "[FAILED] Target returned error %s", iscsi_get_error(sd->iscsi_ctx)); } CU_ASSERT_EQUAL(task->status, SCSI_STATUS_GOOD); logging(LOG_VERBOSE, "Verify residual overflow flag is set"); if (task->residual_status != SCSI_RESIDUAL_OVERFLOW) { logging(LOG_VERBOSE, "[FAILED] Target did not set residual " "overflow flag"); } CU_ASSERT_EQUAL(task->residual_status, SCSI_RESIDUAL_OVERFLOW); logging(LOG_VERBOSE, "Verify we got %zu bytes of residual overflow", block_size); if (task->residual != block_size) { logging(LOG_VERBOSE, "[FAILED] Target did not set correct " "amount of residual. Expected %zu but got %zu.", block_size, task->residual); } CU_ASSERT_EQUAL(task->residual, block_size); scsi_free_scsi_task(task); task = NULL; /* in case the previous test failed the session */ iscsi_set_noautoreconnect(sd->iscsi_ctx, 0); logging(LOG_VERBOSE, "Try writing one block but with iSCSI expected transfer length==10000"); task = malloc(sizeof(struct scsi_task)); CU_ASSERT_PTR_NOT_NULL_FATAL(task); memset(task, 0, sizeof(struct scsi_task)); task->cdb[0] = SCSI_OPCODE_WRITE10; task->cdb[8] = 1; task->cdb_size = 10; task->xfer_dir = SCSI_XFER_WRITE; task->expxferlen = 10000; memset(buf, 0xa6, sizeof(buf)); data.size = task->expxferlen; data.data = &buf[0]; task_ret = iscsi_scsi_command_sync(sd->iscsi_ctx, sd->iscsi_lun, task, &data); CU_ASSERT_PTR_NOT_NULL_FATAL(task_ret); logging(LOG_VERBOSE, "Verify that the target returned SUCCESS"); if (task->status != SCSI_STATUS_GOOD) { logging(LOG_VERBOSE, "[FAILED] Target returned error %s", iscsi_get_error(sd->iscsi_ctx)); } CU_ASSERT_EQUAL(task->status, SCSI_STATUS_GOOD); logging(LOG_VERBOSE, "Verify residual underflow flag is set"); if (task->residual_status != SCSI_RESIDUAL_UNDERFLOW) { logging(LOG_VERBOSE, "[FAILED] Target did not set residual " "underflow flag"); } CU_ASSERT_EQUAL(task->residual_status, SCSI_RESIDUAL_UNDERFLOW); logging(LOG_VERBOSE, "Verify we got %zu bytes of residual underflow", 10000 - block_size); if (task->residual != 10000 - block_size) { logging(LOG_VERBOSE, "[FAILED] Target did not set correct " "amount of residual. Expected %zu but got %zu.", 10000 - block_size, task->residual); } CU_ASSERT_EQUAL(task->residual, 10000 - block_size); scsi_free_scsi_task(task); task = NULL; logging(LOG_VERBOSE, "Try writing one block but with iSCSI expected transfer length==200"); task = malloc(sizeof(struct scsi_task)); CU_ASSERT_PTR_NOT_NULL_FATAL(task); memset(task, 0, sizeof(struct scsi_task)); task->cdb[0] = SCSI_OPCODE_WRITE10; task->cdb[8] = 1; task->cdb_size = 10; task->xfer_dir = SCSI_XFER_WRITE; task->expxferlen = 200; data.size = task->expxferlen; data.data = &buf[0]; task_ret = iscsi_scsi_command_sync(sd->iscsi_ctx, sd->iscsi_lun, task, &data); CU_ASSERT_PTR_NOT_NULL_FATAL(task_ret); logging(LOG_VERBOSE, "Verify that the target returned SUCCESS"); ok = task->status == SCSI_STATUS_GOOD || (task->status == SCSI_STATUS_CHECK_CONDITION && task->sense.key == SCSI_SENSE_ILLEGAL_REQUEST && task->sense.ascq == SCSI_SENSE_ASCQ_INVALID_FIELD_IN_INFORMATION_UNIT); if (!ok) { logging(LOG_VERBOSE, "[FAILED] Target returned error %s", iscsi_get_error(sd->iscsi_ctx)); } CU_ASSERT(ok); logging(LOG_VERBOSE, "Verify residual overflow flag is set"); if (task->residual_status != SCSI_RESIDUAL_OVERFLOW) { logging(LOG_VERBOSE, "[FAILED] Target did not set residual " "overflow flag"); } CU_ASSERT_EQUAL(task->residual_status, SCSI_RESIDUAL_OVERFLOW); logging(LOG_VERBOSE, "Verify we got %zu bytes of residual overflow", block_size - 200); if (task->residual != block_size - 200) { logging(LOG_VERBOSE, "[FAILED] Target did not set correct " "amount of residual. Expected %zu but got %zu.", block_size - 200, task->residual); } CU_ASSERT_EQUAL(task->residual, block_size - 200); scsi_free_scsi_task(task); task = NULL; logging(LOG_VERBOSE, "Try writing two blocks but iSCSI expected " "transfer length==%zu (==one block)", block_size); task = malloc(sizeof(struct scsi_task)); CU_ASSERT_PTR_NOT_NULL_FATAL(task); memset(task, 0, sizeof(struct scsi_task)); task->cdb[0] = SCSI_OPCODE_WRITE10; task->cdb[8] = 2; task->cdb_size = 10; task->xfer_dir = SCSI_XFER_WRITE; task->expxferlen = block_size; data.size = task->expxferlen; data.data = &buf[0]; task_ret = iscsi_scsi_command_sync(sd->iscsi_ctx, sd->iscsi_lun, task, &data); CU_ASSERT_PTR_NOT_NULL_FATAL(task_ret); logging(LOG_VERBOSE, "Verify that the target returned SUCCESS"); if (task->status != SCSI_STATUS_GOOD) { logging(LOG_VERBOSE, "[FAILED] Target returned error %s", iscsi_get_error(sd->iscsi_ctx)); } CU_ASSERT_EQUAL(task->status, SCSI_STATUS_GOOD); logging(LOG_VERBOSE, "Verify residual overflow flag is set"); if (task->residual_status != SCSI_RESIDUAL_OVERFLOW) { logging(LOG_VERBOSE, "[FAILED] Target did not set residual " "overflow flag"); } CU_ASSERT_EQUAL(task->residual_status, SCSI_RESIDUAL_OVERFLOW); logging(LOG_VERBOSE, "Verify we got one block of residual overflow"); if (task->residual != block_size) { logging(LOG_VERBOSE, "[FAILED] Target did not set correct " "amount of residual. Expected %zu but got %zu.", block_size, task->residual); } CU_ASSERT_EQUAL(task->residual, block_size); scsi_free_scsi_task(task); task = NULL; logging(LOG_VERBOSE, "Verify that if iSCSI EDTL > SCSI TL then we only write SCSI TL amount of data"); logging(LOG_VERBOSE, "Write two blocks of 'a'"); memset(buf, 'a', 10000); WRITE10(sd, 0, 2 * block_size, block_size, 0, 0, 0, 0, 0, buf, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Write one block of 'b' but set iSCSI EDTL to 2 blocks."); task = malloc(sizeof(struct scsi_task)); CU_ASSERT_PTR_NOT_NULL_FATAL(task); memset(buf, 'b', 10000); memset(task, 0, sizeof(struct scsi_task)); task->cdb[0] = SCSI_OPCODE_WRITE10; task->cdb[8] = 1; task->cdb_size = 10; task->xfer_dir = SCSI_XFER_WRITE; task->expxferlen = 2 * block_size; data.size = task->expxferlen; data.data = &buf[0]; task_ret = iscsi_scsi_command_sync(sd->iscsi_ctx, sd->iscsi_lun, task, &data); CU_ASSERT_PTR_NOT_NULL_FATAL(task_ret); logging(LOG_VERBOSE, "Verify that the target returned SUCCESS"); if (task->status != SCSI_STATUS_GOOD) { logging(LOG_VERBOSE, "[FAILED] Target returned error %s", iscsi_get_error(sd->iscsi_ctx)); } CU_ASSERT_EQUAL(task->status, SCSI_STATUS_GOOD); logging(LOG_VERBOSE, "Verify residual underflow flag is set"); if (task->residual_status != SCSI_RESIDUAL_UNDERFLOW) { logging(LOG_VERBOSE, "[FAILED] Target did not set residual " "underflow flag"); } CU_ASSERT_EQUAL(task->residual_status, SCSI_RESIDUAL_UNDERFLOW); logging(LOG_VERBOSE, "Verify we got one block of residual underflow"); if (task->residual != block_size) { logging(LOG_VERBOSE, "[FAILED] Target did not set correct " "amount of residual. Expected %zu but got %zu.", block_size, task->residual); } CU_ASSERT_EQUAL(task->residual, block_size); scsi_free_scsi_task(task); task = NULL; logging(LOG_VERBOSE, "Read the two blocks"); READ10(sd, NULL, 0, 2* block_size, block_size, 0, 0, 0, 0, 0, buf, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Verify that the first block was changed to 'b'"); for (i = 0; i < block_size; i++) { if (buf[i] != 'b') { logging(LOG_NORMAL, "First block did not contain expected 'b'"); CU_FAIL("Block was not written correctly"); break; } } logging(LOG_VERBOSE, "Verify that the second block was NOT overwritten and still contains 'a'"); for (i = block_size; i < 2 * block_size; i++) { if (buf[i] != 'a') { logging(LOG_NORMAL, "Second block was overwritten and no longer contain 'a'"); CU_FAIL("Second block was incorrectly overwritten"); break; } } logging(LOG_VERBOSE, "Verify that if iSCSI EDTL < SCSI TL then we only write iSCSI EDTL amount of data"); logging(LOG_VERBOSE, "Write two blocks of 'a'"); memset(buf, 'a', 10000); WRITE10(sd, 0, 2 * block_size, block_size, 0, 0, 0, 0, 0, buf, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Write two blocks of 'b' but set iSCSI EDTL to 1 blocks."); task = malloc(sizeof(struct scsi_task)); CU_ASSERT_PTR_NOT_NULL_FATAL(task); memset(buf, 'b', 10000); memset(task, 0, sizeof(struct scsi_task)); task->cdb[0] = SCSI_OPCODE_WRITE10; task->cdb[8] = 2; task->cdb_size = 10; task->xfer_dir = SCSI_XFER_WRITE; task->expxferlen = block_size; data.size = task->expxferlen; data.data = &buf[0]; task_ret = iscsi_scsi_command_sync(sd->iscsi_ctx, sd->iscsi_lun, task, &data); CU_ASSERT_PTR_NOT_NULL_FATAL(task_ret); logging(LOG_VERBOSE, "Verify that the target returned SUCCESS"); if (task->status != SCSI_STATUS_GOOD) { logging(LOG_VERBOSE, "[FAILED] Target returned error %s", iscsi_get_error(sd->iscsi_ctx)); } CU_ASSERT_EQUAL(task->status, SCSI_STATUS_GOOD); logging(LOG_VERBOSE, "Verify residual overflow flag is set"); if (task->residual_status != SCSI_RESIDUAL_OVERFLOW) { logging(LOG_VERBOSE, "[FAILED] Target did not set residual " "overflow flag"); } CU_ASSERT_EQUAL(task->residual_status, SCSI_RESIDUAL_OVERFLOW); logging(LOG_VERBOSE, "Verify we got one block of residual overflow"); if (task->residual != block_size) { logging(LOG_VERBOSE, "[FAILED] Target did not set correct " "amount of residual. Expected %zu but got %zu.", block_size, task->residual); } CU_ASSERT_EQUAL(task->residual, block_size); scsi_free_scsi_task(task); task = NULL; logging(LOG_VERBOSE, "Read the two blocks"); READ10(sd, NULL, 0, 2* block_size, block_size, 0, 0, 0, 0, 0, buf, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Verify that the first block was changed to 'b'"); for (i = 0; i < block_size; i++) { if (buf[i] != 'b') { logging(LOG_NORMAL, "First block did not contain expected 'b'"); CU_FAIL("Block was not written correctly"); break; } } logging(LOG_VERBOSE, "Verify that the second block was NOT overwritten and still contains 'a'"); for (i = block_size; i < 2 * block_size; i++) { if (buf[i] != 'a') { logging(LOG_NORMAL, "Second block was overwritten and no longer contain 'a'"); CU_FAIL("Second block was incorrectly overwritten"); break; } } } libiscsi-1.17.0/test-tool/test_write10_simple.c000066400000000000000000000051001271371262700214060ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg 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, see . */ #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-support.h" #include "iscsi-test-cu.h" void test_write10_simple(void) { int i; uint32_t lba; CHECK_FOR_DATALOSS; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test WRITE10 of 1-256 blocks at the start of the LUN"); memset(scratch, 0xa6, 256 * block_size); for (i = 1; i <= 256; i++) { if (maximum_transfer_length && maximum_transfer_length < i) { break; } WRITE10(sd, 0, i * block_size, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); } logging(LOG_VERBOSE, "Test WRITE10 of 1-256 blocks at the end of the LUN"); for (i = 1; i <= 256; i++) { if (maximum_transfer_length && maximum_transfer_length < i) { break; } WRITE10(sd, num_blocks - i, i * block_size, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); } lba = ((4 * 1024 * 1024) / block_size) - 3; if (num_blocks > (lba + 256)) { logging(LOG_VERBOSE, "Test WRITE10 of 1-256 blocks at ~4MB offset"); for (i = 1; i <= 256; i++) { if (maximum_transfer_length && maximum_transfer_length < i) { break; } WRITE10(sd, lba, i * block_size, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); } } } libiscsi-1.17.0/test-tool/test_write10_wrprotect.c000066400000000000000000000035341271371262700221570ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg 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, see . */ #include #include #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-test-cu.h" void test_write10_wrprotect(void) { int i; /* * Try out different non-zero values for WRPROTECT. */ logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test WRITE10 with non-zero WRPROTECT"); CHECK_FOR_DATALOSS; CHECK_FOR_SBC; memset(scratch, 0xa6, block_size); if (!inq->protect || (rc16 != NULL && !rc16->prot_en)) { logging(LOG_VERBOSE, "Device does not support/use protection information. All commands should fail."); for (i = 1; i < 8; i++) { WRITE10(sd, 0, block_size, block_size, i, 0, 0, 0, 0, scratch, EXPECT_INVALID_FIELD_IN_CDB); } return; } logging(LOG_NORMAL, "No tests for devices that support protection information yet."); } libiscsi-1.17.0/test-tool/test_write12_0blocks.c000066400000000000000000000035351271371262700214660ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg 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, see . */ #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-test-cu.h" void test_write12_0blocks(void) { CHECK_FOR_DATALOSS; if (num_blocks >= 0x80000000) { CU_PASS("LUN is too big for read-beyond-eol tests with WRITE12. Skipping test.\n"); return; } logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test WRITE12 0-blocks at LBA==0"); WRITE12(sd, 0, 0, block_size, 0, 0, 0, 0, 0, NULL, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Test WRITE12 0-blocks one block past end-of-LUN"); WRITE12(sd, num_blocks + 1, 0, block_size, 0, 0, 0, 0, 0, NULL, EXPECT_LBA_OOB); logging(LOG_VERBOSE, "Test WRITE12 0-blocks at LBA==2^31"); WRITE12(sd, 0x80000000, 0, block_size, 0, 0, 0, 0, 0, NULL, EXPECT_LBA_OOB); logging(LOG_VERBOSE, "Test WRITE12 0-blocks at LBA==-1"); WRITE12(sd, -1, 0, block_size, 0, 0, 0, 0, 0, NULL, EXPECT_LBA_OOB); } libiscsi-1.17.0/test-tool/test_write12_beyond_eol.c000066400000000000000000000056031271371262700222460ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg 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, see . */ #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-test-cu.h" void test_write12_beyond_eol(void) { int i; CHECK_FOR_DATALOSS; if (num_blocks >= 0x80000000) { CU_PASS("LUN is too big for write-beyond-eol tests with WRITE12. Skipping test.\n"); return; } logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test WRITE12 1-256 blocks one block beyond the end"); memset(scratch, 0xa6, 256 * block_size); for (i = 1; i <= 256; i++) { if (maximum_transfer_length && maximum_transfer_length < i) { break; } WRITE12(sd, num_blocks + 1 - i, i * block_size, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_LBA_OOB); } logging(LOG_VERBOSE, "Test WRITE12 1-256 blocks at LBA==2^31"); for (i = 1; i <= 256; i++) { if (maximum_transfer_length && maximum_transfer_length < i) { break; } WRITE12(sd, 0x80000000, i * block_size, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_LBA_OOB); } logging(LOG_VERBOSE, "Test WRITE12 1-256 blocks at LBA==-1"); for (i = 1; i <= 256; i++) { if (maximum_transfer_length && maximum_transfer_length < i) { break; } WRITE12(sd, -1, i * block_size, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_LBA_OOB); } logging(LOG_VERBOSE, "Test WRITE12 2-256 blocks all but one block beyond the end"); for (i = 2; i <= 256; i++) { if (maximum_transfer_length && maximum_transfer_length < i) { break; } WRITE12(sd, num_blocks - 1, i * block_size, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_LBA_OOB); } } libiscsi-1.17.0/test-tool/test_write12_dpofua.c000066400000000000000000000112721271371262700214040ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2014 by Ronnie Sahlberg 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, see . */ #include #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-test-cu.h" void test_write12_dpofua(void) { int dpofua, usage_data_dpofua; struct scsi_task *ms_task = NULL; struct scsi_mode_sense *ms; struct scsi_task *rso_task = NULL; struct scsi_report_supported_op_codes_one_command *rsoc; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test WRITE12 DPO/FUA flags"); CHECK_FOR_SBC; CHECK_FOR_DATALOSS; logging(LOG_VERBOSE, "Read the DPOFUA flag from mode sense data"); MODESENSE6(sd, &ms_task, 0, SCSI_MODESENSE_PC_CURRENT, SCSI_MODEPAGE_RETURN_ALL_PAGES, 0, 255, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "[SUCCESS] Mode sense returned status GOOD"); ms = scsi_datain_unmarshall(ms_task); dpofua = ms && (ms->device_specific_parameter & 0x10); scsi_free_scsi_task(ms_task); if (dpofua) { logging(LOG_VERBOSE, "DPOFUA flag is set. Device should allow " "DPO/FUA flags in CDBs"); } else { logging(LOG_VERBOSE, "DPOFUA flag is clear. Device should fail " "CDBs with DPO/FUA set"); } logging(LOG_VERBOSE, "Test WRITE12 with DPO==1"); memset(scratch, 0xa6, block_size); if (dpofua) { WRITE12(sd, 0, block_size, block_size, 0, 1, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); } else { WRITE12(sd, 0, block_size, block_size, 0, 1, 0, 0, 0, scratch, EXPECT_INVALID_FIELD_IN_CDB); } logging(LOG_VERBOSE, "Test WRITE12 with FUA==1"); if (dpofua) { WRITE12(sd, 0, block_size, block_size, 0, 0, 1, 0, 0, scratch, EXPECT_STATUS_GOOD); } else { WRITE12(sd, 0, block_size, block_size, 0, 0, 1, 0, 0, scratch, EXPECT_INVALID_FIELD_IN_CDB); } logging(LOG_VERBOSE, "Test WRITE12 with DPO==1 FUA==1"); if (dpofua) { WRITE12(sd, 0, block_size, block_size, 0, 1, 1, 0, 0, scratch, EXPECT_STATUS_GOOD); } else { WRITE12(sd, 0, block_size, block_size, 0, 1, 1, 0, 0, scratch, EXPECT_INVALID_FIELD_IN_CDB); } logging(LOG_VERBOSE, "Try fetching REPORT_SUPPORTED_OPCODES " "for WRITE12"); REPORT_SUPPORTED_OPCODES(sd, &rso_task, 0, SCSI_REPORT_SUPPORTING_OPCODE, SCSI_OPCODE_WRITE12, 0, 65535, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Unmarshall the DATA-IN buffer"); rsoc = scsi_datain_unmarshall(rso_task); CU_ASSERT_PTR_NOT_NULL_FATAL(rsoc); usage_data_dpofua = rsoc->cdb_usage_data[1] & 0x18; if (dpofua) { logging(LOG_VERBOSE, "DPOFUA is set. Verify the " "DPO/FUA flags are set in the CDB_USAGE_DATA"); if (!usage_data_dpofua) { logging(LOG_NORMAL, "[FAILED] DpoFua not set " "in CDB_USAGE_DATE"); CU_FAIL("DpoFua not set in CDB_USAGE_DATE"); } } else { logging(LOG_VERBOSE, "DPOFUA is clear. Verify the " "DPO/FUA flags are clear in the CDB_USAGE_DATA"); if (usage_data_dpofua) { logging(LOG_NORMAL, "[FAILED] DpoFua not clear " "in CDB_USAGE_DATE"); CU_FAIL("DpoFua not clear in CDB_USAGE_DATE"); } } scsi_free_scsi_task(rso_task); } libiscsi-1.17.0/test-tool/test_write12_flags.c000066400000000000000000000040231271371262700212160ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg 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, see . */ #include #include #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-test-cu.h" void test_write12_flags(void) { CHECK_FOR_DATALOSS; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test WRITE12 flags"); logging(LOG_VERBOSE, "Test WRITE12 with DPO==1"); memset(scratch, 0xa6, block_size); WRITE12(sd, 0, block_size, block_size, 0, 1, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Test WRITE12 with FUA==1 FUA_NV==0"); WRITE12(sd, 0, block_size, block_size, 0, 0, 1, 0, 0, scratch, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Test WRITE12 with FUA==1 FUA_NV==1"); WRITE12(sd, 0, block_size, block_size, 0, 0, 1, 1, 0, scratch, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Test WRITE12 with FUA==0 FUA_NV==1"); WRITE12(sd, 0, block_size, block_size, 0, 0, 0, 1, 0, scratch, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Test WRITE12 with DPO==1 FUA==1 FUA_NV==1"); WRITE12(sd, 0, block_size, block_size, 0, 1, 1, 1, 0, scratch, EXPECT_STATUS_GOOD); } libiscsi-1.17.0/test-tool/test_write12_residuals.c000066400000000000000000000367601271371262700221320ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 by Ronnie Sahlberg 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, see . */ #include #include #include #include #include "iscsi.h" #include "iscsi-private.h" #include "scsi-lowlevel.h" #include "iscsi-test-cu.h" void test_write12_residuals(void) { struct scsi_task *task_ret; unsigned char buf[10000]; struct iscsi_data data; int ok; unsigned int i; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test WRITE12 commands with residuals"); logging(LOG_VERBOSE, "Block size is %zu", block_size); CHECK_FOR_DATALOSS; CHECK_FOR_SBC; if (sd->iscsi_ctx == NULL) { const char *err = "[SKIPPED] This WRITE12 test is only " "supported for iSCSI backends"; logging(LOG_NORMAL, "%s", err); CU_PASS(err); return; } /* Try a write12 of 1 block but xferlength == 0 */ task = malloc(sizeof(struct scsi_task)); CU_ASSERT_PTR_NOT_NULL_FATAL(task); memset(task, 0, sizeof(struct scsi_task)); task->cdb[0] = SCSI_OPCODE_WRITE12; task->cdb[9] = 1; task->cdb_size = 12; task->xfer_dir = SCSI_XFER_WRITE; task->expxferlen = 0; /* * we don't want autoreconnect since some targets will drop the session * on this condition. */ iscsi_set_noautoreconnect(sd->iscsi_ctx, 1); logging(LOG_VERBOSE, "Try writing one block but with iSCSI expected transfer length==0"); task_ret = iscsi_scsi_command_sync(sd->iscsi_ctx, sd->iscsi_lun, task, NULL); CU_ASSERT_PTR_NOT_NULL_FATAL(task_ret); CU_ASSERT_NOT_EQUAL(task->status, SCSI_STATUS_CANCELLED); /* XXX redundant? */ if (task->status == SCSI_STATUS_CHECK_CONDITION && task->sense.key == SCSI_SENSE_ILLEGAL_REQUEST && task->sense.ascq == SCSI_SENSE_ASCQ_INVALID_OPERATION_CODE) { logging(LOG_NORMAL, "[SKIPPED] WRITE12 is not implemented."); CU_PASS("WRITE12 is not implemented."); return; } logging(LOG_VERBOSE, "Verify that the target returned SUCCESS"); if (task->status != SCSI_STATUS_GOOD) { logging(LOG_VERBOSE, "[FAILED] Target returned error %s", iscsi_get_error(sd->iscsi_ctx)); } CU_ASSERT_EQUAL(task->status, SCSI_STATUS_GOOD); logging(LOG_VERBOSE, "Verify residual overflow flag is set"); if (task->residual_status != SCSI_RESIDUAL_OVERFLOW) { logging(LOG_VERBOSE, "[FAILED] Target did not set residual " "overflow flag"); } CU_ASSERT_EQUAL(task->residual_status, SCSI_RESIDUAL_OVERFLOW); logging(LOG_VERBOSE, "Verify we got %zu bytes of residual overflow", block_size); if (task->residual != block_size) { logging(LOG_VERBOSE, "[FAILED] Target did not set correct " "amount of residual. Expected %zu but got %zu.", block_size, task->residual); } CU_ASSERT_EQUAL(task->residual, block_size); scsi_free_scsi_task(task); task = NULL; /* in case the previous test failed the session */ iscsi_set_noautoreconnect(sd->iscsi_ctx, 0); logging(LOG_VERBOSE, "Try writing one block but with iSCSI expected transfer length==10000"); task = malloc(sizeof(struct scsi_task)); CU_ASSERT_PTR_NOT_NULL_FATAL(task); memset(task, 0, sizeof(struct scsi_task)); task->cdb[0] = SCSI_OPCODE_WRITE12; task->cdb[9] = 1; task->cdb_size = 12; task->xfer_dir = SCSI_XFER_WRITE; task->expxferlen = 10000; memset(buf, 0xa6, sizeof(buf)); data.size = task->expxferlen; data.data = &buf[0]; task_ret = iscsi_scsi_command_sync(sd->iscsi_ctx, sd->iscsi_lun, task, &data); CU_ASSERT_PTR_NOT_NULL_FATAL(task_ret); logging(LOG_VERBOSE, "Verify that the target returned SUCCESS"); if (task->status != SCSI_STATUS_GOOD) { logging(LOG_VERBOSE, "[FAILED] Target returned error %s", iscsi_get_error(sd->iscsi_ctx)); } CU_ASSERT_EQUAL(task->status, SCSI_STATUS_GOOD); logging(LOG_VERBOSE, "Verify residual underflow flag is set"); if (task->residual_status != SCSI_RESIDUAL_UNDERFLOW) { logging(LOG_VERBOSE, "[FAILED] Target did not set residual " "underflow flag"); } CU_ASSERT_EQUAL(task->residual_status, SCSI_RESIDUAL_UNDERFLOW); logging(LOG_VERBOSE, "Verify we got %zu bytes of residual underflow", 10000 - block_size); if (task->residual != 10000 - block_size) { logging(LOG_VERBOSE, "[FAILED] Target did not set correct " "amount of residual. Expected %zu but got %zu.", 10000 - block_size, task->residual); } CU_ASSERT_EQUAL(task->residual, 10000 - block_size); scsi_free_scsi_task(task); task = NULL; logging(LOG_VERBOSE, "Try writing one block but with iSCSI expected transfer length==200"); task = malloc(sizeof(struct scsi_task)); CU_ASSERT_PTR_NOT_NULL_FATAL(task); memset(task, 0, sizeof(struct scsi_task)); task->cdb[0] = SCSI_OPCODE_WRITE12; task->cdb[9] = 1; task->cdb_size = 12; task->xfer_dir = SCSI_XFER_WRITE; task->expxferlen = 200; data.size = task->expxferlen; data.data = &buf[0]; task_ret = iscsi_scsi_command_sync(sd->iscsi_ctx, sd->iscsi_lun, task, &data); CU_ASSERT_PTR_NOT_NULL_FATAL(task_ret); logging(LOG_VERBOSE, "Verify that the target returned SUCCESS"); ok = task->status == SCSI_STATUS_GOOD || (task->status == SCSI_STATUS_CHECK_CONDITION && task->sense.key == SCSI_SENSE_ILLEGAL_REQUEST && task->sense.ascq == SCSI_SENSE_ASCQ_INVALID_FIELD_IN_INFORMATION_UNIT); if (!ok) { logging(LOG_VERBOSE, "[FAILED] Target returned error %s", iscsi_get_error(sd->iscsi_ctx)); } CU_ASSERT(ok); logging(LOG_VERBOSE, "Verify residual overflow flag is set"); if (task->residual_status != SCSI_RESIDUAL_OVERFLOW) { logging(LOG_VERBOSE, "[FAILED] Target did not set residual " "overflow flag"); } CU_ASSERT_EQUAL(task->residual_status, SCSI_RESIDUAL_OVERFLOW); logging(LOG_VERBOSE, "Verify we got %zu bytes of residual overflow", block_size - 200); if (task->residual != block_size - 200) { logging(LOG_VERBOSE, "[FAILED] Target did not set correct " "amount of residual. Expected %zu but got %zu.", block_size - 200, task->residual); } CU_ASSERT_EQUAL(task->residual, block_size - 200); scsi_free_scsi_task(task); task = NULL; logging(LOG_VERBOSE, "Try writing two blocks but iSCSI expected " "transfer length==%zu (==one block)", block_size); task = malloc(sizeof(struct scsi_task)); CU_ASSERT_PTR_NOT_NULL_FATAL(task); memset(task, 0, sizeof(struct scsi_task)); task->cdb[0] = SCSI_OPCODE_WRITE12; task->cdb[9] = 2; task->cdb_size = 12; task->xfer_dir = SCSI_XFER_WRITE; task->expxferlen = block_size; data.size = task->expxferlen; data.data = &buf[0]; task_ret = iscsi_scsi_command_sync(sd->iscsi_ctx, sd->iscsi_lun, task, &data); CU_ASSERT_PTR_NOT_NULL_FATAL(task_ret); logging(LOG_VERBOSE, "Verify that the target returned SUCCESS"); if (task->status != SCSI_STATUS_GOOD) { logging(LOG_VERBOSE, "[FAILED] Target returned error %s", iscsi_get_error(sd->iscsi_ctx)); } CU_ASSERT_EQUAL(task->status, SCSI_STATUS_GOOD); logging(LOG_VERBOSE, "Verify residual overflow flag is set"); if (task->residual_status != SCSI_RESIDUAL_OVERFLOW) { logging(LOG_VERBOSE, "[FAILED] Target did not set residual " "overflow flag"); } CU_ASSERT_EQUAL(task->residual_status, SCSI_RESIDUAL_OVERFLOW); logging(LOG_VERBOSE, "Verify we got one block of residual overflow"); if (task->residual != block_size) { logging(LOG_VERBOSE, "[FAILED] Target did not set correct " "amount of residual. Expected %zu but got %zu.", block_size, task->residual); } CU_ASSERT_EQUAL(task->residual, block_size); scsi_free_scsi_task(task); task = NULL; logging(LOG_VERBOSE, "Verify that if iSCSI EDTL > SCSI TL then we only write SCSI TL amount of data"); logging(LOG_VERBOSE, "Write two blocks of 'a'"); memset(buf, 'a', 10000); WRITE12(sd, 0, 2 * block_size, block_size, 0, 0, 0, 0, 0, buf, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Write one block of 'b' but set iSCSI EDTL to 2 blocks."); task = malloc(sizeof(struct scsi_task)); CU_ASSERT_PTR_NOT_NULL_FATAL(task); memset(buf, 'b', 10000); memset(task, 0, sizeof(struct scsi_task)); task->cdb[0] = SCSI_OPCODE_WRITE12; task->cdb[9] = 1; task->cdb_size = 12; task->xfer_dir = SCSI_XFER_WRITE; task->expxferlen = 2 * block_size; data.size = task->expxferlen; data.data = &buf[0]; task_ret = iscsi_scsi_command_sync(sd->iscsi_ctx, sd->iscsi_lun, task, &data); CU_ASSERT_PTR_NOT_NULL_FATAL(task_ret); logging(LOG_VERBOSE, "Verify that the target returned SUCCESS"); if (task->status != SCSI_STATUS_GOOD) { logging(LOG_VERBOSE, "[FAILED] Target returned error %s", iscsi_get_error(sd->iscsi_ctx)); } CU_ASSERT_EQUAL(task->status, SCSI_STATUS_GOOD); logging(LOG_VERBOSE, "Verify residual underflow flag is set"); if (task->residual_status != SCSI_RESIDUAL_UNDERFLOW) { logging(LOG_VERBOSE, "[FAILED] Target did not set residual " "underflow flag"); } CU_ASSERT_EQUAL(task->residual_status, SCSI_RESIDUAL_UNDERFLOW); logging(LOG_VERBOSE, "Verify we got one block of residual underflow"); if (task->residual != block_size) { logging(LOG_VERBOSE, "[FAILED] Target did not set correct " "amount of residual. Expected %zu but got %zu.", block_size, task->residual); } CU_ASSERT_EQUAL(task->residual, block_size); scsi_free_scsi_task(task); task = NULL; logging(LOG_VERBOSE, "Read the two blocks"); READ12(sd, NULL, 0, 2* block_size, block_size, 0, 0, 0, 0, 0, buf, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Verify that the first block was changed to 'b'"); for (i = 0; i < block_size; i++) { if (buf[i] != 'b') { logging(LOG_NORMAL, "First block did not contain expected 'b'"); CU_FAIL("Block was not written correctly"); break; } } logging(LOG_VERBOSE, "Verify that the second block was NOT overwritten and still contains 'a'"); for (i = block_size; i < 2 * block_size; i++) { if (buf[i] != 'a') { logging(LOG_NORMAL, "Second block was overwritten and no longer contain 'a'"); CU_FAIL("Second block was incorrectly overwritten"); break; } } logging(LOG_VERBOSE, "Verify that if iSCSI EDTL < SCSI TL then we only write iSCSI EDTL amount of data"); logging(LOG_VERBOSE, "Write two blocks of 'a'"); memset(buf, 'a', 10000); WRITE12(sd, 0, 2 * block_size, block_size, 0, 0, 0, 0, 0, buf, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Write two blocks of 'b' but set iSCSI EDTL to 1 blocks."); task = malloc(sizeof(struct scsi_task)); CU_ASSERT_PTR_NOT_NULL_FATAL(task); memset(buf, 'b', 10000); memset(task, 0, sizeof(struct scsi_task)); task->cdb[0] = SCSI_OPCODE_WRITE12; task->cdb[9] = 2; task->cdb_size = 12; task->xfer_dir = SCSI_XFER_WRITE; task->expxferlen = block_size; data.size = task->expxferlen; data.data = &buf[0]; task_ret = iscsi_scsi_command_sync(sd->iscsi_ctx, sd->iscsi_lun, task, &data); CU_ASSERT_PTR_NOT_NULL_FATAL(task_ret); logging(LOG_VERBOSE, "Verify that the target returned SUCCESS"); if (task->status != SCSI_STATUS_GOOD) { logging(LOG_VERBOSE, "[FAILED] Target returned error %s", iscsi_get_error(sd->iscsi_ctx)); } CU_ASSERT_EQUAL(task->status, SCSI_STATUS_GOOD); logging(LOG_VERBOSE, "Verify residual overflow flag is set"); if (task->residual_status != SCSI_RESIDUAL_OVERFLOW) { logging(LOG_VERBOSE, "[FAILED] Target did not set residual " "overflow flag"); } CU_ASSERT_EQUAL(task->residual_status, SCSI_RESIDUAL_OVERFLOW); logging(LOG_VERBOSE, "Verify we got one block of residual overflow"); if (task->residual != block_size) { logging(LOG_VERBOSE, "[FAILED] Target did not set correct " "amount of residual. Expected %zu but got %zu.", block_size, task->residual); } CU_ASSERT_EQUAL(task->residual, block_size); scsi_free_scsi_task(task); task = NULL; logging(LOG_VERBOSE, "Read the two blocks"); READ12(sd, NULL, 0, 2* block_size, block_size, 0, 0, 0, 0, 0, buf, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Verify that the first block was changed to 'b'"); for (i = 0; i < block_size; i++) { if (buf[i] != 'b') { logging(LOG_NORMAL, "First block did not contain expected 'b'"); CU_FAIL("Block was not written correctly"); break; } } logging(LOG_VERBOSE, "Verify that the second block was NOT overwritten and still contains 'a'"); for (i = block_size; i < 2 * block_size; i++) { if (buf[i] != 'a') { logging(LOG_NORMAL, "Second block was overwritten and no longer contain 'a'"); CU_FAIL("Second block was incorrectly overwritten"); break; } } } libiscsi-1.17.0/test-tool/test_write12_simple.c000066400000000000000000000037041271371262700214200ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg 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, see . */ #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-support.h" #include "iscsi-test-cu.h" void test_write12_simple(void) { int i; CHECK_FOR_DATALOSS; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test WRITE12 of 1-256 blocks at the start of the LUN"); memset(scratch, 0xa6, 256 * block_size); for (i = 1; i <= 256; i++) { if (maximum_transfer_length && maximum_transfer_length < i) { break; } WRITE12(sd, 0, i * block_size, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); } logging(LOG_VERBOSE, "Test WRITE12 of 1-256 blocks at the end of the LUN"); for (i = 1; i <= 256; i++) { if (maximum_transfer_length && maximum_transfer_length < i) { break; } WRITE12(sd, num_blocks - i, i * block_size, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); } } libiscsi-1.17.0/test-tool/test_write12_wrprotect.c000066400000000000000000000035341271371262700221610ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg 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, see . */ #include #include #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-test-cu.h" void test_write12_wrprotect(void) { int i; /* * Try out different non-zero values for WRPROTECT. */ logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test WRITE12 with non-zero WRPROTECT"); CHECK_FOR_DATALOSS; CHECK_FOR_SBC; memset(scratch, 0xa6, block_size); if (!inq->protect || (rc16 != NULL && !rc16->prot_en)) { logging(LOG_VERBOSE, "Device does not support/use protection information. All commands should fail."); for (i = 1; i < 8; i++) { WRITE12(sd, 0, block_size, block_size, i, 0, 0, 0, 0, scratch, EXPECT_INVALID_FIELD_IN_CDB); } return; } logging(LOG_NORMAL, "No tests for devices that support protection information yet."); } libiscsi-1.17.0/test-tool/test_write16_0blocks.c000066400000000000000000000033201271371262700214620ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg 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, see . */ #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-test-cu.h" void test_write16_0blocks(void) { CHECK_FOR_DATALOSS; CHECK_FOR_SBC; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test WRITE16 0-blocks at LBA==0"); WRITE16(sd, 0, 0, block_size, 0, 0, 0, 0, 0, NULL, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Test WRITE16 0-blocks one block past end-of-LUN"); WRITE16(sd, num_blocks + 1, 0, block_size, 0, 0, 0, 0, 0, NULL, EXPECT_LBA_OOB); logging(LOG_VERBOSE, "Test WRITE16 0-blocks at LBA==2^63"); WRITE16(sd, 0x8000000000000000ULL, 0, block_size, 0, 0, 0, 0, 0, NULL, EXPECT_LBA_OOB); logging(LOG_VERBOSE, "Test WRITE16 0-blocks at LBA==-1"); WRITE16(sd, -1, 0, block_size, 0, 0, 0, 0, 0, NULL, EXPECT_LBA_OOB); } libiscsi-1.17.0/test-tool/test_write16_beyond_eol.c000066400000000000000000000053751271371262700222600ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg 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, see . */ #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-test-cu.h" void test_write16_beyond_eol(void) { int i; CHECK_FOR_DATALOSS; CHECK_FOR_SBC; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test WRITE16 1-256 blocks one block beyond the end"); memset(scratch, 0xa6, 256 * block_size); for (i = 1; i <= 256; i++) { if (maximum_transfer_length && maximum_transfer_length < i) { break; } WRITE16(sd, num_blocks + 1 - i, i * block_size, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_LBA_OOB); } logging(LOG_VERBOSE, "Test WRITE16 1-256 blocks at LBA==2^63"); for (i = 1; i <= 256; i++) { if (maximum_transfer_length && maximum_transfer_length < i) { break; } WRITE16(sd, 0x8000000000000000ULL, i * block_size, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_LBA_OOB); } logging(LOG_VERBOSE, "Test WRITE16 1-256 blocks at LBA==-1"); for (i = 1; i <= 256; i++) { if (maximum_transfer_length && maximum_transfer_length < i) { break; } WRITE16(sd, -1, i * block_size, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_LBA_OOB); } logging(LOG_VERBOSE, "Test WRITE16 2-256 blocks all but one block beyond the end"); for (i = 2; i <= 256; i++) { if (maximum_transfer_length && maximum_transfer_length < i) { break; } WRITE16(sd, num_blocks - 1, i * block_size, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_LBA_OOB); } } libiscsi-1.17.0/test-tool/test_write16_dpofua.c000066400000000000000000000112721271371262700214100ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2014 by Ronnie Sahlberg 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, see . */ #include #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-test-cu.h" void test_write16_dpofua(void) { int dpofua, usage_data_dpofua; struct scsi_task *ms_task = NULL; struct scsi_mode_sense *ms; struct scsi_task *rso_task = NULL; struct scsi_report_supported_op_codes_one_command *rsoc; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test WRITE16 DPO/FUA flags"); CHECK_FOR_SBC; CHECK_FOR_DATALOSS; logging(LOG_VERBOSE, "Read the DPOFUA flag from mode sense data"); MODESENSE6(sd, &ms_task, 0, SCSI_MODESENSE_PC_CURRENT, SCSI_MODEPAGE_RETURN_ALL_PAGES, 0, 255, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "[SUCCESS] Mode sense returned status GOOD"); ms = scsi_datain_unmarshall(ms_task); dpofua = ms && (ms->device_specific_parameter & 0x10); scsi_free_scsi_task(ms_task); if (dpofua) { logging(LOG_VERBOSE, "DPOFUA flag is set. Device should allow " "DPO/FUA flags in CDBs"); } else { logging(LOG_VERBOSE, "DPOFUA flag is clear. Device should fail " "CDBs with DPO/FUA set"); } logging(LOG_VERBOSE, "Test WRITE16 with DPO==1"); memset(scratch, 0xa6, block_size); if (dpofua) { WRITE16(sd, 0, block_size, block_size, 0, 1, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); } else { WRITE16(sd, 0, block_size, block_size, 0, 1, 0, 0, 0, scratch, EXPECT_INVALID_FIELD_IN_CDB); } logging(LOG_VERBOSE, "Test WRITE16 with FUA==1"); if (dpofua) { WRITE16(sd, 0, block_size, block_size, 0, 0, 1, 0, 0, scratch, EXPECT_STATUS_GOOD); } else { WRITE16(sd, 0, block_size, block_size, 0, 0, 1, 0, 0, scratch, EXPECT_INVALID_FIELD_IN_CDB); } logging(LOG_VERBOSE, "Test WRITE16 with DPO==1 FUA==1"); if (dpofua) { WRITE16(sd, 0, block_size, block_size, 0, 1, 1, 0, 0, scratch, EXPECT_STATUS_GOOD); } else { WRITE16(sd, 0, block_size, block_size, 0, 1, 1, 0, 0, scratch, EXPECT_INVALID_FIELD_IN_CDB); } logging(LOG_VERBOSE, "Try fetching REPORT_SUPPORTED_OPCODES " "for WRITE16"); REPORT_SUPPORTED_OPCODES(sd, &rso_task, 0, SCSI_REPORT_SUPPORTING_OPCODE, SCSI_OPCODE_WRITE16, 0, 65535, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Unmarshall the DATA-IN buffer"); rsoc = scsi_datain_unmarshall(rso_task); CU_ASSERT_PTR_NOT_NULL_FATAL(rsoc); usage_data_dpofua = rsoc->cdb_usage_data[1] & 0x18; if (dpofua) { logging(LOG_VERBOSE, "DPOFUA is set. Verify the " "DPO/FUA flags are set in the CDB_USAGE_DATA"); if (!usage_data_dpofua) { logging(LOG_NORMAL, "[FAILED] DpoFua not set " "in CDB_USAGE_DATE"); CU_FAIL("DpoFua not set in CDB_USAGE_DATE"); } } else { logging(LOG_VERBOSE, "DPOFUA is clear. Verify the " "DPO/FUA flags are clear in the CDB_USAGE_DATA"); if (usage_data_dpofua) { logging(LOG_NORMAL, "[FAILED] DpoFua not clear " "in CDB_USAGE_DATE"); CU_FAIL("DpoFua not clear in CDB_USAGE_DATE"); } } scsi_free_scsi_task(rso_task); } libiscsi-1.17.0/test-tool/test_write16_flags.c000066400000000000000000000040531271371262700212250ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg 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, see . */ #include #include #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-test-cu.h" void test_write16_flags(void) { CHECK_FOR_DATALOSS; CHECK_FOR_SBC; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test WRITE16 flags"); logging(LOG_VERBOSE, "Test WRITE16 with DPO==1"); memset(scratch, 0xa6, block_size); WRITE16(sd, 0, block_size, block_size, 0, 1, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Test WRITE16 with FUA==1 FUA_NV==0"); WRITE16(sd, 0, block_size, block_size, 0, 0, 1, 0, 0, scratch, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Test WRITE16 with FUA==1 FUA_NV==1"); WRITE16(sd, 0, block_size, block_size, 0, 0, 1, 1, 0, scratch, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Test WRITE16 with FUA==0 FUA_NV==1"); WRITE16(sd, 0, block_size, block_size, 0, 0, 0, 1, 0, scratch, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Test WRITE16 with DPO==1 FUA==1 FUA_NV==1"); WRITE16(sd, 0, block_size, block_size, 0, 1, 1, 1, 0, scratch, EXPECT_STATUS_GOOD); } libiscsi-1.17.0/test-tool/test_write16_residuals.c000066400000000000000000000367661271371262700221440ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 by Ronnie Sahlberg 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, see . */ #include #include #include #include #include "iscsi.h" #include "iscsi-private.h" #include "scsi-lowlevel.h" #include "iscsi-test-cu.h" void test_write16_residuals(void) { struct scsi_task *task_ret; unsigned char buf[10000]; struct iscsi_data data; int ok; unsigned int i; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test WRITE16 commands with residuals"); logging(LOG_VERBOSE, "Block size is %zu", block_size); CHECK_FOR_DATALOSS; CHECK_FOR_SBC; if (sd->iscsi_ctx == NULL) { const char *err = "[SKIPPED] This WRITE16 test is only " "supported for iSCSI backends"; logging(LOG_NORMAL, "%s", err); CU_PASS(err); return; } /* Try a write16 of 1 block but xferlength == 0 */ task = malloc(sizeof(struct scsi_task)); CU_ASSERT_PTR_NOT_NULL_FATAL(task); memset(task, 0, sizeof(struct scsi_task)); task->cdb[0] = SCSI_OPCODE_WRITE16; task->cdb[13] = 1; task->cdb_size = 16; task->xfer_dir = SCSI_XFER_WRITE; task->expxferlen = 0; /* * we don't want autoreconnect since some targets will drop the session * on this condition. */ iscsi_set_noautoreconnect(sd->iscsi_ctx, 1); logging(LOG_VERBOSE, "Try writing one block but with iSCSI expected transfer length==0"); task_ret = iscsi_scsi_command_sync(sd->iscsi_ctx, sd->iscsi_lun, task, NULL); CU_ASSERT_PTR_NOT_NULL_FATAL(task_ret); CU_ASSERT_NOT_EQUAL(task->status, SCSI_STATUS_CANCELLED); /* XXX redundant? */ if (task->status == SCSI_STATUS_CHECK_CONDITION && task->sense.key == SCSI_SENSE_ILLEGAL_REQUEST && task->sense.ascq == SCSI_SENSE_ASCQ_INVALID_OPERATION_CODE) { logging(LOG_NORMAL, "[SKIPPED] WRITE16 is not implemented."); CU_PASS("WRITE16 is not implemented."); return; } logging(LOG_VERBOSE, "Verify that the target returned SUCCESS"); if (task->status != SCSI_STATUS_GOOD) { logging(LOG_VERBOSE, "[FAILED] Target returned error %s", iscsi_get_error(sd->iscsi_ctx)); } CU_ASSERT_EQUAL(task->status, SCSI_STATUS_GOOD); logging(LOG_VERBOSE, "Verify residual overflow flag is set"); if (task->residual_status != SCSI_RESIDUAL_OVERFLOW) { logging(LOG_VERBOSE, "[FAILED] Target did not set residual " "overflow flag"); } CU_ASSERT_EQUAL(task->residual_status, SCSI_RESIDUAL_OVERFLOW); logging(LOG_VERBOSE, "Verify we got %zu bytes of residual overflow", block_size); if (task->residual != block_size) { logging(LOG_VERBOSE, "[FAILED] Target did not set correct " "amount of residual. Expected %zu but got %zu.", block_size, task->residual); } CU_ASSERT_EQUAL(task->residual, block_size); scsi_free_scsi_task(task); task = NULL; /* in case the previous test failed the session */ iscsi_set_noautoreconnect(sd->iscsi_ctx, 0); logging(LOG_VERBOSE, "Try writing one block but with iSCSI expected transfer length==10000"); task = malloc(sizeof(struct scsi_task)); CU_ASSERT_PTR_NOT_NULL_FATAL(task); memset(task, 0, sizeof(struct scsi_task)); task->cdb[0] = SCSI_OPCODE_WRITE16; task->cdb[13] = 1; task->cdb_size = 16; task->xfer_dir = SCSI_XFER_WRITE; task->expxferlen = 10000; memset(buf, 0xa6, sizeof(buf)); data.size = task->expxferlen; data.data = &buf[0]; task_ret = iscsi_scsi_command_sync(sd->iscsi_ctx, sd->iscsi_lun, task, &data); CU_ASSERT_PTR_NOT_NULL_FATAL(task_ret); logging(LOG_VERBOSE, "Verify that the target returned SUCCESS"); if (task->status != SCSI_STATUS_GOOD) { logging(LOG_VERBOSE, "[FAILED] Target returned error %s", iscsi_get_error(sd->iscsi_ctx)); } CU_ASSERT_EQUAL(task->status, SCSI_STATUS_GOOD); logging(LOG_VERBOSE, "Verify residual underflow flag is set"); if (task->residual_status != SCSI_RESIDUAL_UNDERFLOW) { logging(LOG_VERBOSE, "[FAILED] Target did not set residual " "underflow flag"); } CU_ASSERT_EQUAL(task->residual_status, SCSI_RESIDUAL_UNDERFLOW); logging(LOG_VERBOSE, "Verify we got %zu bytes of residual underflow", 10000 - block_size); if (task->residual != 10000 - block_size) { logging(LOG_VERBOSE, "[FAILED] Target did not set correct " "amount of residual. Expected %zu but got %zu.", 10000 - block_size, task->residual); } CU_ASSERT_EQUAL(task->residual, 10000 - block_size); scsi_free_scsi_task(task); task = NULL; logging(LOG_VERBOSE, "Try writing one block but with iSCSI expected transfer length==200"); task = malloc(sizeof(struct scsi_task)); CU_ASSERT_PTR_NOT_NULL_FATAL(task); memset(task, 0, sizeof(struct scsi_task)); task->cdb[0] = SCSI_OPCODE_WRITE16; task->cdb[13] = 1; task->cdb_size = 16; task->xfer_dir = SCSI_XFER_WRITE; task->expxferlen = 200; data.size = task->expxferlen; data.data = &buf[0]; task_ret = iscsi_scsi_command_sync(sd->iscsi_ctx, sd->iscsi_lun, task, &data); CU_ASSERT_PTR_NOT_NULL_FATAL(task_ret); logging(LOG_VERBOSE, "Verify that the target returned SUCCESS"); ok = task->status == SCSI_STATUS_GOOD || (task->status == SCSI_STATUS_CHECK_CONDITION && task->sense.key == SCSI_SENSE_ILLEGAL_REQUEST && task->sense.ascq == SCSI_SENSE_ASCQ_INVALID_FIELD_IN_INFORMATION_UNIT); if (!ok) { logging(LOG_VERBOSE, "[FAILED] Target returned error %s", iscsi_get_error(sd->iscsi_ctx)); } CU_ASSERT(ok); logging(LOG_VERBOSE, "Verify residual overflow flag is set"); if (task->residual_status != SCSI_RESIDUAL_OVERFLOW) { logging(LOG_VERBOSE, "[FAILED] Target did not set residual " "overflow flag"); } CU_ASSERT_EQUAL(task->residual_status, SCSI_RESIDUAL_OVERFLOW); logging(LOG_VERBOSE, "Verify we got %zu bytes of residual overflow", block_size - 200); if (task->residual != block_size - 200) { logging(LOG_VERBOSE, "[FAILED] Target did not set correct " "amount of residual. Expected %zu but got %zu.", block_size - 200, task->residual); } CU_ASSERT_EQUAL(task->residual, block_size - 200); scsi_free_scsi_task(task); task = NULL; logging(LOG_VERBOSE, "Try writing two blocks but iSCSI expected " "transfer length==%zu (==one block)", block_size); task = malloc(sizeof(struct scsi_task)); CU_ASSERT_PTR_NOT_NULL_FATAL(task); memset(task, 0, sizeof(struct scsi_task)); task->cdb[0] = SCSI_OPCODE_WRITE16; task->cdb[13] = 2; task->cdb_size = 16; task->xfer_dir = SCSI_XFER_WRITE; task->expxferlen = block_size; data.size = task->expxferlen; data.data = &buf[0]; task_ret = iscsi_scsi_command_sync(sd->iscsi_ctx, sd->iscsi_lun, task, &data); CU_ASSERT_PTR_NOT_NULL_FATAL(task_ret); logging(LOG_VERBOSE, "Verify that the target returned SUCCESS"); if (task->status != SCSI_STATUS_GOOD) { logging(LOG_VERBOSE, "[FAILED] Target returned error %s", iscsi_get_error(sd->iscsi_ctx)); } CU_ASSERT_EQUAL(task->status, SCSI_STATUS_GOOD); logging(LOG_VERBOSE, "Verify residual overflow flag is set"); if (task->residual_status != SCSI_RESIDUAL_OVERFLOW) { logging(LOG_VERBOSE, "[FAILED] Target did not set residual " "overflow flag"); } CU_ASSERT_EQUAL(task->residual_status, SCSI_RESIDUAL_OVERFLOW); logging(LOG_VERBOSE, "Verify we got one block of residual overflow"); if (task->residual != block_size) { logging(LOG_VERBOSE, "[FAILED] Target did not set correct " "amount of residual. Expected %zu but got %zu.", block_size, task->residual); } CU_ASSERT_EQUAL(task->residual, block_size); scsi_free_scsi_task(task); task = NULL; logging(LOG_VERBOSE, "Verify that if iSCSI EDTL > SCSI TL then we only write SCSI TL amount of data"); logging(LOG_VERBOSE, "Write two blocks of 'a'"); memset(buf, 'a', 10000); WRITE16(sd, 0, 2 * block_size, block_size, 0, 0, 0, 0, 0, buf, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Write one block of 'b' but set iSCSI EDTL to 2 blocks."); task = malloc(sizeof(struct scsi_task)); CU_ASSERT_PTR_NOT_NULL_FATAL(task); memset(buf, 'b', 10000); memset(task, 0, sizeof(struct scsi_task)); task->cdb[0] = SCSI_OPCODE_WRITE16; task->cdb[13] = 1; task->cdb_size = 16; task->xfer_dir = SCSI_XFER_WRITE; task->expxferlen = 2 * block_size; data.size = task->expxferlen; data.data = &buf[0]; task_ret = iscsi_scsi_command_sync(sd->iscsi_ctx, sd->iscsi_lun, task, &data); CU_ASSERT_PTR_NOT_NULL_FATAL(task_ret); logging(LOG_VERBOSE, "Verify that the target returned SUCCESS"); if (task->status != SCSI_STATUS_GOOD) { logging(LOG_VERBOSE, "[FAILED] Target returned error %s", iscsi_get_error(sd->iscsi_ctx)); } CU_ASSERT_EQUAL(task->status, SCSI_STATUS_GOOD); logging(LOG_VERBOSE, "Verify residual underflow flag is set"); if (task->residual_status != SCSI_RESIDUAL_UNDERFLOW) { logging(LOG_VERBOSE, "[FAILED] Target did not set residual " "underflow flag"); } CU_ASSERT_EQUAL(task->residual_status, SCSI_RESIDUAL_UNDERFLOW); logging(LOG_VERBOSE, "Verify we got one block of residual underflow"); if (task->residual != block_size) { logging(LOG_VERBOSE, "[FAILED] Target did not set correct " "amount of residual. Expected %zu but got %zu.", block_size, task->residual); } CU_ASSERT_EQUAL(task->residual, block_size); scsi_free_scsi_task(task); task = NULL; logging(LOG_VERBOSE, "Read the two blocks"); READ16(sd, NULL, 0, 2* block_size, block_size, 0, 0, 0, 0, 0, buf, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Verify that the first block was changed to 'b'"); for (i = 0; i < block_size; i++) { if (buf[i] != 'b') { logging(LOG_NORMAL, "First block did not contain expected 'b'"); CU_FAIL("Block was not written correctly"); break; } } logging(LOG_VERBOSE, "Verify that the second block was NOT overwritten and still contains 'a'"); for (i = block_size; i < 2 * block_size; i++) { if (buf[i] != 'a') { logging(LOG_NORMAL, "Second block was overwritten and no longer contain 'a'"); CU_FAIL("Second block was incorrectly overwritten"); break; } } logging(LOG_VERBOSE, "Verify that if iSCSI EDTL < SCSI TL then we only write iSCSI EDTL amount of data"); logging(LOG_VERBOSE, "Write two blocks of 'a'"); memset(buf, 'a', 10000); WRITE16(sd, 0, 2 * block_size, block_size, 0, 0, 0, 0, 0, buf, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Write two blocks of 'b' but set iSCSI EDTL to 1 blocks."); task = malloc(sizeof(struct scsi_task)); CU_ASSERT_PTR_NOT_NULL_FATAL(task); memset(buf, 'b', 10000); memset(task, 0, sizeof(struct scsi_task)); task->cdb[0] = SCSI_OPCODE_WRITE16; task->cdb[13] = 2; task->cdb_size = 16; task->xfer_dir = SCSI_XFER_WRITE; task->expxferlen = block_size; data.size = task->expxferlen; data.data = &buf[0]; task_ret = iscsi_scsi_command_sync(sd->iscsi_ctx, sd->iscsi_lun, task, &data); CU_ASSERT_PTR_NOT_NULL_FATAL(task_ret); logging(LOG_VERBOSE, "Verify that the target returned SUCCESS"); if (task->status != SCSI_STATUS_GOOD) { logging(LOG_VERBOSE, "[FAILED] Target returned error %s", iscsi_get_error(sd->iscsi_ctx)); } CU_ASSERT_EQUAL(task->status, SCSI_STATUS_GOOD); logging(LOG_VERBOSE, "Verify residual overflow flag is set"); if (task->residual_status != SCSI_RESIDUAL_OVERFLOW) { logging(LOG_VERBOSE, "[FAILED] Target did not set residual " "overflow flag"); } CU_ASSERT_EQUAL(task->residual_status, SCSI_RESIDUAL_OVERFLOW); logging(LOG_VERBOSE, "Verify we got one block of residual overflow"); if (task->residual != block_size) { logging(LOG_VERBOSE, "[FAILED] Target did not set correct " "amount of residual. Expected %zu but got %zu.", block_size, task->residual); } CU_ASSERT_EQUAL(task->residual, block_size); scsi_free_scsi_task(task); task = NULL; logging(LOG_VERBOSE, "Read the two blocks"); READ16(sd, NULL, 0, 2* block_size, block_size, 0, 0, 0, 0, 0, buf, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Verify that the first block was changed to 'b'"); for (i = 0; i < block_size; i++) { if (buf[i] != 'b') { logging(LOG_NORMAL, "First block did not contain expected 'b'"); CU_FAIL("Block was not written correctly"); break; } } logging(LOG_VERBOSE, "Verify that the second block was NOT overwritten and still contains 'a'"); for (i = block_size; i < 2 * block_size; i++) { if (buf[i] != 'a') { logging(LOG_NORMAL, "Second block was overwritten and no longer contain 'a'"); CU_FAIL("Second block was incorrectly overwritten"); break; } } } libiscsi-1.17.0/test-tool/test_write16_simple.c000066400000000000000000000037231271371262700214250ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg 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, see . */ #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-support.h" #include "iscsi-test-cu.h" void test_write16_simple(void) { int i; CHECK_FOR_DATALOSS; CHECK_FOR_SBC; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test WRITE16 of 1-256 blocks at the start of the LUN"); memset(scratch, 0xa6, 256 * block_size); for (i = 1; i <= 256; i++) { if (maximum_transfer_length && maximum_transfer_length < i) { break; } WRITE16(sd, 0, i * block_size, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); } logging(LOG_VERBOSE, "Test WRITE16 of 1-256 blocks at the end of the LUN"); for (i = 1; i <= 256; i++) { if (maximum_transfer_length && maximum_transfer_length < i) { break; } WRITE16(sd, num_blocks - i, i * block_size, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); } } libiscsi-1.17.0/test-tool/test_write16_wrprotect.c000066400000000000000000000035341271371262700221650ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg 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, see . */ #include #include #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-test-cu.h" void test_write16_wrprotect(void) { int i; /* * Try out different non-zero values for WRPROTECT. */ logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test WRITE16 with non-zero WRPROTECT"); CHECK_FOR_DATALOSS; CHECK_FOR_SBC; memset(scratch, 0xa6, block_size); if (!inq->protect || (rc16 != NULL && !rc16->prot_en)) { logging(LOG_VERBOSE, "Device does not support/use protection information. All commands should fail."); for (i = 1; i < 8; i++) { WRITE16(sd, 0, block_size, block_size, i, 0, 0, 0, 0, scratch, EXPECT_INVALID_FIELD_IN_CDB); } return; } logging(LOG_NORMAL, "No tests for devices that support protection information yet."); } libiscsi-1.17.0/test-tool/test_writeatomic16_0blocks.c000066400000000000000000000040231271371262700226600ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2015 Ronnie Sahlberg 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, see . */ #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-test-cu.h" void test_writeatomic16_0blocks(void) { int align; CHECK_FOR_DATALOSS; CHECK_FOR_SBC; if (!inq_bl) { CU_PASS("BlockLimits VPD is not available. Skipping test.\n"); return; } logging(LOG_VERBOSE, LOG_BLANK_LINE); align = inq_bl->atomic_align ? inq_bl->atomic_align : 1; logging(LOG_VERBOSE, "Test WRITEATOMIC16 0-blocks at LBA==0"); WRITEATOMIC16(sd, 0, 0, block_size, 0, 0, 0, 0, NULL, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Test WRITEATOMIC16 0-blocks one alignment past end-of-LUN"); WRITEATOMIC16(sd, num_blocks + align, 0, block_size, 0, 0, 0, 0, NULL, EXPECT_LBA_OOB); logging(LOG_VERBOSE, "Test WRITEATOMIC16 0-blocks at LBA==2^63"); WRITEATOMIC16(sd, 0x8000000000000000ULL, 0, block_size, 0, 0, 0, 0, NULL, EXPECT_LBA_OOB); logging(LOG_VERBOSE, "Test WRITEATOMIC16 0-blocks at LBA==-"); WRITEATOMIC16(sd, -align, 0, block_size, 0, 0, 0, 0, NULL, EXPECT_LBA_OOB); } libiscsi-1.17.0/test-tool/test_writeatomic16_beyond_eol.c000066400000000000000000000066351271371262700234550ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2015 Ronnie Sahlberg 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, see . */ #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-test-cu.h" void test_writeatomic16_beyond_eol(void) { int align, i, gran; const size_t bufsz = 256 * 2 * block_size; CHECK_FOR_DATALOSS; CHECK_FOR_SBC; if (!inq_bl) { CU_PASS("BlockLimits VPD is not available. Skipping test.\n"); return; } logging(LOG_VERBOSE, LOG_BLANK_LINE); memset(scratch, 0xa6, bufsz); align = inq_bl->atomic_align ? inq_bl->atomic_align : 1; gran = inq_bl->atomic_gran ? inq_bl->atomic_gran : 1; WRITEATOMIC16(sd, 0, block_size * gran, block_size, 0, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Test WRITEATOMIC16 1-256 blocks blocks beyond the end"); for (i = gran; i <= 256; i += gran) { if (maximum_transfer_length && maximum_transfer_length < i) { break; } WRITEATOMIC16(sd, num_blocks - i, 2 * i * block_size, block_size, 0, 0, 0, 0, scratch, EXPECT_LBA_OOB); } logging(LOG_VERBOSE, "Test WRITEATOMIC16 1-256 blocks at LBA==2^63"); for (i = gran; i <= 256; i += gran) { if (maximum_transfer_length && maximum_transfer_length < i) { break; } WRITEATOMIC16(sd, 0x8000000000000000ULL, i * block_size, block_size, 0, 0, 0, 0, scratch, EXPECT_LBA_OOB); } logging(LOG_VERBOSE, "Test WRITEATOMIC16 1-256 blocks at LBA==-"); for (i = gran; i <= 256; i += gran) { if (maximum_transfer_length && maximum_transfer_length < i) { break; } WRITEATOMIC16(sd, -align, i * block_size, block_size, 0, 0, 0, 0, scratch, EXPECT_LBA_OOB); } logging(LOG_VERBOSE, "Test WRITEATOMIC16 2-256 blocks all but one block beyond the end"); for (i = 2 * gran; i <= 256; i += gran) { if (maximum_transfer_length && maximum_transfer_length < i) { break; } WRITEATOMIC16(sd, num_blocks - gran, i * block_size, block_size, 0, 0, 0, 0, scratch, EXPECT_LBA_OOB); } } libiscsi-1.17.0/test-tool/test_writeatomic16_dpofua.c000066400000000000000000000123061271371262700226040ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2015 by Ronnie Sahlberg 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, see . */ #include #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-test-cu.h" void test_writeatomic16_dpofua(void) { int gran, dpofua, usage_data_dpofua; struct scsi_task *ms_task = NULL; struct scsi_mode_sense *ms; struct scsi_task *rso_task = NULL; struct scsi_report_supported_op_codes_one_command *rsoc; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test WRITEATOMIC16 DPO/FUA flags"); CHECK_FOR_SBC; CHECK_FOR_DATALOSS; if (!inq_bl) { CU_PASS("BlockLimits VPD is not available. Skipping test.\n"); return; } gran = inq_bl->atomic_gran ? inq_bl->atomic_gran : 1; logging(LOG_VERBOSE, "Read the DPOFUA flag from mode sense data"); MODESENSE6(sd, &ms_task, 0, SCSI_MODESENSE_PC_CURRENT, SCSI_MODEPAGE_RETURN_ALL_PAGES, 0, 255, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "[SUCCESS] Mode sense returned status GOOD"); ms = scsi_datain_unmarshall(ms_task); dpofua = ms && (ms->device_specific_parameter & 0x10); scsi_free_scsi_task(ms_task); if (dpofua) { logging(LOG_VERBOSE, "DPOFUA flag is set. Device should allow " "DPO/FUA flags in CDBs"); } else { logging(LOG_VERBOSE, "DPOFUA flag is clear. Device should fail " "CDBs with DPO/FUA set"); } logging(LOG_VERBOSE, "Test WRITEATOMIC16 with DPO==1"); memset(scratch, 0xa6, block_size); if (dpofua) { WRITEATOMIC16(sd, 0, gran * block_size, block_size, 0, 1, 0, 0, scratch, EXPECT_STATUS_GOOD); } else { WRITEATOMIC16(sd, 0, gran * block_size, block_size, 0, 1, 0, 0, scratch, EXPECT_INVALID_FIELD_IN_CDB); } logging(LOG_VERBOSE, "Test WRITEATOMIC16 with FUA==1"); if (dpofua) { WRITEATOMIC16(sd, 0, gran * block_size, block_size, 0, 0, 1, 0, scratch, EXPECT_STATUS_GOOD); } else { WRITEATOMIC16(sd, 0, gran * block_size, block_size, 0, 0, 1, 0, scratch, EXPECT_INVALID_FIELD_IN_CDB); } logging(LOG_VERBOSE, "Test WRITEATOMIC16 with DPO==1 FUA==1"); if (dpofua) { WRITEATOMIC16(sd, 0, gran * block_size, block_size, 0, 1, 1, 0, scratch, EXPECT_STATUS_GOOD); } else { WRITEATOMIC16(sd, 0, gran * block_size, block_size, 0, 1, 1, 0, scratch, EXPECT_INVALID_FIELD_IN_CDB); } logging(LOG_VERBOSE, "Try fetching REPORT_SUPPORTED_OPCODES " "for WRITEATOMIC16"); REPORT_SUPPORTED_OPCODES(sd, &rso_task, 0, SCSI_REPORT_SUPPORTING_OPCODE, SCSI_OPCODE_WRITE_ATOMIC16, 0, 65535, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Unmarshall the DATA-IN buffer"); rsoc = scsi_datain_unmarshall(rso_task); CU_ASSERT_PTR_NOT_NULL_FATAL(rsoc); usage_data_dpofua = rsoc->cdb_usage_data[1] & 0x18; if (dpofua) { logging(LOG_VERBOSE, "DPOFUA is set. Verify the " "DPO/FUA flags are set in the CDB_USAGE_DATA"); if (!usage_data_dpofua) { logging(LOG_NORMAL, "[FAILED] DpoFua not set " "in CDB_USAGE_DATE"); CU_FAIL("DpoFua not set in CDB_USAGE_DATE"); } } else { logging(LOG_VERBOSE, "DPOFUA is clear. Verify the " "DPO/FUA flags are clear in the CDB_USAGE_DATA"); if (usage_data_dpofua) { logging(LOG_NORMAL, "[FAILED] DpoFua not clear " "in CDB_USAGE_DATE"); CU_FAIL("DpoFua not clear in CDB_USAGE_DATE"); } } scsi_free_scsi_task(rso_task); } libiscsi-1.17.0/test-tool/test_writeatomic16_simple.c000066400000000000000000000047621271371262700226260ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2015 Ronnie Sahlberg 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, see . */ #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-support.h" #include "iscsi-test-cu.h" void test_writeatomic16_simple(void) { int i, align, gran; const size_t bufsz = 256 * block_size; CHECK_FOR_DATALOSS; CHECK_FOR_SBC; if (!inq_bl) { CU_PASS("BlockLimits VPD is not available. Skipping test.\n"); return; } logging(LOG_VERBOSE, LOG_BLANK_LINE); memset(scratch, 0, bufsz); gran = inq_bl->atomic_gran ? inq_bl->atomic_gran : 1; WRITEATOMIC16(sd, 0, block_size * gran, block_size, 0, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Test WRITEATOMIC16 of 1-256 blocks at the start of the LUN"); memset(scratch, 0xa6, 256 * block_size); for (i = gran; i <= 256; i += gran) { if (maximum_transfer_length && maximum_transfer_length < i) { break; } WRITEATOMIC16(sd, 0, i * block_size, block_size, 0, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); } logging(LOG_VERBOSE, "Test WRITEATOMIC16 of 1-256 blocks at the end of the LUN"); align = inq_bl->atomic_align ? inq_bl->atomic_align : 1; for (i = align; i <= 256; i += align) { if (maximum_transfer_length && maximum_transfer_length < i) { break; } WRITEATOMIC16(sd, num_blocks - i, i * block_size, block_size, 0, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); } } libiscsi-1.17.0/test-tool/test_writeatomic16_vpd.c000066400000000000000000000140261271371262700221200ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2015 by Ronnie Sahlberg 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, see . */ #include #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-test-cu.h" void test_writeatomic16_vpd(void) { int ret; struct scsi_inquiry_block_limits *bl; struct scsi_task *bl_task = NULL; int gran; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test WRITEATOMIC16 VPD data"); CHECK_FOR_SBC; CHECK_FOR_DATALOSS; logging(LOG_VERBOSE, "Block device. Verify that we can read Block " "Limits VPD"); ret = inquiry(sd, &bl_task, 1, SCSI_INQUIRY_PAGECODE_BLOCK_LIMITS, 255, EXPECT_STATUS_GOOD); CU_ASSERT_EQUAL(ret, 0); if (ret != 0) { logging(LOG_NORMAL, "[FAILURE] failed to read Block Limits VDP"); CU_FAIL("[FAILURE] failed to read Block Limits VDP"); goto finished; } bl = scsi_datain_unmarshall(bl_task); if (bl == NULL) { logging(LOG_NORMAL, "[FAILURE] failed to unmarshall Block Limits VDP"); CU_FAIL("[FAILURE] failed to unmarshall Block Limits VDP"); goto finished; } logging(LOG_VERBOSE, "Check if WRITEATOMIC16 is supported"); gran = inq_bl->atomic_gran ? inq_bl->atomic_gran : 1; memset(scratch , 0x00, block_size * gran); ret = writeatomic16(sd, 0, block_size * gran, block_size, 0, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); if (ret == -2) { logging(LOG_VERBOSE, "WRITEATOMIC16 is NOT supported by the target."); logging(LOG_VERBOSE, "Verify that MAXIMUM_ATOMIC_TRANSFER_LENGTH is zero"); if (bl->max_atomic_xfer_len) { logging(LOG_VERBOSE, "MAXIMUM_ATOMIC_TRANSFER_LENGTH is non-zero but target does not support ATOMICWRITE16"); CU_FAIL("MAXIMUM_ATOMIC_TRANSFER_LENGTH is non-zero but target does not support ATOMICWRITE16"); } logging(LOG_VERBOSE, "Verify that ATOMIC_ALIGNMENT is zero"); if (bl->atomic_align) { logging(LOG_VERBOSE, "ATOMIC_ALIGNMENT is non-zero but target does not support ATOMICWRITE16"); CU_FAIL("ATOMIC_ALIGNMENT is non-zero but target does not support ATOMICWRITE16"); } logging(LOG_VERBOSE, "Verify that ATOMIC_GRANULARITY is zero"); if (bl->atomic_gran) { logging(LOG_VERBOSE, "ATOMIC_GRANULARITY is non-zero but target does not support ATOMICWRITE16"); CU_FAIL("ATOMIC_GRANULARITY is non-zero but target does not support ATOMICWRITE16"); } goto finished; } logging(LOG_VERBOSE, "WRITEATOMIC16 IS supported by the target."); logging(LOG_VERBOSE, "Verify that MAXIMUM_ATOMIC_TRANSFER_LENGTH is non-zero"); if (!bl->max_atomic_xfer_len) { logging(LOG_VERBOSE, "[WARNING] MAXIMUM_ATOMIC_TRANSFER_LENGTH is zero but target supports ATOMICWRITE16"); CU_FAIL("[WARNING] MAXIMUM_ATOMIC_TRANSFER_LENGTH is zero but target supports ATOMICWRITE16"); } logging(LOG_VERBOSE, "Verify that MAXIMUM_ATOMIC_TRANSFER_LENGTH is less than or equal to MAXIMUM_TRANSFER_LENGTH"); if (bl->max_atomic_xfer_len > bl->max_xfer_len) { logging(LOG_VERBOSE, "[FAILED] MAXIMUM_ATOMIC_TRANSFER_LENGTH is greater than MAXIMUM_TRANSFER_LENGTH"); CU_FAIL("[FAILED] MAXIMUM_ATOMIC_TRANSFER_LENGTH is greater than MAXIMUM_TRANSFER_LENGTH"); } logging(LOG_VERBOSE, "Check handling on misaligned writes"); if (bl->atomic_align < 2) { logging(LOG_VERBOSE, "[SKIPPED] No alignment restrictions on this LUN"); } else { logging(LOG_VERBOSE, "Atomic Write at LBA 1 should fail due to misalignment"); ret = writeatomic16(sd, 1, block_size * gran, block_size, 0, 0, 0, 0, scratch, EXPECT_INVALID_FIELD_IN_CDB); if (ret) { logging(LOG_VERBOSE, "[FAILED] Misaligned write did NOT fail with INVALID_FIELD_IN_CDB"); CU_FAIL("[FAILED] Misaligned write did NOT fail with INVALID_FIELD_IN_CDB"); } } logging(LOG_VERBOSE, "Check handling on invalid granularity"); if (bl->atomic_gran < 2) { logging(LOG_VERBOSE, "[SKIPPED] No granularity restrictions on this LUN"); } else { logging(LOG_VERBOSE, "Atomic Write of 1 block should fail due to invalid granularity"); ret = writeatomic16(sd, 0, block_size, block_size, 0, 0, 0, 0, scratch, EXPECT_INVALID_FIELD_IN_CDB); if (ret) { logging(LOG_VERBOSE, "[FAILED] Misgranularity write did NOT fail with INVALID_FIELD_IN_CDB"); CU_FAIL("[FAILED] Misgranularity write did NOT fail with INVALID_FIELD_IN_CDB"); } } finished: scsi_free_scsi_task(bl_task); } libiscsi-1.17.0/test-tool/test_writeatomic16_wrprotect.c000066400000000000000000000042231271371262700233560ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2015 Ronnie Sahlberg 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, see . */ #include #include #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-test-cu.h" void test_writeatomic16_wrprotect(void) { int i, gran; CHECK_FOR_DATALOSS; CHECK_FOR_SBC; if (!inq_bl) { CU_PASS("BlockLimits VPD is not available. Skipping test.\n"); return; } logging(LOG_VERBOSE, LOG_BLANK_LINE); gran = inq_bl->atomic_gran ? inq_bl->atomic_gran : 1; memset(scratch, 0, block_size); WRITEATOMIC16(sd, 0, block_size * gran, block_size, 0, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Test WRITEATOMIC16 with non-zero WRPROTECT"); memset(scratch, 0xa6, block_size); if (!inq->protect || (rc16 != NULL && !rc16->prot_en)) { logging(LOG_VERBOSE, "Device does not support/use protection information. All commands should fail."); for (i = 1; i < 8; i++) { WRITEATOMIC16(sd, 0, gran * block_size, block_size, i, 0, 0, 0, scratch, EXPECT_INVALID_FIELD_IN_CDB); } return; } logging(LOG_NORMAL, "No tests for devices that support protection information yet."); } libiscsi-1.17.0/test-tool/test_writesame10_0blocks.c000066400000000000000000000057161271371262700223350ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg 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, see . */ #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-test-cu.h" void test_writesame10_0blocks(void) { int ret; CHECK_FOR_DATALOSS; CHECK_FOR_SBC; if (num_blocks >= 0x80000000) { CU_PASS("LUN is too big for write-beyond-eol tests with WRITESAME10. Skipping test.\n"); return; } if (!inq_bl) { CU_PASS("BlockLimits VPD is not available. Skipping test.\n"); return; } logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test WRITESAME10 0-blocks at LBA==0 (WSNZ=%d)", inq_bl->wsnz); memset(scratch, 0, block_size); if (inq_bl->wsnz) { WRITESAME10(sd, 0, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_INVALID_FIELD_IN_CDB); logging(LOG_NORMAL, "[SKIPPED] WRITESAME10 does not support 0-blocks."); return; } ret = writesame10(sd, 0, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); if (ret == -2) { CU_PASS("[SKIPPED] Target does not support WRITESAME10. Skipping test"); return; } else if (ret == -3) { CU_PASS("[SKIPPED] Target does not support WRITESAME10 with NUMBER OF LOGICAL BLOCKS == 0"); } else if (ret == -4) { CU_PASS("[SKIPPED] Number of WRITESAME10 logical blocks to be written exceeds MAXIMUM WRITE SAME LENGTH"); } else { CU_ASSERT_EQUAL(ret, 0); } logging(LOG_VERBOSE, "Test WRITESAME10 0-blocks one block past end-of-LUN"); WRITESAME10(sd, num_blocks + 1, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_LBA_OOB); logging(LOG_VERBOSE, "Test WRITESAME10 0-blocks at LBA==2^31"); WRITESAME10(sd, 0x80000000, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_LBA_OOB); logging(LOG_VERBOSE, "Test WRITESAME10 0-blocks at LBA==-1"); WRITESAME10(sd, -1, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_LBA_OOB); } libiscsi-1.17.0/test-tool/test_writesame10_beyond_eol.c000066400000000000000000000046011271371262700231070ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg 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, see . */ #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-test-cu.h" void test_writesame10_beyond_eol(void) { int i; CHECK_FOR_DATALOSS; CHECK_FOR_SBC; if (num_blocks >= 0x80000000) { CU_PASS("LUN is too big for write-beyond-eol tests with WRITESAME10. Skipping test.\n"); return; } logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test WRITESAME10 1-256 blocks one block beyond the end"); memset(scratch, 0, block_size); for (i = 1; i <= 256; i++) { WRITESAME10(sd, num_blocks - i + 1, block_size, i, 0, 0, 0, 0, scratch, EXPECT_LBA_OOB); } logging(LOG_VERBOSE, "Test WRITESAME10 1-256 blocks at LBA==2^31"); for (i = 1; i <= 256; i++) { WRITESAME10(sd, 0x80000000, block_size, i, 0, 0, 0, 0, scratch, EXPECT_LBA_OOB); } logging(LOG_VERBOSE, "Test WRITESAME10 1-256 blocks at LBA==-1"); for (i = 1; i <= 256; i++) { WRITESAME10(sd, -1, block_size, i, 0, 0, 0, 0, scratch, EXPECT_LBA_OOB); } logging(LOG_VERBOSE, "Test WRITESAME10 2-256 blocks all but one block beyond the end"); for (i = 2; i <= 256; i++) { WRITESAME10(sd, num_blocks - 1, block_size, i, 0, 0, 0, 0, scratch, EXPECT_LBA_OOB); } } libiscsi-1.17.0/test-tool/test_writesame10_simple.c000066400000000000000000000032651271371262700222660ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg 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, see . */ #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-support.h" #include "iscsi-test-cu.h" void test_writesame10_simple(void) { int i; CHECK_FOR_DATALOSS; CHECK_FOR_SBC; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test WRITESAME10 of 1-256 blocks at the start of the LUN"); memset(scratch, 0, block_size); for (i = 1; i <= 256; i++) { WRITESAME10(sd, 0, block_size, i, 0, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); } logging(LOG_VERBOSE, "Test WRITESAME10 of 1-256 blocks at the end of the LUN"); for (i = 1; i <= 256; i++) { WRITESAME10(sd, num_blocks - i, block_size, i, 0, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); } } libiscsi-1.17.0/test-tool/test_writesame10_unmap.c000066400000000000000000000163571271371262700221230ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg 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, see . */ #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-support.h" #include "iscsi-test-cu.h" void test_writesame10_unmap(void) { unsigned int i; CHECK_FOR_DATALOSS; CHECK_FOR_THIN_PROVISIONING; CHECK_FOR_LBPWS10; CHECK_FOR_SBC; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test WRITESAME10 of 1-256 blocks at the start of " "the LUN"); memset(scratch, 0xa6, 256 * block_size); for (i = 1; i <= 256; i++) { logging(LOG_VERBOSE, "Write %d blocks of 0xFF", i); memset(scratch, 0xff, i * block_size); WRITE10(sd, 0, i * block_size, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Unmap %d blocks using WRITESAME10", i); memset(scratch, 0, block_size); WRITESAME10(sd, 0, block_size, i, 0, 1, 0, 0, scratch, EXPECT_STATUS_GOOD); if (rc16->lbprz) { logging(LOG_VERBOSE, "LBPRZ is set. Read the unmapped " "blocks back and verify they are all zero"); logging(LOG_VERBOSE, "Read %d blocks and verify they " "are now zero", i); READ10(sd, NULL, 0, i * block_size, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); ALL_ZERO(scratch, i * block_size); } else { logging(LOG_VERBOSE, "LBPRZ is clear. Skip the read " "and verify zero test"); } } logging(LOG_VERBOSE, "Test WRITESAME10 of 1-256 blocks at the end of " "the LUN"); for (i = 1; i <= 256; i++) { logging(LOG_VERBOSE, "Write %d blocks of 0xFF", i); memset(scratch, 0xff, i * block_size); WRITE10(sd, num_blocks - i, i * block_size, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Unmap %d blocks using WRITESAME10", i); memset(scratch, 0, block_size); WRITESAME10(sd, num_blocks - i, block_size, i, 0, 1, 0, 0, scratch, EXPECT_STATUS_GOOD); if (rc16->lbprz) { logging(LOG_VERBOSE, "LBPRZ is set. Read the unmapped " "blocks back and verify they are all zero"); logging(LOG_VERBOSE, "Read %d blocks and verify they " "are now zero", i); READ10(sd, NULL, num_blocks - i, i * block_size, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); ALL_ZERO(scratch, i * block_size); } else { logging(LOG_VERBOSE, "LBPRZ is clear. Skip the read " "and verify zero test"); } } logging(LOG_VERBOSE, "Verify that WRITESAME10 ANCHOR==1 + UNMAP==0 is " "invalid"); WRITESAME10(sd, 0, block_size, 1, 1, 0, 0, 0, scratch, EXPECT_INVALID_FIELD_IN_CDB); if (inq_lbp->anc_sup) { logging(LOG_VERBOSE, "Test WRITESAME10 ANCHOR==1 + UNMAP==0"); memset(scratch, 0, block_size); WRITESAME10(sd, 0, block_size, 1, 1, 1, 0, 0, scratch, EXPECT_STATUS_GOOD); } else { logging(LOG_VERBOSE, "Test WRITESAME10 ANCHOR==1 + UNMAP==0 no " "ANC_SUP so expecting to fail"); WRITESAME10(sd, 0, block_size, 1, 1, 1, 0, 0, scratch, EXPECT_INVALID_FIELD_IN_CDB); } if (inq_bl == NULL) { logging(LOG_VERBOSE, "[FAILED] WRITESAME10 works but " "BlockLimits VPD is missing."); CU_FAIL("[FAILED] WRITESAME10 works but " "BlockLimits VPD is missing."); return; } i = 256; if (i <= num_blocks && (inq_bl->max_ws_len == 0 || inq_bl->max_ws_len >= i)) { logging(LOG_VERBOSE, "Block Limits VPD page reports MAX_WS_LEN " "as either 0 (==no limit) or >= %d. Test Unmapping " "%d blocks to verify that it can handle 2-byte " "lengths", i, i); logging(LOG_VERBOSE, "Write %d blocks of 0xFF", i); memset(scratch, 0xff, i * block_size); WRITE10(sd, 0, i * block_size, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Unmap %d blocks using WRITESAME10", i); memset(scratch, 0, block_size); WRITESAME10(sd, 0, block_size, i, 0, 1, 0, 0, scratch, EXPECT_STATUS_GOOD); if (rc16->lbprz) { logging(LOG_VERBOSE, "LBPRZ is set. Read the unmapped " "blocks back and verify they are all zero"); logging(LOG_VERBOSE, "Read %d blocks and verify they " "are now zero", i); READ10(sd, NULL, 0, i * block_size, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); ALL_ZERO(scratch, i * block_size); } else { logging(LOG_VERBOSE, "LBPRZ is clear. Skip the read " "and verify zero test"); } } else if (i <= num_blocks) { logging(LOG_VERBOSE, "Block Limits VPD page reports MAX_WS_LEN " "as <256. Verify that a 256 block unmap fails with " "INVALID_FIELD_IN_CDB."); logging(LOG_VERBOSE, "Unmap %d blocks using WRITESAME10", i); WRITESAME10(sd, 0, block_size, i, 0, 1, 0, 0, scratch, EXPECT_INVALID_FIELD_IN_CDB); } } libiscsi-1.17.0/test-tool/test_writesame10_unmap_unaligned.c000066400000000000000000000031251271371262700241360ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg 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, see . */ #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-support.h" #include "iscsi-test-cu.h" void test_writesame10_unmap_unaligned(void) { int i; CHECK_FOR_DATALOSS; CHECK_FOR_THIN_PROVISIONING; CHECK_FOR_LBPWS10; CHECK_FOR_LBPPB_GT_1; CHECK_FOR_SBC; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test that unaligned WRITESAME10 Unmap succeeds. LBPPB==%d", lbppb); memset(scratch, 0xa6, block_size); for (i = 1; i < lbppb; i++) { logging(LOG_VERBOSE, "Unmap %d blocks using WRITESAME10 at LBA:%d", lbppb - i, i); WRITESAME10(sd, i, block_size, lbppb - i, 0, 1, 0, 0, scratch, EXPECT_STATUS_GOOD); } } libiscsi-1.17.0/test-tool/test_writesame10_unmap_until_end.c000066400000000000000000000055001271371262700241500ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg 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, see . */ #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-support.h" #include "iscsi-test-cu.h" void test_writesame10_unmap_until_end(void) { unsigned int i; CHECK_FOR_DATALOSS; CHECK_FOR_THIN_PROVISIONING; CHECK_FOR_LBPWS10; CHECK_FOR_SBC; if (inq_bl->wsnz) { logging(LOG_NORMAL, "[SKIPPED] WRITESAME10 does not support 0-blocks."); CU_PASS("[SKIPPED] WRITESAME10 does not support 0-blocks."); return; } logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test WRITESAME10 of 1-256 blocks at the end of the LUN by setting number-of-blocks==0"); for (i = 1; i <= 256; i++) { logging(LOG_VERBOSE, "Write %d blocks of 0xFF", i); memset(scratch, 0xff, block_size * i); WRITE10(sd, num_blocks - i, i * block_size, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Unmap %d blocks using WRITESAME10", i); WRITESAME10(sd, num_blocks - i, block_size, 0, 0, 1, 0, 0, scratch, EXPECT_STATUS_GOOD); if (rc16->lbprz) { logging(LOG_VERBOSE, "LBPRZ is set. Read the unmapped " "blocks back and verify they are all zero"); logging(LOG_VERBOSE, "Read %d blocks and verify they " "are now zero", i); READ10(sd, NULL, num_blocks - i, i * block_size, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); ALL_ZERO(scratch, i * block_size); } else { logging(LOG_VERBOSE, "LBPRZ is clear. Skip the read " "and verify zero test"); } } } libiscsi-1.17.0/test-tool/test_writesame10_unmap_vpd.c000066400000000000000000000063621271371262700227670ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg 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, see . */ #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-support.h" #include "iscsi-test-cu.h" void test_writesame10_unmap_vpd(void) { int ret; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test WRITESAME10 UNMAP availability is " "consistent with VPD settings"); CHECK_FOR_DATALOSS; CHECK_FOR_SBC; logging(LOG_VERBOSE, "Check if WRITESAME10 can be used for UNMAP."); logging(LOG_VERBOSE, "Unmap 1 block using WRITESAME10"); memset(scratch, 0, block_size); ret = writesame10(sd, 0, block_size, 1, 0, 1, 0, 0, scratch, EXPECT_STATUS_GOOD); if (ret != 0) { logging(LOG_VERBOSE, "WRITESAME10 UNMAP is not available. " "Verify that VPD settings reflect this."); logging(LOG_VERBOSE, "Verify that LBPWS10 is clear."); if (inq_lbp && inq_lbp->lbpws10) { logging(LOG_NORMAL, "[FAILED] WRITESAME10 UNMAP is not " "implemented but LBPWS10 is set"); CU_FAIL("[FAILED] WRITESAME10 UNMAP is unavailable but " "LBPWS10==1"); } else { logging(LOG_VERBOSE, "[SUCCESS] LBPWS10 is clear."); } } else { logging(LOG_VERBOSE, "WRITESAME10 UNMAP is available. Verify " "that VPD settings reflect this."); logging(LOG_VERBOSE, "Verify that LBPME is set."); if (rc16 && rc16->lbpme) { logging(LOG_VERBOSE, "[SUCCESS] LBPME is set."); } else { logging(LOG_NORMAL, "[FAILED] WRITESAME10 UNMAP is " "implemented but LBPME is not set"); CU_FAIL("[FAILED] UNMAP is available but LBPME==0"); } logging(LOG_VERBOSE, "Verify that LBPWS10 is set."); if (inq_lbp && inq_lbp->lbpws10) { logging(LOG_VERBOSE, "[SUCCESS] LBPWS10 is set."); } else { logging(LOG_NORMAL, "[FAILED] WRITESAME10 UNMAP is " "implemented but LBPWS10 is not set"); CU_FAIL("[FAILED] UNMAP is available but LBPWS10==0"); } } } libiscsi-1.17.0/test-tool/test_writesame10_wrprotect.c000066400000000000000000000034751271371262700230310ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg 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, see . */ #include #include #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-test-cu.h" void test_writesame10_wrprotect(void) { int i; /* * Try out different non-zero values for WRPROTECT. */ logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test WRITESAME10 with non-zero WRPROTECT"); CHECK_FOR_DATALOSS; CHECK_FOR_SBC; memset(scratch, 0, block_size); if (!inq->protect || (rc16 != NULL && !rc16->prot_en)) { logging(LOG_VERBOSE, "Device does not support/use protection information. All commands should fail."); for (i = 1; i < 8; i++) { WRITESAME10(sd, 0, block_size, 1, 0, 0, i, 0, scratch, EXPECT_INVALID_FIELD_IN_CDB); } return; } logging(LOG_NORMAL, "No tests for devices that support protection information yet."); } libiscsi-1.17.0/test-tool/test_writesame16_0blocks.c000066400000000000000000000055611271371262700223410ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg 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, see . */ #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-test-cu.h" void test_writesame16_0blocks(void) { int ret; CHECK_FOR_DATALOSS; CHECK_FOR_SBC; if (!inq_bl) { CU_PASS("BlockLimits VPD is not available. Skipping test.\n"); return; } logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test WRITESAME16 0-blocks at LBA==0 (WSNZ=%d)", inq_bl->wsnz); memset(scratch, 0, block_size); if (inq_bl->wsnz) { WRITESAME16(sd, 0, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_INVALID_FIELD_IN_CDB); logging(LOG_NORMAL, "[SKIPPED] WRITESAME16 does not support 0-blocks."); return; } ret = writesame16(sd, 0, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); if (ret == -2) { logging(LOG_NORMAL, "[SKIPPED] WRITESAME16 is not implemented."); CU_PASS("[SKIPPED] Target does not support WRITESAME16. Skipping test"); return; } else if (ret == -3) { CU_PASS("[SKIPPED] Target does not support WRITESAME16 with NUMBER OF LOGICAL BLOCKS == 0"); } else if (ret == -4) { CU_PASS("[SKIPPED] Number of WRITESAME16 logical blocks to be written exceeds MAXIMUM WRITE SAME LENGTH"); } else { CU_ASSERT_EQUAL(ret, 0); } logging(LOG_VERBOSE, "Test WRITESAME16 0-blocks one block past end-of-LUN"); WRITESAME16(sd, num_blocks + 1, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_LBA_OOB); logging(LOG_VERBOSE, "Test WRITESAME16 0-blocks at LBA==2^63"); WRITESAME16(sd, 0x8000000000000000ULL, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_LBA_OOB); logging(LOG_VERBOSE, "Test WRITESAME16 0-blocks at LBA==-1"); WRITESAME16(sd, -1, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_LBA_OOB); } libiscsi-1.17.0/test-tool/test_writesame16_beyond_eol.c000066400000000000000000000043301271371262700231140ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg 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, see . */ #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-test-cu.h" void test_writesame16_beyond_eol(void) { int i; CHECK_FOR_DATALOSS; CHECK_FOR_SBC; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test WRITESAME16 1-256 blocks one block beyond the end"); memset(scratch, 0, block_size); for (i = 1; i <= 256; i++) { WRITESAME16(sd, num_blocks - i + 1, block_size, i, 0, 0, 0, 0, scratch, EXPECT_LBA_OOB); } logging(LOG_VERBOSE, "Test WRITESAME16 1-256 blocks at LBA==2^63"); for (i = 1; i <= 256; i++) { WRITESAME16(sd, 0x8000000000000000ULL, block_size, i, 0, 0, 0, 0, scratch, EXPECT_LBA_OOB); } logging(LOG_VERBOSE, "Test WRITESAME16 1-256 blocks at LBA==-1"); for (i = 1; i <= 256; i++) { WRITESAME16(sd, -1, block_size, i, 0, 0, 0, 0, scratch, EXPECT_LBA_OOB); } logging(LOG_VERBOSE, "Test WRITESAME16 2-256 blocks all but one block beyond the end"); for (i = 2; i <= 256; i++) { WRITESAME16(sd, num_blocks - 1, block_size, i, 0, 0, 0, 0, scratch, EXPECT_LBA_OOB); } } libiscsi-1.17.0/test-tool/test_writesame16_simple.c000066400000000000000000000032751271371262700222750ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg 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, see . */ #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-support.h" #include "iscsi-test-cu.h" void test_writesame16_simple(void) { int i; CHECK_FOR_DATALOSS; CHECK_FOR_SBC; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test WRITESAME16 of 1-256 blocks at the start of the LUN"); memset(scratch, 0, block_size); for (i = 1; i <= 256; i++) { WRITESAME16(sd, 0, block_size, i, 0, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); } logging(LOG_VERBOSE, "Test WRITESAME16 of 1-256 blocks at the end of the LUN"); for (i = 1; i <= 256; i++) { WRITESAME16(sd, num_blocks - i, block_size, i, 0, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); } } libiscsi-1.17.0/test-tool/test_writesame16_unmap.c000066400000000000000000000222231271371262700221160ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg 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, see . */ #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-support.h" #include "iscsi-test-cu.h" void test_writesame16_unmap(void) { unsigned int i; CHECK_FOR_DATALOSS; CHECK_FOR_THIN_PROVISIONING; CHECK_FOR_LBPWS; CHECK_FOR_SBC; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test WRITESAME16 of 1-256 blocks at the start of the LUN"); for (i = 1; i <= 256; i++) { logging(LOG_VERBOSE, "Write %d blocks of 0xFF", i); memset(scratch, 0xff, i * block_size); WRITE16(sd, 0, i * block_size, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Unmap %d blocks using WRITESAME16", i); memset(scratch, 0, block_size); WRITESAME16(sd, 0, block_size, i, 0, 1, 0, 0, scratch, EXPECT_STATUS_GOOD); if (rc16->lbprz) { logging(LOG_VERBOSE, "LBPRZ is set. Read the unmapped " "blocks back and verify they are all zero"); logging(LOG_VERBOSE, "Read %d blocks and verify they " "are now zero", i); READ16(sd, NULL, 0, i * block_size, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); ALL_ZERO(scratch, i * block_size); } else { logging(LOG_VERBOSE, "LBPRZ is clear. Skip the read " "and verify zero test"); } } logging(LOG_VERBOSE, "Test WRITESAME16 of 1-256 blocks at the end of the LUN"); for (i = 1; i <= 256; i++) { logging(LOG_VERBOSE, "Write %d blocks of 0xFF", i); memset(scratch, 0xff, i * block_size); WRITE16(sd, num_blocks - i, i * block_size, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Unmap %d blocks using WRITESAME16", i); memset(scratch, 0, block_size); WRITESAME16(sd, num_blocks - i, block_size, i, 0, 1, 0, 0, scratch, EXPECT_STATUS_GOOD); if (rc16->lbprz) { logging(LOG_VERBOSE, "LBPRZ is set. Read the unmapped " "blocks back and verify they are all zero"); logging(LOG_VERBOSE, "Read %d blocks and verify they " "are now zero", i); READ16(sd, NULL, num_blocks - i, i * block_size, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); ALL_ZERO(scratch, i * block_size); } else { logging(LOG_VERBOSE, "LBPRZ is clear. Skip the read " "and verify zero test"); } } logging(LOG_VERBOSE, "Verify that WRITESAME16 ANCHOR==1 + UNMAP==0 is invalid"); WRITESAME16(sd, 0, block_size, 1, 1, 0, 0, 0, scratch, EXPECT_INVALID_FIELD_IN_CDB); if (inq_lbp->anc_sup) { logging(LOG_VERBOSE, "Test WRITESAME16 ANCHOR==1 + UNMAP==0"); memset(scratch, 0, block_size); WRITESAME16(sd, 0, block_size, 1, 1, 1, 0, 0, scratch, EXPECT_STATUS_GOOD); } else { logging(LOG_VERBOSE, "Test WRITESAME16 ANCHOR==1 + UNMAP==0 no ANC_SUP so expecting to fail"); WRITESAME16(sd, 0, block_size, 1, 1, 1, 0, 0, scratch, EXPECT_INVALID_FIELD_IN_CDB); } if (inq_bl == NULL) { logging(LOG_VERBOSE, "[FAILED] WRITESAME16 works but " "BlockLimits VPD is missing."); CU_FAIL("[FAILED] WRITESAME16 works but " "BlockLimits VPD is missing."); return; } i = 256; if (i <= num_blocks && (inq_bl->max_ws_len == 0 || inq_bl->max_ws_len >= i)) { logging(LOG_VERBOSE, "Block Limits VPD page reports MAX_WS_LEN " "as either 0 (==no limit) or >= %d. Test Unmapping " "%d blocks to verify that it can handle 2-byte " "lengths", i, i); logging(LOG_VERBOSE, "Write %d blocks of 0xFF", i); memset(scratch, 0xff, i * block_size); WRITE16(sd, 0, i * block_size, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Unmap %d blocks using WRITESAME16", i); memset(scratch, 0, block_size); WRITESAME16(sd, 0, block_size, i, 0, 1, 0, 0, scratch, EXPECT_STATUS_GOOD); if (rc16->lbprz) { logging(LOG_VERBOSE, "LBPRZ is set. Read the unmapped " "blocks back and verify they are all zero"); logging(LOG_VERBOSE, "Read %d blocks and verify they " "are now zero", i); READ16(sd, NULL, 0, i * block_size, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); ALL_ZERO(scratch, i * block_size); } else { logging(LOG_VERBOSE, "LBPRZ is clear. Skip the read " "and verify zero test"); } } else if (i <= num_blocks) { logging(LOG_VERBOSE, "Block Limits VPD page reports MAX_WS_LEN " "as <256. Verify that a 256 block unmap fails with " "INVALID_FIELD_IN_CDB."); logging(LOG_VERBOSE, "Unmap %d blocks using WRITESAME16", i); WRITESAME16(sd, 0, block_size, i, 0, 1, 0, 0, scratch, EXPECT_INVALID_FIELD_IN_CDB); } i = 65536; if (i <= num_blocks && (inq_bl->max_ws_len == 0 || inq_bl->max_ws_len >= i)) { logging(LOG_VERBOSE, "Block Limits VPD page reports MAX_WS_LEN " "as either 0 (==no limit) or >= %d. Test Unmapping " "%d blocks to verify that it can handle 4-byte " "lengths", i, i); logging(LOG_VERBOSE, "Write %d blocks of 0xFF", i); memset(scratch, 0xff, i * block_size); WRITE16(sd, 0, i * block_size, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Unmap %d blocks using WRITESAME16", i); memset(scratch, 0, block_size); WRITESAME16(sd, 0, block_size, i, 0, 1, 0, 0, scratch, EXPECT_STATUS_GOOD); if (rc16->lbprz) { logging(LOG_VERBOSE, "LBPRZ is set. Read the unmapped " "blocks back and verify they are all zero"); logging(LOG_VERBOSE, "Read %d blocks and verify they " "are now zero", i); READ16(sd, NULL, 0, i * block_size, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); ALL_ZERO(scratch, i * block_size); } else { logging(LOG_VERBOSE, "LBPRZ is clear. Skip the read " "and verify zero test"); } } else if (i <= num_blocks) { logging(LOG_VERBOSE, "Block Limits VPD page reports MAX_WS_LEN " "as <256. Verify that a 256 block unmap fails with " "INVALID_FIELD_IN_CDB."); logging(LOG_VERBOSE, "Unmap %d blocks using WRITESAME16", i); WRITESAME16(sd, 0, block_size, i, 0, 1, 0, 0, scratch, EXPECT_INVALID_FIELD_IN_CDB); } } libiscsi-1.17.0/test-tool/test_writesame16_unmap_unaligned.c000066400000000000000000000030351271371262700241440ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg 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, see . */ #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-support.h" #include "iscsi-test-cu.h" void test_writesame16_unmap_unaligned(void) { int i; CHECK_FOR_DATALOSS; CHECK_FOR_THIN_PROVISIONING; CHECK_FOR_LBPWS; CHECK_FOR_LBPPB_GT_1; CHECK_FOR_SBC; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test that unaligned WRITESAME16 Unmap succeeds. LBPPB==%d", lbppb); for (i = 1; i < lbppb; i++) { logging(LOG_VERBOSE, "Unmap %d blocks using WRITESAME16 at LBA:%d", lbppb - i, i); WRITESAME16(sd, i, 0, lbppb - i, 0, 1, 0, 0, NULL, EXPECT_STATUS_GOOD); } } libiscsi-1.17.0/test-tool/test_writesame16_unmap_until_end.c000066400000000000000000000057031271371262700241630ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg 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, see . */ #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-support.h" #include "iscsi-test-cu.h" void test_writesame16_unmap_until_end(void) { unsigned int i; CHECK_FOR_DATALOSS; CHECK_FOR_THIN_PROVISIONING; CHECK_FOR_LBPWS; CHECK_FOR_SBC; if (inq_bl->wsnz) { logging(LOG_NORMAL, "[SKIPPED] WRITESAME10 does not support 0-blocks."); CU_PASS("[SKIPPED] WRITESAME10 does not support 0-blocks."); return; } logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test WRITESAME16 of 1-256 blocks at the end of the LUN by setting number-of-blocks==0"); memset(scratch, 0xa6, 256 * block_size); for (i = 1; i <= 256; i++) { logging(LOG_VERBOSE, "Write %d blocks of 0xFF", i); memset(scratch, 0xff, block_size * i); WRITE16(sd, num_blocks - i, i * block_size, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Unmap %d blocks using WRITESAME16", i); memset(scratch, 0, block_size); WRITESAME16(sd, num_blocks - i, block_size, 0, 0, 1, 0, 0, scratch, EXPECT_STATUS_GOOD); if (rc16->lbprz) { logging(LOG_VERBOSE, "LBPRZ is set. Read the unmapped " "blocks back and verify they are all zero"); logging(LOG_VERBOSE, "Read %d blocks and verify they " "are now zero", i); READ16(sd, NULL, num_blocks - i, i * block_size, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); ALL_ZERO(scratch, i * block_size); } else { logging(LOG_VERBOSE, "LBPRZ is clear. Skip the read " "and verify zero test"); } } } libiscsi-1.17.0/test-tool/test_writesame16_unmap_vpd.c000066400000000000000000000063371271371262700227770ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg 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, see . */ #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-support.h" #include "iscsi-test-cu.h" void test_writesame16_unmap_vpd(void) { int ret; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test WRITESAME16 UNMAP availability is " "consistent with VPD settings"); CHECK_FOR_DATALOSS; CHECK_FOR_SBC; logging(LOG_VERBOSE, "Check if WRITESAME16 can be used for UNMAP."); logging(LOG_VERBOSE, "Unmap 1 block using WRITESAME16"); memset(scratch, 0, block_size); ret = writesame16(sd, 0, block_size, 1, 0, 1, 0, 0, scratch, EXPECT_STATUS_GOOD); if (ret != 0) { logging(LOG_VERBOSE, "WRITESAME16 UNMAP is not available. " "Verify that VPD settings reflect this."); logging(LOG_VERBOSE, "Verify that LBPWS is clear."); if (inq_lbp && inq_lbp->lbpws) { logging(LOG_NORMAL, "[FAILED] WRITESAME16 UNMAP is not " "implemented but LBPWS is set"); CU_FAIL("[FAILED] WRITESAME16 UNMAP is unavailable but " "LBPWS==1"); } else { logging(LOG_VERBOSE, "[SUCCESS] LBPWS is clear."); } } else { logging(LOG_VERBOSE, "WRITESAME16 UNMAP is available. Verify " "that VPD settings reflect this."); logging(LOG_VERBOSE, "Verify that LBPME is set."); if (rc16 && rc16->lbpme) { logging(LOG_VERBOSE, "[SUCCESS] LBPME is set."); } else { logging(LOG_NORMAL, "[FAILED] WRITESAME16 UNMAP is " "implemented but LBPME is not set"); CU_FAIL("[FAILED] UNMAP is available but LBPME==0"); } logging(LOG_VERBOSE, "Verify that LBPWS is set."); if (inq_lbp && inq_lbp->lbpws) { logging(LOG_VERBOSE, "[SUCCESS] LBPWS is set."); } else { logging(LOG_NORMAL, "[FAILED] WRITESAME16 UNMAP is " "implemented but LBPWS is not set"); CU_FAIL("[FAILED] UNMAP is available but LBPWS==0"); } } } libiscsi-1.17.0/test-tool/test_writesame16_wrprotect.c000066400000000000000000000034751271371262700230370ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg 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, see . */ #include #include #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-test-cu.h" void test_writesame16_wrprotect(void) { int i; /* * Try out different non-zero values for WRPROTECT. */ logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test WRITESAME16 with non-zero WRPROTECT"); CHECK_FOR_DATALOSS; CHECK_FOR_SBC; memset(scratch, 0, block_size); if (!inq->protect || (rc16 != NULL && !rc16->prot_en)) { logging(LOG_VERBOSE, "Device does not support/use protection information. All commands should fail."); for (i = 1; i < 8; i++) { WRITESAME16(sd, 0, block_size, 1, 0, 0, i, 0, scratch, EXPECT_INVALID_FIELD_IN_CDB); } return; } logging(LOG_NORMAL, "No tests for devices that support protection information yet."); } libiscsi-1.17.0/test-tool/test_writeverify10_0blocks.c000066400000000000000000000036451271371262700227130ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg 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, see . */ #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-test-cu.h" void test_writeverify10_0blocks(void) { CHECK_FOR_DATALOSS; if (num_blocks >= 0x80000000) { CU_PASS("LUN is too big for read-beyond-eol tests with WRITEVERIFY10. Skipping test.\n"); return; } logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test WRITEVERIFY10 0-blocks at LBA==0"); WRITEVERIFY10(sd, 0, 0, block_size, 0, 0, 0, 0, NULL, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Test WRITEVERIFY10 0-blocks one block past end-of-LUN"); WRITEVERIFY10(sd, num_blocks + 1, 0, block_size, 0, 0, 0, 0, NULL, EXPECT_LBA_OOB); logging(LOG_VERBOSE, "Test WRITEVERIFY10 0-blocks at LBA==2^31"); WRITEVERIFY10(sd, 0x80000000, 0, block_size, 0, 0, 0, 0, NULL, EXPECT_LBA_OOB); logging(LOG_VERBOSE, "Test WRITEVERIFY10 0-blocks at LBA==-1"); WRITEVERIFY10(sd, -1, 0, block_size, 0, 0, 0, 0, NULL, EXPECT_LBA_OOB); } libiscsi-1.17.0/test-tool/test_writeverify10_beyond_eol.c000066400000000000000000000057431271371262700234760ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg 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, see . */ #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-test-cu.h" void test_writeverify10_beyond_eol(void) { int i; CHECK_FOR_DATALOSS; if (num_blocks >= 0x80000000) { CU_PASS("LUN is too big for write-beyond-eol tests with WRITEVERIFY10. Skipping test.\n"); return; } logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test WRITEVERIFY10 1-256 blocks one block beyond the end"); memset(scratch, 0xa6, 256 * block_size); for (i = 1; i <= 256; i++) { if (maximum_transfer_length && maximum_transfer_length < i) { break; } WRITEVERIFY10(sd, num_blocks + 1 - i, i * block_size, block_size, 0, 0, 0, 0, scratch, EXPECT_LBA_OOB); } logging(LOG_VERBOSE, "Test WRITEVERIFY10 1-256 blocks at LBA==2^31"); for (i = 1; i <= 256; i++) { if (maximum_transfer_length && maximum_transfer_length < i) { break; } WRITEVERIFY10(sd, 0x80000000, i * block_size, block_size, 0, 0, 0, 0, scratch, EXPECT_LBA_OOB); } logging(LOG_VERBOSE, "Test WRITEVERIFY10 1-256 blocks at LBA==-1"); for (i = 1; i <= 256; i++) { if (maximum_transfer_length && maximum_transfer_length < i) { break; } WRITEVERIFY10(sd, -1, i * block_size, block_size, 0, 0, 0, 0, scratch, EXPECT_LBA_OOB); } logging(LOG_VERBOSE, "Test WRITEVERIFY10 2-256 blocks all but one block beyond the end"); for (i = 2; i <= 256; i++) { if (maximum_transfer_length && maximum_transfer_length < i) { break; } WRITEVERIFY10(sd, num_blocks - 1, i * block_size, block_size, 0, 0, 0, 0, scratch, EXPECT_LBA_OOB); } } libiscsi-1.17.0/test-tool/test_writeverify10_dpo.c000066400000000000000000000073101271371262700221310ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2014 by Ronnie Sahlberg 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, see . */ #include #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-test-cu.h" void test_writeverify10_dpo(void) { int dpofua, usage_data_dpo; struct scsi_task *ms_task = NULL; struct scsi_mode_sense *ms; struct scsi_task *rso_task = NULL; struct scsi_report_supported_op_codes_one_command *rsoc; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test WRITEVERIFY10 DPO flag"); CHECK_FOR_SBC; READ10(sd, NULL, 0, block_size, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Read the DPOFUA flag from mode sense data"); MODESENSE6(sd, &ms_task, 0, SCSI_MODESENSE_PC_CURRENT, SCSI_MODEPAGE_RETURN_ALL_PAGES, 0, 255, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "[SUCCESS] Mode sense returned status GOOD"); ms = scsi_datain_unmarshall(ms_task); dpofua = ms && (ms->device_specific_parameter & 0x10); scsi_free_scsi_task(ms_task); if (dpofua) { logging(LOG_VERBOSE, "DPOFUA flag is set. Device should allow " "DPO/FUA flags in CDBs"); } else { logging(LOG_VERBOSE, "DPOFUA flag is clear. Device should fail " "CDBs with DPO/FUA set"); } logging(LOG_VERBOSE, "Test WRITEVERIFY10 with DPO==1"); if (dpofua) { WRITEVERIFY10(sd, 0, block_size, block_size, 0, 1, 0, 0, scratch, EXPECT_STATUS_GOOD); } else { WRITEVERIFY10(sd, 0, block_size, block_size, 0, 1, 0, 0, scratch, EXPECT_INVALID_FIELD_IN_CDB); } logging(LOG_VERBOSE, "Try fetching REPORT_SUPPORTED_OPCODES " "for WRITEVERIFY10"); REPORT_SUPPORTED_OPCODES(sd, &rso_task, 0, SCSI_REPORT_SUPPORTING_OPCODE, SCSI_OPCODE_WRITE_VERIFY10, 0, 65535, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Unmarshall the DATA-IN buffer"); rsoc = scsi_datain_unmarshall(rso_task); CU_ASSERT_PTR_NOT_NULL_FATAL(rsoc); usage_data_dpo = rsoc ? rsoc->cdb_usage_data[1] & 0x10 : -1; if (dpofua) { logging(LOG_VERBOSE, "DPOFUA is set. Verify the DPO flag " "is set in the CDB_USAGE_DATA"); CU_ASSERT_EQUAL(usage_data_dpo, 0x10); } else { logging(LOG_VERBOSE, "DPOFUA is clear. Verify the DPO " "flag is clear in the CDB_USAGE_DATA"); CU_ASSERT_EQUAL(usage_data_dpo, 0x00); } scsi_free_scsi_task(rso_task); } libiscsi-1.17.0/test-tool/test_writeverify10_flags.c000066400000000000000000000025501271371262700224440ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg 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, see . */ #include #include #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-test-cu.h" void test_writeverify10_flags(void) { CHECK_FOR_DATALOSS; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test WRITEVERIFY10 flags"); logging(LOG_VERBOSE, "Test WRITEVERIFY10 with BYTCHK==1"); memset(scratch, 0xa6, block_size); WRITEVERIFY10(sd, 0, block_size, block_size, 0, 0, 1, 0, scratch, EXPECT_STATUS_GOOD); } libiscsi-1.17.0/test-tool/test_writeverify10_residuals.c000066400000000000000000000373331271371262700233520ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 by Ronnie Sahlberg 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, see . */ #include #include #include #include #include "iscsi.h" #include "iscsi-private.h" #include "scsi-lowlevel.h" #include "iscsi-test-cu.h" void test_writeverify10_residuals(void) { struct scsi_task *task_ret; unsigned char buf[10000]; struct iscsi_data data; int ok; unsigned int i; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test WRITEVERIFY10 commands with residuals"); logging(LOG_VERBOSE, "Block size is %zu", block_size); CHECK_FOR_DATALOSS; CHECK_FOR_SBC; if (sd->iscsi_ctx == NULL) { const char *err = "[SKIPPED] This WRITEVERIFY10 test is only " "supported for iSCSI backends"; logging(LOG_NORMAL, "%s", err); CU_PASS(err); return; } /* check if writeverify10 is supported */ WRITEVERIFY10(sd, 0, 0, block_size, 0, 0, 0, 0, NULL, EXPECT_STATUS_GOOD); /* Try a writeverify10 of 1 block but xferlength == 0 */ task = malloc(sizeof(struct scsi_task)); CU_ASSERT_PTR_NOT_NULL_FATAL(task); memset(task, 0, sizeof(struct scsi_task)); task->cdb[0] = SCSI_OPCODE_WRITE_VERIFY10; task->cdb[8] = 1; task->cdb_size = 10; task->xfer_dir = SCSI_XFER_WRITE; task->expxferlen = 0; /* * we don't want autoreconnect since some targets will drop the session * on this condition. */ iscsi_set_noautoreconnect(sd->iscsi_ctx, 1); logging(LOG_VERBOSE, "Try writing one block but with iSCSI expected transfer length==0"); task_ret = iscsi_scsi_command_sync(sd->iscsi_ctx, sd->iscsi_lun, task, NULL); CU_ASSERT_PTR_NOT_NULL_FATAL(task_ret); CU_ASSERT_NOT_EQUAL(task->status, SCSI_STATUS_CANCELLED); /* XXX redundant? */ if (task->status == SCSI_STATUS_CHECK_CONDITION && task->sense.key == SCSI_SENSE_ILLEGAL_REQUEST && task->sense.ascq == SCSI_SENSE_ASCQ_INVALID_OPERATION_CODE) { logging(LOG_NORMAL, "[SKIPPED] WRITEVERIFY10 is not implemented."); CU_PASS("WRITEVERIFY10 is not implemented."); return; } logging(LOG_VERBOSE, "Verify that the target returned SUCCESS"); if (task->status != SCSI_STATUS_GOOD) { logging(LOG_VERBOSE, "[FAILED] Target returned error %s", iscsi_get_error(sd->iscsi_ctx)); } CU_ASSERT_EQUAL(task->status, SCSI_STATUS_GOOD); logging(LOG_VERBOSE, "Verify residual overflow flag is set"); if (task->residual_status != SCSI_RESIDUAL_OVERFLOW) { logging(LOG_VERBOSE, "[FAILED] Target did not set residual " "overflow flag"); } CU_ASSERT_EQUAL(task->residual_status, SCSI_RESIDUAL_OVERFLOW); logging(LOG_VERBOSE, "Verify we got %zu bytes of residual overflow", block_size); if (task->residual != block_size) { logging(LOG_VERBOSE, "[FAILED] Target did not set correct " "amount of residual. Expected %zu but got %zu.", block_size, task->residual); } CU_ASSERT_EQUAL(task->residual, block_size); scsi_free_scsi_task(task); task = NULL; /* in case the previous test failed the session */ iscsi_set_noautoreconnect(sd->iscsi_ctx, 0); logging(LOG_VERBOSE, "Try writing one block but with iSCSI expected transfer length==10000"); task = malloc(sizeof(struct scsi_task)); CU_ASSERT_PTR_NOT_NULL_FATAL(task); memset(task, 0, sizeof(struct scsi_task)); task->cdb[0] = SCSI_OPCODE_WRITE_VERIFY10; task->cdb[8] = 1; task->cdb_size = 10; task->xfer_dir = SCSI_XFER_WRITE; task->expxferlen = 10000; memset(buf, 0xa6, sizeof(buf)); data.size = task->expxferlen; data.data = &buf[0]; task_ret = iscsi_scsi_command_sync(sd->iscsi_ctx, sd->iscsi_lun, task, &data); CU_ASSERT_PTR_NOT_NULL_FATAL(task_ret); logging(LOG_VERBOSE, "Verify that the target returned SUCCESS"); if (task->status != SCSI_STATUS_GOOD) { logging(LOG_VERBOSE, "[FAILED] Target returned error %s", iscsi_get_error(sd->iscsi_ctx)); } CU_ASSERT_EQUAL(task->status, SCSI_STATUS_GOOD); logging(LOG_VERBOSE, "Verify residual underflow flag is set"); if (task->residual_status != SCSI_RESIDUAL_UNDERFLOW) { logging(LOG_VERBOSE, "[FAILED] Target did not set residual " "underflow flag"); } CU_ASSERT_EQUAL(task->residual_status, SCSI_RESIDUAL_UNDERFLOW); logging(LOG_VERBOSE, "Verify we got %zu bytes of residual underflow", 10000 - block_size); if (task->residual != 10000 - block_size) { logging(LOG_VERBOSE, "[FAILED] Target did not set correct " "amount of residual. Expected %zu but got %zu.", 10000 - block_size, task->residual); } CU_ASSERT_EQUAL(task->residual, 10000 - block_size); scsi_free_scsi_task(task); task = NULL; logging(LOG_VERBOSE, "Try writing one block but with iSCSI expected transfer length==200"); task = malloc(sizeof(struct scsi_task)); CU_ASSERT_PTR_NOT_NULL_FATAL(task); memset(task, 0, sizeof(struct scsi_task)); task->cdb[0] = SCSI_OPCODE_WRITE_VERIFY10; task->cdb[8] = 1; task->cdb_size = 10; task->xfer_dir = SCSI_XFER_WRITE; task->expxferlen = 200; data.size = task->expxferlen; data.data = &buf[0]; task_ret = iscsi_scsi_command_sync(sd->iscsi_ctx, sd->iscsi_lun, task, &data); CU_ASSERT_PTR_NOT_NULL_FATAL(task_ret); logging(LOG_VERBOSE, "Verify that the target returned SUCCESS"); ok = task->status == SCSI_STATUS_GOOD || (task->status == SCSI_STATUS_CHECK_CONDITION && task->sense.key == SCSI_SENSE_ILLEGAL_REQUEST && task->sense.ascq == SCSI_SENSE_ASCQ_INVALID_FIELD_IN_INFORMATION_UNIT); if (!ok) { logging(LOG_VERBOSE, "[FAILED] Target returned error %s", iscsi_get_error(sd->iscsi_ctx)); } CU_ASSERT(ok); logging(LOG_VERBOSE, "Verify residual overflow flag is set"); if (task->residual_status != SCSI_RESIDUAL_OVERFLOW) { logging(LOG_VERBOSE, "[FAILED] Target did not set residual " "overflow flag"); } CU_ASSERT_EQUAL(task->residual_status, SCSI_RESIDUAL_OVERFLOW); logging(LOG_VERBOSE, "Verify we got %zu bytes of residual overflow", block_size - 200); if (task->residual != block_size - 200) { logging(LOG_VERBOSE, "[FAILED] Target did not set correct " "amount of residual. Expected %zu but got %zu.", block_size - 200, task->residual); } CU_ASSERT_EQUAL(task->residual, block_size - 200); scsi_free_scsi_task(task); task = NULL; logging(LOG_VERBOSE, "Try writing two blocks but iSCSI expected " "transfer length==%zu (==one block)", block_size); task = malloc(sizeof(struct scsi_task)); CU_ASSERT_PTR_NOT_NULL_FATAL(task); memset(task, 0, sizeof(struct scsi_task)); task->cdb[0] = SCSI_OPCODE_WRITE_VERIFY10; task->cdb[8] = 2; task->cdb_size = 10; task->xfer_dir = SCSI_XFER_WRITE; task->expxferlen = block_size; data.size = task->expxferlen; data.data = &buf[0]; task_ret = iscsi_scsi_command_sync(sd->iscsi_ctx, sd->iscsi_lun, task, &data); CU_ASSERT_PTR_NOT_NULL_FATAL(task_ret); logging(LOG_VERBOSE, "Verify that the target returned SUCCESS"); if (task->status != SCSI_STATUS_GOOD) { logging(LOG_VERBOSE, "[FAILED] Target returned error %s", iscsi_get_error(sd->iscsi_ctx)); } CU_ASSERT_EQUAL(task->status, SCSI_STATUS_GOOD); logging(LOG_VERBOSE, "Verify residual overflow flag is set"); if (task->residual_status != SCSI_RESIDUAL_OVERFLOW) { logging(LOG_VERBOSE, "[FAILED] Target did not set residual " "overflow flag"); } CU_ASSERT_EQUAL(task->residual_status, SCSI_RESIDUAL_OVERFLOW); logging(LOG_VERBOSE, "Verify we got one block of residual overflow"); if (task->residual != block_size) { logging(LOG_VERBOSE, "[FAILED] Target did not set correct " "amount of residual. Expected %zu but got %zu.", block_size, task->residual); } CU_ASSERT_EQUAL(task->residual, block_size); scsi_free_scsi_task(task); task = NULL; logging(LOG_VERBOSE, "Verify that if iSCSI EDTL > SCSI TL then we only write SCSI TL amount of data"); logging(LOG_VERBOSE, "Write two blocks of 'a'"); memset(buf, 'a', 10000); WRITE10(sd, 0, 2 * block_size, block_size, 0, 0, 0, 0, 0, buf, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Write one block of 'b' but set iSCSI EDTL to 2 blocks."); task = malloc(sizeof(struct scsi_task)); CU_ASSERT_PTR_NOT_NULL_FATAL(task); memset(buf, 'b', 10000); memset(task, 0, sizeof(struct scsi_task)); task->cdb[0] = SCSI_OPCODE_WRITE_VERIFY10; task->cdb[8] = 1; task->cdb_size = 10; task->xfer_dir = SCSI_XFER_WRITE; task->expxferlen = 2 * block_size; data.size = task->expxferlen; data.data = &buf[0]; task_ret = iscsi_scsi_command_sync(sd->iscsi_ctx, sd->iscsi_lun, task, &data); CU_ASSERT_PTR_NOT_NULL_FATAL(task_ret); logging(LOG_VERBOSE, "Verify that the target returned SUCCESS"); if (task->status != SCSI_STATUS_GOOD) { logging(LOG_VERBOSE, "[FAILED] Target returned error %s", iscsi_get_error(sd->iscsi_ctx)); } CU_ASSERT_EQUAL(task->status, SCSI_STATUS_GOOD); logging(LOG_VERBOSE, "Verify residual underflow flag is set"); if (task->residual_status != SCSI_RESIDUAL_UNDERFLOW) { logging(LOG_VERBOSE, "[FAILED] Target did not set residual " "underflow flag"); } CU_ASSERT_EQUAL(task->residual_status, SCSI_RESIDUAL_UNDERFLOW); logging(LOG_VERBOSE, "Verify we got one block of residual underflow"); if (task->residual != block_size) { logging(LOG_VERBOSE, "[FAILED] Target did not set correct " "amount of residual. Expected %zu but got %zu.", block_size, task->residual); } CU_ASSERT_EQUAL(task->residual, block_size); scsi_free_scsi_task(task); task = NULL; logging(LOG_VERBOSE, "Read the two blocks"); READ10(sd, NULL, 0, 2* block_size, block_size, 0, 0, 0, 0, 0, buf, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Verify that the first block was changed to 'b'"); for (i = 0; i < block_size; i++) { if (buf[i] != 'b') { logging(LOG_NORMAL, "First block did not contain expected 'b'"); CU_FAIL("Block was not written correctly"); break; } } logging(LOG_VERBOSE, "Verify that the second block was NOT overwritten and still contains 'a'"); for (i = block_size; i < 2 * block_size; i++) { if (buf[i] != 'a') { logging(LOG_NORMAL, "Second block was overwritten and no longer contain 'a'"); CU_FAIL("Second block was incorrectly overwritten"); break; } } logging(LOG_VERBOSE, "Verify that if iSCSI EDTL < SCSI TL then we only write iSCSI EDTL amount of data"); logging(LOG_VERBOSE, "Write two blocks of 'a'"); memset(buf, 'a', 10000); WRITE10(sd, 0, 2 * block_size, block_size, 0, 0, 0, 0, 0, buf, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Write two blocks of 'b' but set iSCSI EDTL to 1 blocks."); task = malloc(sizeof(struct scsi_task)); CU_ASSERT_PTR_NOT_NULL_FATAL(task); memset(buf, 'b', 10000); memset(task, 0, sizeof(struct scsi_task)); task->cdb[0] = SCSI_OPCODE_WRITE_VERIFY10; task->cdb[8] = 2; task->cdb_size = 10; task->xfer_dir = SCSI_XFER_WRITE; task->expxferlen = block_size; data.size = task->expxferlen; data.data = &buf[0]; task_ret = iscsi_scsi_command_sync(sd->iscsi_ctx, sd->iscsi_lun, task, &data); CU_ASSERT_PTR_NOT_NULL_FATAL(task_ret); logging(LOG_VERBOSE, "Verify that the target returned SUCCESS"); if (task->status != SCSI_STATUS_GOOD) { logging(LOG_VERBOSE, "[FAILED] Target returned error %s", iscsi_get_error(sd->iscsi_ctx)); } CU_ASSERT_EQUAL(task->status, SCSI_STATUS_GOOD); logging(LOG_VERBOSE, "Verify residual overflow flag is set"); if (task->residual_status != SCSI_RESIDUAL_OVERFLOW) { logging(LOG_VERBOSE, "[FAILED] Target did not set residual " "overflow flag"); } CU_ASSERT_EQUAL(task->residual_status, SCSI_RESIDUAL_OVERFLOW); logging(LOG_VERBOSE, "Verify we got one block of residual overflow"); if (task->residual != block_size) { logging(LOG_VERBOSE, "[FAILED] Target did not set correct " "amount of residual. Expected %zu but got %zu.", block_size, task->residual); } CU_ASSERT_EQUAL(task->residual, block_size); scsi_free_scsi_task(task); task = NULL; logging(LOG_VERBOSE, "Read the two blocks"); READ10(sd, NULL, 0, 2* block_size, block_size, 0, 0, 0, 0, 0, buf, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Verify that the first block was changed to 'b'"); for (i = 0; i < block_size; i++) { if (buf[i] != 'b') { logging(LOG_NORMAL, "First block did not contain expected 'b'"); CU_FAIL("Block was not written correctly"); break; } } logging(LOG_VERBOSE, "Verify that the second block was NOT overwritten and still contains 'a'"); for (i = block_size; i < 2 * block_size; i++) { if (buf[i] != 'a') { logging(LOG_NORMAL, "Second block was overwritten and no longer contain 'a'"); CU_FAIL("Second block was incorrectly overwritten"); break; } } } libiscsi-1.17.0/test-tool/test_writeverify10_simple.c000066400000000000000000000037541271371262700226500ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg 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, see . */ #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-support.h" #include "iscsi-test-cu.h" void test_writeverify10_simple(void) { int i; CHECK_FOR_DATALOSS; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test WRITEVERIFY10 of 1-256 blocks at the start of the LUN"); memset(scratch, 0xa6, 256 * block_size); for (i = 1; i <= 256; i++) { if (maximum_transfer_length && maximum_transfer_length < i) { break; } WRITEVERIFY10(sd, 0, i * block_size, block_size, 0, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); } logging(LOG_VERBOSE, "Test WRITEVERIFY10 of 1-256 blocks at the end of the LUN"); for (i = 1; i <= 256; i++) { if (maximum_transfer_length && maximum_transfer_length < i) { break; } WRITEVERIFY10(sd, num_blocks - i, i * block_size, block_size, 0, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); } } libiscsi-1.17.0/test-tool/test_writeverify10_wrprotect.c000066400000000000000000000035671271371262700234120ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg 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, see . */ #include #include #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-test-cu.h" void test_writeverify10_wrprotect(void) { int i; /* * Try out different non-zero values for WRPROTECT. */ logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test WRITEVERIFY10 with non-zero WRPROTECT"); CHECK_FOR_DATALOSS; CHECK_FOR_SBC; memset(scratch, 0xa6, block_size); if (!inq->protect || (rc16 != NULL && !rc16->prot_en)) { logging(LOG_VERBOSE, "Device does not support/use protection information. All commands should fail."); for (i = 1; i < 8; i++) { WRITEVERIFY10(sd, 0, block_size, block_size, i, 0, 0, 0, scratch, EXPECT_INVALID_FIELD_IN_CDB); } return; } logging(LOG_NORMAL, "No tests for devices that support protection information yet."); } libiscsi-1.17.0/test-tool/test_writeverify12_0blocks.c000066400000000000000000000036451271371262700227150ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg 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, see . */ #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-test-cu.h" void test_writeverify12_0blocks(void) { CHECK_FOR_DATALOSS; if (num_blocks >= 0x80000000) { CU_PASS("LUN is too big for read-beyond-eol tests with WRITEVERIFY12. Skipping test.\n"); return; } logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test WRITEVERIFY12 0-blocks at LBA==0"); WRITEVERIFY12(sd, 0, 0, block_size, 0, 0, 0, 0, NULL, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Test WRITEVERIFY12 0-blocks one block past end-of-LUN"); WRITEVERIFY12(sd, num_blocks + 1, 0, block_size, 0, 0, 0, 0, NULL, EXPECT_LBA_OOB); logging(LOG_VERBOSE, "Test WRITEVERIFY12 0-blocks at LBA==2^31"); WRITEVERIFY12(sd, 0x80000000, 0, block_size, 0, 0, 0, 0, NULL, EXPECT_LBA_OOB); logging(LOG_VERBOSE, "Test WRITEVERIFY12 0-blocks at LBA==-1"); WRITEVERIFY12(sd, -1, 0, block_size, 0, 0, 0, 0, NULL, EXPECT_LBA_OOB); } libiscsi-1.17.0/test-tool/test_writeverify12_beyond_eol.c000066400000000000000000000057431271371262700235000ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg 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, see . */ #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-test-cu.h" void test_writeverify12_beyond_eol(void) { int i; CHECK_FOR_DATALOSS; if (num_blocks >= 0x80000000) { CU_PASS("LUN is too big for write-beyond-eol tests with WRITEVERIFY12. Skipping test.\n"); return; } logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test WRITEVERIFY12 1-256 blocks one block beyond the end"); memset(scratch, 0xa6, 256 * block_size); for (i = 1; i <= 256; i++) { if (maximum_transfer_length && maximum_transfer_length < i) { break; } WRITEVERIFY12(sd, num_blocks + 1 - i, i * block_size, block_size, 0, 0, 0, 0, scratch, EXPECT_LBA_OOB); } logging(LOG_VERBOSE, "Test WRITEVERIFY12 1-256 blocks at LBA==2^31"); for (i = 1; i <= 256; i++) { if (maximum_transfer_length && maximum_transfer_length < i) { break; } WRITEVERIFY12(sd, 0x80000000, i * block_size, block_size, 0, 0, 0, 0, scratch, EXPECT_LBA_OOB); } logging(LOG_VERBOSE, "Test WRITEVERIFY12 1-256 blocks at LBA==-1"); for (i = 1; i <= 256; i++) { if (maximum_transfer_length && maximum_transfer_length < i) { break; } WRITEVERIFY12(sd, -1, i * block_size, block_size, 0, 0, 0, 0, scratch, EXPECT_LBA_OOB); } logging(LOG_VERBOSE, "Test WRITEVERIFY12 2-256 blocks all but one block beyond the end"); for (i = 2; i <= 256; i++) { if (maximum_transfer_length && maximum_transfer_length < i) { break; } WRITEVERIFY12(sd, num_blocks - 1, i * block_size, block_size, 0, 0, 0, 0, scratch, EXPECT_LBA_OOB); } } libiscsi-1.17.0/test-tool/test_writeverify12_dpo.c000066400000000000000000000073071271371262700221410ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2014 by Ronnie Sahlberg 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, see . */ #include #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-test-cu.h" void test_writeverify12_dpo(void) { int dpofua, usage_data_dpo; struct scsi_task *ms_task = NULL; struct scsi_mode_sense *ms; struct scsi_task *rso_task = NULL; struct scsi_report_supported_op_codes_one_command *rsoc; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test WRITEVERIFY12 DPO flag"); CHECK_FOR_SBC; READ10(sd, NULL, 0, block_size, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Read the DPOFUA flag from mode sense data"); MODESENSE6(sd, &ms_task, 0, SCSI_MODESENSE_PC_CURRENT, SCSI_MODEPAGE_RETURN_ALL_PAGES, 0, 255, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "[SUCCESS] Mode sense returned status GOOD"); ms = scsi_datain_unmarshall(ms_task); dpofua = ms && (ms->device_specific_parameter & 0x10); scsi_free_scsi_task(ms_task); if (dpofua) { logging(LOG_VERBOSE, "DPOFUA flag is set. Device should allow " "DPO/FUA flags in CDBs"); } else { logging(LOG_VERBOSE, "DPOFUA flag is clear. Device should fail " "CDBs with DPO/FUA set"); } logging(LOG_VERBOSE, "Test WRITEVERIFY12 with DPO==1"); if (dpofua) { WRITEVERIFY12(sd, 0, block_size, block_size, 0, 1, 0, 0, scratch, EXPECT_STATUS_GOOD); } else { WRITEVERIFY12(sd, 0, block_size, block_size, 0, 1, 0, 0, scratch, EXPECT_INVALID_FIELD_IN_CDB); } logging(LOG_VERBOSE, "Try fetching REPORT_SUPPORTED_OPCODES " "for WRITEVERIFY12"); REPORT_SUPPORTED_OPCODES(sd, &rso_task, 0, SCSI_REPORT_SUPPORTING_OPCODE, SCSI_OPCODE_WRITE_VERIFY12, 0, 65535, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Unmarshall the DATA-IN buffer"); rsoc = scsi_datain_unmarshall(rso_task); CU_ASSERT_PTR_NOT_NULL_FATAL(rsoc); usage_data_dpo = rsoc ? rsoc->cdb_usage_data[1] & 0x10 : -1; if (dpofua) { logging(LOG_VERBOSE, "DPOFUA is set. Verify the DPO flag " "is set in the CDB_USAGE_DATA"); CU_ASSERT_EQUAL(usage_data_dpo, 0x10); } else { logging(LOG_VERBOSE, "DPOFUA is clear. Verify the DPO " "flag is clear in the CDB_USAGE_DATA"); CU_ASSERT_EQUAL(usage_data_dpo, 0x00); } scsi_free_scsi_task(rso_task); } libiscsi-1.17.0/test-tool/test_writeverify12_flags.c000066400000000000000000000025501271371262700224460ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg 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, see . */ #include #include #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-test-cu.h" void test_writeverify12_flags(void) { CHECK_FOR_DATALOSS; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test WRITEVERIFY12 flags"); logging(LOG_VERBOSE, "Test WRITEVERIFY12 with BYTCHK==1"); memset(scratch, 0xa6, block_size); WRITEVERIFY12(sd, 0, block_size, block_size, 0, 0, 1, 0, scratch, EXPECT_STATUS_GOOD); } libiscsi-1.17.0/test-tool/test_writeverify12_residuals.c000066400000000000000000000373331271371262700233540ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 by Ronnie Sahlberg 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, see . */ #include #include #include #include #include "iscsi.h" #include "iscsi-private.h" #include "scsi-lowlevel.h" #include "iscsi-test-cu.h" void test_writeverify12_residuals(void) { struct scsi_task *task_ret; unsigned char buf[10000]; struct iscsi_data data; int ok; unsigned int i; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test WRITEVERIFY12 commands with residuals"); logging(LOG_VERBOSE, "Block size is %zu", block_size); CHECK_FOR_DATALOSS; CHECK_FOR_SBC; if (sd->iscsi_ctx == NULL) { const char *err = "[SKIPPED] This WRITEVERIFY12 test is only " "supported for iSCSI backends"; logging(LOG_NORMAL, "%s", err); CU_PASS(err); return; } /* check if writeverify12 is supported */ WRITEVERIFY12(sd, 0, 0, block_size, 0, 0, 0, 0, NULL, EXPECT_STATUS_GOOD); /* Try a writeverify12 of 1 block but xferlength == 0 */ task = malloc(sizeof(struct scsi_task)); CU_ASSERT_PTR_NOT_NULL_FATAL(task); memset(task, 0, sizeof(struct scsi_task)); task->cdb[0] = SCSI_OPCODE_WRITE_VERIFY12; task->cdb[9] = 1; task->cdb_size = 12; task->xfer_dir = SCSI_XFER_WRITE; task->expxferlen = 0; /* * we don't want autoreconnect since some targets will drop the session * on this condition. */ iscsi_set_noautoreconnect(sd->iscsi_ctx, 1); logging(LOG_VERBOSE, "Try writing one block but with iSCSI expected transfer length==0"); task_ret = iscsi_scsi_command_sync(sd->iscsi_ctx, sd->iscsi_lun, task, NULL); CU_ASSERT_PTR_NOT_NULL_FATAL(task_ret); CU_ASSERT_NOT_EQUAL(task->status, SCSI_STATUS_CANCELLED); /* XXX redundant? */ if (task->status == SCSI_STATUS_CHECK_CONDITION && task->sense.key == SCSI_SENSE_ILLEGAL_REQUEST && task->sense.ascq == SCSI_SENSE_ASCQ_INVALID_OPERATION_CODE) { logging(LOG_NORMAL, "[SKIPPED] WRITEVERIFY12 is not implemented."); CU_PASS("WRITEVERIFY12 is not implemented."); return; } logging(LOG_VERBOSE, "Verify that the target returned SUCCESS"); if (task->status != SCSI_STATUS_GOOD) { logging(LOG_VERBOSE, "[FAILED] Target returned error %s", iscsi_get_error(sd->iscsi_ctx)); } CU_ASSERT_EQUAL(task->status, SCSI_STATUS_GOOD); logging(LOG_VERBOSE, "Verify residual overflow flag is set"); if (task->residual_status != SCSI_RESIDUAL_OVERFLOW) { logging(LOG_VERBOSE, "[FAILED] Target did not set residual " "overflow flag"); } CU_ASSERT_EQUAL(task->residual_status, SCSI_RESIDUAL_OVERFLOW); logging(LOG_VERBOSE, "Verify we got %zu bytes of residual overflow", block_size); if (task->residual != block_size) { logging(LOG_VERBOSE, "[FAILED] Target did not set correct " "amount of residual. Expected %zu but got %zu.", block_size, task->residual); } CU_ASSERT_EQUAL(task->residual, block_size); scsi_free_scsi_task(task); task = NULL; /* in case the previous test failed the session */ iscsi_set_noautoreconnect(sd->iscsi_ctx, 0); logging(LOG_VERBOSE, "Try writing one block but with iSCSI expected transfer length==10000"); task = malloc(sizeof(struct scsi_task)); CU_ASSERT_PTR_NOT_NULL_FATAL(task); memset(task, 0, sizeof(struct scsi_task)); task->cdb[0] = SCSI_OPCODE_WRITE_VERIFY12; task->cdb[9] = 1; task->cdb_size = 12; task->xfer_dir = SCSI_XFER_WRITE; task->expxferlen = 10000; memset(buf, 0xa6, sizeof(buf)); data.size = task->expxferlen; data.data = &buf[0]; task_ret = iscsi_scsi_command_sync(sd->iscsi_ctx, sd->iscsi_lun, task, &data); CU_ASSERT_PTR_NOT_NULL_FATAL(task_ret); logging(LOG_VERBOSE, "Verify that the target returned SUCCESS"); if (task->status != SCSI_STATUS_GOOD) { logging(LOG_VERBOSE, "[FAILED] Target returned error %s", iscsi_get_error(sd->iscsi_ctx)); } CU_ASSERT_EQUAL(task->status, SCSI_STATUS_GOOD); logging(LOG_VERBOSE, "Verify residual underflow flag is set"); if (task->residual_status != SCSI_RESIDUAL_UNDERFLOW) { logging(LOG_VERBOSE, "[FAILED] Target did not set residual " "underflow flag"); } CU_ASSERT_EQUAL(task->residual_status, SCSI_RESIDUAL_UNDERFLOW); logging(LOG_VERBOSE, "Verify we got %zu bytes of residual underflow", 10000 - block_size); if (task->residual != 10000 - block_size) { logging(LOG_VERBOSE, "[FAILED] Target did not set correct " "amount of residual. Expected %zu but got %zu.", 10000 - block_size, task->residual); } CU_ASSERT_EQUAL(task->residual, 10000 - block_size); scsi_free_scsi_task(task); task = NULL; logging(LOG_VERBOSE, "Try writing one block but with iSCSI expected transfer length==200"); task = malloc(sizeof(struct scsi_task)); CU_ASSERT_PTR_NOT_NULL_FATAL(task); memset(task, 0, sizeof(struct scsi_task)); task->cdb[0] = SCSI_OPCODE_WRITE_VERIFY12; task->cdb[9] = 1; task->cdb_size = 12; task->xfer_dir = SCSI_XFER_WRITE; task->expxferlen = 200; data.size = task->expxferlen; data.data = &buf[0]; task_ret = iscsi_scsi_command_sync(sd->iscsi_ctx, sd->iscsi_lun, task, &data); CU_ASSERT_PTR_NOT_NULL_FATAL(task_ret); logging(LOG_VERBOSE, "Verify that the target returned SUCCESS"); ok = task->status == SCSI_STATUS_GOOD || (task->status == SCSI_STATUS_CHECK_CONDITION && task->sense.key == SCSI_SENSE_ILLEGAL_REQUEST && task->sense.ascq == SCSI_SENSE_ASCQ_INVALID_FIELD_IN_INFORMATION_UNIT); if (!ok) { logging(LOG_VERBOSE, "[FAILED] Target returned error %s", iscsi_get_error(sd->iscsi_ctx)); } CU_ASSERT(ok); logging(LOG_VERBOSE, "Verify residual overflow flag is set"); if (task->residual_status != SCSI_RESIDUAL_OVERFLOW) { logging(LOG_VERBOSE, "[FAILED] Target did not set residual " "overflow flag"); } CU_ASSERT_EQUAL(task->residual_status, SCSI_RESIDUAL_OVERFLOW); logging(LOG_VERBOSE, "Verify we got %zu bytes of residual overflow", block_size - 200); if (task->residual != block_size - 200) { logging(LOG_VERBOSE, "[FAILED] Target did not set correct " "amount of residual. Expected %zu but got %zu.", block_size - 200, task->residual); } CU_ASSERT_EQUAL(task->residual, block_size - 200); scsi_free_scsi_task(task); task = NULL; logging(LOG_VERBOSE, "Try writing two blocks but iSCSI expected " "transfer length==%zu (==one block)", block_size); task = malloc(sizeof(struct scsi_task)); CU_ASSERT_PTR_NOT_NULL_FATAL(task); memset(task, 0, sizeof(struct scsi_task)); task->cdb[0] = SCSI_OPCODE_WRITE_VERIFY12; task->cdb[9] = 2; task->cdb_size = 12; task->xfer_dir = SCSI_XFER_WRITE; task->expxferlen = block_size; data.size = task->expxferlen; data.data = &buf[0]; task_ret = iscsi_scsi_command_sync(sd->iscsi_ctx, sd->iscsi_lun, task, &data); CU_ASSERT_PTR_NOT_NULL_FATAL(task_ret); logging(LOG_VERBOSE, "Verify that the target returned SUCCESS"); if (task->status != SCSI_STATUS_GOOD) { logging(LOG_VERBOSE, "[FAILED] Target returned error %s", iscsi_get_error(sd->iscsi_ctx)); } CU_ASSERT_EQUAL(task->status, SCSI_STATUS_GOOD); logging(LOG_VERBOSE, "Verify residual overflow flag is set"); if (task->residual_status != SCSI_RESIDUAL_OVERFLOW) { logging(LOG_VERBOSE, "[FAILED] Target did not set residual " "overflow flag"); } CU_ASSERT_EQUAL(task->residual_status, SCSI_RESIDUAL_OVERFLOW); logging(LOG_VERBOSE, "Verify we got one block of residual overflow"); if (task->residual != block_size) { logging(LOG_VERBOSE, "[FAILED] Target did not set correct " "amount of residual. Expected %zu but got %zu.", block_size, task->residual); } CU_ASSERT_EQUAL(task->residual, block_size); scsi_free_scsi_task(task); task = NULL; logging(LOG_VERBOSE, "Verify that if iSCSI EDTL > SCSI TL then we only write SCSI TL amount of data"); logging(LOG_VERBOSE, "Write two blocks of 'a'"); memset(buf, 'a', 10000); WRITE12(sd, 0, 2 * block_size, block_size, 0, 0, 0, 0, 0, buf, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Write one block of 'b' but set iSCSI EDTL to 2 blocks."); task = malloc(sizeof(struct scsi_task)); CU_ASSERT_PTR_NOT_NULL_FATAL(task); memset(buf, 'b', 10000); memset(task, 0, sizeof(struct scsi_task)); task->cdb[0] = SCSI_OPCODE_WRITE_VERIFY12; task->cdb[9] = 1; task->cdb_size = 12; task->xfer_dir = SCSI_XFER_WRITE; task->expxferlen = 2 * block_size; data.size = task->expxferlen; data.data = &buf[0]; task_ret = iscsi_scsi_command_sync(sd->iscsi_ctx, sd->iscsi_lun, task, &data); CU_ASSERT_PTR_NOT_NULL_FATAL(task_ret); logging(LOG_VERBOSE, "Verify that the target returned SUCCESS"); if (task->status != SCSI_STATUS_GOOD) { logging(LOG_VERBOSE, "[FAILED] Target returned error %s", iscsi_get_error(sd->iscsi_ctx)); } CU_ASSERT_EQUAL(task->status, SCSI_STATUS_GOOD); logging(LOG_VERBOSE, "Verify residual underflow flag is set"); if (task->residual_status != SCSI_RESIDUAL_UNDERFLOW) { logging(LOG_VERBOSE, "[FAILED] Target did not set residual " "underflow flag"); } CU_ASSERT_EQUAL(task->residual_status, SCSI_RESIDUAL_UNDERFLOW); logging(LOG_VERBOSE, "Verify we got one block of residual underflow"); if (task->residual != block_size) { logging(LOG_VERBOSE, "[FAILED] Target did not set correct " "amount of residual. Expected %zu but got %zu.", block_size, task->residual); } CU_ASSERT_EQUAL(task->residual, block_size); scsi_free_scsi_task(task); task = NULL; logging(LOG_VERBOSE, "Read the two blocks"); READ12(sd, NULL, 0, 2* block_size, block_size, 0, 0, 0, 0, 0, buf, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Verify that the first block was changed to 'b'"); for (i = 0; i < block_size; i++) { if (buf[i] != 'b') { logging(LOG_NORMAL, "First block did not contain expected 'b'"); CU_FAIL("Block was not written correctly"); break; } } logging(LOG_VERBOSE, "Verify that the second block was NOT overwritten and still contains 'a'"); for (i = block_size; i < 2 * block_size; i++) { if (buf[i] != 'a') { logging(LOG_NORMAL, "Second block was overwritten and no longer contain 'a'"); CU_FAIL("Second block was incorrectly overwritten"); break; } } logging(LOG_VERBOSE, "Verify that if iSCSI EDTL < SCSI TL then we only write iSCSI EDTL amount of data"); logging(LOG_VERBOSE, "Write two blocks of 'a'"); memset(buf, 'a', 10000); WRITE12(sd, 0, 2 * block_size, block_size, 0, 0, 0, 0, 0, buf, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Write two blocks of 'b' but set iSCSI EDTL to 1 blocks."); task = malloc(sizeof(struct scsi_task)); CU_ASSERT_PTR_NOT_NULL_FATAL(task); memset(buf, 'b', 10000); memset(task, 0, sizeof(struct scsi_task)); task->cdb[0] = SCSI_OPCODE_WRITE_VERIFY12; task->cdb[9] = 2; task->cdb_size = 12; task->xfer_dir = SCSI_XFER_WRITE; task->expxferlen = block_size; data.size = task->expxferlen; data.data = &buf[0]; task_ret = iscsi_scsi_command_sync(sd->iscsi_ctx, sd->iscsi_lun, task, &data); CU_ASSERT_PTR_NOT_NULL_FATAL(task_ret); logging(LOG_VERBOSE, "Verify that the target returned SUCCESS"); if (task->status != SCSI_STATUS_GOOD) { logging(LOG_VERBOSE, "[FAILED] Target returned error %s", iscsi_get_error(sd->iscsi_ctx)); } CU_ASSERT_EQUAL(task->status, SCSI_STATUS_GOOD); logging(LOG_VERBOSE, "Verify residual overflow flag is set"); if (task->residual_status != SCSI_RESIDUAL_OVERFLOW) { logging(LOG_VERBOSE, "[FAILED] Target did not set residual " "overflow flag"); } CU_ASSERT_EQUAL(task->residual_status, SCSI_RESIDUAL_OVERFLOW); logging(LOG_VERBOSE, "Verify we got one block of residual overflow"); if (task->residual != block_size) { logging(LOG_VERBOSE, "[FAILED] Target did not set correct " "amount of residual. Expected %zu but got %zu.", block_size, task->residual); } CU_ASSERT_EQUAL(task->residual, block_size); scsi_free_scsi_task(task); task = NULL; logging(LOG_VERBOSE, "Read the two blocks"); READ12(sd, NULL, 0, 2* block_size, block_size, 0, 0, 0, 0, 0, buf, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Verify that the first block was changed to 'b'"); for (i = 0; i < block_size; i++) { if (buf[i] != 'b') { logging(LOG_NORMAL, "First block did not contain expected 'b'"); CU_FAIL("Block was not written correctly"); break; } } logging(LOG_VERBOSE, "Verify that the second block was NOT overwritten and still contains 'a'"); for (i = block_size; i < 2 * block_size; i++) { if (buf[i] != 'a') { logging(LOG_NORMAL, "Second block was overwritten and no longer contain 'a'"); CU_FAIL("Second block was incorrectly overwritten"); break; } } } libiscsi-1.17.0/test-tool/test_writeverify12_simple.c000066400000000000000000000037461271371262700226530ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg 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, see . */ #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-support.h" #include "iscsi-test-cu.h" void test_writeverify12_simple(void) { int i; CHECK_FOR_DATALOSS; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test WRITEVERIFY12 of 1-256 blocks at the start of the LUN"); memset(scratch, 0xa6, 256 * block_size); for (i = 1; i <= 256; i++) { if (maximum_transfer_length && maximum_transfer_length < i) { break; } WRITEVERIFY12(sd, 0, i * block_size, block_size, 0, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); } logging(LOG_VERBOSE, "Test WRITE12 of 1-256 blocks at the end of the LUN"); for (i = 1; i <= 256; i++) { if (maximum_transfer_length && maximum_transfer_length < i) { break; } WRITEVERIFY12(sd, num_blocks - i, i * block_size, block_size, 0, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); } } libiscsi-1.17.0/test-tool/test_writeverify12_wrprotect.c000066400000000000000000000035671271371262700234140ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg 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, see . */ #include #include #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-test-cu.h" void test_writeverify12_wrprotect(void) { int i; /* * Try out different non-zero values for WRPROTECT. */ logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test WRITEVERIFY12 with non-zero WRPROTECT"); CHECK_FOR_DATALOSS; CHECK_FOR_SBC; memset(scratch, 0xa6, block_size); if (!inq->protect || (rc16 != NULL && !rc16->prot_en)) { logging(LOG_VERBOSE, "Device does not support/use protection information. All commands should fail."); for (i = 1; i < 8; i++) { WRITEVERIFY12(sd, 0, block_size, block_size, i, 0, 0, 0, scratch, EXPECT_INVALID_FIELD_IN_CDB); } return; } logging(LOG_NORMAL, "No tests for devices that support protection information yet."); } libiscsi-1.17.0/test-tool/test_writeverify16_0blocks.c000066400000000000000000000034501271371262700227130ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg 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, see . */ #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-test-cu.h" void test_writeverify16_0blocks(void) { CHECK_FOR_DATALOSS; CHECK_FOR_SBC; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test WRITEVERIFY16 0-blocks at LBA==0"); WRITEVERIFY16(sd, 0, 0, block_size, 0, 0, 0, 0, NULL, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Test WRITEVERIFY16 0-blocks one block past end-of-LUN"); WRITEVERIFY16(sd, num_blocks + 1, 0, block_size, 0, 0, 0, 0, NULL, EXPECT_LBA_OOB); logging(LOG_VERBOSE, "Test WRITEVERIFY16 0-blocks at LBA==2^63"); WRITEVERIFY16(sd, 0x8000000000000000ULL, 0, block_size, 0, 0, 0, 0, NULL, EXPECT_LBA_OOB); logging(LOG_VERBOSE, "Test WRITEVERIFY16 0-blocks at LBA==-1"); WRITEVERIFY16(sd, -1, 0, block_size, 0, 0, 0, 0, NULL, EXPECT_LBA_OOB); } libiscsi-1.17.0/test-tool/test_writeverify16_beyond_eol.c000066400000000000000000000055171271371262700235030ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg 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, see . */ #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-test-cu.h" void test_writeverify16_beyond_eol(void) { int i; CHECK_FOR_DATALOSS; CHECK_FOR_SBC; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test WRITEVERIFY16 1-256 blocks one block beyond the end"); memset(scratch, 0xa6, 256 * block_size); for (i = 1; i <= 256; i++) { if (maximum_transfer_length && maximum_transfer_length < i) { break; } WRITEVERIFY16(sd, num_blocks + 1 - i, i * block_size, block_size, 0, 0, 0, 0, scratch, EXPECT_LBA_OOB); } logging(LOG_VERBOSE, "Test WRITEVERIFY16 1-256 blocks at LBA==2^63"); for (i = 1; i <= 256; i++) { if (maximum_transfer_length && maximum_transfer_length < i) { break; } WRITEVERIFY16(sd, 0x8000000000000000ULL, i * block_size, block_size, 0, 0, 0, 0, scratch, EXPECT_LBA_OOB); } logging(LOG_VERBOSE, "Test WRITEVERIFY16 1-256 blocks at LBA==-1"); for (i = 1; i <= 256; i++) { if (maximum_transfer_length && maximum_transfer_length < i) { break; } WRITEVERIFY16(sd, -1, i * block_size, block_size, 0, 0, 0, 0, scratch, EXPECT_LBA_OOB); } logging(LOG_VERBOSE, "Test WRITEVERIFY16 2-256 blocks all but one block beyond the end"); for (i = 2; i <= 256; i++) { if (maximum_transfer_length && maximum_transfer_length < i) { break; } WRITEVERIFY16(sd, num_blocks - 1, i * block_size, block_size, 0, 0, 0, 0, scratch, EXPECT_LBA_OOB); } } libiscsi-1.17.0/test-tool/test_writeverify16_dpo.c000066400000000000000000000073631271371262700221470ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2014 by Ronnie Sahlberg 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, see . */ #include #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-test-cu.h" void test_writeverify16_dpo(void) { int dpofua, usage_data_dpo; struct scsi_task *ms_task = NULL; struct scsi_mode_sense *ms; struct scsi_task *rso_task = NULL; struct scsi_report_supported_op_codes_one_command *rsoc; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test WRITEVERIFY16 DPO flag"); CHECK_FOR_SBC; READ10(sd, NULL, 0, block_size, block_size, 0, 0, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Read the DPOFUA flag from mode sense data"); MODESENSE6(sd, &ms_task, 0, SCSI_MODESENSE_PC_CURRENT, SCSI_MODEPAGE_RETURN_ALL_PAGES, 0, 255, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "[SUCCESS] Mode sense returned status GOOD"); ms = scsi_datain_unmarshall(ms_task); dpofua = ms && (ms->device_specific_parameter & 0x10); scsi_free_scsi_task(ms_task); if (dpofua) { logging(LOG_VERBOSE, "DPOFUA flag is set. Device should allow " "DPO/FUA flags in CDBs"); } else { logging(LOG_VERBOSE, "DPOFUA flag is clear. Device should fail " "CDBs with DPO/FUA set"); } logging(LOG_VERBOSE, "Test WRITEVERIFY16 with DPO==1"); memset(scratch, 0xa6, block_size); if (dpofua) { WRITEVERIFY16(sd, 0, block_size, block_size, 0, 1, 0, 0, scratch, EXPECT_STATUS_GOOD); } else { WRITEVERIFY16(sd, 0, block_size, block_size, 0, 1, 0, 0, scratch, EXPECT_INVALID_FIELD_IN_CDB); } logging(LOG_VERBOSE, "Try fetching REPORT_SUPPORTED_OPCODES " "for WRITEVERIFY16"); REPORT_SUPPORTED_OPCODES(sd, &rso_task, 0, SCSI_REPORT_SUPPORTING_OPCODE, SCSI_OPCODE_WRITE_VERIFY16, 0, 65535, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Unmarshall the DATA-IN buffer"); rsoc = scsi_datain_unmarshall(rso_task); CU_ASSERT_PTR_NOT_NULL_FATAL(rsoc); usage_data_dpo = rsoc ? rsoc->cdb_usage_data[1] & 0x10 : -1; if (dpofua) { logging(LOG_VERBOSE, "DPOFUA is set. Verify the DPO flag " "is set in the CDB_USAGE_DATA"); CU_ASSERT_EQUAL(usage_data_dpo, 0x10); } else { logging(LOG_VERBOSE, "DPOFUA is clear. Verify the DPO " "flag is clear in the CDB_USAGE_DATA"); CU_ASSERT_EQUAL(usage_data_dpo, 0x00); } scsi_free_scsi_task(rso_task); } libiscsi-1.17.0/test-tool/test_writeverify16_flags.c000066400000000000000000000025501271371262700224520ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg 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, see . */ #include #include #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-test-cu.h" void test_writeverify16_flags(void) { CHECK_FOR_DATALOSS; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test WRITEVERIFY16 flags"); logging(LOG_VERBOSE, "Test WRITEVERIFY16 with BYTCHK==1"); memset(scratch, 0xa6, block_size); WRITEVERIFY16(sd, 0, block_size, block_size, 0, 0, 1, 0, scratch, EXPECT_STATUS_GOOD); } libiscsi-1.17.0/test-tool/test_writeverify16_residuals.c000066400000000000000000000377711271371262700233660ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 by Ronnie Sahlberg 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, see . */ #include #include #include #include #include "iscsi.h" #include "iscsi-private.h" #include "scsi-lowlevel.h" #include "iscsi-test-cu.h" void test_writeverify16_residuals(void) { struct scsi_task *task_ret; unsigned char buf[10000]; struct iscsi_data data; int ok; unsigned int i; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test WRITEVERIFY16 commands with residuals"); logging(LOG_VERBOSE, "Block size is %zu", block_size); CHECK_FOR_DATALOSS; CHECK_FOR_SBC; if (sd->iscsi_ctx == NULL) { const char *err = "[SKIPPED] This WRITEVERIFY16 test is only " "supported for iSCSI backends"; logging(LOG_NORMAL, "%s", err); CU_PASS(err); return; } /* check if writeverify16 is supported */ WRITEVERIFY16(sd, 0, 0, block_size, 0, 0, 0, 0, NULL, EXPECT_STATUS_GOOD); if (sd->iscsi_ctx == NULL) { const char *err = "[SKIPPED] WRITEVERIFY16 tests are only " "supported for iSCSI backends"; logging(LOG_NORMAL, "%s", err); CU_PASS(err); return; } /* Try a writeverify16 of 1 block but xferlength == 0 */ task = malloc(sizeof(struct scsi_task)); CU_ASSERT_PTR_NOT_NULL_FATAL(task); memset(task, 0, sizeof(struct scsi_task)); task->cdb[0] = SCSI_OPCODE_WRITE_VERIFY16; task->cdb[13] = 1; task->cdb_size = 16; task->xfer_dir = SCSI_XFER_WRITE; task->expxferlen = 0; /* * we don't want autoreconnect since some targets will drop the session * on this condition. */ iscsi_set_noautoreconnect(sd->iscsi_ctx, 1); logging(LOG_VERBOSE, "Try writing one block but with iSCSI expected transfer length==0"); task_ret = iscsi_scsi_command_sync(sd->iscsi_ctx, sd->iscsi_lun, task, NULL); CU_ASSERT_PTR_NOT_NULL_FATAL(task_ret); CU_ASSERT_NOT_EQUAL(task->status, SCSI_STATUS_CANCELLED); /* XXX redundant? */ if (task->status == SCSI_STATUS_CHECK_CONDITION && task->sense.key == SCSI_SENSE_ILLEGAL_REQUEST && task->sense.ascq == SCSI_SENSE_ASCQ_INVALID_OPERATION_CODE) { logging(LOG_NORMAL, "[SKIPPED] WRITEVERIFY16 is not implemented."); CU_PASS("WRITEVERIFY16 is not implemented."); return; } logging(LOG_VERBOSE, "Verify that the target returned SUCCESS"); if (task->status != SCSI_STATUS_GOOD) { logging(LOG_VERBOSE, "[FAILED] Target returned error %s", iscsi_get_error(sd->iscsi_ctx)); } CU_ASSERT_EQUAL(task->status, SCSI_STATUS_GOOD); logging(LOG_VERBOSE, "Verify residual overflow flag is set"); if (task->residual_status != SCSI_RESIDUAL_OVERFLOW) { logging(LOG_VERBOSE, "[FAILED] Target did not set residual " "overflow flag"); } CU_ASSERT_EQUAL(task->residual_status, SCSI_RESIDUAL_OVERFLOW); logging(LOG_VERBOSE, "Verify we got %zu bytes of residual overflow", block_size); if (task->residual != block_size) { logging(LOG_VERBOSE, "[FAILED] Target did not set correct " "amount of residual. Expected %zu but got %zu.", block_size, task->residual); } CU_ASSERT_EQUAL(task->residual, block_size); scsi_free_scsi_task(task); task = NULL; /* in case the previous test failed the session */ iscsi_set_noautoreconnect(sd->iscsi_ctx, 0); logging(LOG_VERBOSE, "Try writing one block but with iSCSI expected transfer length==10000"); task = malloc(sizeof(struct scsi_task)); CU_ASSERT_PTR_NOT_NULL_FATAL(task); memset(task, 0, sizeof(struct scsi_task)); task->cdb[0] = SCSI_OPCODE_WRITE_VERIFY16; task->cdb[13] = 1; task->cdb_size = 16; task->xfer_dir = SCSI_XFER_WRITE; task->expxferlen = 10000; memset(buf, 0xa6, sizeof(buf)); data.size = task->expxferlen; data.data = &buf[0]; task_ret = iscsi_scsi_command_sync(sd->iscsi_ctx, sd->iscsi_lun, task, &data); CU_ASSERT_PTR_NOT_NULL_FATAL(task_ret); logging(LOG_VERBOSE, "Verify that the target returned SUCCESS"); if (task->status != SCSI_STATUS_GOOD) { logging(LOG_VERBOSE, "[FAILED] Target returned error %s", iscsi_get_error(sd->iscsi_ctx)); } CU_ASSERT_EQUAL(task->status, SCSI_STATUS_GOOD); logging(LOG_VERBOSE, "Verify residual underflow flag is set"); if (task->residual_status != SCSI_RESIDUAL_UNDERFLOW) { logging(LOG_VERBOSE, "[FAILED] Target did not set residual " "underflow flag"); } CU_ASSERT_EQUAL(task->residual_status, SCSI_RESIDUAL_UNDERFLOW); logging(LOG_VERBOSE, "Verify we got %zu bytes of residual underflow", 10000 - block_size); if (task->residual != 10000 - block_size) { logging(LOG_VERBOSE, "[FAILED] Target did not set correct " "amount of residual. Expected %zu but got %zu.", 10000 - block_size, task->residual); } CU_ASSERT_EQUAL(task->residual, 10000 - block_size); scsi_free_scsi_task(task); task = NULL; logging(LOG_VERBOSE, "Try writing one block but with iSCSI expected transfer length==200"); task = malloc(sizeof(struct scsi_task)); CU_ASSERT_PTR_NOT_NULL_FATAL(task); memset(task, 0, sizeof(struct scsi_task)); task->cdb[0] = SCSI_OPCODE_WRITE_VERIFY16; task->cdb[13] = 1; task->cdb_size = 16; task->xfer_dir = SCSI_XFER_WRITE; task->expxferlen = 200; data.size = task->expxferlen; data.data = &buf[0]; task_ret = iscsi_scsi_command_sync(sd->iscsi_ctx, sd->iscsi_lun, task, &data); CU_ASSERT_PTR_NOT_NULL_FATAL(task_ret); logging(LOG_VERBOSE, "Verify that the target returned SUCCESS"); ok = task->status == SCSI_STATUS_GOOD || (task->status == SCSI_STATUS_CHECK_CONDITION && task->sense.key == SCSI_SENSE_ILLEGAL_REQUEST && task->sense.ascq == SCSI_SENSE_ASCQ_INVALID_FIELD_IN_INFORMATION_UNIT); if (!ok) { logging(LOG_VERBOSE, "[FAILED] Target returned error %s", iscsi_get_error(sd->iscsi_ctx)); } CU_ASSERT(ok); logging(LOG_VERBOSE, "Verify residual overflow flag is set"); if (task->residual_status != SCSI_RESIDUAL_OVERFLOW) { logging(LOG_VERBOSE, "[FAILED] Target did not set residual " "overflow flag"); } CU_ASSERT_EQUAL(task->residual_status, SCSI_RESIDUAL_OVERFLOW); logging(LOG_VERBOSE, "Verify we got %zu bytes of residual overflow", block_size - 200); if (task->residual != block_size - 200) { logging(LOG_VERBOSE, "[FAILED] Target did not set correct " "amount of residual. Expected %zu but got %zu.", block_size - 200, task->residual); } CU_ASSERT_EQUAL(task->residual, block_size - 200); scsi_free_scsi_task(task); task = NULL; logging(LOG_VERBOSE, "Try writing two blocks but iSCSI expected " "transfer length==%zu (==one block)", block_size); task = malloc(sizeof(struct scsi_task)); CU_ASSERT_PTR_NOT_NULL_FATAL(task); memset(task, 0, sizeof(struct scsi_task)); task->cdb[0] = SCSI_OPCODE_WRITE_VERIFY16; task->cdb[13] = 2; task->cdb_size = 16; task->xfer_dir = SCSI_XFER_WRITE; task->expxferlen = block_size; data.size = task->expxferlen; data.data = &buf[0]; task_ret = iscsi_scsi_command_sync(sd->iscsi_ctx, sd->iscsi_lun, task, &data); CU_ASSERT_PTR_NOT_NULL_FATAL(task_ret); logging(LOG_VERBOSE, "Verify that the target returned SUCCESS"); if (task->status != SCSI_STATUS_GOOD) { logging(LOG_VERBOSE, "[FAILED] Target returned error %s", iscsi_get_error(sd->iscsi_ctx)); } CU_ASSERT_EQUAL(task->status, SCSI_STATUS_GOOD); logging(LOG_VERBOSE, "Verify residual overflow flag is set"); if (task->residual_status != SCSI_RESIDUAL_OVERFLOW) { logging(LOG_VERBOSE, "[FAILED] Target did not set residual " "overflow flag"); } CU_ASSERT_EQUAL(task->residual_status, SCSI_RESIDUAL_OVERFLOW); logging(LOG_VERBOSE, "Verify we got one block of residual overflow"); if (task->residual != block_size) { logging(LOG_VERBOSE, "[FAILED] Target did not set correct " "amount of residual. Expected %zu but got %zu.", block_size, task->residual); } CU_ASSERT_EQUAL(task->residual, block_size); scsi_free_scsi_task(task); task = NULL; logging(LOG_VERBOSE, "Verify that if iSCSI EDTL > SCSI TL then we only write SCSI TL amount of data"); logging(LOG_VERBOSE, "Write two blocks of 'a'"); memset(buf, 'a', 10000); WRITE16(sd, 0, 2 * block_size, block_size, 0, 0, 0, 0, 0, buf, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Write one block of 'b' but set iSCSI EDTL to 2 blocks."); task = malloc(sizeof(struct scsi_task)); CU_ASSERT_PTR_NOT_NULL_FATAL(task); memset(buf, 'b', 10000); memset(task, 0, sizeof(struct scsi_task)); task->cdb[0] = SCSI_OPCODE_WRITE_VERIFY16; task->cdb[13] = 1; task->cdb_size = 16; task->xfer_dir = SCSI_XFER_WRITE; task->expxferlen = 2 * block_size; data.size = task->expxferlen; data.data = &buf[0]; task_ret = iscsi_scsi_command_sync(sd->iscsi_ctx, sd->iscsi_lun, task, &data); CU_ASSERT_PTR_NOT_NULL_FATAL(task_ret); logging(LOG_VERBOSE, "Verify that the target returned SUCCESS"); if (task->status != SCSI_STATUS_GOOD) { logging(LOG_VERBOSE, "[FAILED] Target returned error %s", iscsi_get_error(sd->iscsi_ctx)); } CU_ASSERT_EQUAL(task->status, SCSI_STATUS_GOOD); logging(LOG_VERBOSE, "Verify residual underflow flag is set"); if (task->residual_status != SCSI_RESIDUAL_UNDERFLOW) { logging(LOG_VERBOSE, "[FAILED] Target did not set residual " "underflow flag"); } CU_ASSERT_EQUAL(task->residual_status, SCSI_RESIDUAL_UNDERFLOW); logging(LOG_VERBOSE, "Verify we got one block of residual underflow"); if (task->residual != block_size) { logging(LOG_VERBOSE, "[FAILED] Target did not set correct " "amount of residual. Expected %zu but got %zu.", block_size, task->residual); } CU_ASSERT_EQUAL(task->residual, block_size); scsi_free_scsi_task(task); task = NULL; logging(LOG_VERBOSE, "Read the two blocks"); READ16(sd, NULL, 0, 2* block_size, block_size, 0, 0, 0, 0, 0, buf, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Verify that the first block was changed to 'b'"); for (i = 0; i < block_size; i++) { if (buf[i] != 'b') { logging(LOG_NORMAL, "First block did not contain expected 'b'"); CU_FAIL("Block was not written correctly"); break; } } logging(LOG_VERBOSE, "Verify that the second block was NOT overwritten and still contains 'a'"); for (i = block_size; i < 2 * block_size; i++) { if (buf[i] != 'a') { logging(LOG_NORMAL, "Second block was overwritten and no longer contain 'a'"); CU_FAIL("Second block was incorrectly overwritten"); break; } } logging(LOG_VERBOSE, "Verify that if iSCSI EDTL < SCSI TL then we only write iSCSI EDTL amount of data"); logging(LOG_VERBOSE, "Write two blocks of 'a'"); memset(buf, 'a', 10000); WRITE16(sd, 0, 2 * block_size, block_size, 0, 0, 0, 0, 0, buf, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Write two blocks of 'b' but set iSCSI EDTL to 1 blocks."); task = malloc(sizeof(struct scsi_task)); CU_ASSERT_PTR_NOT_NULL_FATAL(task); memset(buf, 'b', 10000); memset(task, 0, sizeof(struct scsi_task)); task->cdb[0] = SCSI_OPCODE_WRITE_VERIFY16; task->cdb[13] = 2; task->cdb_size = 16; task->xfer_dir = SCSI_XFER_WRITE; task->expxferlen = block_size; data.size = task->expxferlen; data.data = &buf[0]; task_ret = iscsi_scsi_command_sync(sd->iscsi_ctx, sd->iscsi_lun, task, &data); CU_ASSERT_PTR_NOT_NULL_FATAL(task_ret); logging(LOG_VERBOSE, "Verify that the target returned SUCCESS"); if (task->status != SCSI_STATUS_GOOD) { logging(LOG_VERBOSE, "[FAILED] Target returned error %s", iscsi_get_error(sd->iscsi_ctx)); } CU_ASSERT_EQUAL(task->status, SCSI_STATUS_GOOD); logging(LOG_VERBOSE, "Verify residual overflow flag is set"); if (task->residual_status != SCSI_RESIDUAL_OVERFLOW) { logging(LOG_VERBOSE, "[FAILED] Target did not set residual " "overflow flag"); } CU_ASSERT_EQUAL(task->residual_status, SCSI_RESIDUAL_OVERFLOW); logging(LOG_VERBOSE, "Verify we got one block of residual overflow"); if (task->residual != block_size) { logging(LOG_VERBOSE, "[FAILED] Target did not set correct " "amount of residual. Expected %zu but got %zu.", block_size, task->residual); } CU_ASSERT_EQUAL(task->residual, block_size); scsi_free_scsi_task(task); task = NULL; logging(LOG_VERBOSE, "Read the two blocks"); READ16(sd, NULL, 0, 2* block_size, block_size, 0, 0, 0, 0, 0, buf, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Verify that the first block was changed to 'b'"); for (i = 0; i < block_size; i++) { if (buf[i] != 'b') { logging(LOG_NORMAL, "First block did not contain expected 'b'"); CU_FAIL("Block was not written correctly"); break; } } logging(LOG_VERBOSE, "Verify that the second block was NOT overwritten and still contains 'a'"); for (i = block_size; i < 2 * block_size; i++) { if (buf[i] != 'a') { logging(LOG_NORMAL, "Second block was overwritten and no longer contain 'a'"); CU_FAIL("Second block was incorrectly overwritten"); break; } } } libiscsi-1.17.0/test-tool/test_writeverify16_simple.c000066400000000000000000000040031271371262700226420ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg 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, see . */ #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-support.h" #include "iscsi-test-cu.h" void test_writeverify16_simple(void) { int i; CHECK_FOR_DATALOSS; CHECK_FOR_SBC; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test WRITEVERIFY16 of 1-256 blocks at the start of the LUN"); memset(scratch, 0xa6, 256 * block_size); for (i = 1; i <= 256; i++) { if (maximum_transfer_length && maximum_transfer_length < i) { break; } WRITEVERIFY16(sd, 0, i * block_size, block_size, 0, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); } logging(LOG_VERBOSE, "Test WRITEVERIFY16 of 1-256 blocks at the end of the LUN"); for (i = 1; i <= 256; i++) { if (maximum_transfer_length && maximum_transfer_length < i) { break; } WRITEVERIFY16(sd, num_blocks - i, i * block_size, block_size, 0, 0, 0, 0, scratch, EXPECT_STATUS_GOOD); } } libiscsi-1.17.0/test-tool/test_writeverify16_wrprotect.c000066400000000000000000000035701271371262700234120ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 Ronnie Sahlberg 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, see . */ #include #include #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #include "iscsi-test-cu.h" void test_writeverify16_wrprotect(void) { int i; /* * Try out different non-zero values for WRPROTECT. */ logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test WRITEVERIFY16 with non-zero WRPROTECT"); CHECK_FOR_DATALOSS; CHECK_FOR_SBC; memset(scratch, 0xa6, block_size); if (!inq->protect || (rc16 != NULL && !rc16->prot_en)) { logging(LOG_VERBOSE, "Device does not support/use protection information. All commands should fail."); for (i = 1; i < 8; i++) { WRITEVERIFY16(sd, 0, block_size, block_size, i, 0, 0, 0, scratch, EXPECT_INVALID_FIELD_IN_CDB); } return; } logging(LOG_NORMAL, "No tests for devices that support protection information yet."); } libiscsi-1.17.0/tests/000077500000000000000000000000001271371262700145535ustar00rootroot00000000000000libiscsi-1.17.0/tests/.gitignore000066400000000000000000000001071271371262700165410ustar00rootroot00000000000000/prog_noop_reply /prog_reconnect /prog_reconnect_timeout /prog_timeout libiscsi-1.17.0/tests/Makefile.am000066400000000000000000000007001271371262700166040ustar00rootroot00000000000000AM_CPPFLAGS = -I../include "-D_U_=__attribute__((unused))" \ "-D_R_(A,B)=__attribute__((format(printf,A,B)))" AM_CFLAGS = $(WARN_CFLAGS) LDADD = ../lib/libiscsi.la noinst_PROGRAMS = prog_reconnect prog_reconnect_timeout prog_noop_reply \ prog_timeout T = `ls test_*.sh` test: $(noinst_PROGRAMS) for TEST in $(T); do \ echo "Running $$TEST"; \ echo "--------------"; \ sh $$TEST || exit 1; \ echo "--------------"; \ echo; \ done libiscsi-1.17.0/tests/README000066400000000000000000000001401271371262700154260ustar00rootroot00000000000000The tests require that TGTD version 1.0.58 or later is installed. To run the tests: make testlibiscsi-1.17.0/tests/functions.sh000066400000000000000000000036031271371262700171210ustar00rootroot00000000000000export TGT_IPC_SOCKET=`pwd`/tgtd.socket TGTD="tgtd" TGTADM="tgtadm" TGTLUN=`pwd`/LUN TGTPORTAL=127.0.0.1:3269 IQNTARGET=iqn.libiscsi.unittest.target IQNINITIATOR=iqn.libiscsi.unittest.initiator TGTURL=iscsi://${TGTPORTAL}/${IQNTARGET}/1 start_target() { # in case we have one still running from a previous run ${TGTADM} --op delete --force --mode target --tid 1 2>/dev/null ${TGTADM} --op delete --mode system 2>/dev/null # Setup target echo "Starting iSCSI target" ${TGTD} --iscsi portal=${TGTPORTAL},${1} sleep 1 ${TGTADM} --op new --mode target --tid 1 -T ${IQNTARGET} ${TGTADM} --op bind --mode target --tid 1 -I ALL #${TGTADM} --op show --mode target } shutdown_target() { # Remove target echo "Shutting down iSCSI target" ${TGTADM} --op delete --force --mode target --tid 1 ${TGTADM} --op delete --mode system } create_lun() { # Setup LUN truncate --size=100M ${TGTLUN} ${TGTADM} --op new --mode logicalunit --tid 1 --lun 1 -b ${TGTLUN} --blocksize=4096 } delete_lun() { # Remove LUN rm ${TGTLUN} } create_disk_lun() { # Setup LUN truncate --size=$2 ${TGTLUN}.$1 ${TGTADM} --op new --mode logicalunit --tid 1 --lun $1 -b ${TGTLUN}.$1 --blocksize=512 } delete_disk_lun() { # Remove LUN rm ${TGTLUN}.$1 } add_disk_lun() { ${TGTADM} --op new --mode logicalunit --tid 1 --lun $1 -b ${TGTLUN}.$1 --blocksize=512 } remove_disk_lun() { ${TGTADM} --op delete --mode logicalunit --tid 1 --lun $1 } setup_chap() { ${TGTADM} --op new --mode account --user libiscsi --password libiscsi ${TGTADM} --op bind --mode account --tid 1 --user libiscsi ${TGTADM} --op new --mode account --user outgoing --password outgoing ${TGTADM} --op bind --mode account --tid 1 --user outgoing --outgoing } success() { echo "[OK]" rm ${TEST_TMP} 2> /dev/null } failure() { echo "[FAILED]" exit 1 } libiscsi-1.17.0/tests/prog_noop_reply.c000066400000000000000000000142431271371262700201400ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2015 by Ronnie Sahlberg 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, see . */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #ifdef HAVE_POLL_H #include #endif #ifdef HAVE_UNISTD_H #include #endif #include #include #include #include #include #include #include "iscsi.h" #include "iscsi-private.h" #include "scsi-lowlevel.h" #ifndef discard_const #define discard_const(ptr) ((void *)((intptr_t)(ptr))) #endif const char *initiator = "iqn.2007-10.com.github:sahlberg:libiscsi:prog-noop-reply"; struct client_state { int finished; int status; int lun; }; #define TIMER_START(x) gettimeofday(&x, NULL) #define TIMER_ELAPSED(x, y) do { \ struct timeval t; \ int wrap = 0; \ gettimeofday(&t, NULL); \ if (t.tv_usec < x.tv_usec) wrap = 1; \ y.tv_sec = t.tv_sec - x.tv_sec - wrap; \ y.tv_usec = wrap * 10000000 + t.tv_usec - x.tv_usec; \ } while(0) void event_loop(struct iscsi_context *iscsi, struct client_state *state, int timeout) { struct pollfd pfd; struct timeval start_time, elapsed_time; TIMER_START(start_time); while (state->finished == 0) { pfd.fd = iscsi_get_fd(iscsi); pfd.events = iscsi_which_events(iscsi); if (poll(&pfd, 1, 1000) < 0) { fprintf(stderr, "Poll failed"); exit(10); } if (iscsi_service(iscsi, pfd.revents) < 0) { fprintf(stderr, "iscsi_service failed with : %s\n", iscsi_get_error(iscsi)); exit(10); } TIMER_ELAPSED(start_time, elapsed_time); if (timeout && elapsed_time.tv_sec > timeout) { break; } } } void tur_cb(struct iscsi_context *iscsi _U_, int status, void *command_data _U_, void *private_data) { struct client_state *state = (struct client_state *)private_data; if (status != 0) { fprintf(stderr, "TestUnitReady failed\n"); state->status = status; } state->finished = 1; } void print_usage(void) { fprintf(stderr, "Usage: prog_noop_reply [-?|--help] [--usage] " "[-i|--initiator-name=iqn-name]\n" "\t\t\n"); fprintf(stderr, "\n"); fprintf(stderr, "This command is used to test that if the target " "disconnects libiscsi will automatically reconnect and " "re-issue all queued tasks.\n"); } void print_help(void) { fprintf(stderr, "Usage: prog_noop_reply [OPTION...] \n"); fprintf(stderr, " -i, --initiator-name=iqn-name " "Initiatorname to use\n"); fprintf(stderr, "\n"); fprintf(stderr, "Help options:\n"); fprintf(stderr, " -?, --help " "Show this help message\n"); fprintf(stderr, " --usage " "Display brief usage message\n"); fprintf(stderr, "\n"); fprintf(stderr, "iSCSI Portal URL format : %s\n", ISCSI_PORTAL_URL_SYNTAX); fprintf(stderr, "\n"); fprintf(stderr, " is either of:\n"); fprintf(stderr, " \"hostname\" iscsi.example\n"); fprintf(stderr, " \"ipv4-address\" 10.1.1.27\n"); fprintf(stderr, " \"ipv6-address\" [fce0::1]\n"); } int main(int argc, char *argv[]) { struct iscsi_context *iscsi; struct iscsi_url *iscsi_url = NULL; struct client_state state; const char *url = NULL; int c; static int show_help = 0, show_usage = 0, debug = 0; static struct option long_options[] = { {"help", no_argument, NULL, 'h'}, {"usage", no_argument, NULL, 'u'}, {"debug", no_argument, NULL, 'd'}, {"initiator-name", required_argument, NULL, 'i'}, {0, 0, 0, 0} }; int option_index; while ((c = getopt_long(argc, argv, "h?uUdi:s", long_options, &option_index)) != -1) { switch (c) { case 'h': case '?': show_help = 1; break; case 'u': show_usage = 1; break; case 'd': debug = 1; break; case 'i': initiator = optarg; break; default: fprintf(stderr, "Unrecognized option '%c'\n\n", c); print_help(); exit(0); } } if (show_help != 0) { print_help(); exit(0); } if (show_usage != 0) { print_usage(); exit(0); } if (optind != argc -1) { print_usage(); exit(0); } memset(&state, 0, sizeof(state)); if (argv[optind] != NULL) { url = strdup(argv[optind]); } if (url == NULL) { fprintf(stderr, "You must specify iscsi target portal.\n"); print_usage(); exit(10); } iscsi = iscsi_create_context(initiator); if (iscsi == NULL) { printf("Failed to create context\n"); exit(10); } if (debug > 0) { iscsi_set_log_level(iscsi, debug); iscsi_set_log_fn(iscsi, iscsi_log_to_stderr); } iscsi_url = iscsi_parse_full_url(iscsi, url); if (url) { free(discard_const(url)); } if (iscsi_url == NULL) { fprintf(stderr, "Failed to parse URL: %s\n", iscsi_get_error(iscsi)); exit(10); } iscsi_set_session_type(iscsi, ISCSI_SESSION_NORMAL); state.lun = iscsi_url->lun; if (iscsi_full_connect_sync(iscsi, iscsi_url->portal, iscsi_url->lun) != 0) { fprintf(stderr, "iscsi_connect failed. %s\n", iscsi_get_error(iscsi)); exit(10); } printf("Disable iscsi reconnect on session failure\n"); iscsi_set_noautoreconnect(iscsi, 1); printf("Run the event loop and reply to NOPs for 5 seconds\n"); event_loop(iscsi, &state, 5); printf("Verify that the connection still works\n"); if (iscsi_testunitready_task(iscsi, state.lun, tur_cb, &state) == NULL) { fprintf(stderr, "testunitready failed\n"); exit(10); } event_loop(iscsi, &state, 3); iscsi_destroy_url(iscsi_url); iscsi_disconnect(iscsi); iscsi_destroy_context(iscsi); if (state.status != 0) { exit(10); } return 0; } libiscsi-1.17.0/tests/prog_reconnect.c000066400000000000000000000211461271371262700177320ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2015 by Ronnie Sahlberg 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, see . */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #ifdef HAVE_POLL_H #include #endif #ifdef HAVE_UNISTD_H #include #endif #include #include #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #ifndef discard_const #define discard_const(ptr) ((void *)((intptr_t)(ptr))) #endif const char *initiator = "iqn.2007-10.com.github:sahlberg:libiscsi:prog-reconnect"; struct client_state { int finished; int status; int lun; int concurrency; int read_pos; int num_remaining; uint32_t block_size; uint32_t num_lbas; }; struct read16_state { uint32_t lba; struct client_state *client; }; void event_loop(struct iscsi_context *iscsi, struct client_state *state) { struct pollfd pfd; while (state->finished == 0) { pfd.fd = iscsi_get_fd(iscsi); pfd.events = iscsi_which_events(iscsi); if (poll(&pfd, 1, -1) < 0) { fprintf(stderr, "Poll failed"); exit(10); } if (iscsi_service(iscsi, pfd.revents) < 0) { fprintf(stderr, "iscsi_service failed with : %s\n", iscsi_get_error(iscsi)); exit(10); } } } void logout_cb(struct iscsi_context *iscsi, int status, void *command_data _U_, void *private_data) { struct client_state *state = (struct client_state *)private_data; if (status != 0) { fprintf(stderr, "Failed to logout from target. : %s\n", iscsi_get_error(iscsi)); exit(10); } if (iscsi_disconnect(iscsi) != 0) { fprintf(stderr, "Failed to disconnect old socket\n"); exit(10); } state->finished = 1; } void read_cb(struct iscsi_context *iscsi, int status, void *command_data, void *private_data) { struct read16_state *r16_state = private_data; struct client_state *state = r16_state->client; struct scsi_task *task = command_data; printf("READ returned for LBA %d\n", (int)r16_state->lba); if (status == SCSI_STATUS_CHECK_CONDITION && task->sense.key == SCSI_SENSE_UNIT_ATTENTION) { printf("Received UNIT_ATTENTION. Ignoring.\n"); } else if (status != 0) { fprintf(stderr, "READ16 failed. %s\n", iscsi_get_error(iscsi)); scsi_free_scsi_task(task); exit(10); } if (state->read_pos == 6) { char buf[256]; ssize_t count; printf("write garbage to the socket to trigger a server " "disconnect\n"); memset(buf, 0, sizeof(buf)); count = write(iscsi_get_fd(iscsi), buf, sizeof(buf)); if (count < (ssize_t)sizeof(buf)) { fprintf(stderr, "write failed.\n"); scsi_free_scsi_task(task); exit(10); } } free(r16_state); scsi_free_scsi_task(task); if (state->num_remaining > state->concurrency) { r16_state = malloc(sizeof(struct read16_state)); r16_state->lba = state->read_pos++; r16_state->client = state; printf("SENT READ for LBA %d\n", r16_state->lba); if (iscsi_read16_task(iscsi, state->lun, r16_state->lba, state->block_size, state->block_size, 0, 0, 0, 0, 0, read_cb, r16_state) == NULL) { fprintf(stderr, "iscsi_read16_task failed : %s\n", iscsi_get_error(iscsi)); exit(10); } } if (--state->num_remaining) { return; } if (iscsi_logout_async(iscsi, logout_cb, state) != 0) { fprintf(stderr, "iscsi_logout_async failed : %s\n", iscsi_get_error(iscsi)); exit(10); } } void print_usage(void) { fprintf(stderr, "Usage: prog_reconnect [-?|--help] [--usage] " "[-i|--initiator-name=iqn-name]\n" "\t\t\n"); fprintf(stderr, "\n"); fprintf(stderr, "This command is used to test that if the target " "disconnects libiscsi will automatically reconnect and " "re-issue all queued tasks.\n"); } void print_help(void) { fprintf(stderr, "Usage: prog_reconnect [OPTION...] \n"); fprintf(stderr, " -i, --initiator-name=iqn-name " "Initiatorname to use\n"); fprintf(stderr, "\n"); fprintf(stderr, "Help options:\n"); fprintf(stderr, " -?, --help " "Show this help message\n"); fprintf(stderr, " --usage " "Display brief usage message\n"); fprintf(stderr, "\n"); fprintf(stderr, "iSCSI Portal URL format : %s\n", ISCSI_PORTAL_URL_SYNTAX); fprintf(stderr, "\n"); fprintf(stderr, " is either of:\n"); fprintf(stderr, " \"hostname\" iscsi.example\n"); fprintf(stderr, " \"ipv4-address\" 10.1.1.27\n"); fprintf(stderr, " \"ipv6-address\" [fce0::1]\n"); } int main(int argc, char *argv[]) { struct iscsi_context *iscsi; struct iscsi_url *iscsi_url = NULL; struct client_state state; const char *url = NULL; int i, c; static int show_help = 0, show_usage = 0, debug = 0; struct scsi_readcapacity10 *rc10; struct scsi_task *task; static struct option long_options[] = { {"help", no_argument, NULL, 'h'}, {"usage", no_argument, NULL, 'u'}, {"debug", no_argument, NULL, 'd'}, {"initiator-name", required_argument, NULL, 'i'}, {0, 0, 0, 0} }; int option_index; while ((c = getopt_long(argc, argv, "h?uUdi:s", long_options, &option_index)) != -1) { switch (c) { case 'h': case '?': show_help = 1; break; case 'u': show_usage = 1; break; case 'd': debug = 1; break; case 'i': initiator = optarg; break; default: fprintf(stderr, "Unrecognized option '%c'\n\n", c); print_help(); exit(0); } } if (show_help != 0) { print_help(); exit(0); } if (show_usage != 0) { print_usage(); exit(0); } if (optind != argc -1) { print_usage(); exit(0); } memset(&state, 0, sizeof(state)); if (argv[optind] != NULL) { url = strdup(argv[optind]); } if (url == NULL) { fprintf(stderr, "You must specify iscsi target portal.\n"); print_usage(); exit(10); } iscsi = iscsi_create_context(initiator); if (iscsi == NULL) { printf("Failed to create context\n"); exit(10); } if (debug > 0) { iscsi_set_log_level(iscsi, debug); iscsi_set_log_fn(iscsi, iscsi_log_to_stderr); } iscsi_url = iscsi_parse_full_url(iscsi, url); if (url) { free(discard_const(url)); } if (iscsi_url == NULL) { fprintf(stderr, "Failed to parse URL: %s\n", iscsi_get_error(iscsi)); exit(10); } iscsi_set_session_type(iscsi, ISCSI_SESSION_NORMAL); state.lun = iscsi_url->lun; if (iscsi_full_connect_sync(iscsi, iscsi_url->portal, iscsi_url->lun) != 0) { fprintf(stderr, "iscsi_connect failed. %s\n", iscsi_get_error(iscsi)); exit(10); } task = iscsi_readcapacity10_sync(iscsi, iscsi_url->lun, 0, 0); if (task == NULL || task->status != SCSI_STATUS_GOOD) { fprintf(stderr, "failed to send readcapacity command\n"); exit(10); } rc10 = scsi_datain_unmarshall(task); if (rc10 == NULL) { fprintf(stderr, "failed to unmarshall readcapacity10 data\n"); exit(10); } state.block_size = rc10->block_size; state.num_lbas = rc10->lba; scsi_free_scsi_task(task); state.num_remaining = 10; state.concurrency = 3; /* Queue up a bunch of READ16 calls and then send more are * the replies come trickling in. Once all num_remaining * reads have been processed we will log out and end the test. */ for (i = 0; i < state.concurrency; i++) { struct read16_state *r16_state; r16_state = malloc(sizeof(struct read16_state)); r16_state->lba = state.read_pos++; r16_state->client = &state; printf("SENT READ for LBA %d\n", r16_state->lba); if (iscsi_read16_task(iscsi, state.lun, r16_state->lba, state.block_size, state.block_size, 0, 0, 0, 0, 0, read_cb, r16_state) == NULL) { fprintf(stderr, "iscsi_read16_task failed : %s\n", iscsi_get_error(iscsi)); exit(10); } } event_loop(iscsi, &state); iscsi_destroy_url(iscsi_url); iscsi_destroy_context(iscsi); return 0; } libiscsi-1.17.0/tests/prog_reconnect_timeout.c000066400000000000000000000227371271371262700215070ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2015 by Ronnie Sahlberg 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, see . */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #ifdef HAVE_POLL_H #include #endif #ifdef HAVE_UNISTD_H #include #endif #include #include #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #ifndef discard_const #define discard_const(ptr) ((void *)((intptr_t)(ptr))) #endif const char *initiator = "iqn.2007-10.com.github:sahlberg:libiscsi:prog-reconnect-timeout"; struct client_state { int finished; int status; int lun; int concurrency; int read_pos; int num_remaining; uint32_t block_size; uint32_t num_lbas; char portal[MAX_STRING_SIZE]; int got_zero_events; }; struct read16_state { uint32_t lba; struct client_state *client; }; void event_loop(struct iscsi_context *iscsi, struct client_state *state) { struct pollfd pfd; while (state->finished == 0) { pfd.fd = iscsi_get_fd(iscsi); pfd.events = iscsi_which_events(iscsi); if (pfd.events == 0) { state->got_zero_events = 1; printf("iscsi_which_events() returned 0\n"); sleep(1); printf("change portal back to the right portal\n"); iscsi_full_connect_async(iscsi, state->portal, state->lun, NULL, NULL); } if (poll(&pfd, 1, -1) < 0) { fprintf(stderr, "Poll failed"); exit(10); } if (iscsi_service(iscsi, pfd.revents) < 0) { fprintf(stderr, "iscsi_service failed with : %s\n", iscsi_get_error(iscsi)); exit(10); } } } void logout_cb(struct iscsi_context *iscsi, int status, void *command_data _U_, void *private_data) { struct client_state *state = (struct client_state *)private_data; if (status != 0) { fprintf(stderr, "Failed to logout from target. : %s\n", iscsi_get_error(iscsi)); exit(10); } if (iscsi_disconnect(iscsi) != 0) { fprintf(stderr, "Failed to disconnect old socket\n"); exit(10); } state->finished = 1; } void read_cb(struct iscsi_context *iscsi, int status, void *command_data, void *private_data) { struct read16_state *r16_state = private_data; struct client_state *state = r16_state->client; struct scsi_task *task = command_data; printf("READ returned for LBA %d\n", (int)r16_state->lba); if (status == SCSI_STATUS_CHECK_CONDITION && task->sense.key == SCSI_SENSE_UNIT_ATTENTION) { printf("Received UNIT_ATTENTION. Ignoring.\n"); } else if (status != 0) { fprintf(stderr, "READ16 failed. %s\n", iscsi_get_error(iscsi)); scsi_free_scsi_task(task); exit(10); } if (state->read_pos == 6) { char buf[256]; ssize_t count; printf("write garbage to the socket to trigger a server " "disconnect\n"); memset(buf, 0, sizeof(buf)); /* If we do a full connect on an already connected context * we will update the iscsi->portal field but aotherwise * fail the command completely. * This should trigger the reconnect to fail to establish * a connection and thus lead to iscsi_which_events() * returning 0, signaling a 'wait and try again later'. */ printf("change portal to point to a closed socket\n"); iscsi_full_connect_async(iscsi, "127.0.0.1:1", state->lun, NULL, NULL); count = write(iscsi_get_fd(iscsi), buf, sizeof(buf)); if (count < (ssize_t)sizeof(buf)) { fprintf(stderr, "write failed.\n"); scsi_free_scsi_task(task); exit(10); } } free(r16_state); scsi_free_scsi_task(task); if (state->num_remaining > state->concurrency) { r16_state = malloc(sizeof(struct read16_state)); r16_state->lba = state->read_pos++; r16_state->client = state; printf("SENT READ for LBA %d\n", r16_state->lba); if (iscsi_read16_task(iscsi, state->lun, r16_state->lba, state->block_size, state->block_size, 0, 0, 0, 0, 0, read_cb, r16_state) == NULL) { fprintf(stderr, "iscsi_read16_task failed : %s\n", iscsi_get_error(iscsi)); exit(10); } } if (--state->num_remaining) { return; } if (iscsi_logout_async(iscsi, logout_cb, state) != 0) { fprintf(stderr, "iscsi_logout_async failed : %s\n", iscsi_get_error(iscsi)); exit(10); } } void print_usage(void) { fprintf(stderr, "Usage: prog_reconnect_timeout [-?|--help] [--usage] " "[-i|--initiator-name=iqn-name]\n" "\t\t\n"); fprintf(stderr, "\n"); fprintf(stderr, "This command is used to test that during reconnect," "if we fail to connect the TCP socket then " "iscsi_which_events() will return 0 to signal " "'no events right now, wait a while and try again'\n"); } void print_help(void) { fprintf(stderr, "Usage: prog_reconnect_timeout [OPTION...] \n"); fprintf(stderr, " -i, --initiator-name=iqn-name " "Initiatorname to use\n"); fprintf(stderr, "\n"); fprintf(stderr, "Help options:\n"); fprintf(stderr, " -?, --help " "Show this help message\n"); fprintf(stderr, " --usage " "Display brief usage message\n"); fprintf(stderr, "\n"); fprintf(stderr, "iSCSI Portal URL format : %s\n", ISCSI_PORTAL_URL_SYNTAX); fprintf(stderr, "\n"); fprintf(stderr, " is either of:\n"); fprintf(stderr, " \"hostname\" iscsi.example\n"); fprintf(stderr, " \"ipv4-address\" 10.1.1.27\n"); fprintf(stderr, " \"ipv6-address\" [fce0::1]\n"); } int main(int argc, char *argv[]) { struct iscsi_context *iscsi; struct iscsi_url *iscsi_url = NULL; struct client_state state; const char *url = NULL; int i, c; static int show_help = 0, show_usage = 0, debug = 0; struct scsi_readcapacity10 *rc10; struct scsi_task *task; static struct option long_options[] = { {"help", no_argument, NULL, 'h'}, {"usage", no_argument, NULL, 'u'}, {"debug", no_argument, NULL, 'd'}, {"initiator-name", required_argument, NULL, 'i'}, {0, 0, 0, 0} }; int option_index; while ((c = getopt_long(argc, argv, "h?uUdi:s", long_options, &option_index)) != -1) { switch (c) { case 'h': case '?': show_help = 1; break; case 'u': show_usage = 1; break; case 'd': debug = 1; break; case 'i': initiator = optarg; break; default: fprintf(stderr, "Unrecognized option '%c'\n\n", c); print_help(); exit(0); } } if (show_help != 0) { print_help(); exit(0); } if (show_usage != 0) { print_usage(); exit(0); } if (optind != argc -1) { print_usage(); exit(0); } memset(&state, 0, sizeof(state)); if (argv[optind] != NULL) { url = strdup(argv[optind]); } if (url == NULL) { fprintf(stderr, "You must specify iscsi target portal.\n"); print_usage(); exit(10); } iscsi = iscsi_create_context(initiator); if (iscsi == NULL) { printf("Failed to create context\n"); exit(10); } if (debug > 0) { iscsi_set_log_level(iscsi, debug); iscsi_set_log_fn(iscsi, iscsi_log_to_stderr); } iscsi_url = iscsi_parse_full_url(iscsi, url); if (url) { free(discard_const(url)); } if (iscsi_url == NULL) { fprintf(stderr, "Failed to parse URL: %s\n", iscsi_get_error(iscsi)); exit(10); } iscsi_set_session_type(iscsi, ISCSI_SESSION_NORMAL); state.lun = iscsi_url->lun; if (iscsi_full_connect_sync(iscsi, iscsi_url->portal, iscsi_url->lun) != 0) { fprintf(stderr, "iscsi_connect failed. %s\n", iscsi_get_error(iscsi)); exit(10); } task = iscsi_readcapacity10_sync(iscsi, iscsi_url->lun, 0, 0); if (task == NULL || task->status != SCSI_STATUS_GOOD) { fprintf(stderr, "failed to send readcapacity command\n"); exit(10); } rc10 = scsi_datain_unmarshall(task); if (rc10 == NULL) { fprintf(stderr, "failed to unmarshall readcapacity10 data\n"); exit(10); } state.block_size = rc10->block_size; state.num_lbas = rc10->lba; scsi_free_scsi_task(task); state.num_remaining = 10; state.concurrency = 3; strncpy(state.portal, iscsi_url->portal, MAX_STRING_SIZE); /* Queue up a bunch of READ16 calls and then send more are * the replies come trickling in. Once all num_remaining * reads have been processed we will log out and end the test. */ for (i = 0; i < state.concurrency; i++) { struct read16_state *r16_state; r16_state = malloc(sizeof(struct read16_state)); r16_state->lba = state.read_pos++; r16_state->client = &state; printf("SENT READ for LBA %d\n", r16_state->lba); if (iscsi_read16_task(iscsi, state.lun, r16_state->lba, state.block_size, state.block_size, 0, 0, 0, 0, 0, read_cb, r16_state) == NULL) { fprintf(stderr, "iscsi_read16_task failed : %s\n", iscsi_get_error(iscsi)); exit(10); } } event_loop(iscsi, &state); iscsi_destroy_url(iscsi_url); iscsi_destroy_context(iscsi); if (state.got_zero_events != 1) { fprintf(stderr, "iscsi_which_events() never returned 0\n"); exit(10); } return 0; } libiscsi-1.17.0/tests/prog_timeout.c000066400000000000000000000127451271371262700174450ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2015 by Ronnie Sahlberg 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, see . */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #ifdef HAVE_POLL_H #include #endif #ifdef HAVE_UNISTD_H #include #endif #include #include #include #include #include #include #include #include #include "iscsi.h" #include "iscsi-private.h" #include "scsi-lowlevel.h" #ifndef discard_const #define discard_const(ptr) ((void *)((intptr_t)(ptr))) #endif const char *initiator = "iqn.2007-10.com.github:sahlberg:libiscsi:prog-timeout"; void print_usage(void) { fprintf(stderr, "Usage: prog_timeout [-?|--help] [--usage] " "[-i|--initiator-name=iqn-name]\n" "\t\t\n"); fprintf(stderr, "\n"); fprintf(stderr, "This command is used to test that if we do not " "receive a responmse we recognize to a task that we will " "trigger the task timeout.\n"); } void print_help(void) { fprintf(stderr, "Usage: prog_timeout [OPTION...] \n"); fprintf(stderr, " -i, --initiator-name=iqn-name " "Initiatorname to use\n"); fprintf(stderr, "\n"); fprintf(stderr, "Help options:\n"); fprintf(stderr, " -?, --help " "Show this help message\n"); fprintf(stderr, " --usage " "Display brief usage message\n"); fprintf(stderr, "\n"); fprintf(stderr, "iSCSI Portal URL format : %s\n", ISCSI_PORTAL_URL_SYNTAX); fprintf(stderr, "\n"); fprintf(stderr, " is either of:\n"); fprintf(stderr, " \"hostname\" iscsi.example\n"); fprintf(stderr, " \"ipv4-address\" 10.1.1.27\n"); fprintf(stderr, " \"ipv6-address\" [fce0::1]\n"); } void tur_cb(struct iscsi_context *iscsi _U_, int status, void *command_data _U_, void *private_data) { uint32_t *i = private_data; (*i)--; printf("testunitready cb\n"); if (status != SCSI_STATUS_TIMEOUT) { printf("Failed. We did NOT get a TIMEOUT error for the SCSI " "task\n"); exit(10); } } void logout_cb(struct iscsi_context *iscsi _U_, int status, void *command_data _U_, void *private_data) { uint32_t *i = private_data; (*i)--; printf("logout command cb\n"); if (status != SCSI_STATUS_TIMEOUT) { printf("Failed. We did NOT get a TIMEOUT error for the iSCSI " "logout command\n"); exit(10); } } int main(int argc, char *argv[]) { struct iscsi_context *iscsi; struct iscsi_url *iscsi_url = NULL; const char *url = NULL; int c; static int show_help = 0, show_usage = 0, debug = 0; uint32_t count; static struct option long_options[] = { {"help", no_argument, NULL, 'h'}, {"usage", no_argument, NULL, 'u'}, {"debug", no_argument, NULL, 'd'}, {"initiator-name", required_argument, NULL, 'i'}, {0, 0, 0, 0} }; int option_index; while ((c = getopt_long(argc, argv, "h?uUdi:s", long_options, &option_index)) != -1) { switch (c) { case 'h': case '?': show_help = 1; break; case 'u': show_usage = 1; break; case 'd': debug = 1; break; case 'i': initiator = optarg; break; default: fprintf(stderr, "Unrecognized option '%c'\n\n", c); print_help(); exit(0); } } if (show_help != 0) { print_help(); exit(0); } if (show_usage != 0) { print_usage(); exit(0); } if (optind != argc -1) { print_usage(); exit(0); } if (argv[optind] != NULL) { url = strdup(argv[optind]); } if (url == NULL) { fprintf(stderr, "You must specify iscsi target portal.\n"); print_usage(); exit(10); } iscsi = iscsi_create_context(initiator); if (iscsi == NULL) { printf("Failed to create context\n"); exit(10); } if (debug > 0) { iscsi_set_log_level(iscsi, debug); iscsi_set_log_fn(iscsi, iscsi_log_to_stderr); } iscsi_url = iscsi_parse_full_url(iscsi, url); if (url) { free(discard_const(url)); } if (iscsi_url == NULL) { fprintf(stderr, "Failed to parse URL: %s\n", iscsi_get_error(iscsi)); exit(10); } iscsi_set_session_type(iscsi, ISCSI_SESSION_NORMAL); if (iscsi_full_connect_sync(iscsi, iscsi_url->portal, iscsi_url->lun) != 0) { fprintf(stderr, "iscsi_connect failed. %s\n", iscsi_get_error(iscsi)); exit(10); } iscsi_set_timeout(iscsi, 3); count=0; printf("Send a TUR we will never get a reply for\n"); count++; iscsi_testunitready_task(iscsi, iscsi_url->lun, tur_cb, &count); printf("Send a LOGOUT we will never get a reply for\n"); count++; iscsi_logout_async(iscsi, logout_cb, &count); printf("Spin on iscsi_service(iscsi, 0) until all callbacks are " "triggered\n"); while (count) { iscsi_service(iscsi, 0); sleep(1); } printf("yey, we got all timeouts we expected\n"); iscsi_destroy_url(iscsi_url); iscsi_destroy_context(iscsi); return 0; } libiscsi-1.17.0/tests/test_0100_login.sh000077500000000000000000000004001271371262700177130ustar00rootroot00000000000000#!/bin/sh . ./functions.sh echo "Login tests" start_target create_lun echo -n "Test logging in to target ... " ../utils/iscsi-inq -i ${IQNINITIATOR} iscsi://${TGTPORTAL}/${IQNTARGET}/1 > /dev/null || failure success shutdown_target delete_lun exit 0 libiscsi-1.17.0/tests/test_0200_discovery.sh000077500000000000000000000004531271371262700206230ustar00rootroot00000000000000#!/bin/sh . ./functions.sh echo "Discovery tests" start_target create_lun TEST_TMP=${0}.tmp echo -n "Test discovery ... " ../utils/iscsi-ls -i ${IQNINITIATOR} iscsi://${TGTPORTAL} > ${TEST_TMP} && grep ${IQNTARGET} ${TEST_TMP} > /dev/null || failure success shutdown_target delete_lun exit 0 libiscsi-1.17.0/tests/test_0300_nop_reply.sh000077500000000000000000000004741271371262700206270ustar00rootroot00000000000000#!/bin/sh . ./functions.sh echo "NOP reply tests" start_target "nop_interval=1,nop_count=3" create_lun echo -n "Test that we reply to target initiated NOPs correctly ... " ./prog_noop_reply -i ${IQNINITIATOR} iscsi://${TGTPORTAL}/${IQNTARGET}/1 > /dev/null || failure success shutdown_target delete_lun exit 0 libiscsi-1.17.0/tests/test_1000_chap.sh000077500000000000000000000041511271371262700175250ustar00rootroot00000000000000#!/bin/sh . ./functions.sh echo "CHAP tests" start_target create_lun setup_chap echo -n "Test logging in without credentials (should fail) ... " ../utils/iscsi-inq -i ${IQNINITIATOR} iscsi://${TGTPORTAL}/${IQNTARGET}/1 > /dev/null && failure success echo -n "Test logging in with invalid user (should fail) ... " LIBISCSI_CHAP_USERNAME=wrong \ LIBISCSI_CHAP_PASSWORD=libiscsi \ ../utils/iscsi-inq -i ${IQNINITIATOR} iscsi://${TGTPORTAL}/${IQNTARGET}/1 > /dev/null && failure success echo -n "Test logging in with wrong password (should fail) ... " LIBISCSI_CHAP_USERNAME=libiscsi \ LIBISCSI_CHAP_PASSWORD=wrong \ ../utils/iscsi-inq -i ${IQNINITIATOR} iscsi://${TGTPORTAL}/${IQNTARGET}/1 > /dev/null && failure success echo -n "Test logging in with correct credentials (ENV) ... " LIBISCSI_CHAP_USERNAME=libiscsi \ LIBISCSI_CHAP_PASSWORD=libiscsi \ ../utils/iscsi-inq -i ${IQNINITIATOR} iscsi://${TGTPORTAL}/${IQNTARGET}/1 > /dev/null || failure success echo -n "Test logging in with correct credentials (URL) ... " ../utils/iscsi-inq -i ${IQNINITIATOR} iscsi://libiscsi%libiscsi@${TGTPORTAL}/${IQNTARGET}/1 > /dev/null || failure success echo -n "Test bidir-chap with incorrect user (should fail) ... " LIBISCSI_CHAP_USERNAME=libiscsi \ LIBISCSI_CHAP_PASSWORD=libiscsi \ LIBISCSI_CHAP_TARGET_USERNAME=wrong \ LIBISCSI_CHAP_TARGET_PASSWORD=outgoing \ ../utils/iscsi-inq -i ${IQNINITIATOR} iscsi://${TGTPORTAL}/${IQNTARGET}/1 > /dev/null && failure success echo -n "Test bidir-chap with incorrect password (should fail) ... " LIBISCSI_CHAP_USERNAME=libiscsi \ LIBISCSI_CHAP_PASSWORD=libiscsi \ LIBISCSI_CHAP_TARGET_USERNAME=outgoing \ LIBISCSI_CHAP_TARGET_PASSWORD=wrong \ ../utils/iscsi-inq -i ${IQNINITIATOR} iscsi://${TGTPORTAL}/${IQNTARGET}/1 > /dev/null && failure success echo -n "Test bidir-chap with correct credentials we get from... " LIBISCSI_CHAP_USERNAME=libiscsi \ LIBISCSI_CHAP_PASSWORD=libiscsi \ LIBISCSI_CHAP_TARGET_USERNAME=outgoing \ LIBISCSI_CHAP_TARGET_PASSWORD=outgoing \ ../utils/iscsi-inq -i ${IQNINITIATOR} iscsi://${TGTPORTAL}/${IQNTARGET}/1 > /dev/null || failure success shutdown_target delete_lun exit 0 libiscsi-1.17.0/tests/test_2000_reconnect.sh000077500000000000000000000004401271371262700205700ustar00rootroot00000000000000#!/bin/sh . ./functions.sh echo "Basic Reconnect test" start_target create_lun echo -n "Test reading from the LUN when a reconnect happens ... " ./prog_reconnect -i ${IQNINITIATOR} iscsi://${TGTPORTAL}/${IQNTARGET}/1 > /dev/null || failure success shutdown_target delete_lun exit 0 libiscsi-1.17.0/tests/test_2001_reconnect_timeout.sh000077500000000000000000000004371271371262700223450ustar00rootroot00000000000000#!/bin/sh . ./functions.sh echo "Basic Reconnect test" start_target create_lun echo -n "Test iscsi_which_events return 0 on reconnect failure ... " ./prog_reconnect_timeout -i ${IQNINITIATOR} iscsi://${TGTPORTAL}/${IQNTARGET}/1 || failure success shutdown_target delete_lun exit 0 libiscsi-1.17.0/tests/test_2100_timeout.sh000077500000000000000000000004411271371262700203000ustar00rootroot00000000000000#!/bin/sh . ./functions.sh echo "Basic iSCSI Timeout test" start_target create_lun echo -n "Test that timeouts trigger when we get no reply to an iSCSI PDU..." ./prog_timeout -i ${IQNINITIATOR} iscsi://${TGTPORTAL}/${IQNTARGET}/1 || failure success shutdown_target delete_lun exit 0 libiscsi-1.17.0/utils/000077500000000000000000000000001271371262700145515ustar00rootroot00000000000000libiscsi-1.17.0/utils/Makefile.am000066400000000000000000000004001271371262700165770ustar00rootroot00000000000000AM_CPPFLAGS = -I${srcdir}/../include "-D_U_=__attribute__((unused))" \ "-D_R_(A,B)=__attribute__((format(printf,A,B)))" AM_CFLAGS = $(WARN_CFLAGS) LDADD = ../lib/libiscsi.la bin_PROGRAMS = iscsi-inq iscsi-ls iscsi-perf iscsi-readcapacity16 \ iscsi-swp libiscsi-1.17.0/utils/iscsi-inq.c000066400000000000000000000241171271371262700166210ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2010 by Ronnie Sahlberg 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, see . */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #ifdef HAVE_POLL_H #include #endif #include #include #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #ifndef discard_const #define discard_const(ptr) ((void *)((intptr_t)(ptr))) #endif const char *initiator = "iqn.2007-10.com.github:sahlberg:libiscsi:iscsi-inq"; void inquiry_block_limits(struct scsi_inquiry_block_limits *inq) { printf("wsnz:%d\n", inq->wsnz); printf("maximum compare and write length:%d\n", inq->max_cmp); printf("optimal transfer length granularity:%d\n", inq->opt_gran); printf("maximum transfer length:%d\n", inq->max_xfer_len); printf("optimal transfer length:%d\n",inq->opt_xfer_len); printf("maximum prefetch xdread xdwrite transfer length:%d\n", inq->max_prefetch); printf("maximum unmap lba count:%d\n", inq->max_unmap); printf("maximum unmap block descriptor count:%d\n", inq->max_unmap_bdc); printf("optimal unmap granularity:%d\n", inq->opt_unmap_gran); printf("ugavalid:%d\n", inq->ugavalid); printf("unmap granularity alignment:%d\n", inq->unmap_gran_align); printf("maximum write same length:%d\n", (int)inq->max_ws_len); } void inquiry_logical_block_provisioning(struct scsi_inquiry_logical_block_provisioning *inq) { printf("Threshold Exponent:%d\n", inq->threshold_exponent); printf("lbpu:%d\n", inq->lbpu); printf("lbpws:%d\n", inq->lbpws); printf("lbpws10:%d\n", inq->lbpws10); printf("lbprz:%d\n", inq->lbprz); printf("anc_sup:%d\n", inq->anc_sup); printf("dp:%d\n", inq->dp); printf("provisioning type:%d\n", inq->provisioning_type); } void inquiry_block_device_characteristics(struct scsi_inquiry_block_device_characteristics *inq) { printf("Medium Rotation Rate:%dRPM\n", inq->medium_rotation_rate); } void inquiry_device_identification(struct scsi_inquiry_device_identification *inq) { struct scsi_inquiry_device_designator *dev; int i; printf("Peripheral Qualifier:%s\n", scsi_devqualifier_to_str(inq->qualifier)); printf("Peripheral Device Type:%s\n", scsi_devtype_to_str(inq->device_type)); printf("Page Code:(0x%02x) %s\n", inq->pagecode, scsi_inquiry_pagecode_to_str(inq->pagecode)); for (i=0, dev = inq->designators; dev; i++, dev = dev->next) { printf("DEVICE DESIGNATOR #%d\n", i); if (dev->piv != 0) { printf("Device Protocol Identifier:(%d) %s\n", dev->protocol_identifier, scsi_protocol_identifier_to_str(dev->protocol_identifier)); } printf("Code Set:(%d) %s\n", dev->code_set, scsi_codeset_to_str(dev->code_set)); printf("PIV:%d\n", dev->piv); printf("Association:(%d) %s\n", dev->association, scsi_association_to_str(dev->association)); printf("Designator Type:(%d) %s\n", dev->designator_type, scsi_designator_type_to_str(dev->designator_type)); printf("Designator:[%s]\n", dev->designator); } } void inquiry_unit_serial_number(struct scsi_inquiry_unit_serial_number *inq) { printf("Unit Serial Number:[%s]\n", inq->usn); } void inquiry_supported_pages(struct scsi_inquiry_supported_pages *inq) { int i; for (i = 0; i < inq->num_pages; i++) { printf("Page:0x%02x %s\n", inq->pages[i], scsi_inquiry_pagecode_to_str(inq->pages[i])); } } void inquiry_standard(struct scsi_inquiry_standard *inq) { int i; printf("Peripheral Qualifier:%s\n", scsi_devqualifier_to_str(inq->qualifier)); printf("Peripheral Device Type:%s\n", scsi_devtype_to_str(inq->device_type)); printf("Removable:%d\n", inq->rmb); printf("Version:%d %s\n", inq->version, scsi_version_to_str(inq->version)); printf("NormACA:%d\n", inq->normaca); printf("HiSup:%d\n", inq->hisup); printf("ReponseDataFormat:%d\n", inq->response_data_format); printf("SCCS:%d\n", inq->sccs); printf("ACC:%d\n", inq->acc); printf("TPGS:%d\n", inq->tpgs); printf("3PC:%d\n", inq->threepc); printf("Protect:%d\n", inq->protect); printf("EncServ:%d\n", inq->encserv); printf("MultiP:%d\n", inq->multip); printf("SYNC:%d\n", inq->sync); printf("CmdQue:%d\n", inq->cmdque); printf("Vendor:%s\n", inq->vendor_identification); printf("Product:%s\n", inq->product_identification); printf("Revision:%s\n", inq->product_revision_level); for (i = 0; i < 8; i++) { if (inq->version_descriptor[i] == 0) { continue; } printf("Version Descriptor:%04x %s\n", inq->version_descriptor[i], scsi_version_descriptor_to_str( inq->version_descriptor[i])); } } void do_inquiry(struct iscsi_context *iscsi, int lun, int evpd, int pc) { struct scsi_task *task; int full_size; void *inq; /* See how big this inquiry data is */ task = iscsi_inquiry_sync(iscsi, lun, evpd, pc, 64); if (task == NULL || task->status != SCSI_STATUS_GOOD) { fprintf(stderr, "Inquiry command failed : %s\n", iscsi_get_error(iscsi)); exit(10); } full_size = scsi_datain_getfullsize(task); if (full_size > task->datain.size) { scsi_free_scsi_task(task); /* we need more data for the full list */ if ((task = iscsi_inquiry_sync(iscsi, lun, evpd, pc, full_size)) == NULL) { fprintf(stderr, "Inquiry command failed : %s\n", iscsi_get_error(iscsi)); exit(10); } } inq = scsi_datain_unmarshall(task); if (inq == NULL) { fprintf(stderr, "failed to unmarshall inquiry datain blob\n"); exit(10); } if (evpd == 0) { inquiry_standard(inq); } else { switch (pc) { case SCSI_INQUIRY_PAGECODE_SUPPORTED_VPD_PAGES: inquiry_supported_pages(inq); break; case SCSI_INQUIRY_PAGECODE_UNIT_SERIAL_NUMBER: inquiry_unit_serial_number(inq); break; case SCSI_INQUIRY_PAGECODE_DEVICE_IDENTIFICATION: inquiry_device_identification(inq); break; case SCSI_INQUIRY_PAGECODE_BLOCK_LIMITS: inquiry_block_limits(inq); break; case SCSI_INQUIRY_PAGECODE_BLOCK_DEVICE_CHARACTERISTICS: inquiry_block_device_characteristics(inq); break; case SCSI_INQUIRY_PAGECODE_LOGICAL_BLOCK_PROVISIONING: inquiry_logical_block_provisioning(inq); break; default: fprintf(stderr, "Usupported pagecode:0x%02x\n", pc); } } scsi_free_scsi_task(task); } void print_usage(void) { fprintf(stderr, "Usage: iscsi-inq [-?] [-?|--help] [--usage] [-i|--initiator-name=iqn-name]\n" "\t\t[-e|--evpd=integer] [-c|--pagecode=integer] \n"); } void print_help(void) { fprintf(stderr, "Usage: iscsi-inq [OPTION...] \n"); fprintf(stderr, " -i, --initiator-name=iqn-name Initiatorname to use\n"); fprintf(stderr, " -e, --evpd=integer evpd\n"); fprintf(stderr, " -c, --pagecode=integer page code\n"); fprintf(stderr, " -d, --debug=integer debug level (0=disabled)\n"); fprintf(stderr, "\n"); fprintf(stderr, "Help options:\n"); fprintf(stderr, " -?, --help Show this help message\n"); fprintf(stderr, " --usage Display brief usage message\n"); fprintf(stderr, "\n"); fprintf(stderr, "iSCSI URL format : %s\n", ISCSI_URL_SYNTAX); fprintf(stderr, "\n"); fprintf(stderr, " is either of:\n"); fprintf(stderr, " \"hostname\" iscsi.example\n"); fprintf(stderr, " \"ipv4-address\" 10.1.1.27\n"); fprintf(stderr, " \"ipv6-address\" [fce0::1]\n"); } int main(int argc, char *argv[]) { struct iscsi_context *iscsi; const char *url = NULL; struct iscsi_url *iscsi_url = NULL; int evpd = 0, pagecode = 0; int show_help = 0, show_usage = 0, debug = 0; int c; static struct option long_options[] = { {"help", no_argument, NULL, 'h'}, {"usage", no_argument, NULL, 'u'}, {"debug", no_argument, NULL, 'd'}, {"initiator-name", required_argument, NULL, 'i'}, {"evpd", required_argument, NULL, 'e'}, {"pagecode", required_argument, NULL, 'c'}, {0, 0, 0, 0} }; int option_index; while ((c = getopt_long(argc, argv, "h?udi:e:c:", long_options, &option_index)) != -1) { switch (c) { case 'h': case '?': show_help = 1; break; case 'u': show_usage = 1; break; case 'd': debug = 1; break; case 'i': initiator = optarg; break; case 'e': evpd = atoi(optarg); break; case 'c': pagecode = atoi(optarg); break; default: fprintf(stderr, "Unrecognized option '%c'\n\n", c); print_help(); exit(0); } } if (show_help != 0) { print_help(); exit(0); } if (show_usage != 0) { print_usage(); exit(0); } iscsi = iscsi_create_context(initiator); if (iscsi == NULL) { fprintf(stderr, "Failed to create context\n"); exit(10); } if (debug > 0) { iscsi_set_log_level(iscsi, debug); iscsi_set_log_fn(iscsi, iscsi_log_to_stderr); } if (argv[optind] != NULL) { url = strdup(argv[optind]); } if (url == NULL) { fprintf(stderr, "You must specify the URL\n"); print_usage(); exit(10); } iscsi_url = iscsi_parse_full_url(iscsi, url); if (url) { free(discard_const(url)); } if (iscsi_url == NULL) { fprintf(stderr, "Failed to parse URL: %s\n", iscsi_get_error(iscsi)); exit(10); } iscsi_set_session_type(iscsi, ISCSI_SESSION_NORMAL); iscsi_set_header_digest(iscsi, ISCSI_HEADER_DIGEST_NONE_CRC32C); if (iscsi_full_connect_sync(iscsi, iscsi_url->portal, iscsi_url->lun) != 0) { fprintf(stderr, "Login Failed. %s\n", iscsi_get_error(iscsi)); iscsi_destroy_url(iscsi_url); iscsi_destroy_context(iscsi); exit(10); } do_inquiry(iscsi, iscsi_url->lun, evpd, pagecode); iscsi_destroy_url(iscsi_url); iscsi_logout_sync(iscsi); iscsi_destroy_context(iscsi); return 0; } libiscsi-1.17.0/utils/iscsi-ls.c000066400000000000000000000266451271371262700164600ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2010 by Ronnie Sahlberg 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, see . */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #ifdef HAVE_POLL_H #include #endif #ifdef HAVE_UNISTD_H #include #endif #include #include #include #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #ifndef discard_const #define discard_const(ptr) ((void *)((intptr_t)(ptr))) #endif int showluns; int useurls; const char *initiator = "iqn.2007-10.com.github:sahlberg:libiscsi:iscsi-ls"; struct client_state { int finished; int status; int lun; int type; const char *username; const char *password; }; void event_loop(struct iscsi_context *iscsi, struct client_state *state) { struct pollfd pfd; while (state->finished == 0) { pfd.fd = iscsi_get_fd(iscsi); pfd.events = iscsi_which_events(iscsi); if (!pfd.events) { sleep(1); continue; } if (poll(&pfd, 1, -1) < 0) { fprintf(stderr, "Poll failed"); exit(10); } if (iscsi_service(iscsi, pfd.revents) < 0) { fprintf(stderr, "iscsi_service failed with : %s\n", iscsi_get_error(iscsi)); exit(10); } } } void show_lun(struct iscsi_context *iscsi, int lun) { struct scsi_task *task; struct scsi_inquiry_standard *inq; int type, no_media; long long size = 0; int size_pf = 0; static const char sf[] = {' ', 'k', 'M', 'G', 'T' }; /* check we can talk to the lun */ tur_try_again: if ((task = iscsi_testunitready_sync(iscsi, lun)) == NULL) { fprintf(stderr, "testunitready failed\n"); exit(10); } if (task->status == SCSI_STATUS_CHECK_CONDITION) { if (task->sense.key == SCSI_SENSE_UNIT_ATTENTION && task->sense.ascq == SCSI_SENSE_ASCQ_BUS_RESET) { scsi_free_scsi_task(task); goto tur_try_again; } } no_media = 0; if (task->status == SCSI_STATUS_CHECK_CONDITION && task->sense.key == SCSI_SENSE_NOT_READY && task->sense.ascq == SCSI_SENSE_ASCQ_MEDIUM_NOT_PRESENT) { /* not an error, just a cdrom without a disk most likely */ no_media = 1; } else if (task->status != SCSI_STATUS_GOOD) { fprintf(stderr, "TESTUNITREADY failed with %s\n", iscsi_get_error(iscsi)); exit(10); } scsi_free_scsi_task(task); /* check what type of lun we have */ task = iscsi_inquiry_sync(iscsi, lun, 0, 0, 64); if (task == NULL || task->status != SCSI_STATUS_GOOD) { fprintf(stderr, "failed to send inquiry command : %s\n", iscsi_get_error(iscsi)); exit(10); } inq = scsi_datain_unmarshall(task); if (inq == NULL) { fprintf(stderr, "failed to unmarshall inquiry datain blob\n"); exit(10); } type = inq->device_type; scsi_free_scsi_task(task); if (type == SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_DIRECT_ACCESS) { struct scsi_readcapacity10 *rc10; task = iscsi_readcapacity10_sync(iscsi, lun, 0, 0); if (task == NULL || task->status != SCSI_STATUS_GOOD) { fprintf(stderr, "failed to send readcapacity command\n"); exit(10); } rc10 = scsi_datain_unmarshall(task); if (rc10 == NULL) { fprintf(stderr, "failed to unmarshall readcapacity10 data\n"); exit(10); } size = rc10->block_size; size *= rc10->lba; for (size_pf=0; size_pf<4 && size > 1024; size_pf++) { size /= 1024; } scsi_free_scsi_task(task); } printf("Lun:%-4d Type:%s", lun, scsi_devtype_to_str(type)); if (type == SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_DIRECT_ACCESS) { printf(" (Size:%lld%c)", size, sf[size_pf]); } if (no_media) { printf(" (No media loaded)"); } printf("\n"); } void list_luns(struct client_state *clnt, const char *target, const char *portal) { struct iscsi_context *iscsi; struct scsi_task *task; struct scsi_reportluns_list *list; int full_report_size; int i; if (strncasecmp(portal, "[fe80:", 6) == 0) { fprintf(stderr, "skipping link-local address\n"); return; } iscsi = iscsi_create_context(initiator); if (iscsi == NULL) { printf("Failed to create context\n"); exit(10); } iscsi_set_initiator_username_pwd(iscsi, clnt->username, clnt->password); if (iscsi_set_targetname(iscsi, target)) { fprintf(stderr, "Failed to set target name\n"); exit(10); } iscsi_set_session_type(iscsi, ISCSI_SESSION_NORMAL); iscsi_set_header_digest(iscsi, ISCSI_HEADER_DIGEST_NONE_CRC32C); if (iscsi_full_connect_sync(iscsi, portal, -1) != 0) { printf("iscsi_connect failed. %s\n", iscsi_get_error(iscsi)); exit(10); } /* get initial reportluns data, all targets can report 16 bytes but some * fail if we ask for too much. */ if ((task = iscsi_reportluns_sync(iscsi, 0, 16)) == NULL) { fprintf(stderr, "reportluns failed : %s\n", iscsi_get_error(iscsi)); exit(10); } full_report_size = scsi_datain_getfullsize(task); if (full_report_size > task->datain.size) { scsi_free_scsi_task(task); /* we need more data for the full list */ if ((task = iscsi_reportluns_sync(iscsi, 0, full_report_size)) == NULL) { fprintf(stderr, "reportluns failed : %s\n", iscsi_get_error(iscsi)); exit(10); } } list = scsi_datain_unmarshall(task); if (list == NULL) { fprintf(stderr, "failed to unmarshall reportluns datain blob\n"); exit(10); } for (i=0; i < (int)list->num; i++) { show_lun(iscsi, list->luns[i]); } scsi_free_scsi_task(task); iscsi_destroy_context(iscsi); } void discoverylogout_cb(struct iscsi_context *iscsi, int status, void *command_data _U_, void *private_data) { struct client_state *state = (struct client_state *)private_data; if (status != 0) { fprintf(stderr, "Failed to logout from target. : %s\n", iscsi_get_error(iscsi)); exit(10); } if (iscsi_disconnect(iscsi) != 0) { fprintf(stderr, "Failed to disconnect old socket\n"); exit(10); } state->finished = 1; } void discovery_cb(struct iscsi_context *iscsi, int status, void *command_data, void *private_data) { struct iscsi_discovery_address *addr; if (status != 0) { fprintf(stderr, "Failed to do discovery on target. : %s\n", iscsi_get_error(iscsi)); exit(10); } for(addr=command_data; addr; addr=addr->next) { struct iscsi_target_portal *portal = addr->portals; while (portal != NULL) { if (useurls == 1 && showluns == 0) { printf("iscsi://%s/%s/0\n", portal->portal, addr->target_name); } else { printf("Target:%s Portal:%s\n", addr->target_name, portal->portal); } if (showluns != 0) { list_luns(private_data, addr->target_name, portal->portal); } portal = portal->next; } } if (iscsi_logout_async(iscsi, discoverylogout_cb, private_data) != 0) { fprintf(stderr, "iscsi_logout_async failed : %s\n", iscsi_get_error(iscsi)); exit(10); } } void discoverylogin_cb(struct iscsi_context *iscsi, int status, void *command_data _U_, void *private_data) { if (status != 0) { fprintf(stderr, "Login failed. %s\n", iscsi_get_error(iscsi)); exit(10); } if (iscsi_discovery_async(iscsi, discovery_cb, private_data) != 0) { fprintf(stderr, "failed to send discovery command : %s\n", iscsi_get_error(iscsi)); exit(10); } } void discoveryconnect_cb(struct iscsi_context *iscsi, int status, void *command_data _U_, void *private_data) { if (status != 0) { fprintf(stderr, "discoveryconnect_cb: connection failed : %s\n", iscsi_get_error(iscsi)); exit(10); } if (iscsi_login_async(iscsi, discoverylogin_cb, private_data) != 0) { fprintf(stderr, "iscsi_login_async failed : %s\n", iscsi_get_error(iscsi)); exit(10); } } void print_usage(void) { fprintf(stderr, "Usage: iscsi-ls [-?|--help] [--usage] [-i|--initiator-name=iqn-name]\n" "\t\t[-s|--show-luns] \n"); } void print_help(void) { fprintf(stderr, "Usage: iscsi-ls [OPTION...] \n"); fprintf(stderr, " -i, --initiator-name=iqn-name Initiatorname to use\n"); fprintf(stderr, " -s, --show-luns Show the luns for each target\n"); fprintf(stderr, " --url Output targets in URL format\n"); fprintf(stderr, " (does not work with -s)\n"); fprintf(stderr, "\n"); fprintf(stderr, "Help options:\n"); fprintf(stderr, " -?, --help Show this help message\n"); fprintf(stderr, " --usage Display brief usage message\n"); fprintf(stderr, "\n"); fprintf(stderr, "iSCSI Portal URL format : %s\n", ISCSI_PORTAL_URL_SYNTAX); fprintf(stderr, "\n"); fprintf(stderr, " is either of:\n"); fprintf(stderr, " \"hostname\" iscsi.example\n"); fprintf(stderr, " \"ipv4-address\" 10.1.1.27\n"); fprintf(stderr, " \"ipv6-address\" [fce0::1]\n"); } int main(int argc, char *argv[]) { struct iscsi_context *iscsi; struct iscsi_url *iscsi_url = NULL; struct client_state state; const char *url = NULL; int c; static int show_help = 0, show_usage = 0, debug = 0; static struct option long_options[] = { {"help", no_argument, NULL, 'h'}, {"usage", no_argument, NULL, 'u'}, {"debug", no_argument, NULL, 'd'}, {"show-luns", no_argument, NULL, 's'}, {"url", no_argument, NULL, 'U'}, {"initiator-name", required_argument, NULL, 'i'}, {0, 0, 0, 0} }; int option_index; while ((c = getopt_long(argc, argv, "h?uUdi:s", long_options, &option_index)) != -1) { switch (c) { case 'h': case '?': show_help = 1; break; case 'u': show_usage = 1; break; case 'U': useurls = 1; break; case 'd': debug = 1; break; case 'i': initiator = optarg; break; case 's': showluns = 1; break; default: fprintf(stderr, "Unrecognized option '%c'\n\n", c); print_help(); exit(0); } } if (show_help != 0) { print_help(); exit(0); } if (show_usage != 0) { print_usage(); exit(0); } if (optind != argc -1) { print_usage(); exit(0); } memset(&state, 0, sizeof(state)); if (argv[optind] != NULL) { url = strdup(argv[optind]); } if (url == NULL) { fprintf(stderr, "You must specify iscsi target portal.\n"); print_usage(); exit(10); } iscsi = iscsi_create_context(initiator); if (iscsi == NULL) { printf("Failed to create context\n"); exit(10); } if (debug > 0) { iscsi_set_log_level(iscsi, debug); iscsi_set_log_fn(iscsi, iscsi_log_to_stderr); } iscsi_url = iscsi_parse_portal_url(iscsi, url); if (url) { free(discard_const(url)); } if (iscsi_url == NULL) { fprintf(stderr, "Failed to parse URL: %s\n", iscsi_get_error(iscsi)); exit(10); } iscsi_set_session_type(iscsi, ISCSI_SESSION_DISCOVERY); state.username = iscsi_url->user; state.password = iscsi_url->passwd; if (iscsi_connect_async(iscsi, iscsi_url->portal, discoveryconnect_cb, &state) != 0) { fprintf(stderr, "iscsi_connect failed. %s\n", iscsi_get_error(iscsi)); exit(10); } event_loop(iscsi, &state); iscsi_destroy_url(iscsi_url); iscsi_destroy_context(iscsi); return 0; } libiscsi-1.17.0/utils/iscsi-perf.c000066400000000000000000000272021271371262700167640ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2014-2015 by Peter Lieven 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, see . */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #ifndef HAVE_CLOCK_GETTIME #include #endif #define PERF_VERSION "0.1" #define NOP_INTERVAL 5 #define MAX_NOP_FAILURES 3 const char *initiator = "iqn.2010-11.libiscsi:iscsi-perf"; int proc_alarm = 0; int max_in_flight = 32; int blocks_per_io = 8; uint64_t runtime = 0; uint64_t finished = 0; struct client { int finished; int in_flight; int random; int random_blocks; struct iscsi_context *iscsi; struct scsi_iovec perf_iov; int lun; int blocksize; uint64_t num_blocks; uint64_t pos; uint64_t last_ns; uint64_t first_ns; uint64_t iops; uint64_t last_iops; uint64_t bytes; uint64_t last_bytes; int ignore_errors; int max_reconnects; int busy_cnt; int err_cnt; int retry_cnt; }; uint64_t get_clock_ns(void) { int res; uint64_t ns; #ifdef HAVE_CLOCK_GETTIME struct timespec ts; res = clock_gettime (CLOCK_MONOTONIC, &ts); ns = ts.tv_sec * 1000000000ULL + ts.tv_nsec; #else struct timeval tv; res = gettimeofday(&tv, NULL); ns = tv.tv_sec * 1000000000ULL + tv.tv_usec * 1000; #endif if (res == -1) { fprintf(stderr,"could not get requested clock\n"); exit(10); } return ns; } void fill_read_queue(struct client *client); void progress(struct client *client) { uint64_t now = get_clock_ns(); if (now - client->last_ns < 1000000000ULL) return; uint64_t _runtime = (now - client->first_ns) / 1000000000ULL; if (runtime) _runtime = runtime - _runtime; printf ("\r"); uint64_t aiops = 1000000000.0 * (client->iops) / (now - client->first_ns); uint64_t ambps = 1000000000.0 * (client->bytes) / (now - client->first_ns); if (!_runtime) { finished = 1; printf ("iops average %" PRIu64 " (%" PRIu64 " MB/s) ", aiops, (aiops * blocks_per_io * client->blocksize) >> 20); } else { uint64_t iops = 1000000000ULL * (client->iops - client->last_iops) / (now - client->last_ns); uint64_t mbps = 1000000000ULL * (client->bytes - client->last_bytes) / (now - client->last_ns); printf ("%02" PRIu64 ":%02" PRIu64 ":%02" PRIu64 " - ", _runtime / 3600, (_runtime % 3600) / 60, _runtime % 60); printf ("lba %" PRIu64 ", iops current %" PRIu64 " (%" PRIu64 " MB/s), ", client->pos, iops, mbps >> 20); printf ("iops average %" PRIu64 " (%" PRIu64 " MB/s), in_flight %d, busy %d ", aiops, ambps >> 20, client->in_flight, client->busy_cnt); } fflush(stdout); client->last_ns = now; client->last_iops = client->iops; client->last_bytes = client->bytes; } void cb(struct iscsi_context *iscsi _U_, int status, void *command_data, void *private_data) { struct client *client = (struct client *)private_data; struct scsi_task *task = command_data, *task2 = NULL; struct scsi_read16_cdb *read16_cdb = NULL; read16_cdb = scsi_cdb_unmarshall(task, SCSI_OPCODE_READ16); if (read16_cdb == NULL) { fprintf(stderr, "Failed to unmarshall READ16 CDB.\n"); client->err_cnt++; goto out; } if (status == SCSI_STATUS_BUSY || (status == SCSI_STATUS_CHECK_CONDITION && task->sense.key == SCSI_SENSE_UNIT_ATTENTION)) { if (client->retry_cnt++ > 4 * max_in_flight) { fprintf(stderr, "maxium number of command retries reached...\n"); client->err_cnt++; goto out; } task2 = iscsi_read16_task(client->iscsi, client->lun, read16_cdb->lba, read16_cdb->transfer_length * client->blocksize, client->blocksize, 0, 0, 0, 0, 0, cb, client); if (task2 == NULL) { fprintf(stderr, "failed to send read16 command\n"); client->err_cnt++; } scsi_task_set_iov_in(task2, &client->perf_iov, 1); if (status == SCSI_STATUS_BUSY) { client->busy_cnt++; } } else if (status == SCSI_STATUS_CANCELLED) { client->err_cnt++; } else if (status == SCSI_STATUS_GOOD) { client->retry_cnt = 0; client->bytes += read16_cdb->transfer_length * client->blocksize; } else { fprintf(stderr, "Read16 failed with %s\n", iscsi_get_error(iscsi)); if (!client->ignore_errors) { client->err_cnt++; } } out: scsi_free_scsi_task(task); if (!client->err_cnt) { progress(client); client->iops++; client->in_flight--; fill_read_queue(client); } } void fill_read_queue(struct client *client) { int num_blocks; if (finished) return; if (client->pos >= client->num_blocks) client->pos = 0; while(client->in_flight < max_in_flight && client->pos < client->num_blocks) { struct scsi_task *task; client->in_flight++; if (client->random) { client->pos = rand() % client->num_blocks; } num_blocks = client->num_blocks - client->pos; if (num_blocks > blocks_per_io) { num_blocks = blocks_per_io; } if (client->random_blocks) { num_blocks = rand() % num_blocks + 1; } task = iscsi_read16_task(client->iscsi, client->lun, client->pos, num_blocks * client->blocksize, client->blocksize, 0, 0, 0, 0, 0, cb, client); if (task == NULL) { fprintf(stderr, "failed to send read16 command\n"); iscsi_destroy_context(client->iscsi); exit(10); } scsi_task_set_iov_in(task, &client->perf_iov, 1); client->pos += num_blocks; } } void usage(void) { fprintf(stderr,"Usage: iscsi-perf [-i ] [-m ] [-b blocks_per_request] [-t timeout] [-r|--random] [-n|--ignore-errors] [-x ] \n"); exit(1); } void sig_handler (int signum ) { if (signum == SIGALRM) { if (proc_alarm) { fprintf(stderr, "\n\nABORT: Last alarm was not processed.\n"); exit(10); } proc_alarm = 1; alarm(NOP_INTERVAL); } else { finished++; } } int main(int argc, char *argv[]) { char *url = NULL; struct iscsi_url *iscsi_url; struct scsi_task *task; struct scsi_readcapacity16 *rc16; int c; struct pollfd pfd[1]; struct client client; static struct option long_options[] = { {"initiator-name", required_argument, NULL, 'i'}, {"max", required_argument, NULL, 'm'}, {"blocks", required_argument, NULL, 'b'}, {"runtime", required_argument, NULL, 't'}, {"random", no_argument, NULL, 'r'}, {"random-blocks", no_argument, NULL, 'R'}, {"ignore-errors", no_argument, NULL, 'n'}, {0, 0, 0, 0} }; int option_index; memset(&client, 0, sizeof(client)); client.max_reconnects = -1; srand(time(NULL)); printf("iscsi-perf version %s - (c) 2014-2015 by Peter Lieven \n\n", PERF_VERSION); while ((c = getopt_long(argc, argv, "i:m:b:t:nrRx:", long_options, &option_index)) != -1) { switch (c) { case 'i': initiator = optarg; break; case 'm': max_in_flight = atoi(optarg); break; case 't': runtime = atoi(optarg); break; case 'b': blocks_per_io = atoi(optarg); break; case 'n': client.ignore_errors = 1; break; case 'r': client.random = 1; break; case 'R': client.random_blocks = 1; break; case 'x': client.max_reconnects = atoi(optarg); break; default: fprintf(stderr, "Unrecognized option '%c'\n\n", c); usage(); } } if (optind != argc -1 ) usage(); if (argv[optind] != NULL) { url = strdup(argv[optind]); } if (url == NULL) usage(); client.iscsi = iscsi_create_context(initiator); if (client.iscsi == NULL) { fprintf(stderr, "Failed to create context\n"); exit(10); } iscsi_url = iscsi_parse_full_url(client.iscsi, url); if (iscsi_url == NULL) { fprintf(stderr, "Failed to parse URL: %s\n", iscsi_get_error(client.iscsi)); exit(10); } iscsi_set_session_type(client.iscsi, ISCSI_SESSION_NORMAL); iscsi_set_header_digest(client.iscsi, ISCSI_HEADER_DIGEST_NONE_CRC32C); if (iscsi_full_connect_sync(client.iscsi, iscsi_url->portal, iscsi_url->lun) != 0) { fprintf(stderr, "Login Failed. %s\n", iscsi_get_error(client.iscsi)); iscsi_destroy_url(iscsi_url); iscsi_destroy_context(client.iscsi); exit(10); } printf("connected to %s\n", url); free(url); client.lun = iscsi_url->lun; iscsi_destroy_url(iscsi_url); task = iscsi_readcapacity16_sync(client.iscsi, client.lun); if (task == NULL || task->status != SCSI_STATUS_GOOD) { fprintf(stderr, "failed to send readcapacity command\n"); exit(10); } rc16 = scsi_datain_unmarshall(task); if (rc16 == NULL) { fprintf(stderr, "failed to unmarshall readcapacity16 data\n"); exit(10); } client.blocksize = rc16->block_length; client.num_blocks = rc16->returned_lba + 1; scsi_free_scsi_task(task); client.perf_iov.iov_base = malloc(blocks_per_io * client.blocksize); if (!client.perf_iov.iov_base) { fprintf(stderr, "Out of Memory\n"); exit(10); } client.perf_iov.iov_len = blocks_per_io * client.blocksize; printf("capacity is %" PRIu64 " blocks or %" PRIu64 " byte (%" PRIu64 " MB)\n", client.num_blocks, client.num_blocks * client.blocksize, (client.num_blocks * client.blocksize) >> 20); printf("performing %s READ with %d parallel requests\n", client.random ? "RANDOM" : "SEQUENTIAL", max_in_flight); if (client.random_blocks) { printf("RANDOM transfer size of 1 - %d blocks (%d - %d byte)\n", blocks_per_io, client.blocksize, blocks_per_io * client.blocksize); } else { printf("FIXED transfer size of %d blocks (%d byte)\n", blocks_per_io, blocks_per_io * client.blocksize); } if (runtime) { printf("will run for %" PRIu64 " seconds.\n", runtime); } else { printf("infinite runtime - press CTRL-C to abort.\n"); } struct sigaction sa; sa.sa_handler = &sig_handler; sa.sa_flags = SA_RESTART; sigemptyset(&sa.sa_mask); sigaction(SIGINT, &sa, NULL); sigaction(SIGTERM, &sa, NULL); sigaction(SIGALRM, &sa, NULL); printf("\n"); client.first_ns = client.last_ns = get_clock_ns(); iscsi_set_reconnect_max_retries(client.iscsi, client.max_reconnects); fill_read_queue(&client); alarm(NOP_INTERVAL); while (client.in_flight && !client.err_cnt && finished < 2) { pfd[0].fd = iscsi_get_fd(client.iscsi); pfd[0].events = iscsi_which_events(client.iscsi); if (proc_alarm) { if (iscsi_get_nops_in_flight(client.iscsi) > MAX_NOP_FAILURES) { iscsi_reconnect(client.iscsi); } else { iscsi_nop_out_async(client.iscsi, NULL, NULL, 0, NULL); } if (!iscsi_get_nops_in_flight(client.iscsi)) { finished = 0; } proc_alarm = 0; } if (!pfd[0].events) { sleep(1); continue; } if (poll(&pfd[0], 1, -1) < 0) { continue; } if (iscsi_service(client.iscsi, pfd[0].revents) < 0) { fprintf(stderr, "iscsi_service failed with : %s\n", iscsi_get_error(client.iscsi)); break; } } alarm(0); progress(&client); if (!client.err_cnt && finished < 2) { printf ("\n\nfinished.\n"); iscsi_logout_sync(client.iscsi); } else { printf ("\nABORTED!\n"); } iscsi_destroy_context(client.iscsi); free(client.perf_iov.iov_base); return client.err_cnt ? 1 : 0; } libiscsi-1.17.0/utils/iscsi-readcapacity16.c000066400000000000000000000132311271371262700206250ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2012 by Peter Lieven Copyright (C) 2010 by Ronnie Sahlberg 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, see . */ #include #include #include #include #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #ifndef discard_const #define discard_const(ptr) ((void *)((intptr_t)(ptr))) #endif const char *initiator = "iqn.2007-10.com.github:sahlberg:libiscsi:iscsi-readcapacity16"; void print_usage(void) { fprintf(stderr, "Usage: iscsi-readcapacity16 [-?] [-?|--help] [--usage] [-i|--initiator-name=iqn-name] [-s] \n"); } void print_help(void) { fprintf(stderr, "Usage: iscsi_readcapacity16 [OPTION...] \n"); fprintf(stderr, " -i, --initiator-name=iqn-name Initiatorname to use\n"); fprintf(stderr, " -s, --size print target size only\n"); fprintf(stderr, " -d, --debug=integer debug level (0=disabled)\n"); fprintf(stderr, "\n"); fprintf(stderr, "Help options:\n"); fprintf(stderr, " -?, --help Show this help message\n"); fprintf(stderr, " --usage Display brief usage message\n"); fprintf(stderr, "\n"); fprintf(stderr, "iSCSI URL format : %s\n", ISCSI_URL_SYNTAX); fprintf(stderr, "\n"); fprintf(stderr, " is either of:\n"); fprintf(stderr, " \"hostname\" iscsi.example\n"); fprintf(stderr, " \"ipv4-address\" 10.1.1.27\n"); fprintf(stderr, " \"ipv6-address\" [fce0::1]\n"); } int main(int argc, char *argv[]) { struct iscsi_context *iscsi; const char *url = NULL; struct iscsi_url *iscsi_url = NULL; int show_help = 0, show_usage = 0, debug = 0, size_only=0; int c; struct scsi_task *task; struct scsi_readcapacity16 *rc16; static struct option long_options[] = { {"help", no_argument, NULL, 'h'}, {"usage", no_argument, NULL, 'u'}, {"debug", no_argument, NULL, 'd'}, {"size", no_argument, NULL, 's'}, {"initiator-name", required_argument, NULL, 'i'}, {"evpd", required_argument, NULL, 'e'}, {"pagecode", required_argument, NULL, 'c'}, {0, 0, 0, 0} }; int option_index; while ((c = getopt_long(argc, argv, "h?udi:s", long_options, &option_index)) != -1) { switch (c) { case 'h': case '?': show_help = 1; break; case 'u': show_usage = 1; break; case 's': size_only = 1; break; case 'd': debug = 1; break; case 'i': initiator = optarg; break; default: fprintf(stderr, "Unrecognized option '%c'\n\n", c); print_help(); exit(0); } } if (show_help != 0) { print_help(); exit(0); } if (show_usage != 0) { print_usage(); exit(0); } iscsi = iscsi_create_context(initiator); if (iscsi == NULL) { fprintf(stderr, "Failed to create context\n"); exit(10); } if (debug > 0) { iscsi_set_log_fn(iscsi, iscsi_log_to_stderr); iscsi_set_log_level(iscsi, debug); } if (argv[optind] != NULL) { url = strdup(argv[optind]); } if (url == NULL) { fprintf(stderr, "You must specify the URL\n"); print_usage(); exit(10); } iscsi_url = iscsi_parse_full_url(iscsi, url); if (url) { free(discard_const(url)); } if (iscsi_url == NULL) { fprintf(stderr, "Failed to parse URL: %s\n", iscsi_get_error(iscsi)); exit(10); } iscsi_set_session_type(iscsi, ISCSI_SESSION_NORMAL); iscsi_set_header_digest(iscsi, ISCSI_HEADER_DIGEST_NONE_CRC32C); if (iscsi_full_connect_sync(iscsi, iscsi_url->portal, iscsi_url->lun) != 0) { fprintf(stderr, "Login Failed. %s\n", iscsi_get_error(iscsi)); iscsi_destroy_url(iscsi_url); iscsi_destroy_context(iscsi); exit(10); } task = iscsi_readcapacity16_sync(iscsi, iscsi_url->lun); if (task == NULL || task->status != SCSI_STATUS_GOOD) { fprintf(stderr,"failed to send readcapacity command\n"); iscsi_destroy_url(iscsi_url); iscsi_destroy_context(iscsi); exit(10); } rc16 = scsi_datain_unmarshall(task); if (rc16 == NULL) { fprintf(stderr,"failed to unmarshall readcapacity16 data\n"); scsi_free_scsi_task(task); iscsi_destroy_url(iscsi_url); iscsi_destroy_context(iscsi); exit(10); } if (!size_only) { printf("RETURNED LOGICAL BLOCK ADDRESS:%" PRIu64 "\n", rc16->returned_lba); printf("LOGICAL BLOCK LENGTH IN BYTES:%u\n", rc16->block_length); printf("P_TYPE:%d PROT_EN:%d\n", rc16->p_type, rc16->prot_en); printf("P_I_EXPONENT:%d LOGICAL BLOCKS PER PHYSICAL BLOCK EXPONENT:%d\n", rc16->p_i_exp, rc16->lbppbe); printf("LBPME:%d LBPRZ:%d\n", rc16->lbpme, rc16->lbprz); printf("LOWEST ALIGNED LOGICAL BLOCK ADDRESS:%d\n", rc16->lalba); printf("Total size:%" PRIu64 "\n", rc16->block_length * (rc16->returned_lba + 1)); } else { printf("%" PRIu64 "\n", rc16->block_length * (rc16->returned_lba + 1)); } scsi_free_scsi_task(task); iscsi_destroy_url(iscsi_url); iscsi_logout_sync(iscsi); iscsi_destroy_context(iscsi); return 0; } libiscsi-1.17.0/utils/iscsi-swp.c000066400000000000000000000142171271371262700166430ustar00rootroot00000000000000/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ /* Copyright (C) 2013 by Ronnie Sahlberg 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, see . */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #ifdef HAVE_POLL_H #include #endif #include #include #include #include #include #include "iscsi.h" #include "scsi-lowlevel.h" #ifndef discard_const #define discard_const(ptr) ((void *)((intptr_t)(ptr))) #endif const char *initiator = "iqn.2007-10.com.github:sahlberg:libiscsi:iscsi-swp"; void print_usage(void) { fprintf(stderr, "Usage: iscsi-swp [-?] [-?|--help] [--usage] [-i|--initiator-name=iqn-name]\n" "\t\t[-s|--swp {on|off}] \n"); } void print_help(void) { fprintf(stderr, "Usage: iscsi-swp [OPTION...] \n"); fprintf(stderr, " -i, --initiator-name=iqn-name Initiatorname to use\n"); fprintf(stderr, " -s, --swp={on|off} Turn software write protect on/off\n"); fprintf(stderr, " -d, --debug=integer debug level (0=disabled)\n"); fprintf(stderr, "\n"); fprintf(stderr, "Help options:\n"); fprintf(stderr, " -?, --help Show this help message\n"); fprintf(stderr, " --usage Display brief usage message\n"); fprintf(stderr, "\n"); fprintf(stderr, "iSCSI URL format : %s\n", ISCSI_URL_SYNTAX); fprintf(stderr, "\n"); fprintf(stderr, " is either of:\n"); fprintf(stderr, " \"hostname\" iscsi.example\n"); fprintf(stderr, " \"ipv4-address\" 10.1.1.27\n"); fprintf(stderr, " \"ipv6-address\" [fce0::1]\n"); } int main(int argc, char *argv[]) { struct iscsi_context *iscsi; const char *url = NULL; struct iscsi_url *iscsi_url = NULL; int show_help = 0, show_usage = 0, debug = 0; int c; int ret = 0; int swp = 0; struct scsi_task *sense_task = NULL; struct scsi_task *select_task = NULL; struct scsi_mode_sense *ms; struct scsi_mode_page *mp; static struct option long_options[] = { {"help", no_argument, NULL, 'h'}, {"usage", no_argument, NULL, 'u'}, {"debug", no_argument, NULL, 'd'}, {"initiator-name", required_argument, NULL, 'i'}, {"swp", required_argument, NULL, 's'}, {0, 0, 0, 0} }; int option_index; while ((c = getopt_long(argc, argv, "h?udi:s:", long_options, &option_index)) != -1) { switch (c) { case 'h': case '?': show_help = 1; break; case 'u': show_usage = 1; break; case 'd': debug = 1; break; case 'i': initiator = optarg; break; case 's': if (!strcmp(optarg, "on") || !strcmp(optarg, "ON")) { swp = 1; } if (!strcmp(optarg, "off") || !strcmp(optarg, "OFF")) { swp = 2; } break; default: fprintf(stderr, "Unrecognized option '%c'\n\n", c); print_help(); exit(0); } } if (show_help != 0) { print_help(); exit(0); } if (show_usage != 0) { print_usage(); exit(0); } iscsi = iscsi_create_context(initiator); if (iscsi == NULL) { fprintf(stderr, "Failed to create context\n"); exit(10); } if (debug > 0) { iscsi_set_log_level(iscsi, debug); iscsi_set_log_fn(iscsi, iscsi_log_to_stderr); } if (argv[optind] != NULL) { url = strdup(argv[optind]); } if (url == NULL) { fprintf(stderr, "You must specify the URL\n"); print_usage(); ret = 10; goto finished; } iscsi_url = iscsi_parse_full_url(iscsi, url); free(discard_const(url)); if (iscsi_url == NULL) { fprintf(stderr, "Failed to parse URL: %s\n", iscsi_get_error(iscsi)); ret = 10; goto finished; } iscsi_set_session_type(iscsi, ISCSI_SESSION_NORMAL); iscsi_set_header_digest(iscsi, ISCSI_HEADER_DIGEST_NONE_CRC32C); if (iscsi_full_connect_sync(iscsi, iscsi_url->portal, iscsi_url->lun) != 0) { fprintf(stderr, "Login Failed. %s\n", iscsi_get_error(iscsi)); ret = 10; goto finished; } sense_task = iscsi_modesense10_sync(iscsi, iscsi_url->lun, 0, 1, SCSI_MODESENSE_PC_CURRENT, SCSI_MODEPAGE_CONTROL, 0, 255); if (sense_task == NULL) { printf("Failed to send MODE_SENSE10 command: %s\n", iscsi_get_error(iscsi)); ret = 10; goto finished; } if (sense_task->status != SCSI_STATUS_GOOD) { printf("MODE_SENSE10 failed: %s\n", iscsi_get_error(iscsi)); ret = 10; goto finished; } ms = scsi_datain_unmarshall(sense_task); if (ms == NULL) { printf("failed to unmarshall mode sense datain blob\n"); ret = 10; goto finished; } mp = scsi_modesense_get_page(ms, SCSI_MODEPAGE_CONTROL, 0); if (mp == NULL) { printf("failed to read control mode page\n"); ret = 10; goto finished; } /* For MODE SELECT PS is reserved and hence must be cleared */ mp->ps = 0; printf("SWP:%d\n", mp->control.swp); switch (swp) { case 1: mp->control.swp = 1; break; case 2: mp->control.swp = 0; break; default: goto finished; } printf("Turning SWP %s\n", (swp == 1) ? "ON" : "OFF"); select_task = iscsi_modeselect10_sync(iscsi, iscsi_url->lun, 1, 0, mp); if (select_task == NULL) { printf("Failed to send MODE_SELECT10 command: %s\n", iscsi_get_error(iscsi)); ret = 10; goto finished; } if (select_task->status != SCSI_STATUS_GOOD) { printf("MODE_SELECT10 failed: %s\n", iscsi_get_error(iscsi)); ret = 10; goto finished; } finished: if (sense_task != NULL) { scsi_free_scsi_task(sense_task); } if (select_task != NULL) { scsi_free_scsi_task(select_task); } if (iscsi_url != NULL) { iscsi_destroy_url(iscsi_url); } iscsi_logout_sync(iscsi); iscsi_destroy_context(iscsi); return ret; } libiscsi-1.17.0/win32/000077500000000000000000000000001271371262700143535ustar00rootroot00000000000000libiscsi-1.17.0/win32/vsbuild.bat000066400000000000000000000050601271371262700165140ustar00rootroot00000000000000rem rem build script for win32 rem rem rem generate core part of library rem cl /I. /Iinclude -Zi -Od -c -D_U_="" -DWIN32 -D_WIN32_WINNT=0x0600 -MDd lib\connect.c -Folib\connect.obj cl /I. /Iinclude -Zi -Od -c -D_U_="" -DWIN32 -D_WIN32_WINNT=0x0600 -MDd lib\crc32c.c -Folib\crc32c.obj cl /I. /Iinclude -Zi -Od -c -D_U_="" -DWIN32 -D_WIN32_WINNT=0x0600 -MDd lib\discovery.c -Folib\discovery.obj cl /I. /Iinclude -Zi -Od -c -D_U_="" -DWIN32 -D_WIN32_WINNT=0x0600 -MDd lib\init.c -Folib\init.obj cl /I. /Iinclude -Zi -Od -c -D_U_="" -DWIN32 -D_WIN32_WINNT=0x0600 -MDd lib\login.c -Folib\login.obj cl /I. /Iinclude -Zi -Od -c -D_U_="" -DWIN32 -D_WIN32_WINNT=0x0600 -MDd lib\logging.c -Folib\logging.obj cl /I. /Iinclude -Zi -Od -c -D_U_="" -DWIN32 -D_WIN32_WINNT=0x0600 -MDd lib\md5.c -Folib\md5.obj cl /I. /Iinclude -Zi -Od -c -D_U_="" -DWIN32 -D_WIN32_WINNT=0x0600 -MDd lib\nop.c -Folib\nop.obj cl /I. /Iinclude -Zi -Od -c -D_U_="" -DWIN32 -D_WIN32_WINNT=0x0600 -MDd lib\pdu.c -Folib\pdu.obj cl /I. /Iinclude -Zi -Od -c -D_U_="" -DWIN32 -D_WIN32_WINNT=0x0600 -MDd lib\iscsi-command.c -Folib\iscsi-command.obj cl /I. /Iinclude -Zi -Od -c -D_U_="" -DWIN32 -D_WIN32_WINNT=0x0600 -MDd lib\scsi-lowlevel.c -Folib\scsi-lowlevel.obj cl /I. /Iinclude -Zi -Od -c -D_U_="" -DWIN32 -D_WIN32_WINNT=0x0600 -MDd lib\socket.c -Folib\socket.obj cl /I. /Iinclude -Zi -Od -c -D_U_="" -DWIN32 -D_WIN32_WINNT=0x0600 -MDd lib\sync.c -Folib\sync.obj cl /I. /Iinclude -Zi -Od -c -D_U_="" -DWIN32 -D_WIN32_WINNT=0x0600 -MDd lib\task_mgmt.c -Folib\task_mgmt.obj cl /I. /Iinclude -Zi -Od -c -D_U_="" -DWIN32 -D_WIN32_WINNT=0x0600 -MDd win32\win32_compat.c -Folib\win32_compat.obj rem rem create a linklibrary/dll rem lib /out:lib\libiscsi.lib /def:lib\libiscsi.def lib\connect.obj lib\crc32c.obj lib\discovery.obj lib\init.obj lib\login.obj lib\logging.obj lib\md5.obj lib\nop.obj lib\pdu.obj lib\iscsi-command.obj lib\scsi-lowlevel.obj lib\socket.obj lib\sync.obj lib\task_mgmt.obj lib\win32_compat.obj link /DLL /out:lib\libiscsi.dll /DEBUG /DEBUGTYPE:cv lib\libiscsi.exp lib\connect.obj lib\crc32c.obj lib\discovery.obj lib\init.obj lib\login.obj lib\logging.obj lib\md5.obj lib\nop.obj lib\pdu.obj lib\iscsi-command.obj lib\scsi-lowlevel.obj lib\socket.obj lib\sync.obj lib\task_mgmt.obj lib\win32_compat.obj ws2_32.lib kernel32.lib rem rem build a test application rem cl /I. /Iinclude -Zi -Od -DWIN32 -D_WIN32_WINNT=0x0600 -MDd -D_U_="" examples\iscsiclient.c lib\win32_compat.obj lib\libiscsi.lib WS2_32.lib kernel32.lib mswsock.lib advapi32.lib wsock32.lib advapi32.lib libiscsi-1.17.0/win32/win32_compat.c000066400000000000000000000121261271371262700170260ustar00rootroot00000000000000/* Copyright (c) 2006 by Dan Kennedy. Copyright (c) 2006 by Juliusz Chroboczek. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef WIN32 static int dummy ATTRIBUTE((unused)); #else #include "win32_compat.h" #include #include #include < time.h > #undef poll #undef socket #undef connect #undef accept #undef shutdown #undef getpeername #undef sleep #undef inet_aton #undef gettimeofday #undef stat #define assert(a) /* Windows needs this header file for the implementation of inet_aton() */ #include int win32_inet_pton(int af, const char * src, void * dst) { struct sockaddr_in sa; int len = sizeof(SOCKADDR); int ret = -1; int strLen = strlen(src) + 1; #ifdef UNICODE wchar_t *srcNonConst = (wchar_t *)malloc(strLen*sizeof(wchar_t)); memset(srcNonConst, 0, strLen); MultiByteToWideChar(CP_ACP, 0, src, -1, srcNonConst, strLen); #else char *srcNonConst = (char *)malloc(strLen); memset(srcNonConst, 0, strLen); strncpy(srcNonConst, src, strLen); #endif if( WSAStringToAddress(srcNonConst,af,NULL,(LPSOCKADDR)&sa,&len) == 0 ) { ret = 1; } else { if( WSAGetLastError() == WSAEINVAL ) { ret = -1; } } free(srcNonConst); memcpy(dst, &sa.sin_addr, sizeof(struct in_addr)); return ret; } int win32_poll(struct pollfd *fds, unsigned int nfds, int timo) { struct timeval timeout, *toptr; fd_set ifds, ofds, efds, *ip, *op; unsigned int i; int rc; // Set up the file-descriptor sets in ifds, ofds and efds. FD_ZERO(&ifds); FD_ZERO(&ofds); FD_ZERO(&efds); for (i = 0, op = ip = 0; i < nfds; ++i) { fds[i].revents = 0; if(fds[i].events & (POLLIN|POLLPRI)) { ip = &ifds; FD_SET(fds[i].fd, ip); } if(fds[i].events & POLLOUT) { op = &ofds; FD_SET(fds[i].fd, op); } FD_SET(fds[i].fd, &efds); } // Set up the timeval structure for the timeout parameter if(timo < 0) { toptr = 0; } else { toptr = &timeout; timeout.tv_sec = timo / 1000; timeout.tv_usec = (timo - timeout.tv_sec * 1000) * 1000; } #ifdef DEBUG_POLL printf("Entering select() sec=%ld usec=%ld ip=%lx op=%lx\n", (long)timeout.tv_sec, (long)timeout.tv_usec, (long)ip, (long)op); #endif rc = select(0, ip, op, &efds, toptr); #ifdef DEBUG_POLL printf("Exiting select rc=%d\n", rc); #endif if(rc <= 0) return rc; if(rc > 0) { for (i = 0; i < nfds; ++i) { int fd = fds[i].fd; if(fds[i].events & (POLLIN|POLLPRI) && FD_ISSET(fd, &ifds)) fds[i].revents |= POLLIN; if(fds[i].events & POLLOUT && FD_ISSET(fd, &ofds)) fds[i].revents |= POLLOUT; if(FD_ISSET(fd, &efds)) // Some error was detected ... should be some way to know. fds[i].revents |= POLLHUP; #ifdef DEBUG_POLL printf("%d %d %d revent = %x\n", FD_ISSET(fd, &ifds), FD_ISSET(fd, &ofds), FD_ISSET(fd, &efds), fds[i].revents ); #endif } } return rc; } #if defined(_MSC_VER) || defined(_MSC_EXTENSIONS) #define DELTA_EPOCH_IN_MICROSECS 11644473600000000Ui64 #else #define DELTA_EPOCH_IN_MICROSECS 11644473600000000ULL #endif struct timezone { int tz_minuteswest; /* minutes W of Greenwich */ int tz_dsttime; /* type of dst correction */ }; int win32_gettimeofday(struct timeval *tv, struct timezone *tz) { FILETIME ft; unsigned __int64 tmpres = 0; static int tzflag; if (NULL != tv) { GetSystemTimeAsFileTime(&ft); tmpres |= ft.dwHighDateTime; tmpres <<= 32; tmpres |= ft.dwLowDateTime; /*converting file time to unix epoch*/ tmpres -= DELTA_EPOCH_IN_MICROSECS; tmpres /= 10; /*convert into microseconds*/ tv->tv_sec = (long)(tmpres / 1000000UL); tv->tv_usec = (long)(tmpres % 1000000UL); } if (NULL != tz) { if (!tzflag) { _tzset(); tzflag++; } tz->tz_minuteswest = _timezone / 60; tz->tz_dsttime = _daylight; } return 0; } ssize_t win32_readv(int fd, const struct iovec *iov, int iovcnt) { return read(fd, iov[0].iov_base, iov[0].iov_len); } ssize_t win32_writev(int fd, const struct iovec *iov, int iovcnt) { return write(fd, iov[0].iov_base, iov[0].iov_len); } #endif libiscsi-1.17.0/win32/win32_compat.h000066400000000000000000000041241271371262700170320ustar00rootroot00000000000000/* Copyright (c) 2006 by Dan Kennedy. Copyright (c) 2006 by Juliusz Chroboczek. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*Adaptions by memphiz@xbmc.org*/ #ifdef WIN32 #ifndef win32_COMPAT_H_ #define win32_COMPAT_H_ #define NO_IPv6 1 #include #include #include #include #include #include #include #define SOL_TCP IPPROTO_TCP typedef int ssize_t; typedef int uid_t; typedef int gid_t; typedef int socklen_t; /* Wrapper macros to call misc. functions win32 is missing */ #define writev win32_writev #define readv win32_readv #define poll(x, y, z) win32_poll(x, y, z) #define inet_pton(x,y,z) win32_inet_pton(x,y,z) #define sleep(x) Sleep(x * 1000) #define snprintf(a, b, c, ...) _snprintf_s(a, b, b, c, ## __VA_ARGS__) int win32_inet_pton(int af, const char * src, void * dst); int win32_poll(struct pollfd *fds, unsigned int nfsd, int timeout); int win32_gettimeofday(struct timeval *tv, struct timezone *tz); struct iovec { void *iov_base; size_t iov_len; }; #define inline #endif//win32_COMPAT_H_ #endif//WIN32